hwmon updates for v6.7-rc1

* New drivers
 
 - Driver for LTC2991
 
 - Driver for POWER-Z
 
 * Added chip / system support to existing drivers
 
 - The ina238 driver now also supports INA237
 
 - The asus-ec-sensors driver now supports ROG Crosshair X670E Gene
 
 - The aquacomputer_d5next now supports Aquacomputer High Flow USB and MPS Flow
 
 - The pmbus/mpq7932 driver now also supports MPQ2286
 
 - The nct6683 now also supports ASRock X670E Taichi
 
 * Various other minor improvements and fixes
 
 - One patch series to call out is the conversion of hwmon platform
   drivers to use the platform remove callback returning void
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmU/vmUACgkQyx8mb86f
 mYEgMw/+LPsHi/uo6y4xfScVChU4zeIY8mhuCwxQXLnVH2tjkwovZ4MwPMV4sFjb
 Mwr/IwJKBa8AkelL8xkQy9dMiCsFs9q4CB2ZCY94A41O6Mwra/ILT2YFsk/CIKOw
 bZgEViVpeJSjcKGcdzrHkoEC/WuLSR9iul0P+ZC88h1ixUV21+EoJUfGo/a5RC4n
 xS1WgbJyFxx+S+VjWDHigJRcGOuGlg1j+25nWjcLr59Qv3COLF3CF9V7w4DM7PAH
 zVBO1UCUSLyVjtPZruLEs2yxfYre+apBomqCenBdYOwBJulCLvLm3SnLPo8Ee4rk
 9TBiEpqALxvjRnbxfk8VzI7oSLbH5zwOko9PAH7e9sXrhEARXhPWI+8lkXqE8wrc
 3I8uv115XOp/Yfo29xF+A4xLJVNRhU6MLieN7afY3YuDipND1gtSAoh8qyqJunf7
 dwXDowIlWF+SUmwsxWXxgpKJYVeYzAJYheNKvaIapnCmB/oPKs2dHcwJAqp/voDs
 4odnxkhuBBgKpGqqj5C9RKcKdOsku8O9PYBnTMb2IOsimRQg20QTnKn/esfMdK1U
 gJBQf/Motg3o3cw5gR/YKb7LdRuxAtxVgrbpwqN9gS/ppCmekgK+o3+4feTN+WAg
 746rdIDO88gQIDRuRu+VZOSmXrSOMUFqgLvouyUfwnS6sXdy8Vo=
 =8mxv
 -----END PGP SIGNATURE-----

Merge tag 'hwmon-for-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Driver for LTC2991

   - Driver for POWER-Z

  Added chip / system support to existing drivers:

   - The ina238 driver now also supports INA237

   - The asus-ec-sensors driver now supports ROG Crosshair X670E Gene

   - The aquacomputer_d5next now supports Aquacomputer High Flow USB and
     MPS Flow

   - The pmbus/mpq7932 driver now also supports MPQ2286

   - The nct6683 now also supports ASRock X670E Taichi

  Various other minor improvements and fixes:

   - One patch series to call out is the conversion of hwmon platform
     drivers to use the platform remove callback returning void"

* tag 'hwmon-for-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (69 commits)
  hwmon: (aquacomputer_d5next) Check if temp sensors of legacy devices are connected
  hwmon: (aquacomputer_d5next) Add support for Aquacomputer High Flow USB and MPS Flow
  dt-bindings: hwmon: npcm: Add npcm845 compatible string
  hwmon: Add driver for ltc2991
  dt-bindings: hwmon: ltc2991: add bindings
  hwmon: (pmbus/max31785) Add delay between bus accesses
  hwmon: (ina238) add ina237 support
  dt-bindings: hwmon: ti,ina2xx: add ti,ina237
  hwmon: (asus-ec-sensors) add ROG Crosshair X670E Gene.
  hwmon: (max31827) handle vref regulator
  hwmon: (ina3221) Add support for channel summation disable
  dt-bindings: hwmon: ina3221: Add ti,summation-disable
  dt-bindings: hwmon: ina3221: Convert to json-schema
  hwmon: (pmbus/mpq7932) Add a support for mpq2286 Power Management IC
  hwmon: (pmbus/core) Add helper macro to define single pmbus regulator
  regulator: dt-bindings: Add mps,mpq2286 power-management IC
  hwmon: (pmbus/mpq7932) Get page count based on chip info
  dt-bindings: hwmon: Add possible new properties to max31827 bindings
  hwmon: (max31827) Modify conversion wait time
  hwmon: (max31827) Make code cleaner
  ...
This commit is contained in:
Linus Torvalds 2023-10-31 17:44:17 -10:00
commit f9a7eda4d7
69 changed files with 2292 additions and 456 deletions

View File

@ -0,0 +1,128 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/adi,ltc2991.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices LTC2991 Octal I2C Voltage, Current and Temperature Monitor
maintainers:
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The LTC2991 is used to monitor system temperatures, voltages and currents.
Through the I2C serial interface, the eight monitors can individually measure
supply voltages and can be paired for differential measurements of current
sense resistors or temperature sensing transistors.
Datasheet:
https://www.analog.com/en/products/ltc2991.html
properties:
compatible:
const: adi,ltc2991
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
vcc-supply: true
patternProperties:
"^channel@[0-3]$":
type: object
description:
Represents the differential/temperature channels.
properties:
reg:
description:
The channel number. LTC2991 can monitor 4 currents/temperatures.
items:
minimum: 0
maximum: 3
shunt-resistor-micro-ohms:
description:
The value of curent sense resistor in micro ohms. Pin configuration is
set for differential input pair.
adi,temperature-enable:
description:
Enables temperature readings. Pin configuration is set for remote
diode temperature measurement.
type: boolean
required:
- reg
allOf:
- if:
required:
- shunt-resistor-micro-ohms
then:
properties:
adi,temperature-enable: false
additionalProperties: false
required:
- compatible
- reg
- vcc-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
hwmon@48 {
compatible = "adi,ltc2991";
reg = <0x48>;
vcc-supply = <&vcc>;
};
};
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
hwmon@48 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "adi,ltc2991";
reg = <0x48>;
vcc-supply = <&vcc>;
channel@0 {
reg = <0x0>;
shunt-resistor-micro-ohms = <100000>;
};
channel@1 {
reg = <0x1>;
shunt-resistor-micro-ohms = <100000>;
};
channel@2 {
reg = <0x2>;
adi,temperature-enable;
};
channel@3 {
reg = <0x3>;
adi,temperature-enable;
};
};
};
...

View File

@ -32,6 +32,68 @@ properties:
Must have values in the interval (1.6V; 3.6V) in order for the device to
function correctly.
adi,comp-int:
description:
If present interrupt mode is used. If not present comparator mode is used
(default).
type: boolean
adi,alarm-pol:
description:
Sets the alarms active state.
- 0 = active low
- 1 = active high
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
adi,fault-q:
description:
Select how many consecutive temperature faults must occur before
overtemperature or undertemperature faults are indicated in the
corresponding status bits.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 4, 8]
adi,timeout-enable:
description:
Enables timeout. Bus timeout resets the I2C-compatible interface when SCL
is low for more than 30ms (nominal).
type: boolean
allOf:
- if:
properties:
compatible:
contains:
const: adi,max31829
then:
properties:
adi,alarm-pol:
default: 1
else:
properties:
adi,alarm-pol:
default: 0
- if:
properties:
compatible:
contains:
const: adi,max31827
then:
properties:
adi,fault-q:
default: 1
else:
properties:
adi,fault-q:
default: 4
required:
- compatible
- reg
@ -49,6 +111,10 @@ examples:
compatible = "adi,max31827";
reg = <0x42>;
vref-supply = <&reg_vdd>;
adi,comp-int;
adi,alarm-pol = <0>;
adi,fault-q = <1>;
adi,timeout-enable;
};
};
...

View File

@ -1,54 +0,0 @@
Texas Instruments INA3221 Device Tree Bindings
1) ina3221 node
Required properties:
- compatible: Must be "ti,ina3221"
- reg: I2C address
Optional properties:
- ti,single-shot: This chip has two power modes: single-shot (chip takes one
measurement and then shuts itself down) and continuous (
chip takes continuous measurements). The continuous mode is
more reliable and suitable for hardware monitor type device,
but the single-shot mode is more power-friendly and useful
for battery-powered device which cares power consumptions
while still needs some measurements occasionally.
If this property is present, the single-shot mode will be
used, instead of the default continuous one for monitoring.
= The node contains optional child nodes for three channels =
= Each child node describes the information of input source =
- #address-cells: Required only if a child node is present. Must be 1.
- #size-cells: Required only if a child node is present. Must be 0.
2) child nodes
Required properties:
- reg: Must be 0, 1 or 2, corresponding to IN1, IN2 or IN3 port of INA3221
Optional properties:
- label: Name of the input source
- shunt-resistor-micro-ohms: Shunt resistor value in micro-Ohm
Example:
ina3221@40 {
compatible = "ti,ina3221";
reg = <0x40>;
#address-cells = <1>;
#size-cells = <0>;
input@0 {
reg = <0x0>;
status = "disabled";
};
input@1 {
reg = <0x1>;
shunt-resistor-micro-ohms = <5000>;
};
input@2 {
reg = <0x2>;
label = "VDD_5V";
shunt-resistor-micro-ohms = <5000>;
};
};

View File

@ -1,12 +1,16 @@
Nuvoton NPCM7xx PWM and Fan Tacho controller device
Nuvoton NPCM PWM and Fan Tacho controller device
The Nuvoton BMC NPCM7XX supports 8 Pulse-width modulation (PWM)
controller outputs and 16 Fan tachometer controller inputs.
The Nuvoton BMC NPCM8XX supports 12 Pulse-width modulation (PWM)
controller outputs and 16 Fan tachometer controller inputs.
Required properties for pwm-fan node
- #address-cells : should be 1.
- #size-cells : should be 0.
- compatible : "nuvoton,npcm750-pwm-fan" for Poleg NPCM7XX.
: "nuvoton,npcm845-pwm-fan" for Arbel NPCM8XX.
- reg : specifies physical base address and size of the registers.
- reg-names : must contain:
* "pwm" for the PWM registers.

View File

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/infineon,tda38640.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Infineon TDA38640 Synchronous Buck Regulator with SVID and I2C
maintainers:
- Naresh Solanki <naresh.solanki@9elements.com>
description: |
The Infineon TDA38640 is a 40A Single-voltage Synchronous Buck
Regulator with SVID and I2C designed for Industrial use.
Datasheet: https://www.infineon.com/dgdl/Infineon-TDA38640-0000-DataSheet-v02_04-EN.pdf?fileId=8ac78c8c80027ecd018042f2337f00c9
properties:
compatible:
enum:
- infineon,tda38640
reg:
maxItems: 1
infineon,en-pin-fixed-level:
description:
Indicates that the chip EN pin is at fixed level or left
unconnected(has internal pull-down).
type: boolean
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
tda38640@40 {
compatible = "infineon,tda38640";
reg = <0x40>;
};
};

View File

@ -26,6 +26,7 @@ properties:
- ti,ina226
- ti,ina230
- ti,ina231
- ti,ina237
- ti,ina238
reg:

View File

@ -0,0 +1,121 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,ina3221.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments INA3221 Current and Voltage Monitor
maintainers:
- Jean Delvare <jdelvare@suse.com>
- Guenter Roeck <linux@roeck-us.net>
properties:
compatible:
const: ti,ina3221
reg:
maxItems: 1
ti,single-shot:
description: |
This chip has two power modes: single-shot (chip takes one measurement
and then shuts itself down) and continuous (chip takes continuous
measurements). The continuous mode is more reliable and suitable for
hardware monitor type device, but the single-shot mode is more power-
friendly and useful for battery-powered device which cares power
consumptions while still needs some measurements occasionally.
If this property is present, the single-shot mode will be used, instead
of the default continuous one for monitoring.
$ref: /schemas/types.yaml#/definitions/flag
"#address-cells":
description: Required only if a child node is present.
const: 1
"#size-cells":
description: Required only if a child node is present.
const: 0
patternProperties:
"^input@[0-2]$":
description: The node contains optional child nodes for three channels.
Each child node describes the information of input source. Input channels
default to enabled in the chip. Unless channels are explicitly disabled
in device-tree, input channels will be enabled.
type: object
additionalProperties: false
properties:
reg:
description: Must be 0, 1 and 2, corresponding to the IN1, IN2 or IN3
ports of the INA3221, respectively.
enum: [ 0, 1, 2 ]
label:
description: name of the input source
shunt-resistor-micro-ohms:
description: shunt resistor value in micro-Ohm
ti,summation-disable:
description: |
The INA3221 has a critical alert pin that can be controlled by the
summation control function. This function adds the single
shunt-voltage conversions for the desired channels in order to
compare the combined sum to the programmed limit. The Shunt-Voltage
Sum Limit register contains the programmed value that is compared
to the value in the Shunt-Voltage Sum register in order to
determine if the total summed limit is exceeded. If the
shunt-voltage sum limit value is exceeded, the critical alert pin
is asserted.
For the summation limit to have a meaningful value, it is necessary
to use the same shunt-resistor value on all enabled channels. If
this is not the case or if a channel should not be used for
triggering the critical alert pin, then this property can be used
exclude specific channels from the summation control function.
type: boolean
required:
- reg
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
power-sensor@40 {
compatible = "ti,ina3221";
reg = <0x40>;
#address-cells = <1>;
#size-cells = <0>;
input@0 {
reg = <0x0>;
/*
* Input channels are enabled by default in the device and so
* to disable, must be explicitly disabled in device-tree.
*/
status = "disabled";
};
input@1 {
reg = <0x1>;
shunt-resistor-micro-ohms = <5000>;
};
input@2 {
reg = <0x2>;
label = "VDD_5V";
shunt-resistor-micro-ohms = <5000>;
};
};
};

View File

@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/mps,mpq2286.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Monolithic Power System MPQ2286 PMIC
maintainers:
- Saravanan Sekar <saravanan@linumiz.com>
properties:
compatible:
enum:
- mps,mpq2286
reg:
maxItems: 1
regulators:
type: object
properties:
buck:
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@3 {
compatible = "mps,mpq2286";
reg = <0x3>;
regulators {
buck {
regulator-name = "buck";
regulator-min-microvolt = <1600000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
};
};
};
};
...

View File

@ -151,8 +151,6 @@ properties:
- infineon,slb9645tt
# Infineon SLB9673 I2C TPM 2.0
- infineon,slb9673
# Infineon TDA38640 Voltage Regulator
- infineon,tda38640
# Infineon TLV493D-A1B6 I2C 3D Magnetic Sensor
- infineon,tlv493d-a1b6
# Infineon Multi-phase Digital VR Controller xdpe11280

View File

@ -90,7 +90,7 @@ ADT7476:
ADT7490:
* 6 voltage inputs
* 1 Imon input (not implemented)
* 1 Imon input
* PECI support (not implemented)
* 2 GPIO pins (not implemented)
* system acoustics optimizations (not implemented)
@ -107,6 +107,7 @@ in2 VCC (4) VCC (4) VCC (4) VCC (3)
in3 5VIN (20) 5VIN (20)
in4 12VIN (21) 12VIN (21)
in5 VTT (8)
in6 Imon (19)
==== =========== =========== ========= ==========
Special Features

View File

@ -16,6 +16,8 @@ Supported devices:
* Aquacomputer Aquastream XT watercooling pump
* Aquacomputer Aquastream Ultimate watercooling pump
* Aquacomputer Poweradjust 3 fan controller
* Aquacomputer High Flow USB flow meter
* Aquacomputer MPS Flow devices
Author: Aleksa Savic
@ -73,6 +75,11 @@ It also exposes pressure and flow speed readings.
The Poweradjust 3 controller exposes a single external temperature sensor.
The High Flow USB exposes an internal and external temperature sensor, and a flow meter.
The MPS Flow devices expose the same entries as the High Flow USB because they have
the same USB product ID and report sensors equivalently.
Depending on the device, not all sysfs and debugfs entries will be available.
Writing to virtual temperature sensors is not currently supported.

View File

@ -15,6 +15,7 @@ Supported boards:
* ROG CROSSHAIR VIII HERO
* ROG CROSSHAIR VIII IMPACT
* ROG CROSSHAIR X670E HERO
* ROG CROSSHAIR X670E GENE
* ROG MAXIMUS XI HERO
* ROG MAXIMUS XI HERO (WI-FI)
* ROG STRIX B550-E GAMING

View File

@ -121,6 +121,7 @@ Hardware Monitoring Kernel Drivers
ltc2947
ltc2978
ltc2990
ltc2991
ltc3815
ltc4151
ltc4215
@ -178,6 +179,7 @@ Hardware Monitoring Kernel Drivers
peci-cputemp
peci-dimmtemp
pmbus
powerz
powr1220
pxe1610
pwm-fan

View File

@ -0,0 +1,43 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver ltc2991
=====================
Supported chips:
* Analog Devices LTC2991
Prefix: 'ltc2991'
Addresses scanned: I2C 0x48 - 0x4f
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2991ff.pdf
Authors:
- Antoniu Miclaus <antoniu.miclaus@analog.com>
Description
-----------
This driver supports hardware monitoring for Analog Devices LTC2991 Octal I2C
Voltage, Current and Temperature Monitor.
The LTC2991 is used to monitor system temperatures, voltages and currents.
Through the I2C serial interface, the eight monitors can individually measure
supply voltages and can be paired for differential measurements of current sense
resistors or temperature sensing transistors. Additional measurements include
internal temperatureand internal VCC.
sysfs-Interface
---------------
The following attributes are supported. Limits are read-only.
=============== =================
inX_input: voltage input
currX_input: current input
tempX_input: temperature input
=============== =================

View File

@ -73,8 +73,8 @@ the conversion frequency to 1 conv/s. The conversion time varies depending on
the resolution. The conversion time doubles with every bit of increased
resolution. For 10 bit resolution 35ms are needed, while for 12 bit resolution
(default) 140ms. When chip is in shutdown mode and a read operation is
requested, one-shot is triggered, the device waits for 140 (conversion time) + 1
(error) ms, and only after that is the temperature value register read.
requested, one-shot is triggered, the device waits for 140 (conversion time) ms,
and only after that is the temperature value register read.
The LSB of the temperature values is 0.0625 degrees Celsius, but the values of
the temperatures are displayed in milli-degrees. This means, that some data is

View File

@ -62,5 +62,6 @@ Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13
Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13
Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13
ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19
ASRock X670E NCT6686D EC firmware version 1.0 build 05/19/22
MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20
=============== ===============================================

View File

@ -0,0 +1,30 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver POWERZ
====================
Supported chips:
* ChargerLAB POWER-Z KM003C
Prefix: 'powerz'
Addresses scanned: -
Author:
- Thomas Weißschuh <linux@weissschuh.net>
Description
-----------
This driver implements support for the ChargerLAB POWER-Z USB-C power testing
family.
The device communicates with the custom protocol over USB.
The channel labels exposed via hwmon match the labels used by the on-device
display and the official POWER-Z PC software.
As current can flow in both directions through the tester the sign of the
channel "curr1_input" (label "IBUS") indicates the direction.

View File

@ -33,3 +33,13 @@ The hardware monitoring part of the SMSC SCH5627 is accessed by talking
through an embedded microcontroller. An application note describing the
protocol for communicating with the microcontroller is available upon
request. Please mail me if you want a copy.
Controlling fan speed
---------------------
The SCH5627 allows for partially controlling the fan speed. If a temperature
channel excedes tempX_max, all fans are forced to maximum speed. The same is not
true for tempX_crit, presumably some other measures to cool down the system are
take in this case.
In which way the value of fanX_min affects the fan speed is currently unknown.

View File

@ -4828,6 +4828,13 @@ X: drivers/char/ipmi/
X: drivers/char/random.c
X: drivers/char/tpm/
CHARGERLAB POWER-Z HARDWARE MONITOR DRIVER
M: Thomas Weißschuh <linux@weissschuh.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/powerz.rst
F: drivers/hwmon/powerz.c
CHECKPATCH
M: Andy Whitcroft <apw@canonical.com>
M: Joe Perches <joe@perches.com>
@ -12491,6 +12498,14 @@ F: drivers/hwmon/ltc2947-i2c.c
F: drivers/hwmon/ltc2947-spi.c
F: drivers/hwmon/ltc2947.h
LTC2991 HARDWARE MONITOR DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-hwmon@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml
F: drivers/hwmon/ltc2991.c
LTC2983 IIO TEMPERATURE DRIVER
M: Nuno Sá <nuno.sa@analog.com>
L: linux-iio@vger.kernel.org
@ -19441,6 +19456,7 @@ F: drivers/net/ethernet/sfc/
SFCTEMP HWMON DRIVER
M: Emil Renner Berthing <kernel@esmil.dk>
M: Hal Feng <hal.feng@starfivetech.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml

View File

@ -40,7 +40,7 @@ comment "Native drivers"
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
depends on X86 && DMI
depends on (X86 && DMI) || COMPILE_TEST
help
If you say yes here you get support for the sensor part of the first
and second revision of the Abit uGuru chip. The voltage and frequency
@ -55,7 +55,7 @@ config SENSORS_ABITUGURU
config SENSORS_ABITUGURU3
tristate "Abit uGuru (rev 3)"
depends on X86 && DMI
depends on (X86 && DMI) || COMPILE_TEST
help
If you say yes here you get support for the sensor part of the
third revision of the Abit uGuru chip. Only reading the sensors
@ -839,6 +839,16 @@ config SENSORS_JC42
This driver can also be built as a module. If so, the module
will be called jc42.
config SENSORS_POWERZ
tristate "ChargerLAB POWER-Z USB-C tester"
depends on USB
help
If you say yes here you get support for ChargerLAB POWER-Z series of
USB-C charging testers.
This driver can also be built as a module. If so, the module
will be called powerz.
config SENSORS_POWR1220
tristate "Lattice POWR1220 Power Monitoring"
depends on I2C
@ -932,6 +942,17 @@ config SENSORS_LTC2990
This driver can also be built as a module. If so, the module will
be called ltc2990.
config SENSORS_LTC2991
tristate "Analog Devices LTC2991"
depends on I2C
help
If you say yes here you get support for Analog Devices LTC2991
Octal I2C Voltage, Current, and Temperature Monitor. The LTC2991
supports a combination of voltage, current and temperature monitoring.
This driver can also be built as a module. If so, the module will
be called ltc2991.
config SENSORS_LTC2992
tristate "Linear Technology LTC2992"
depends on I2C
@ -1909,6 +1930,7 @@ config SENSORS_SMSC47B397
config SENSORS_SCH56XX_COMMON
tristate
select REGMAP
config SENSORS_SCH5627
tristate "SMSC SCH5627"

View File

@ -127,6 +127,7 @@ obj-$(CONFIG_SENSORS_LTC2947) += ltc2947-core.o
obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o
obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o
obj-$(CONFIG_SENSORS_LTC2990) += ltc2990.o
obj-$(CONFIG_SENSORS_LTC2991) += ltc2991.o
obj-$(CONFIG_SENSORS_LTC2992) += ltc2992.o
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
@ -176,6 +177,7 @@ obj-$(CONFIG_SENSORS_OXP) += oxp-sensors.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_POWERZ) += powerz.o
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o

View File

@ -1428,7 +1428,7 @@ abituguru_probe_error:
return res;
}
static int abituguru_remove(struct platform_device *pdev)
static void abituguru_remove(struct platform_device *pdev)
{
int i;
struct abituguru_data *data = platform_get_drvdata(pdev);
@ -1439,8 +1439,6 @@ static int abituguru_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr);
return 0;
}
static struct abituguru_data *abituguru_update_device(struct device *dev)
@ -1533,7 +1531,7 @@ static struct platform_driver abituguru_driver = {
.pm = pm_sleep_ptr(&abituguru_pm),
},
.probe = abituguru_probe,
.remove = abituguru_remove,
.remove_new = abituguru_remove,
};
static int __init abituguru_detect(void)

View File

@ -1061,7 +1061,7 @@ abituguru3_probe_error:
return res;
}
static int abituguru3_remove(struct platform_device *pdev)
static void abituguru3_remove(struct platform_device *pdev)
{
int i;
struct abituguru3_data *data = platform_get_drvdata(pdev);
@ -1072,7 +1072,6 @@ static int abituguru3_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru3_sysfs_attr[i].dev_attr);
return 0;
}
static struct abituguru3_data *abituguru3_update_device(struct device *dev)
@ -1153,7 +1152,7 @@ static struct platform_driver abituguru3_driver = {
.pm = pm_sleep_ptr(&abituguru3_pm),
},
.probe = abituguru3_probe,
.remove = abituguru3_remove,
.remove_new = abituguru3_remove,
};
static int __init abituguru3_dmi_detect(void)

View File

@ -43,6 +43,7 @@
/* 7475 Common Registers */
#define REG_DEVREV2 0x12 /* ADT7490 only */
#define REG_IMON 0x1D /* ADT7490 only */
#define REG_VTT 0x1E /* ADT7490 only */
#define REG_EXTEND3 0x1F /* ADT7490 only */
@ -103,6 +104,9 @@
#define REG_VTT_MIN 0x84 /* ADT7490 only */
#define REG_VTT_MAX 0x86 /* ADT7490 only */
#define REG_IMON_MIN 0x85 /* ADT7490 only */
#define REG_IMON_MAX 0x87 /* ADT7490 only */
#define VID_VIDSEL 0x80 /* ADT7476 only */
#define CONFIG2_ATTN 0x20
@ -123,7 +127,7 @@
/* ADT7475 Settings */
#define ADT7475_VOLTAGE_COUNT 5 /* Not counting Vtt */
#define ADT7475_VOLTAGE_COUNT 5 /* Not counting Vtt or Imon */
#define ADT7475_TEMP_COUNT 3
#define ADT7475_TACH_COUNT 4
#define ADT7475_PWM_COUNT 3
@ -204,7 +208,7 @@ struct adt7475_data {
u8 has_fan4:1;
u8 has_vid:1;
u32 alarms;
u16 voltage[3][6];
u16 voltage[3][7];
u16 temp[7][3];
u16 tach[2][4];
u8 pwm[4][3];
@ -215,7 +219,7 @@ struct adt7475_data {
u8 vid;
u8 vrm;
const struct attribute_group *groups[9];
const struct attribute_group *groups[10];
};
static struct i2c_driver adt7475_driver;
@ -273,13 +277,14 @@ static inline u16 rpm2tach(unsigned long rpm)
}
/* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */
static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = {
static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 2][2] = {
{ 45, 94 }, /* +2.5V */
{ 175, 525 }, /* Vccp */
{ 68, 71 }, /* Vcc */
{ 93, 47 }, /* +5V */
{ 120, 20 }, /* +12V */
{ 45, 45 }, /* Vtt */
{ 45, 45 }, /* Imon */
};
static inline int reg2volt(int channel, u16 reg, u8 bypass_attn)
@ -369,11 +374,16 @@ static ssize_t voltage_store(struct device *dev,
reg = VOLTAGE_MIN_REG(sattr->index);
else
reg = VOLTAGE_MAX_REG(sattr->index);
} else {
} else if (sattr->index == 5) {
if (sattr->nr == MIN)
reg = REG_VTT_MIN;
else
reg = REG_VTT_MAX;
} else {
if (sattr->nr == MIN)
reg = REG_IMON_MIN;
else
reg = REG_IMON_MAX;
}
i2c_smbus_write_byte_data(client, reg,
@ -1104,6 +1114,10 @@ static SENSOR_DEVICE_ATTR_2_RO(in5_input, voltage, INPUT, 5);
static SENSOR_DEVICE_ATTR_2_RW(in5_max, voltage, MAX, 5);
static SENSOR_DEVICE_ATTR_2_RW(in5_min, voltage, MIN, 5);
static SENSOR_DEVICE_ATTR_2_RO(in5_alarm, voltage, ALARM, 31);
static SENSOR_DEVICE_ATTR_2_RO(in6_input, voltage, INPUT, 6);
static SENSOR_DEVICE_ATTR_2_RW(in6_max, voltage, MAX, 6);
static SENSOR_DEVICE_ATTR_2_RW(in6_min, voltage, MIN, 6);
static SENSOR_DEVICE_ATTR_2_RO(in6_alarm, voltage, ALARM, 30);
static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, INPUT, 0);
static SENSOR_DEVICE_ATTR_2_RO(temp1_alarm, temp, ALARM, 0);
static SENSOR_DEVICE_ATTR_2_RO(temp1_fault, temp, FAULT, 0);
@ -1294,6 +1308,14 @@ static struct attribute *in5_attrs[] = {
NULL
};
static struct attribute *in6_attrs[] = {
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in6_max.dev_attr.attr,
&sensor_dev_attr_in6_min.dev_attr.attr,
&sensor_dev_attr_in6_alarm.dev_attr.attr,
NULL
};
static struct attribute *vid_attrs[] = {
&dev_attr_cpu0_vid.attr,
&dev_attr_vrm.attr,
@ -1307,6 +1329,7 @@ static const struct attribute_group in0_attr_group = { .attrs = in0_attrs };
static const struct attribute_group in3_attr_group = { .attrs = in3_attrs };
static const struct attribute_group in4_attr_group = { .attrs = in4_attrs };
static const struct attribute_group in5_attr_group = { .attrs = in5_attrs };
static const struct attribute_group in6_attr_group = { .attrs = in6_attrs };
static const struct attribute_group vid_attr_group = { .attrs = vid_attrs };
static int adt7475_detect(struct i2c_client *client,
@ -1389,6 +1412,18 @@ static int adt7475_update_limits(struct i2c_client *client)
data->voltage[MAX][5] = ret << 2;
}
if (data->has_voltage & (1 << 6)) {
ret = adt7475_read(REG_IMON_MIN);
if (ret < 0)
return ret;
data->voltage[MIN][6] = ret << 2;
ret = adt7475_read(REG_IMON_MAX);
if (ret < 0)
return ret;
data->voltage[MAX][6] = ret << 2;
}
for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
/* Adjust values so they match the input precision */
ret = adt7475_read(TEMP_MIN_REG(i));
@ -1663,7 +1698,7 @@ static int adt7475_probe(struct i2c_client *client)
revision = adt7475_read(REG_DEVID2) & 0x07;
break;
case adt7490:
data->has_voltage = 0x3e; /* in1 to in5 */
data->has_voltage = 0x7e; /* in1 to in6 */
revision = adt7475_read(REG_DEVID2) & 0x03;
if (revision == 0x03)
revision += adt7475_read(REG_DEVREV2);
@ -1775,6 +1810,9 @@ static int adt7475_probe(struct i2c_client *client)
if (data->has_voltage & (1 << 5)) {
data->groups[group_num++] = &in5_attr_group;
}
if (data->has_voltage & (1 << 6)) {
data->groups[group_num++] = &in6_attr_group;
}
if (data->has_vid) {
data->vrm = vid_which_vrm();
data->groups[group_num] = &vid_attr_group;
@ -1960,6 +1998,24 @@ static int adt7475_update_measure(struct device *dev)
((ext >> 4) & 3);
}
if (data->has_voltage & (1 << 6)) {
ret = adt7475_read(REG_STATUS4);
if (ret < 0)
return ret;
data->alarms |= ret << 24;
ret = adt7475_read(REG_EXTEND3);
if (ret < 0)
return ret;
ext = ret;
ret = adt7475_read(REG_IMON);
if (ret < 0)
return ret;
data->voltage[INPUT][6] = ret << 2 |
((ext >> 6) & 3);
}
for (i = 0; i < ADT7475_TACH_COUNT; i++) {
if (i == 3 && !data->has_fan4)
continue;

View File

@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo,
* Quadro, High Flow Next, Aquaero, Aquastream Ultimate, Leakshield)
* Quadro, High Flow Next, Aquaero, Aquastream Ultimate, Leakshield,
* High Flow USB/MPS Flow family)
*
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
* sensor values, except for devices that communicate through the
* legacy way (currently, Poweradjust 3).
* legacy way (currently, Poweradjust 3 and High Flow USB/MPS Flow family).
*
* Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
* Copyright 2022 Jack Doan <me@jackdoan.com>
@ -35,11 +36,12 @@
#define USB_PRODUCT_ID_AQUASTREAMXT 0xf0b6
#define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b
#define USB_PRODUCT_ID_POWERADJUST3 0xf0bd
#define USB_PRODUCT_ID_HIGHFLOW 0xf003
enum kinds {
d5next, farbwerk, farbwerk360, octo, quadro,
highflownext, aquaero, poweradjust3, aquastreamult,
aquastreamxt, leakshield
aquastreamxt, leakshield, highflow
};
static const char *const aqc_device_names[] = {
@ -53,7 +55,8 @@ static const char *const aqc_device_names[] = {
[aquastreamxt] = "aquastreamxt",
[aquaero] = "aquaero",
[aquastreamult] = "aquastreamultimate",
[poweradjust3] = "poweradjust3"
[poweradjust3] = "poweradjust3",
[highflow] = "highflow" /* Covers MPS Flow devices */
};
#define DRIVER_NAME "aquacomputer_d5next"
@ -90,6 +93,8 @@ static u8 aquaero_secondary_ctrl_report[] = {
#define POWERADJUST3_STATUS_REPORT_ID 0x03
#define HIGHFLOW_STATUS_REPORT_ID 0x02
/* Data types for reading and writing control reports */
#define AQC_8 0
#define AQC_BE16 1
@ -282,6 +287,17 @@ static u16 aquastreamxt_sensor_fan_offsets[] = { 0x13, 0x1b };
/* Sensor report offsets for the Poweradjust 3 */
#define POWERADJUST3_SENSOR_START 0x03
/* Specs of the High Flow USB */
#define HIGHFLOW_NUM_SENSORS 2
#define HIGHFLOW_NUM_FLOW_SENSORS 1
#define HIGHFLOW_SENSOR_REPORT_SIZE 0x76
/* Sensor report offsets for the High Flow USB */
#define HIGHFLOW_FIRMWARE_VERSION 0x3
#define HIGHFLOW_SERIAL_START 0x9
#define HIGHFLOW_FLOW_SENSOR_OFFSET 0x23
#define HIGHFLOW_SENSOR_START 0x2b
/* Labels for D5 Next */
static const char *const label_d5next_temp[] = {
"Coolant temp"
@ -486,6 +502,16 @@ static const char *const label_poweradjust3_temp_sensors[] = {
"External sensor"
};
/* Labels for Highflow */
static const char *const label_highflow_temp[] = {
"External temp",
"Internal temp"
};
static const char *const label_highflow_speeds[] = {
"Flow speed [dL/h]"
};
struct aqc_fan_structure_offsets {
u8 voltage;
u8 curr;
@ -819,6 +845,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
break;
case aquaero:
case quadro:
case highflow:
/* Special case to support flow sensors */
if (channel < priv->num_fans + priv->num_flow_sensors)
return 0444;
@ -926,7 +953,10 @@ static int aqc_legacy_read(struct aqc_data *priv)
for (i = 0; i < priv->num_temp_sensors; i++) {
sensor_value = get_unaligned_le16(priv->buffer + priv->temp_sensor_start_offset +
i * AQC_SENSOR_SIZE);
priv->temp_input[i] = sensor_value * 10;
if (sensor_value == AQC_SENSOR_NA)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
}
/* Special-case sensor readings */
@ -962,6 +992,17 @@ static int aqc_legacy_read(struct aqc_data *priv)
sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_FAN_VOLTAGE_OFFSET);
priv->voltage_input[1] = DIV_ROUND_CLOSEST(sensor_value * 1000, 63);
break;
case highflow:
/* Info provided with every report */
priv->serial_number[0] = get_unaligned_le16(priv->buffer +
priv->serial_number_start_offset);
priv->firmware_version =
get_unaligned_le16(priv->buffer + priv->firmware_version_offset);
/* Read flow speed */
priv->speed_input[0] = get_unaligned_le16(priv->buffer +
priv->flow_sensors_start_offset);
break;
default:
break;
}
@ -1747,6 +1788,20 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->temp_label = label_poweradjust3_temp_sensors;
break;
case USB_PRODUCT_ID_HIGHFLOW:
priv->kind = highflow;
priv->num_fans = 0;
priv->num_temp_sensors = HIGHFLOW_NUM_SENSORS;
priv->temp_sensor_start_offset = HIGHFLOW_SENSOR_START;
priv->num_flow_sensors = HIGHFLOW_NUM_FLOW_SENSORS;
priv->flow_sensors_start_offset = HIGHFLOW_FLOW_SENSOR_OFFSET;
priv->buffer_size = HIGHFLOW_SENSOR_REPORT_SIZE;
priv->temp_label = label_highflow_temp;
priv->speed_label = label_highflow_speeds;
break;
default:
break;
}
@ -1772,6 +1827,12 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->status_report_id = AQUASTREAMXT_STATUS_REPORT_ID;
break;
case highflow:
priv->serial_number_start_offset = HIGHFLOW_SERIAL_START;
priv->firmware_version_offset = HIGHFLOW_FIRMWARE_VERSION;
priv->status_report_id = HIGHFLOW_STATUS_REPORT_ID;
break;
default:
priv->serial_number_start_offset = AQC_SERIAL_START;
priv->firmware_version_offset = AQC_FIRMWARE_VERSION;
@ -1846,6 +1907,7 @@ static const struct hid_device_id aqc_table[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMXT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOW) },
{ }
};

View File

@ -244,6 +244,8 @@ static const struct ec_sensor_info sensors_family_amd_600[] = {
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
[ec_sensor_temp_vrm] =
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[ec_sensor_temp_water_out] =
@ -344,6 +346,14 @@ static const struct ec_board_info board_info_crosshair_x670e_hero = {
.family = family_amd_600_series,
};
static const struct ec_board_info board_info_crosshair_x670e_gene = {
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
SENSOR_TEMP_T_SENSOR |
SENSOR_TEMP_MB | SENSOR_TEMP_VRM,
.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
.family = family_amd_600_series,
};
static const struct ec_board_info board_info_crosshair_viii_dark_hero = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR |
@ -490,6 +500,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_crosshair_viii_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO",
&board_info_crosshair_x670e_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E GENE",
&board_info_crosshair_x670e_gene),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO",
&board_info_maximus_xi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)",

View File

@ -496,6 +496,21 @@ static int axi_fan_control_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = axi_fan_control_init(ctl, pdev->dev.of_node);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize device\n");
return ret;
}
ctl->hdev = devm_hwmon_device_register_with_info(&pdev->dev,
name,
ctl,
&axi_chip_info,
axi_fan_control_groups);
if (IS_ERR(ctl->hdev))
return PTR_ERR(ctl->hdev);
ctl->irq = platform_get_irq(pdev, 0);
if (ctl->irq < 0)
return ctl->irq;
@ -509,19 +524,7 @@ static int axi_fan_control_probe(struct platform_device *pdev)
return ret;
}
ret = axi_fan_control_init(ctl, pdev->dev.of_node);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize device\n");
return ret;
}
ctl->hdev = devm_hwmon_device_register_with_info(&pdev->dev,
name,
ctl,
&axi_chip_info,
axi_fan_control_groups);
return PTR_ERR_OR_ZERO(ctl->hdev);
return 0;
}
static struct platform_driver axi_fan_control_driver = {

View File

@ -42,7 +42,7 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */
#define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */
#define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */
#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */
#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)

View File

@ -479,7 +479,7 @@ exit_regulator:
return err;
}
static int da9052_hwmon_remove(struct platform_device *pdev)
static void da9052_hwmon_remove(struct platform_device *pdev)
{
struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
@ -487,13 +487,11 @@ static int da9052_hwmon_remove(struct platform_device *pdev)
da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon);
regulator_disable(hwmon->tsiref);
}
return 0;
}
static struct platform_driver da9052_hwmon_driver = {
.probe = da9052_hwmon_probe,
.remove = da9052_hwmon_remove,
.remove_new = da9052_hwmon_remove,
.driver = {
.name = "da9052-hwmon",
},

View File

@ -2710,14 +2710,12 @@ exit_remove_files:
return err;
}
static int dme1737_isa_remove(struct platform_device *pdev)
static void dme1737_isa_remove(struct platform_device *pdev)
{
struct dme1737_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
dme1737_remove_files(&pdev->dev);
return 0;
}
static struct platform_driver dme1737_isa_driver = {
@ -2725,7 +2723,7 @@ static struct platform_driver dme1737_isa_driver = {
.name = "dme1737",
},
.probe = dme1737_isa_probe,
.remove = dme1737_isa_remove,
.remove_new = dme1737_isa_remove,
};
/* ---------------------------------------------------------------------

View File

@ -1480,7 +1480,7 @@ exit_remove_files:
return err;
}
static int f71805f_remove(struct platform_device *pdev)
static void f71805f_remove(struct platform_device *pdev)
{
struct f71805f_data *data = platform_get_drvdata(pdev);
int i;
@ -1490,8 +1490,6 @@ static int f71805f_remove(struct platform_device *pdev)
for (i = 0; i < 4; i++)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
return 0;
}
static struct platform_driver f71805f_driver = {
@ -1499,7 +1497,7 @@ static struct platform_driver f71805f_driver = {
.name = DRVNAME,
},
.probe = f71805f_probe,
.remove = f71805f_remove,
.remove_new = f71805f_remove,
};
static int __init f71805f_device_add(unsigned short address,

View File

@ -2223,7 +2223,7 @@ static int f71882fg_create_fan_sysfs_files(
return err;
}
static int f71882fg_remove(struct platform_device *pdev)
static void f71882fg_remove(struct platform_device *pdev)
{
struct f71882fg_data *data = platform_get_drvdata(pdev);
int nr_fans = f71882fg_nr_fans[data->type];
@ -2333,7 +2333,6 @@ static int f71882fg_remove(struct platform_device *pdev)
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
}
}
return 0;
}
static int f71882fg_probe(struct platform_device *pdev)
@ -2659,7 +2658,7 @@ static struct platform_driver f71882fg_driver = {
.name = DRVNAME,
},
.probe = f71882fg_probe,
.remove = f71882fg_remove,
.remove_new = f71882fg_remove,
};
static int __init f71882fg_init(void)

View File

@ -62,7 +62,7 @@ static u32 hs3001_extract_humidity(u16 raw)
{
u32 hum = (raw & HS3001_MASK_HUMIDITY_0X3FFF) * HS3001_FIXPOINT_ARITH * 100;
return hum /= (1 << 14) - 1;
return hum / (1 << 14) - 1;
}
static int hs3001_data_fetch_command(struct i2c_client *client,

View File

@ -555,7 +555,7 @@ err:
return res;
}
static int i5k_amb_remove(struct platform_device *pdev)
static void i5k_amb_remove(struct platform_device *pdev)
{
int i;
struct i5k_amb_data *data = platform_get_drvdata(pdev);
@ -568,7 +568,6 @@ static int i5k_amb_remove(struct platform_device *pdev)
iounmap(data->amb_mmio);
release_mem_region(data->amb_base, data->amb_len);
kfree(data);
return 0;
}
static struct platform_driver i5k_amb_driver = {
@ -576,7 +575,7 @@ static struct platform_driver i5k_amb_driver = {
.name = DRVNAME,
},
.probe = i5k_amb_probe,
.remove = i5k_amb_remove,
.remove_new = i5k_amb_remove,
};
static int __init i5k_amb_init(void)

View File

@ -33,7 +33,7 @@
#define INA238_BUS_UNDER_VOLTAGE 0xf
#define INA238_TEMP_LIMIT 0x10
#define INA238_POWER_LIMIT 0x11
#define INA238_DEVICE_ID 0x3f
#define INA238_DEVICE_ID 0x3f /* not available on INA237 */
#define INA238_CONFIG_ADCRANGE BIT(4)
@ -622,6 +622,7 @@ static const struct i2c_device_id ina238_id[] = {
MODULE_DEVICE_TABLE(i2c, ina238_id);
static const struct of_device_id __maybe_unused ina238_of_match[] = {
{ .compatible = "ti,ina237" },
{ .compatible = "ti,ina238" },
{ },
};

View File

@ -6,6 +6,7 @@
* Andrew F. Davis <afd@ti.com>
*/
#include <linux/debugfs.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
@ -99,11 +100,13 @@ enum ina3221_channels {
* @label: label of channel input source
* @shunt_resistor: shunt resistor value of channel input source
* @disconnected: connection status of channel input source
* @summation_disable: channel summation status of input source
*/
struct ina3221_input {
const char *label;
int shunt_resistor;
bool disconnected;
bool summation_disable;
};
/**
@ -113,8 +116,10 @@ struct ina3221_input {
* @fields: Register fields of the device
* @inputs: Array of channel input source specific structures
* @lock: mutex lock to serialize sysfs attribute accesses
* @debugfs: Pointer to debugfs entry for device
* @reg_config: Register value of INA3221_CONFIG
* @summation_shunt_resistor: equivalent shunt resistor value for summation
* @summation_channel_control: Value written to SCC field in INA3221_MASK_ENABLE
* @single_shot: running in single-shot operating mode
*/
struct ina3221_data {
@ -123,8 +128,10 @@ struct ina3221_data {
struct regmap_field *fields[F_MAX_FIELDS];
struct ina3221_input inputs[INA3221_NUM_CHANNELS];
struct mutex lock;
struct dentry *debugfs;
u32 reg_config;
int summation_shunt_resistor;
u32 summation_channel_control;
bool single_shot;
};
@ -154,7 +161,8 @@ static inline int ina3221_summation_shunt_resistor(struct ina3221_data *ina)
int i, shunt_resistor = 0;
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
if (input[i].disconnected || !input[i].shunt_resistor)
if (input[i].disconnected || !input[i].shunt_resistor ||
input[i].summation_disable)
continue;
if (!shunt_resistor) {
/* Found the reference shunt resistor value */
@ -786,6 +794,9 @@ static int ina3221_probe_child_from_dt(struct device *dev,
/* Save the connected input label if available */
of_property_read_string(child, "label", &input->label);
/* summation channel control */
input->summation_disable = of_property_read_bool(child, "ti,summation-disable");
/* Overwrite default shunt resistor value optionally */
if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) {
if (val < 1 || val > INT_MAX) {
@ -827,6 +838,7 @@ static int ina3221_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct ina3221_data *ina;
struct device *hwmon_dev;
char name[32];
int i, ret;
ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
@ -873,6 +885,10 @@ static int ina3221_probe(struct i2c_client *client)
/* Initialize summation_shunt_resistor for summation channel control */
ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina);
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
if (!ina->inputs[i].summation_disable)
ina->summation_channel_control |= BIT(14 - i);
}
ina->pm_dev = dev;
mutex_init(&ina->lock);
@ -900,6 +916,15 @@ static int ina3221_probe(struct i2c_client *client)
goto fail;
}
scnprintf(name, sizeof(name), "%s-%s", INA3221_DRIVER_NAME, dev_name(dev));
ina->debugfs = debugfs_create_dir(name, NULL);
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
scnprintf(name, sizeof(name), "in%d_summation_disable", i);
debugfs_create_bool(name, 0400, ina->debugfs,
&ina->inputs[i].summation_disable);
}
return 0;
fail:
@ -918,6 +943,8 @@ static void ina3221_remove(struct i2c_client *client)
struct ina3221_data *ina = dev_get_drvdata(&client->dev);
int i;
debugfs_remove_recursive(ina->debugfs);
pm_runtime_disable(ina->pm_dev);
pm_runtime_set_suspended(ina->pm_dev);
@ -978,13 +1005,13 @@ static int ina3221_resume(struct device *dev)
/* Initialize summation channel control */
if (ina->summation_shunt_resistor) {
/*
* Take all three channels into summation by default
* Sum only channels that are not disabled for summation.
* Shunt measurements of disconnected channels should
* be 0, so it does not matter for summation.
*/
ret = regmap_update_bits(ina->regmap, INA3221_MASK_ENABLE,
INA3221_MASK_ENABLE_SCC_MASK,
INA3221_MASK_ENABLE_SCC_MASK);
ina->summation_channel_control);
if (ret) {
dev_err(dev, "Unable to control summation channel\n");
return ret;

437
drivers/hwmon/ltc2991.c Normal file
View File

@ -0,0 +1,437 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 Analog Devices, Inc.
* Author: Antoniu Miclaus <antoniu.miclaus@analog.com>
*/
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#define LTC2991_STATUS_LOW 0x00
#define LTC2991_CH_EN_TRIGGER 0x01
#define LTC2991_V1_V4_CTRL 0x06
#define LTC2991_V5_V8_CTRL 0x07
#define LTC2991_PWM_TH_LSB_T_INT 0x08
#define LTC2991_PWM_TH_MSB 0x09
#define LTC2991_CHANNEL_V_MSB(x) (0x0A + ((x) * 2))
#define LTC2991_CHANNEL_T_MSB(x) (0x0A + ((x) * 4))
#define LTC2991_CHANNEL_C_MSB(x) (0x0C + ((x) * 4))
#define LTC2991_T_INT_MSB 0x1A
#define LTC2991_VCC_MSB 0x1C
#define LTC2991_V7_V8_EN BIT(7)
#define LTC2991_V5_V6_EN BIT(6)
#define LTC2991_V3_V4_EN BIT(5)
#define LTC2991_V1_V2_EN BIT(4)
#define LTC2991_T_INT_VCC_EN BIT(3)
#define LTC2991_V3_V4_FILT_EN BIT(7)
#define LTC2991_V3_V4_TEMP_EN BIT(5)
#define LTC2991_V3_V4_DIFF_EN BIT(4)
#define LTC2991_V1_V2_FILT_EN BIT(3)
#define LTC2991_V1_V2_TEMP_EN BIT(1)
#define LTC2991_V1_V2_DIFF_EN BIT(0)
#define LTC2991_V7_V8_FILT_EN BIT(7)
#define LTC2991_V7_V8_TEMP_EN BIT(5)
#define LTC2991_V7_V8_DIFF_EN BIT(4)
#define LTC2991_V5_V6_FILT_EN BIT(7)
#define LTC2991_V5_V6_TEMP_EN BIT(5)
#define LTC2991_V5_V6_DIFF_EN BIT(4)
#define LTC2991_REPEAT_ACQ_EN BIT(4)
#define LTC2991_T_INT_FILT_EN BIT(3)
#define LTC2991_MAX_CHANNEL 4
#define LTC2991_T_INT_CH_NR 4
#define LTC2991_VCC_CH_NR 0
struct ltc2991_state {
struct device *dev;
struct regmap *regmap;
u32 r_sense_uohm[LTC2991_MAX_CHANNEL];
bool temp_en[LTC2991_MAX_CHANNEL];
};
static int ltc2991_read_reg(struct ltc2991_state *st, u8 addr, u8 reg_len,
int *val)
{
__be16 regvals;
int ret;
if (reg_len < 2)
return regmap_read(st->regmap, addr, val);
ret = regmap_bulk_read(st->regmap, addr, &regvals, reg_len);
if (ret)
return ret;
*val = be16_to_cpu(regvals);
return 0;
}
static int ltc2991_get_voltage(struct ltc2991_state *st, u32 reg, long *val)
{
int reg_val, ret, offset = 0;
ret = ltc2991_read_reg(st, reg, 2, &reg_val);
if (ret)
return ret;
if (reg == LTC2991_VCC_MSB)
/* Vcc 2.5V offset */
offset = 2500;
/* Vx, 305.18uV/LSB */
*val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 14) * 30518,
1000 * 100) + offset;
return 0;
}
static int ltc2991_read_in(struct device *dev, u32 attr, int channel, long *val)
{
struct ltc2991_state *st = dev_get_drvdata(dev);
u32 reg;
switch (attr) {
case hwmon_in_input:
if (channel == LTC2991_VCC_CH_NR)
reg = LTC2991_VCC_MSB;
else
reg = LTC2991_CHANNEL_V_MSB(channel - 1);
return ltc2991_get_voltage(st, reg, val);
default:
return -EOPNOTSUPP;
}
}
static int ltc2991_get_curr(struct ltc2991_state *st, u32 reg, int channel,
long *val)
{
int reg_val, ret;
ret = ltc2991_read_reg(st, reg, 2, &reg_val);
if (ret)
return ret;
/* Vx-Vy, 19.075uV/LSB */
*val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 14) * 19075,
st->r_sense_uohm[channel]);
return 0;
}
static int ltc2991_read_curr(struct device *dev, u32 attr, int channel,
long *val)
{
struct ltc2991_state *st = dev_get_drvdata(dev);
u32 reg;
switch (attr) {
case hwmon_curr_input:
reg = LTC2991_CHANNEL_C_MSB(channel);
return ltc2991_get_curr(st, reg, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int ltc2991_get_temp(struct ltc2991_state *st, u32 reg, int channel,
long *val)
{
int reg_val, ret;
ret = ltc2991_read_reg(st, reg, 2, &reg_val);
if (ret)
return ret;
/* Temp LSB = 0.0625 Degrees */
*val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 12) * 1000, 16);
return 0;
}
static int ltc2991_read_temp(struct device *dev, u32 attr, int channel,
long *val)
{
struct ltc2991_state *st = dev_get_drvdata(dev);
u32 reg;
switch (attr) {
case hwmon_temp_input:
if (channel == LTC2991_T_INT_CH_NR)
reg = LTC2991_T_INT_MSB;
else
reg = LTC2991_CHANNEL_T_MSB(channel);
return ltc2991_get_temp(st, reg, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int ltc2991_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_in:
return ltc2991_read_in(dev, attr, channel, val);
case hwmon_curr:
return ltc2991_read_curr(dev, attr, channel, val);
case hwmon_temp:
return ltc2991_read_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t ltc2991_is_visible(const void *data,
enum hwmon_sensor_types type, u32 attr,
int channel)
{
const struct ltc2991_state *st = data;
switch (type) {
case hwmon_in:
switch (attr) {
case hwmon_in_input:
if (channel == LTC2991_VCC_CH_NR)
return 0444;
if (st->temp_en[(channel - 1) / 2])
break;
if (channel % 2)
return 0444;
if (!st->r_sense_uohm[(channel - 1) / 2])
return 0444;
}
break;
case hwmon_curr:
switch (attr) {
case hwmon_curr_input:
if (st->r_sense_uohm[channel])
return 0444;
break;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
if (st->temp_en[channel] ||
channel == LTC2991_T_INT_CH_NR)
return 0444;
break;
}
break;
default:
break;
}
return 0;
}
static const struct hwmon_ops ltc2991_hwmon_ops = {
.is_visible = ltc2991_is_visible,
.read = ltc2991_read,
};
static const struct hwmon_channel_info *ltc2991_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT
),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT,
HWMON_C_INPUT,
HWMON_C_INPUT,
HWMON_C_INPUT
),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT
),
NULL
};
static const struct hwmon_chip_info ltc2991_chip_info = {
.ops = &ltc2991_hwmon_ops,
.info = ltc2991_info,
};
static const struct regmap_config ltc2991_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x1D,
};
static int ltc2991_init(struct ltc2991_state *st)
{
struct fwnode_handle *child;
int ret;
u32 val, addr;
u8 v5_v8_reg_data = 0, v1_v4_reg_data = 0;
ret = devm_regulator_get_enable(st->dev, "vcc");
if (ret)
return dev_err_probe(st->dev, ret,
"failed to enable regulator\n");
device_for_each_child_node(st->dev, child) {
ret = fwnode_property_read_u32(child, "reg", &addr);
if (ret < 0) {
fwnode_handle_put(child);
return ret;
}
if (addr > 3) {
fwnode_handle_put(child);
return -EINVAL;
}
ret = fwnode_property_read_u32(child,
"shunt-resistor-micro-ohms",
&val);
if (!ret) {
if (!val)
return dev_err_probe(st->dev, -EINVAL,
"shunt resistor value cannot be zero\n");
st->r_sense_uohm[addr] = val;
switch (addr) {
case 0:
v1_v4_reg_data |= LTC2991_V1_V2_DIFF_EN;
break;
case 1:
v1_v4_reg_data |= LTC2991_V3_V4_DIFF_EN;
break;
case 2:
v5_v8_reg_data |= LTC2991_V5_V6_DIFF_EN;
break;
case 3:
v5_v8_reg_data |= LTC2991_V7_V8_DIFF_EN;
break;
default:
break;
}
}
ret = fwnode_property_read_bool(child,
"adi,temperature-enable");
if (ret) {
st->temp_en[addr] = ret;
switch (addr) {
case 0:
v1_v4_reg_data |= LTC2991_V1_V2_TEMP_EN;
break;
case 1:
v1_v4_reg_data |= LTC2991_V3_V4_TEMP_EN;
break;
case 2:
v5_v8_reg_data |= LTC2991_V5_V6_TEMP_EN;
break;
case 3:
v5_v8_reg_data |= LTC2991_V7_V8_TEMP_EN;
break;
default:
break;
}
}
}
ret = regmap_write(st->regmap, LTC2991_V5_V8_CTRL, v5_v8_reg_data);
if (ret)
return dev_err_probe(st->dev, ret,
"Error: Failed to set V5-V8 CTRL reg.\n");
ret = regmap_write(st->regmap, LTC2991_V1_V4_CTRL, v1_v4_reg_data);
if (ret)
return dev_err_probe(st->dev, ret,
"Error: Failed to set V1-V4 CTRL reg.\n");
ret = regmap_write(st->regmap, LTC2991_PWM_TH_LSB_T_INT,
LTC2991_REPEAT_ACQ_EN);
if (ret)
return dev_err_probe(st->dev, ret,
"Error: Failed to set contiuous mode.\n");
/* Enable all channels and trigger conversions */
return regmap_write(st->regmap, LTC2991_CH_EN_TRIGGER,
LTC2991_V7_V8_EN | LTC2991_V5_V6_EN |
LTC2991_V3_V4_EN | LTC2991_V1_V2_EN |
LTC2991_T_INT_VCC_EN);
}
static int ltc2991_i2c_probe(struct i2c_client *client)
{
int ret;
struct device *hwmon_dev;
struct ltc2991_state *st;
st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
st->dev = &client->dev;
st->regmap = devm_regmap_init_i2c(client, &ltc2991_regmap_config);
if (IS_ERR(st->regmap))
return PTR_ERR(st->regmap);
ret = ltc2991_init(st);
if (ret)
return ret;
hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
client->name, st,
&ltc2991_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct of_device_id ltc2991_of_match[] = {
{ .compatible = "adi,ltc2991" },
{ }
};
MODULE_DEVICE_TABLE(of, ltc2991_of_match);
static const struct i2c_device_id ltc2991_i2c_id[] = {
{ "ltc2991", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ltc2991_i2c_id);
static struct i2c_driver ltc2991_i2c_driver = {
.driver = {
.name = "ltc2991",
.of_match_table = ltc2991_of_match,
},
.probe = ltc2991_i2c_probe,
.id_table = ltc2991_i2c_id,
};
module_i2c_driver(ltc2991_i2c_driver);
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
MODULE_DESCRIPTION("Analog Devices LTC2991 HWMON Driver");
MODULE_LICENSE("GPL");

View File

@ -875,8 +875,12 @@ static int ltc2992_parse_dt(struct ltc2992_state *st)
}
ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val);
if (!ret)
if (!ret) {
if (!val)
return dev_err_probe(&st->client->dev, -EINVAL,
"shunt resistor value cannot be zero\n");
st->r_sense_uohm[addr] = val;
}
}
return 0;

View File

@ -312,14 +312,12 @@ error:
return ret;
}
static int max197_remove(struct platform_device *pdev)
static void max197_remove(struct platform_device *pdev)
{
struct max197_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
return 0;
}
static const struct platform_device_id max197_device_ids[] = {
@ -334,7 +332,7 @@ static struct platform_driver max197_driver = {
.name = "max197",
},
.probe = max197_probe,
.remove = max197_remove,
.remove_new = max197_remove,
.id_table = max197_device_ids,
};
module_platform_driver(max197_driver);

View File

@ -25,20 +25,32 @@
#define MAX31827_CONFIGURATION_U_TEMP_STAT_MASK BIT(14)
#define MAX31827_CONFIGURATION_O_TEMP_STAT_MASK BIT(15)
#define MAX31827_12_BIT_CNV_TIME 141
#define MAX31827_CNV_1_DIV_64_HZ 0x1
#define MAX31827_CNV_1_DIV_32_HZ 0x2
#define MAX31827_CNV_1_DIV_16_HZ 0x3
#define MAX31827_CNV_1_DIV_4_HZ 0x4
#define MAX31827_CNV_1_HZ 0x5
#define MAX31827_CNV_4_HZ 0x6
#define MAX31827_CNV_8_HZ 0x7
#define MAX31827_12_BIT_CNV_TIME 140
#define MAX31827_16_BIT_TO_M_DGR(x) (sign_extend32(x, 15) * 1000 / 16)
#define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000)
#define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0)
enum max31827_cnv {
MAX31827_CNV_1_DIV_64_HZ = 1,
MAX31827_CNV_1_DIV_32_HZ,
MAX31827_CNV_1_DIV_16_HZ,
MAX31827_CNV_1_DIV_4_HZ,
MAX31827_CNV_1_HZ,
MAX31827_CNV_4_HZ,
MAX31827_CNV_8_HZ,
};
static const u16 max31827_conversions[] = {
[MAX31827_CNV_1_DIV_64_HZ] = 64000,
[MAX31827_CNV_1_DIV_32_HZ] = 32000,
[MAX31827_CNV_1_DIV_16_HZ] = 16000,
[MAX31827_CNV_1_DIV_4_HZ] = 4000,
[MAX31827_CNV_1_HZ] = 1000,
[MAX31827_CNV_4_HZ] = 250,
[MAX31827_CNV_8_HZ] = 125,
};
struct max31827_state {
/*
* Prevent simultaneous access to the i2c client.
@ -54,15 +66,13 @@ static const struct regmap_config max31827_regmap = {
.max_register = 0xA,
};
static int write_alarm_val(struct max31827_state *st, unsigned int reg,
long val)
static int shutdown_write(struct max31827_state *st, unsigned int reg,
unsigned int val)
{
unsigned int cfg;
unsigned int tmp;
unsigned int cnv_rate;
int ret;
val = MAX31827_M_DGR_TO_16_BIT(val);
/*
* Before the Temperature Threshold Alarm and Alarm Hysteresis Threshold
* register values are changed over I2C, the part must be in shutdown
@ -82,9 +92,10 @@ static int write_alarm_val(struct max31827_state *st, unsigned int reg,
if (ret)
goto unlock;
tmp = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK |
cnv_rate = MAX31827_CONFIGURATION_CNV_RATE_MASK & cfg;
cfg = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK |
MAX31827_CONFIGURATION_CNV_RATE_MASK);
ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, tmp);
ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg);
if (ret)
goto unlock;
@ -92,13 +103,23 @@ static int write_alarm_val(struct max31827_state *st, unsigned int reg,
if (ret)
goto unlock;
ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg);
ret = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG,
MAX31827_CONFIGURATION_CNV_RATE_MASK,
cnv_rate);
unlock:
mutex_unlock(&st->lock);
return ret;
}
static int write_alarm_val(struct max31827_state *st, unsigned int reg,
long val)
{
val = MAX31827_M_DGR_TO_16_BIT(val);
return shutdown_write(st, reg, val);
}
static umode_t max31827_is_visible(const void *state,
enum hwmon_sensor_types type, u32 attr,
int channel)
@ -243,32 +264,7 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type,
uval = FIELD_GET(MAX31827_CONFIGURATION_CNV_RATE_MASK,
uval);
switch (uval) {
case MAX31827_CNV_1_DIV_64_HZ:
*val = 64000;
break;
case MAX31827_CNV_1_DIV_32_HZ:
*val = 32000;
break;
case MAX31827_CNV_1_DIV_16_HZ:
*val = 16000;
break;
case MAX31827_CNV_1_DIV_4_HZ:
*val = 4000;
break;
case MAX31827_CNV_1_HZ:
*val = 1000;
break;
case MAX31827_CNV_4_HZ:
*val = 250;
break;
case MAX31827_CNV_8_HZ:
*val = 125;
break;
default:
*val = 0;
break;
}
*val = max31827_conversions[uval];
}
break;
@ -284,6 +280,7 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct max31827_state *st = dev_get_drvdata(dev);
int res = 1;
int ret;
switch (type) {
@ -333,39 +330,27 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
if (!st->enable)
return -EINVAL;
switch (val) {
case 125:
val = MAX31827_CNV_8_HZ;
break;
case 250:
val = MAX31827_CNV_4_HZ;
break;
case 1000:
val = MAX31827_CNV_1_HZ;
break;
case 4000:
val = MAX31827_CNV_1_DIV_4_HZ;
break;
case 16000:
val = MAX31827_CNV_1_DIV_16_HZ;
break;
case 32000:
val = MAX31827_CNV_1_DIV_32_HZ;
break;
case 64000:
val = MAX31827_CNV_1_DIV_64_HZ;
break;
default:
return -EINVAL;
}
/*
* Convert the desired conversion rate into register
* bits. res is already initialized with 1.
*
* This was inspired by lm73 driver.
*/
while (res < ARRAY_SIZE(max31827_conversions) &&
val < max31827_conversions[res])
res++;
val = FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK,
val);
if (res == ARRAY_SIZE(max31827_conversions) ||
val != max31827_conversions[res])
return -EINVAL;
res = FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK,
res);
return regmap_update_bits(st->regmap,
MAX31827_CONFIGURATION_REG,
MAX31827_CONFIGURATION_CNV_RATE_MASK,
val);
res);
}
break;
@ -427,6 +412,10 @@ static int max31827_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(st->regmap),
"Failed to allocate regmap.\n");
err = devm_regulator_get_enable(dev, "vref");
if (err)
return dev_err_probe(dev, err, "failed to enable regulator\n");
err = max31827_init_client(st);
if (err)
return err;

View File

@ -285,7 +285,7 @@ out_err_create_16chans:
return ret;
}
static int mc13783_adc_remove(struct platform_device *pdev)
static void mc13783_adc_remove(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
@ -299,8 +299,6 @@ static int mc13783_adc_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans);
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base);
return 0;
}
static const struct platform_device_id mc13783_adc_idtable[] = {
@ -317,7 +315,7 @@ static const struct platform_device_id mc13783_adc_idtable[] = {
MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable);
static struct platform_driver mc13783_adc_driver = {
.remove = mc13783_adc_remove,
.remove_new = mc13783_adc_remove,
.driver = {
.name = DRIVER_NAME,
},

View File

@ -176,6 +176,7 @@ superio_exit(int ioreg)
#define NCT6683_CUSTOMER_ID_MSI2 0x200
#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c
#define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b
#define NCT6683_CUSTOMER_ID_ASROCK3 0x1631
#define NCT6683_REG_BUILD_YEAR 0x604
#define NCT6683_REG_BUILD_MONTH 0x605
@ -1227,6 +1228,8 @@ static int nct6683_probe(struct platform_device *pdev)
break;
case NCT6683_CUSTOMER_ID_ASROCK2:
break;
case NCT6683_CUSTOMER_ID_ASROCK3:
break;
default:
if (!force)
return -ENODEV;

View File

@ -1614,17 +1614,21 @@ struct nct6775_data *nct6775_update_device(struct device *dev)
data->fan_div[i]);
if (data->has_fan_min & BIT(i)) {
err = nct6775_read_value(data, data->REG_FAN_MIN[i], &reg);
u16 tmp;
err = nct6775_read_value(data, data->REG_FAN_MIN[i], &tmp);
if (err)
goto out;
data->fan_min[i] = reg;
data->fan_min[i] = tmp;
}
if (data->REG_FAN_PULSES[i]) {
err = nct6775_read_value(data, data->REG_FAN_PULSES[i], &reg);
u16 tmp;
err = nct6775_read_value(data, data->REG_FAN_PULSES[i], &tmp);
if (err)
goto out;
data->fan_pulses[i] = (reg >> data->FAN_PULSE_SHIFT[i]) & 0x03;
data->fan_pulses[i] = (tmp >> data->FAN_PULSE_SHIFT[i]) & 0x03;
}
err = nct6775_select_fan_div(dev, data, i, reg);

View File

@ -1465,10 +1465,8 @@ static const char * const asus_msi_boards[] = {
static int nct6775_asuswmi_device_match(struct device *dev, void *data)
{
struct acpi_device *adev = to_acpi_device(dev);
const char *uid = acpi_device_uid(adev);
const char *hid = acpi_device_hid(adev);
if (hid && !strcmp(hid, ASUSWMI_DEVICE_HID) && uid && !strcmp(uid, data)) {
if (acpi_dev_hid_uid_match(adev, ASUSWMI_DEVICE_HID, data)) {
asus_acpi_dev = adev;
return 1;
}

View File

@ -875,6 +875,8 @@ static int npcm7xx_en_pwm_fan(struct device *dev,
data->pwm_present[pwm_port] = true;
ret = npcm7xx_pwm_config_set(data, pwm_port,
NPCM7XX_PWM_CMR_DEFAULT_NUM);
if (ret)
return ret;
ret = of_property_count_u8_elems(child, "cooling-levels");
if (ret > 0) {

View File

@ -167,7 +167,7 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
return rc;
}
static int p9_sbe_occ_remove(struct platform_device *pdev)
static void p9_sbe_occ_remove(struct platform_device *pdev)
{
struct occ *occ = platform_get_drvdata(pdev);
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
@ -178,8 +178,6 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
occ_shutdown(occ);
kvfree(ctx->ffdc);
return 0;
}
static const struct of_device_id p9_sbe_occ_of_match[] = {
@ -195,7 +193,7 @@ static struct platform_driver p9_sbe_occ_driver = {
.of_match_table = p9_sbe_occ_of_match,
},
.probe = p9_sbe_occ_probe,
.remove = p9_sbe_occ_remove,
.remove_new = p9_sbe_occ_remove,
};
module_platform_driver(p9_sbe_occ_driver);

View File

@ -1586,14 +1586,12 @@ error:
return err;
}
static int pc87360_remove(struct platform_device *pdev)
static void pc87360_remove(struct platform_device *pdev)
{
struct pc87360_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
pc87360_remove_files(&pdev->dev);
return 0;
}
/*
@ -1604,7 +1602,7 @@ static struct platform_driver pc87360_driver = {
.name = DRIVER_NAME,
},
.probe = pc87360_probe,
.remove = pc87360_remove,
.remove_new = pc87360_remove,
};
/*

View File

@ -1115,14 +1115,12 @@ exit_remove_files:
return err;
}
static int pc87427_remove(struct platform_device *pdev)
static void pc87427_remove(struct platform_device *pdev)
{
struct pc87427_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
pc87427_remove_files(&pdev->dev);
return 0;
}
@ -1131,7 +1129,7 @@ static struct platform_driver pc87427_driver = {
.name = DRVNAME,
},
.probe = pc87427_probe,
.remove = pc87427_remove,
.remove_new = pc87427_remove,
};
static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data)

View File

@ -3,6 +3,7 @@
* Copyright (C) 2017 IBM Corp.
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@ -23,19 +24,119 @@ enum max31785_regs {
#define MAX31785_NR_PAGES 23
#define MAX31785_NR_FAN_PAGES 6
#define MAX31785_WAIT_DELAY_US 250
static int max31785_read_byte_data(struct i2c_client *client, int page,
int reg)
struct max31785_data {
ktime_t access; /* Chip access time */
struct pmbus_driver_info info;
};
#define to_max31785_data(x) container_of(x, struct max31785_data, info)
/*
* MAX31785 Driver Workaround
*
* The MAX31785 fan controller occasionally exhibits communication issues.
* These issues are not indicated by the device itself, except for occasional
* NACK responses during master transactions. No error bits are set in STATUS_BYTE.
*
* To address this, we introduce a delay of 250us between consecutive accesses
* to the fan controller. This delay helps mitigate communication problems by
* allowing sufficient time between accesses.
*/
static inline void max31785_wait(const struct max31785_data *data)
{
if (page < MAX31785_NR_PAGES)
return -ENODATA;
s64 delta = ktime_us_delta(ktime_get(), data->access);
if (delta < MAX31785_WAIT_DELAY_US)
usleep_range(MAX31785_WAIT_DELAY_US - delta,
MAX31785_WAIT_DELAY_US);
}
static int max31785_i2c_write_byte_data(struct i2c_client *client,
struct max31785_data *driver_data,
int command, u16 data)
{
int rc;
max31785_wait(driver_data);
rc = i2c_smbus_write_byte_data(client, command, data);
driver_data->access = ktime_get();
return rc;
}
static int max31785_i2c_read_word_data(struct i2c_client *client,
struct max31785_data *driver_data,
int command)
{
int rc;
max31785_wait(driver_data);
rc = i2c_smbus_read_word_data(client, command);
driver_data->access = ktime_get();
return rc;
}
static int _max31785_read_byte_data(struct i2c_client *client,
struct max31785_data *driver_data,
int page, int command)
{
int rc;
max31785_wait(driver_data);
rc = pmbus_read_byte_data(client, page, command);
driver_data->access = ktime_get();
return rc;
}
static int _max31785_write_byte_data(struct i2c_client *client,
struct max31785_data *driver_data,
int page, int command, u16 data)
{
int rc;
max31785_wait(driver_data);
rc = pmbus_write_byte_data(client, page, command, data);
driver_data->access = ktime_get();
return rc;
}
static int _max31785_read_word_data(struct i2c_client *client,
struct max31785_data *driver_data,
int page, int phase, int command)
{
int rc;
max31785_wait(driver_data);
rc = pmbus_read_word_data(client, page, phase, command);
driver_data->access = ktime_get();
return rc;
}
static int _max31785_write_word_data(struct i2c_client *client,
struct max31785_data *driver_data,
int page, int command, u16 data)
{
int rc;
max31785_wait(driver_data);
rc = pmbus_write_word_data(client, page, command, data);
driver_data->access = ktime_get();
return rc;
}
static int max31785_read_byte_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct max31785_data *driver_data = to_max31785_data(info);
switch (reg) {
case PMBUS_VOUT_MODE:
return -ENOTSUPP;
case PMBUS_FAN_CONFIG_12:
return pmbus_read_byte_data(client, page - MAX31785_NR_PAGES,
reg);
return _max31785_read_byte_data(client, driver_data,
page - MAX31785_NR_PAGES,
reg);
}
return -ENODATA;
@ -102,16 +203,19 @@ static int max31785_get_pwm(struct i2c_client *client, int page)
return rv;
}
static int max31785_get_pwm_mode(struct i2c_client *client, int page)
static int max31785_get_pwm_mode(struct i2c_client *client,
struct max31785_data *driver_data, int page)
{
int config;
int command;
config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
config = _max31785_read_byte_data(client, driver_data, page,
PMBUS_FAN_CONFIG_12);
if (config < 0)
return config;
command = pmbus_read_word_data(client, page, 0xff, PMBUS_FAN_COMMAND_1);
command = _max31785_read_word_data(client, driver_data, page, 0xff,
PMBUS_FAN_COMMAND_1);
if (command < 0)
return command;
@ -129,6 +233,8 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page)
static int max31785_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 max31785_data *driver_data = to_max31785_data(info);
u32 val;
int rv;
@ -157,7 +263,7 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
rv = max31785_get_pwm(client, page);
break;
case PMBUS_VIRT_PWM_ENABLE_1:
rv = max31785_get_pwm_mode(client, page);
rv = max31785_get_pwm_mode(client, driver_data, page);
break;
default:
rv = -ENODATA;
@ -188,8 +294,36 @@ static inline u32 max31785_scale_pwm(u32 sensor_val)
return (sensor_val * 100) / 255;
}
static int max31785_pwm_enable(struct i2c_client *client, int page,
u16 word)
static int max31785_update_fan(struct i2c_client *client,
struct max31785_data *driver_data, int page,
u8 config, u8 mask, u16 command)
{
int from, rv;
u8 to;
from = _max31785_read_byte_data(client, driver_data, page,
PMBUS_FAN_CONFIG_12);
if (from < 0)
return from;
to = (from & ~mask) | (config & mask);
if (to != from) {
rv = _max31785_write_byte_data(client, driver_data, page,
PMBUS_FAN_CONFIG_12, to);
if (rv < 0)
return rv;
}
rv = _max31785_write_word_data(client, driver_data, page,
PMBUS_FAN_COMMAND_1, command);
return rv;
}
static int max31785_pwm_enable(struct i2c_client *client,
struct max31785_data *driver_data, int page,
u16 word)
{
int config = 0;
int rate;
@ -217,18 +351,23 @@ static int max31785_pwm_enable(struct i2c_client *client, int page,
return -EINVAL;
}
return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
return max31785_update_fan(client, driver_data, page, config,
PB_FAN_1_RPM, rate);
}
static int max31785_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 max31785_data *driver_data = to_max31785_data(info);
switch (reg) {
case PMBUS_VIRT_PWM_1:
return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
max31785_scale_pwm(word));
return max31785_update_fan(client, driver_data, page, 0,
PB_FAN_1_RPM,
max31785_scale_pwm(word));
case PMBUS_VIRT_PWM_ENABLE_1:
return max31785_pwm_enable(client, page, word);
return max31785_pwm_enable(client, driver_data, page, word);
default:
break;
}
@ -303,13 +442,16 @@ static int max31785_configure_dual_tach(struct i2c_client *client,
{
int ret;
int i;
struct max31785_data *driver_data = to_max31785_data(info);
for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
ret = max31785_i2c_write_byte_data(client, driver_data,
PMBUS_PAGE, i);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
ret = max31785_i2c_read_word_data(client, driver_data,
MFR_FAN_CONFIG);
if (ret < 0)
return ret;
@ -329,6 +471,7 @@ static int max31785_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pmbus_driver_info *info;
struct max31785_data *driver_data;
bool dual_tach = false;
int ret;
@ -337,13 +480,16 @@ static int max31785_probe(struct i2c_client *client)
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info)
driver_data = devm_kzalloc(dev, sizeof(struct max31785_data), GFP_KERNEL);
if (!driver_data)
return -ENOMEM;
info = &driver_data->info;
driver_data->access = ktime_get();
*info = max31785_info;
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
ret = max31785_i2c_write_byte_data(client, driver_data,
PMBUS_PAGE, 255);
if (ret < 0)
return ret;

View File

@ -297,6 +297,11 @@ static int mp2973_read_word_data(struct i2c_client *client, int page,
int ret;
switch (reg) {
case PMBUS_STATUS_WORD:
/* MP2973 & MP2971 return PGOOD instead of PB_STATUS_POWER_GOOD_N. */
ret = pmbus_read_word_data(client, page, phase, reg);
ret ^= PB_STATUS_POWER_GOOD_N;
break;
case PMBUS_OT_FAULT_LIMIT:
ret = mp2975_read_word_helper(client, page, phase, reg,
GENMASK(7, 0));
@ -380,11 +385,6 @@ static int mp2975_read_word_data(struct i2c_client *client, int page,
int ret;
switch (reg) {
case PMBUS_STATUS_WORD:
/* MP2973 & MP2971 return PGOOD instead of PB_STATUS_POWER_GOOD_N. */
ret = pmbus_read_word_data(client, page, phase, reg);
ret ^= PB_STATUS_POWER_GOOD_N;
break;
case PMBUS_OT_FAULT_LIMIT:
ret = mp2975_read_word_helper(client, page, phase, reg,
GENMASK(7, 0));

View File

@ -21,6 +21,7 @@
#define MPQ7932_N_VOLTAGES 256
#define MPQ7932_VOUT_MAX 0xFF
#define MPQ7932_NUM_PAGES 6
#define MPQ2286_NUM_PAGES 1
#define MPQ7932_TON_DELAY 0x60
#define MPQ7932_VOUT_STARTUP_SLEW 0xA3
@ -48,6 +49,11 @@ static struct regulator_desc mpq7932_regulators_desc[] = {
PMBUS_REGULATOR_STEP("buck", 5, MPQ7932_N_VOLTAGES,
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
};
static const struct regulator_desc mpq7932_regulators_desc_one[] = {
PMBUS_REGULATOR_STEP_ONE("buck", MPQ7932_N_VOLTAGES,
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
};
#endif
static int mpq7932_write_word_data(struct i2c_client *client, int page, int reg,
@ -105,7 +111,7 @@ static int mpq7932_probe(struct i2c_client *client)
return -ENOMEM;
info = &data->info;
info->pages = MPQ7932_NUM_PAGES;
info->pages = (int)(unsigned long)device_get_match_data(&client->dev);
info->format[PSC_VOLTAGE_OUT] = direct;
info->m[PSC_VOLTAGE_OUT] = 160;
info->b[PSC_VOLTAGE_OUT] = -33;
@ -115,8 +121,11 @@ static int mpq7932_probe(struct i2c_client *client)
}
#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR)
info->num_regulators = ARRAY_SIZE(mpq7932_regulators_desc);
info->reg_desc = mpq7932_regulators_desc;
info->num_regulators = info->pages;
if (info->num_regulators == 1)
info->reg_desc = mpq7932_regulators_desc_one;
else
info->reg_desc = mpq7932_regulators_desc;
#endif
info->read_word_data = mpq7932_read_word_data;
@ -129,12 +138,14 @@ static int mpq7932_probe(struct i2c_client *client)
}
static const struct of_device_id mpq7932_of_match[] = {
{ .compatible = "mps,mpq7932"},
{ .compatible = "mps,mpq2286", .data = (void *)MPQ2286_NUM_PAGES },
{ .compatible = "mps,mpq7932", .data = (void *)MPQ7932_NUM_PAGES },
{},
};
MODULE_DEVICE_TABLE(of, mpq7932_of_match);
static const struct i2c_device_id mpq7932_id[] = {
{ "mpq2286", },
{ "mpq7932", },
{ },
};

View File

@ -243,6 +243,15 @@ enum pmbus_regs {
*/
#define PB_OPERATION_CONTROL_ON BIT(7)
/*
* ON_OFF_CONFIG
*/
#define PB_ON_OFF_CONFIG_POWERUP_CONTROL BIT(4)
#define PB_ON_OFF_CONFIG_OPERATION_REQ BIT(3)
#define PB_ON_OFF_CONFIG_EN_PIN_REQ BIT(2)
#define PB_ON_OFF_CONFIG_POLARITY_HIGH BIT(1)
#define PB_ON_OFF_CONFIG_TURN_OFF_FAST BIT(0)
/*
* WRITE_PROTECT
*/
@ -480,6 +489,21 @@ extern const struct regulator_ops pmbus_regulator_ops;
#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0)
#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \
{ \
.name = (_name), \
.of_match = of_match_ptr(_name), \
.regulators_node = of_match_ptr("regulators"), \
.ops = &pmbus_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.n_voltages = _voltages, \
.uV_step = _step, \
.min_uV = _min_uV, \
}
#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0)
/* Function declarations */
void pmbus_clear_cache(struct i2c_client *client);

View File

@ -18,6 +18,127 @@ static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {
PMBUS_REGULATOR("vout", 0),
};
struct tda38640_data {
struct pmbus_driver_info info;
u32 en_pin_lvl;
};
#define to_tda38640_data(x) container_of(x, struct tda38640_data, info)
/*
* Map PB_ON_OFF_CONFIG_POLARITY_HIGH to PB_OPERATION_CONTROL_ON.
*/
static int tda38640_read_byte_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct tda38640_data *data = to_tda38640_data(info);
int ret, on_off_config, enabled;
if (reg != PMBUS_OPERATION)
return -ENODATA;
ret = pmbus_read_byte_data(client, page, reg);
if (ret < 0)
return ret;
on_off_config = pmbus_read_byte_data(client, page,
PMBUS_ON_OFF_CONFIG);
if (on_off_config < 0)
return on_off_config;
enabled = !!(on_off_config & PB_ON_OFF_CONFIG_POLARITY_HIGH);
enabled ^= data->en_pin_lvl;
if (enabled)
ret &= ~PB_OPERATION_CONTROL_ON;
else
ret |= PB_OPERATION_CONTROL_ON;
return ret;
}
/*
* Map PB_OPERATION_CONTROL_ON to PB_ON_OFF_CONFIG_POLARITY_HIGH.
*/
static int tda38640_write_byte_data(struct i2c_client *client, int page,
int reg, u8 byte)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct tda38640_data *data = to_tda38640_data(info);
int enable, ret;
if (reg != PMBUS_OPERATION)
return -ENODATA;
enable = !!(byte & PB_OPERATION_CONTROL_ON);
byte &= ~PB_OPERATION_CONTROL_ON;
ret = pmbus_write_byte_data(client, page, reg, byte);
if (ret < 0)
return ret;
enable ^= data->en_pin_lvl;
return pmbus_update_byte_data(client, page, PMBUS_ON_OFF_CONFIG,
PB_ON_OFF_CONFIG_POLARITY_HIGH,
enable ? 0 : PB_ON_OFF_CONFIG_POLARITY_HIGH);
}
static int svid_mode(struct i2c_client *client, struct tda38640_data *data)
{
/* PMBUS_MFR_READ(0xD0) + MTP Address offset */
u8 write_buf[] = {0xd0, 0x44, 0x00};
u8 read_buf[2];
int ret, svid;
bool off, reg_en_pin_pol;
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.buf = write_buf,
.len = sizeof(write_buf),
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.buf = read_buf,
.len = sizeof(read_buf),
}
};
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret < 0) {
dev_err(&client->dev, "i2c_transfer failed. %d", ret);
return ret;
}
/*
* 0x44[15] determines PMBus Operating Mode
* If bit is set then it is SVID mode.
*/
svid = !!(read_buf[1] & BIT(7));
/*
* Determine EN pin level for use in SVID mode.
* This is done with help of STATUS_BYTE bit 6(OFF) & ON_OFF_CONFIG bit 2(EN pin polarity).
*/
if (svid) {
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
if (ret < 0)
return ret;
off = !!(ret & PB_STATUS_OFF);
ret = i2c_smbus_read_byte_data(client, PMBUS_ON_OFF_CONFIG);
if (ret < 0)
return ret;
reg_en_pin_pol = !!(ret & PB_ON_OFF_CONFIG_POLARITY_HIGH);
data->en_pin_lvl = off ^ reg_en_pin_pol;
}
return svid;
}
static struct pmbus_driver_info tda38640_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
@ -26,7 +147,6 @@ static struct pmbus_driver_info tda38640_info = {
.format[PSC_CURRENT_IN] = linear,
.format[PSC_POWER] = linear,
.format[PSC_TEMPERATURE] = linear,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
| PMBUS_HAVE_IIN
@ -41,7 +161,37 @@ static struct pmbus_driver_info tda38640_info = {
static int tda38640_probe(struct i2c_client *client)
{
return pmbus_do_probe(client, &tda38640_info);
struct tda38640_data *data;
int svid;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(&data->info, &tda38640_info, sizeof(tda38640_info));
if (IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR) &&
of_property_read_bool(client->dev.of_node, "infineon,en-pin-fixed-level")) {
svid = svid_mode(client, data);
if (svid < 0) {
dev_err_probe(&client->dev, svid, "Could not determine operating mode.");
return svid;
}
/*
* Apply ON_OFF_CONFIG workaround as enabling the regulator using the
* OPERATION register doesn't work in SVID mode.
*
* One should configure PMBUS_ON_OFF_CONFIG here, but
* PB_ON_OFF_CONFIG_POWERUP_CONTROL and PB_ON_OFF_CONFIG_EN_PIN_REQ
* are ignored by the device.
* Only PB_ON_OFF_CONFIG_POLARITY_HIGH has an effect.
*/
if (svid) {
data->info.read_byte_data = tda38640_read_byte_data;
data->info.write_byte_data = tda38640_write_byte_data;
}
}
return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id tda38640_id[] = {

275
drivers/hwmon/powerz.c Normal file
View File

@ -0,0 +1,275 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023 Thomas Weißschuh <linux@weissschuh.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/usb.h>
#define DRIVER_NAME "powerz"
#define POWERZ_EP_CMD_OUT 0x01
#define POWERZ_EP_DATA_IN 0x81
struct powerz_sensor_data {
u8 _unknown_1[8];
__le32 V_bus;
__le32 I_bus;
__le32 V_bus_avg;
__le32 I_bus_avg;
u8 _unknown_2[8];
u8 temp[2];
__le16 V_cc1;
__le16 V_cc2;
__le16 V_dp;
__le16 V_dm;
__le16 V_dd;
u8 _unknown_3[4];
} __packed;
struct powerz_priv {
char transfer_buffer[64]; /* first member to satisfy DMA alignment */
struct mutex mutex;
struct completion completion;
struct urb *urb;
int status;
};
static const struct hwmon_channel_info *const powerz_info[] = {
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_AVERAGE,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_AVERAGE),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
NULL
};
static umode_t powerz_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static int powerz_read_string(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
if (type == hwmon_curr && attr == hwmon_curr_label) {
*str = "IBUS";
} else if (type == hwmon_in && attr == hwmon_in_label) {
if (channel == 0)
*str = "VBUS";
else if (channel == 1)
*str = "VCC1";
else if (channel == 2)
*str = "VCC2";
else if (channel == 3)
*str = "VDP";
else if (channel == 4)
*str = "VDM";
else if (channel == 5)
*str = "VDD";
else
return -EOPNOTSUPP;
} else if (type == hwmon_temp && attr == hwmon_temp_label) {
*str = "TEMP";
} else {
return -EOPNOTSUPP;
}
return 0;
}
static void powerz_usb_data_complete(struct urb *urb)
{
struct powerz_priv *priv = urb->context;
complete(&priv->completion);
}
static void powerz_usb_cmd_complete(struct urb *urb)
{
struct powerz_priv *priv = urb->context;
usb_fill_bulk_urb(urb, urb->dev,
usb_rcvbulkpipe(urb->dev, POWERZ_EP_DATA_IN),
priv->transfer_buffer, sizeof(priv->transfer_buffer),
powerz_usb_data_complete, priv);
priv->status = usb_submit_urb(urb, GFP_ATOMIC);
if (priv->status)
complete(&priv->completion);
}
static int powerz_read_data(struct usb_device *udev, struct powerz_priv *priv)
{
int ret;
priv->status = -ETIMEDOUT;
reinit_completion(&priv->completion);
priv->transfer_buffer[0] = 0x0c;
priv->transfer_buffer[1] = 0x00;
priv->transfer_buffer[2] = 0x02;
priv->transfer_buffer[3] = 0x00;
usb_fill_bulk_urb(priv->urb, udev,
usb_sndbulkpipe(udev, POWERZ_EP_CMD_OUT),
priv->transfer_buffer, 4, powerz_usb_cmd_complete,
priv);
ret = usb_submit_urb(priv->urb, GFP_KERNEL);
if (ret)
return ret;
if (!wait_for_completion_interruptible_timeout
(&priv->completion, msecs_to_jiffies(5))) {
usb_kill_urb(priv->urb);
return -EIO;
}
if (priv->urb->actual_length < sizeof(struct powerz_sensor_data))
return -EIO;
return priv->status;
}
static int powerz_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct usb_interface *intf = to_usb_interface(dev->parent);
struct usb_device *udev = interface_to_usbdev(intf);
struct powerz_priv *priv = usb_get_intfdata(intf);
struct powerz_sensor_data *data;
int ret;
if (!priv)
return -EIO; /* disconnected */
mutex_lock(&priv->mutex);
ret = powerz_read_data(udev, priv);
if (ret)
goto out;
data = (struct powerz_sensor_data *)priv->transfer_buffer;
if (type == hwmon_curr) {
if (attr == hwmon_curr_input)
*val = ((s32)le32_to_cpu(data->I_bus)) / 1000;
else if (attr == hwmon_curr_average)
*val = ((s32)le32_to_cpu(data->I_bus_avg)) / 1000;
else
ret = -EOPNOTSUPP;
} else if (type == hwmon_in) {
if (attr == hwmon_in_input) {
if (channel == 0)
*val = le32_to_cpu(data->V_bus) / 1000;
else if (channel == 1)
*val = le16_to_cpu(data->V_cc1) / 10;
else if (channel == 2)
*val = le16_to_cpu(data->V_cc2) / 10;
else if (channel == 3)
*val = le16_to_cpu(data->V_dp) / 10;
else if (channel == 4)
*val = le16_to_cpu(data->V_dm) / 10;
else if (channel == 5)
*val = le16_to_cpu(data->V_dd) / 10;
else
ret = -EOPNOTSUPP;
} else if (attr == hwmon_in_average && channel == 0) {
*val = le32_to_cpu(data->V_bus_avg) / 1000;
} else {
ret = -EOPNOTSUPP;
}
} else if (type == hwmon_temp && attr == hwmon_temp_input) {
*val = data->temp[1] * 2000 + data->temp[0] * 1000 / 128;
} else {
ret = -EOPNOTSUPP;
}
out:
mutex_unlock(&priv->mutex);
return ret;
}
static const struct hwmon_ops powerz_hwmon_ops = {
.is_visible = powerz_is_visible,
.read = powerz_read,
.read_string = powerz_read_string,
};
static const struct hwmon_chip_info powerz_chip_info = {
.ops = &powerz_hwmon_ops,
.info = powerz_info,
};
static int powerz_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct powerz_priv *priv;
struct device *hwmon_dev;
struct device *parent;
parent = &intf->dev;
priv = devm_kzalloc(parent, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!priv->urb)
return -ENOMEM;
mutex_init(&priv->mutex);
init_completion(&priv->completion);
hwmon_dev =
devm_hwmon_device_register_with_info(parent, DRIVER_NAME, priv,
&powerz_chip_info, NULL);
if (IS_ERR(hwmon_dev)) {
usb_free_urb(priv->urb);
return PTR_ERR(hwmon_dev);
}
usb_set_intfdata(intf, priv);
return 0;
}
static void powerz_disconnect(struct usb_interface *intf)
{
struct powerz_priv *priv = usb_get_intfdata(intf);
mutex_lock(&priv->mutex);
usb_kill_urb(priv->urb);
usb_free_urb(priv->urb);
mutex_unlock(&priv->mutex);
}
static const struct usb_device_id powerz_id_table[] = {
{ USB_DEVICE_INTERFACE_NUMBER(0x5FC9, 0x0061, 0x00) }, /* ChargerLAB POWER-Z KM002C */
{ USB_DEVICE_INTERFACE_NUMBER(0x5FC9, 0x0063, 0x00) }, /* ChargerLAB POWER-Z KM003C */
{ }
};
MODULE_DEVICE_TABLE(usb, powerz_id_table);
static struct usb_driver powerz_driver = {
.name = DRIVER_NAME,
.id_table = powerz_id_table,
.probe = powerz_probe,
.disconnect = powerz_disconnect,
};
module_usb_driver(powerz_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net>");
MODULE_DESCRIPTION("ChargerLAB POWER-Z USB-C tester");

View File

@ -6,9 +6,13 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bits.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/pm.h>
#include <linux/init.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
@ -32,6 +36,10 @@
#define SCH5627_REG_PRIMARY_ID 0x3f
#define SCH5627_REG_CTRL 0x40
#define SCH5627_CTRL_START BIT(0)
#define SCH5627_CTRL_LOCK BIT(1)
#define SCH5627_CTRL_VBAT BIT(4)
#define SCH5627_NO_TEMPS 8
#define SCH5627_NO_FANS 4
#define SCH5627_NO_IN 5
@ -67,11 +75,9 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
"VCC", "VTT", "VBAT", "VTR", "V_IN" };
struct sch5627_data {
struct regmap *regmap;
unsigned short addr;
u8 control;
u8 temp_max[SCH5627_NO_TEMPS];
u8 temp_crit[SCH5627_NO_TEMPS];
u16 fan_min[SCH5627_NO_FANS];
struct mutex update_lock;
unsigned long last_battery; /* In jiffies */
@ -86,6 +92,36 @@ struct sch5627_data {
u16 in[SCH5627_NO_IN];
};
static const struct regmap_range sch5627_tunables_ranges[] = {
regmap_reg_range(0x57, 0x57),
regmap_reg_range(0x59, 0x59),
regmap_reg_range(0x5B, 0x5B),
regmap_reg_range(0x5D, 0x5D),
regmap_reg_range(0x5F, 0x5F),
regmap_reg_range(0x61, 0x69),
regmap_reg_range(0x96, 0x9B),
regmap_reg_range(0xA0, 0xA3),
regmap_reg_range(0x184, 0x184),
regmap_reg_range(0x186, 0x186),
regmap_reg_range(0x1A8, 0x1A9),
};
static const struct regmap_access_table sch5627_tunables_table = {
.yes_ranges = sch5627_tunables_ranges,
.n_yes_ranges = ARRAY_SIZE(sch5627_tunables_ranges),
};
static const struct regmap_config sch5627_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.wr_table = &sch5627_tunables_table,
.rd_table = &sch5627_tunables_table,
.cache_type = REGCACHE_RBTREE,
.use_single_read = true,
.use_single_write = true,
.can_sleep = true,
};
static int sch5627_update_temp(struct sch5627_data *data)
{
int ret = 0;
@ -147,7 +183,8 @@ static int sch5627_update_in(struct sch5627_data *data)
/* Trigger a Vbat voltage measurement every 5 minutes */
if (time_after(jiffies, data->last_battery + 300 * HZ)) {
sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10);
sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
data->control | SCH5627_CTRL_VBAT);
data->last_battery = jiffies;
}
@ -171,38 +208,6 @@ abort:
return ret;
}
static int sch5627_read_limits(struct sch5627_data *data)
{
int i, val;
for (i = 0; i < SCH5627_NO_TEMPS; i++) {
/*
* Note what SMSC calls ABS, is what lm_sensors calls max
* (aka high), and HIGH is what lm_sensors calls crit.
*/
val = sch56xx_read_virtual_reg(data->addr,
SCH5627_REG_TEMP_ABS[i]);
if (val < 0)
return val;
data->temp_max[i] = val;
val = sch56xx_read_virtual_reg(data->addr,
SCH5627_REG_TEMP_HIGH[i]);
if (val < 0)
return val;
data->temp_crit[i] = val;
}
for (i = 0; i < SCH5627_NO_FANS; i++) {
val = sch56xx_read_virtual_reg16(data->addr,
SCH5627_REG_FAN_MIN[i]);
if (val < 0)
return val;
data->fan_min[i] = val;
}
return 0;
}
static int reg_to_temp(u16 reg)
{
return (reg * 625) / 10 - 64000;
@ -223,11 +228,65 @@ static int reg_to_rpm(u16 reg)
return 5400540 / reg;
}
static u8 sch5627_temp_limit_to_reg(long value)
{
long limit = (value / 1000) + 64;
return clamp_val(limit, 0, U8_MAX);
}
static u16 sch5627_rpm_to_reg(long value)
{
long pulses;
if (value <= 0)
return U16_MAX - 1;
pulses = 5400540 / value;
return clamp_val(pulses, 1, U16_MAX - 1);
}
static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
{
if (type == hwmon_pwm && attr == hwmon_pwm_auto_channels_temp)
return 0644;
const struct sch5627_data *data = drvdata;
/* Once the lock bit is set, the virtual registers become read-only
* until the next power cycle.
*/
if (data->control & SCH5627_CTRL_LOCK)
return 0444;
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_max:
case hwmon_temp_crit:
return 0644;
default:
break;
}
break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_min:
return 0644;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_auto_channels_temp:
return 0644;
default:
break;
}
break;
default:
break;
}
return 0444;
}
@ -236,24 +295,37 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at
long *val)
{
struct sch5627_data *data = dev_get_drvdata(dev);
int ret;
int ret, value;
switch (type) {
case hwmon_temp:
ret = sch5627_update_temp(data);
if (ret < 0)
return ret;
switch (attr) {
case hwmon_temp_input:
ret = sch5627_update_temp(data);
if (ret < 0)
return ret;
*val = reg_to_temp(data->temp[channel]);
return 0;
case hwmon_temp_max:
*val = reg_to_temp_limit(data->temp_max[channel]);
ret = regmap_read(data->regmap, SCH5627_REG_TEMP_ABS[channel], &value);
if (ret < 0)
return ret;
*val = reg_to_temp_limit((u8)value);
return 0;
case hwmon_temp_crit:
*val = reg_to_temp_limit(data->temp_crit[channel]);
ret = regmap_read(data->regmap, SCH5627_REG_TEMP_HIGH[channel], &value);
if (ret < 0)
return ret;
*val = reg_to_temp_limit((u8)value);
return 0;
case hwmon_temp_fault:
ret = sch5627_update_temp(data);
if (ret < 0)
return ret;
*val = (data->temp[channel] == 0);
return 0;
default:
@ -261,23 +333,35 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at
}
break;
case hwmon_fan:
ret = sch5627_update_fan(data);
if (ret < 0)
return ret;
switch (attr) {
case hwmon_fan_input:
ret = sch5627_update_fan(data);
if (ret < 0)
return ret;
ret = reg_to_rpm(data->fan[channel]);
if (ret < 0)
return ret;
*val = ret;
return 0;
case hwmon_fan_min:
ret = reg_to_rpm(data->fan_min[channel]);
ret = sch56xx_regmap_read16(data->regmap, SCH5627_REG_FAN_MIN[channel],
&value);
if (ret < 0)
return ret;
ret = reg_to_rpm((u16)value);
if (ret < 0)
return ret;
*val = ret;
return 0;
case hwmon_fan_fault:
ret = sch5627_update_fan(data);
if (ret < 0)
return ret;
*val = (data->fan[channel] == 0xffff);
return 0;
default:
@ -287,15 +371,11 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_auto_channels_temp:
mutex_lock(&data->update_lock);
ret = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel]);
mutex_unlock(&data->update_lock);
ret = regmap_read(data->regmap, SCH5627_REG_PWM_MAP[channel], &value);
if (ret < 0)
return ret;
*val = ret;
*val = value;
return 0;
default:
break;
@ -345,9 +425,33 @@ static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 a
long val)
{
struct sch5627_data *data = dev_get_drvdata(dev);
int ret;
u16 fan;
u8 temp;
switch (type) {
case hwmon_temp:
temp = sch5627_temp_limit_to_reg(val);
switch (attr) {
case hwmon_temp_max:
return regmap_write(data->regmap, SCH5627_REG_TEMP_ABS[channel], temp);
case hwmon_temp_crit:
return regmap_write(data->regmap, SCH5627_REG_TEMP_HIGH[channel], temp);
default:
break;
}
break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_min:
fan = sch5627_rpm_to_reg(val);
return sch56xx_regmap_write16(data->regmap, SCH5627_REG_FAN_MIN[channel],
fan);
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_auto_channels_temp:
@ -355,12 +459,7 @@ static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 a
if (val > U8_MAX || val < 0)
return -EINVAL;
mutex_lock(&data->update_lock);
ret = sch56xx_write_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel],
val);
mutex_unlock(&data->update_lock);
return ret;
return regmap_write(data->regmap, SCH5627_REG_PWM_MAP[channel], val);
default:
break;
}
@ -422,7 +521,7 @@ static int sch5627_probe(struct platform_device *pdev)
{
struct sch5627_data *data;
struct device *hwmon_dev;
int err, build_code, build_id, hwmon_rev, val;
int build_code, build_id, hwmon_rev, val;
data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data),
GFP_KERNEL);
@ -483,24 +582,21 @@ static int sch5627_probe(struct platform_device *pdev)
return val;
data->control = val;
if (!(data->control & 0x01)) {
if (!(data->control & SCH5627_CTRL_START)) {
pr_err("hardware monitoring not enabled\n");
return -ENODEV;
}
data->regmap = devm_regmap_init_sch56xx(&pdev->dev, &data->update_lock, data->addr,
&sch5627_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
/* Trigger a Vbat voltage measurement, so that we get a valid reading
the first time we read Vbat */
sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
data->control | 0x10);
sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | SCH5627_CTRL_VBAT);
data->last_battery = jiffies;
/*
* Read limits, we do this only once as reading a register on
* the sch5627 is quite expensive (and they don't change).
*/
err = sch5627_read_limits(data);
if (err)
return err;
pr_info("found %s chip at %#hx\n", DEVNAME, data->addr);
pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n",
build_code, build_id, hwmon_rev);
@ -518,6 +614,30 @@ static int sch5627_probe(struct platform_device *pdev)
return 0;
}
static int sch5627_suspend(struct device *dev)
{
struct sch5627_data *data = dev_get_drvdata(dev);
regcache_cache_only(data->regmap, true);
regcache_mark_dirty(data->regmap);
return 0;
}
static int sch5627_resume(struct device *dev)
{
struct sch5627_data *data = dev_get_drvdata(dev);
regcache_cache_only(data->regmap, false);
/* We must not access the virtual registers when the lock bit is set */
if (data->control & SCH5627_CTRL_LOCK)
return regcache_drop_region(data->regmap, 0, U16_MAX);
return regcache_sync(data->regmap);
}
static DEFINE_SIMPLE_DEV_PM_OPS(sch5627_dev_pm_ops, sch5627_suspend, sch5627_resume);
static const struct platform_device_id sch5627_device_id[] = {
{
.name = "sch5627",
@ -529,6 +649,7 @@ MODULE_DEVICE_TABLE(platform, sch5627_device_id);
static struct platform_driver sch5627_driver = {
.driver = {
.name = DRVNAME,
.pm = pm_sleep_ptr(&sch5627_dev_pm_ops),
},
.probe = sch5627_probe,
.id_table = sch5627_device_id,

View File

@ -367,7 +367,7 @@ static struct sensor_device_attribute sch5636_fan_attr[] = {
SENSOR_ATTR_RO(fan8_alarm, fan_alarm, 7),
};
static int sch5636_remove(struct platform_device *pdev)
static void sch5636_remove(struct platform_device *pdev)
{
struct sch5636_data *data = platform_get_drvdata(pdev);
int i;
@ -385,8 +385,6 @@ static int sch5636_remove(struct platform_device *pdev)
for (i = 0; i < SCH5636_NO_FANS * 3; i++)
device_remove_file(&pdev->dev,
&sch5636_fan_attr[i].dev_attr);
return 0;
}
static int sch5636_probe(struct platform_device *pdev)
@ -515,7 +513,7 @@ static struct platform_driver sch5636_driver = {
.name = DRVNAME,
},
.probe = sch5636_probe,
.remove = sch5636_remove,
.remove_new = sch5636_remove,
.id_table = sch5636_device_id,
};

View File

@ -7,10 +7,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/regmap.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/acpi.h>
@ -21,10 +20,7 @@
#include <linux/slab.h>
#include "sch56xx-common.h"
static bool ignore_dmi;
module_param(ignore_dmi, bool, 0);
MODULE_PARM_DESC(ignore_dmi, "Omit DMI check for supported devices (default=0)");
/* Insmod parameters */
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
@ -64,6 +60,11 @@ struct sch56xx_watchdog_data {
u8 watchdog_output_enable;
};
struct sch56xx_bus_context {
struct mutex *lock; /* Used to serialize access to the mailbox registers */
u16 addr;
};
static struct platform_device *sch56xx_pdev;
/* Super I/O functions */
@ -243,6 +244,107 @@ int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
}
EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
/*
* Regmap support
*/
int sch56xx_regmap_read16(struct regmap *map, unsigned int reg, unsigned int *val)
{
int lsb, msb, ret;
/* See sch56xx_read_virtual_reg16() */
ret = regmap_read(map, reg, &lsb);
if (ret < 0)
return ret;
ret = regmap_read(map, reg + 1, &msb);
if (ret < 0)
return ret;
*val = lsb | (msb << 8);
return 0;
}
EXPORT_SYMBOL(sch56xx_regmap_read16);
int sch56xx_regmap_write16(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
ret = regmap_write(map, reg, val & 0xff);
if (ret < 0)
return ret;
return regmap_write(map, reg + 1, (val >> 8) & 0xff);
}
EXPORT_SYMBOL(sch56xx_regmap_write16);
static int sch56xx_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct sch56xx_bus_context *bus = context;
int ret;
mutex_lock(bus->lock);
ret = sch56xx_write_virtual_reg(bus->addr, (u16)reg, (u8)val);
mutex_unlock(bus->lock);
return ret;
}
static int sch56xx_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct sch56xx_bus_context *bus = context;
int ret;
mutex_lock(bus->lock);
ret = sch56xx_read_virtual_reg(bus->addr, (u16)reg);
mutex_unlock(bus->lock);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static void sch56xx_free_context(void *context)
{
kfree(context);
}
static const struct regmap_bus sch56xx_bus = {
.reg_write = sch56xx_reg_write,
.reg_read = sch56xx_reg_read,
.free_context = sch56xx_free_context,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
struct regmap *devm_regmap_init_sch56xx(struct device *dev, struct mutex *lock, u16 addr,
const struct regmap_config *config)
{
struct sch56xx_bus_context *context;
struct regmap *map;
if (config->reg_bits != 16 && config->val_bits != 8)
return ERR_PTR(-EOPNOTSUPP);
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return ERR_PTR(-ENOMEM);
context->lock = lock;
context->addr = addr;
map = devm_regmap_init(dev, &sch56xx_bus, context, config);
if (IS_ERR(map))
kfree(context);
return map;
}
EXPORT_SYMBOL(devm_regmap_init_sch56xx);
/*
* Watchdog routines
*/
@ -523,66 +625,11 @@ static int __init sch56xx_device_add(int address, const char *name)
return PTR_ERR_OR_ZERO(sch56xx_pdev);
}
static const struct dmi_system_id sch56xx_dmi_override_table[] __initconst = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS W380"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO P710"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO E9900"),
},
},
{ }
};
/* For autoloading only */
static const struct dmi_system_id sch56xx_dmi_table[] __initconst = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
},
},
{ }
};
MODULE_DEVICE_TABLE(dmi, sch56xx_dmi_table);
static int __init sch56xx_init(void)
{
const char *name = NULL;
int address;
const char *name = NULL;
if (!ignore_dmi) {
if (!dmi_check_system(sch56xx_dmi_table))
return -ENODEV;
if (!dmi_check_system(sch56xx_dmi_override_table)) {
/*
* Some machines like the Esprimo P720 and Esprimo C700 have
* onboard devices named " Antiope"/" Theseus" instead of
* "Antiope"/"Theseus", so we need to check for both.
*/
if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL))
return -ENODEV;
}
}
/*
* Some devices like the Esprimo C700 have both onboard devices,
* so we still have to check manually
*/
address = sch56xx_find(0x4e, &name);
if (address < 0)
address = sch56xx_find(0x2e, &name);

View File

@ -5,9 +5,15 @@
***************************************************************************/
#include <linux/mutex.h>
#include <linux/regmap.h>
struct sch56xx_watchdog_data;
struct regmap *devm_regmap_init_sch56xx(struct device *dev, struct mutex *lock, u16 addr,
const struct regmap_config *config);
int sch56xx_regmap_read16(struct regmap *map, unsigned int reg, unsigned int *val);
int sch56xx_regmap_write16(struct regmap *map, unsigned int reg, unsigned int val);
int sch56xx_read_virtual_reg(u16 addr, u16 reg);
int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val);
int sch56xx_read_virtual_reg16(u16 addr, u16 reg);

View File

@ -1017,7 +1017,7 @@ err_release_reg:
return ret;
}
static int sht15_remove(struct platform_device *pdev)
static void sht15_remove(struct platform_device *pdev)
{
struct sht15_data *data = platform_get_drvdata(pdev);
int ret;
@ -1033,8 +1033,6 @@ static int sht15_remove(struct platform_device *pdev)
regulator_unregister_notifier(data->reg, &data->nb);
regulator_disable(data->reg);
}
return 0;
}
static const struct platform_device_id sht15_device_ids[] = {
@ -1053,7 +1051,7 @@ static struct platform_driver sht15_driver = {
.of_match_table = of_match_ptr(sht15_dt_match),
},
.probe = sht15_probe,
.remove = sht15_remove,
.remove_new = sht15_remove,
.id_table = sht15_device_ids,
};
module_platform_driver(sht15_driver);

View File

@ -709,7 +709,7 @@ exit_remove_files:
return err;
}
static int sis5595_remove(struct platform_device *pdev)
static void sis5595_remove(struct platform_device *pdev)
{
struct sis5595_data *data = platform_get_drvdata(pdev);
@ -717,8 +717,6 @@ static int sis5595_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group);
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4);
sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1);
return 0;
}
static const struct pci_device_id sis5595_pci_ids[] = {
@ -790,7 +788,7 @@ static struct platform_driver sis5595_driver = {
.name = DRIVER_NAME,
},
.probe = sis5595_probe,
.remove = sis5595_remove,
.remove_new = sis5595_remove,
};
static int sis5595_pci_probe(struct pci_dev *dev,

View File

@ -73,9 +73,6 @@
#define TMP51X_PGA_DEFAULT 8
#define TMP51X_MAX_REGISTER_ADDR 0xFF
#define TMP512_TEMP_CONFIG_DEFAULT 0xBF80
#define TMP513_TEMP_CONFIG_DEFAULT 0xFF80
// Mask and shift
#define CURRENT_SENSE_VOLTAGE_320_MASK 0x1800
#define CURRENT_SENSE_VOLTAGE_160_MASK 0x1000
@ -113,6 +110,17 @@
#define MAX_TEMP_HYST 127500
#define TMP512_MAX_CHANNELS 3
#define TMP513_MAX_CHANNELS 4
#define TMP51X_TEMP_CONFIG_CONV_RATE GENMASK(9, 7)
#define TMP51X_TEMP_CONFIG_RC BIT(10)
#define TMP51X_TEMP_CHANNEL_MASK(n) (GENMASK((n) - 1, 0) << 11)
#define TMP51X_TEMP_CONFIG_CONT BIT(15)
#define TMP51X_TEMP_CONFIG_DEFAULT(n) \
(TMP51X_TEMP_CHANNEL_MASK(n) | TMP51X_TEMP_CONFIG_CONT | \
TMP51X_TEMP_CONFIG_CONV_RATE | TMP51X_TEMP_CONFIG_RC)
static const u8 TMP51X_TEMP_INPUT[4] = {
TMP51X_LOCAL_TEMP_RESULT,
TMP51X_REMOTE_TEMP_RESULT_1,
@ -152,10 +160,6 @@ static struct regmap_config tmp51x_regmap_config = {
.max_register = TMP51X_MAX_REGISTER_ADDR,
};
enum tmp51x_ids {
tmp512, tmp513
};
struct tmp51x_data {
u16 shunt_config;
u16 pga_gain;
@ -169,7 +173,7 @@ struct tmp51x_data {
u32 curr_lsb_ua;
u32 pwr_lsb_uw;
enum tmp51x_ids id;
u8 max_channels;
struct regmap *regmap;
};
@ -434,7 +438,7 @@ static umode_t tmp51x_is_visible(const void *_data,
switch (type) {
case hwmon_temp:
if (data->id == tmp512 && channel == 3)
if (channel >= data->max_channels)
return 0;
switch (attr) {
case hwmon_temp_input:
@ -585,7 +589,7 @@ static int tmp51x_init(struct tmp51x_data *data)
if (ret < 0)
return ret;
if (data->id == tmp513) {
if (data->max_channels == TMP513_MAX_CHANNELS) {
ret = regmap_write(data->regmap, TMP513_N_FACTOR_3,
data->nfactor[2] << 8);
if (ret < 0)
@ -601,22 +605,16 @@ static int tmp51x_init(struct tmp51x_data *data)
}
static const struct i2c_device_id tmp51x_id[] = {
{ "tmp512", tmp512 },
{ "tmp513", tmp513 },
{ "tmp512", TMP512_MAX_CHANNELS },
{ "tmp513", TMP513_MAX_CHANNELS },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp51x_id);
static const struct of_device_id tmp51x_of_match[] = {
{
.compatible = "ti,tmp512",
.data = (void *)tmp512
},
{
.compatible = "ti,tmp513",
.data = (void *)tmp513
},
{ },
{ .compatible = "ti,tmp512", .data = (void *)TMP512_MAX_CHANNELS },
{ .compatible = "ti,tmp513", .data = (void *)TMP513_MAX_CHANNELS },
{ }
};
MODULE_DEVICE_TABLE(of, tmp51x_of_match);
@ -655,7 +653,6 @@ static int tmp51x_pga_gain_to_reg(struct device *dev, struct tmp51x_data *data)
static int tmp51x_read_properties(struct device *dev, struct tmp51x_data *data)
{
int ret;
u32 nfactor[3];
u32 val;
ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val);
@ -673,10 +670,8 @@ static int tmp51x_read_properties(struct device *dev, struct tmp51x_data *data)
if (ret < 0)
return ret;
ret = device_property_read_u32_array(dev, "ti,nfactor", nfactor,
(data->id == tmp513) ? 3 : 2);
if (ret >= 0)
memcpy(data->nfactor, nfactor, (data->id == tmp513) ? 3 : 2);
device_property_read_u32_array(dev, "ti,nfactor", data->nfactor,
data->max_channels - 1);
// Check if shunt value is compatible with pga-gain
if (data->shunt_uohms > data->pga_gain * 40 * 1000 * 1000) {
@ -698,8 +693,7 @@ static void tmp51x_use_default(struct tmp51x_data *data)
static int tmp51x_configure(struct device *dev, struct tmp51x_data *data)
{
data->shunt_config = TMP51X_SHUNT_CONFIG_DEFAULT;
data->temp_config = (data->id == tmp513) ?
TMP513_TEMP_CONFIG_DEFAULT : TMP512_TEMP_CONFIG_DEFAULT;
data->temp_config = TMP51X_TEMP_CONFIG_DEFAULT(data->max_channels);
if (dev->of_node)
return tmp51x_read_properties(dev, data);
@ -720,7 +714,7 @@ static int tmp51x_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
data->id = (uintptr_t)i2c_get_match_data(client);
data->max_channels = (uintptr_t)i2c_get_match_data(client);
ret = tmp51x_configure(dev, data);
if (ret < 0) {

View File

@ -291,7 +291,7 @@ out_iounmap:
goto out;
}
static int env_remove(struct platform_device *op)
static void env_remove(struct platform_device *op)
{
struct env *p = platform_get_drvdata(op);
@ -300,8 +300,6 @@ static int env_remove(struct platform_device *op)
hwmon_device_unregister(p->hwmon_dev);
of_iounmap(&op->resource[0], p->regs, REG_SIZE);
}
return 0;
}
static const struct of_device_id env_match[] = {
@ -319,7 +317,7 @@ static struct platform_driver env_driver = {
.of_match_table = env_match,
},
.probe = env_probe,
.remove = env_remove,
.remove_new = env_remove,
};
module_platform_driver(env_driver);

View File

@ -182,7 +182,7 @@ exit_remove:
return err;
}
static int via_cputemp_remove(struct platform_device *pdev)
static void via_cputemp_remove(struct platform_device *pdev)
{
struct via_cputemp_data *data = platform_get_drvdata(pdev);
@ -190,7 +190,6 @@ static int via_cputemp_remove(struct platform_device *pdev)
if (data->vrm)
device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
return 0;
}
static struct platform_driver via_cputemp_driver = {
@ -198,7 +197,7 @@ static struct platform_driver via_cputemp_driver = {
.name = DRVNAME,
},
.probe = via_cputemp_probe,
.remove = via_cputemp_remove,
.remove_new = via_cputemp_remove,
};
struct pdev_entry {

View File

@ -786,14 +786,12 @@ exit_remove_files:
return err;
}
static int via686a_remove(struct platform_device *pdev)
static void via686a_remove(struct platform_device *pdev)
{
struct via686a_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
return 0;
}
static struct platform_driver via686a_driver = {
@ -801,7 +799,7 @@ static struct platform_driver via686a_driver = {
.name = DRIVER_NAME,
},
.probe = via686a_probe,
.remove = via686a_remove,
.remove_new = via686a_remove,
};
static const struct pci_device_id via686a_pci_ids[] = {

View File

@ -1208,14 +1208,12 @@ EXIT_DEV_REMOVE_SILENT:
return err;
}
static int vt1211_remove(struct platform_device *pdev)
static void vt1211_remove(struct platform_device *pdev)
{
struct vt1211_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
vt1211_remove_sysfs(pdev);
return 0;
}
static struct platform_driver vt1211_driver = {
@ -1223,7 +1221,7 @@ static struct platform_driver vt1211_driver = {
.name = DRVNAME,
},
.probe = vt1211_probe,
.remove = vt1211_remove,
.remove_new = vt1211_remove,
};
static int __init vt1211_device_add(unsigned short address)

View File

@ -892,7 +892,7 @@ exit_remove_files:
return err;
}
static int vt8231_remove(struct platform_device *pdev)
static void vt8231_remove(struct platform_device *pdev)
{
struct vt8231_data *data = platform_get_drvdata(pdev);
int i;
@ -906,8 +906,6 @@ static int vt8231_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
return 0;
}
@ -916,7 +914,7 @@ static struct platform_driver vt8231_driver = {
.name = DRIVER_NAME,
},
.probe = vt8231_probe,
.remove = vt8231_remove,
.remove_new = vt8231_remove,
};
static const struct pci_device_id vt8231_pci_ids[] = {

View File

@ -1828,7 +1828,7 @@ static int w83627hf_probe(struct platform_device *pdev)
return err;
}
static int w83627hf_remove(struct platform_device *pdev)
static void w83627hf_remove(struct platform_device *pdev)
{
struct w83627hf_data *data = platform_get_drvdata(pdev);
@ -1836,8 +1836,6 @@ static int w83627hf_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group);
sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt);
return 0;
}
static struct platform_driver w83627hf_driver = {
@ -1846,7 +1844,7 @@ static struct platform_driver w83627hf_driver = {
.pm = W83627HF_DEV_PM_OPS,
},
.probe = w83627hf_probe,
.remove = w83627hf_remove,
.remove_new = w83627hf_remove,
};
static int __init w83627hf_find(int sioaddr, unsigned short *addr,

View File

@ -1816,16 +1816,13 @@ w83781d_isa_probe(struct platform_device *pdev)
return err;
}
static int
w83781d_isa_remove(struct platform_device *pdev)
static void w83781d_isa_remove(struct platform_device *pdev)
{
struct w83781d_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
w83781d_remove_files(&pdev->dev);
device_remove_file(&pdev->dev, &dev_attr_name);
return 0;
}
static struct platform_driver w83781d_isa_driver = {
@ -1833,7 +1830,7 @@ static struct platform_driver w83781d_isa_driver = {
.name = "w83781d",
},
.probe = w83781d_isa_probe,
.remove = w83781d_isa_remove,
.remove_new = w83781d_isa_remove,
};
/* return 1 if a supported chip is found, 0 otherwise */

View File

@ -751,7 +751,7 @@ out_mbox_free:
return rc;
}
static int xgene_hwmon_remove(struct platform_device *pdev)
static void xgene_hwmon_remove(struct platform_device *pdev)
{
struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
@ -762,8 +762,6 @@ static int xgene_hwmon_remove(struct platform_device *pdev)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->pcc_chan);
return 0;
}
static const struct of_device_id xgene_hwmon_of_match[] = {
@ -774,7 +772,7 @@ MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
static struct platform_driver xgene_hwmon_driver = {
.probe = xgene_hwmon_probe,
.remove = xgene_hwmon_remove,
.remove_new = xgene_hwmon_remove,
.driver = {
.name = "xgene-slimpro-hwmon",
.of_match_table = xgene_hwmon_of_match,