mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-01 10:43:43 +00:00
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:
commit
f9a7eda4d7
128
Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml
Normal file
128
Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -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 = <®_vdd>;
|
||||
adi,comp-int;
|
||||
adi,alarm-pol = <0>;
|
||||
adi,fault-q = <1>;
|
||||
adi,timeout-enable;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -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.
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -26,6 +26,7 @@ properties:
|
||||
- ti,ina226
|
||||
- ti,ina230
|
||||
- ti,ina231
|
||||
- ti,ina237
|
||||
- ti,ina238
|
||||
|
||||
reg:
|
||||
|
121
Documentation/devicetree/bindings/hwmon/ti,ina3221.yaml
Normal file
121
Documentation/devicetree/bindings/hwmon/ti,ina3221.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
59
Documentation/devicetree/bindings/regulator/mps,mpq2286.yaml
Normal file
59
Documentation/devicetree/bindings/regulator/mps,mpq2286.yaml
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
43
Documentation/hwmon/ltc2991.rst
Normal file
43
Documentation/hwmon/ltc2991.rst
Normal 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
|
||||
=============== =================
|
@ -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
|
||||
|
@ -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
|
||||
=============== ===============================================
|
||||
|
30
Documentation/hwmon/powerz.rst
Normal file
30
Documentation/hwmon/powerz.rst
Normal 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.
|
@ -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.
|
||||
|
16
MAINTAINERS
16
MAINTAINERS
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -1428,7 +1428,7 @@ static int abituguru_probe(struct platform_device *pdev)
|
||||
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)
|
||||
|
@ -1061,7 +1061,7 @@ static int abituguru3_probe(struct platform_device *pdev)
|
||||
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)
|
||||
|
@ -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;
|
||||
|
@ -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) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -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)",
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
@ -479,7 +479,7 @@ static int da9052_hwmon_probe(struct platform_device *pdev)
|
||||
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",
|
||||
},
|
||||
|
@ -2710,14 +2710,12 @@ static int dme1737_isa_probe(struct platform_device *pdev)
|
||||
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,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
@ -1480,7 +1480,7 @@ static int f71805f_probe(struct platform_device *pdev)
|
||||
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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -555,7 +555,7 @@ static int i5k_amb_probe(struct platform_device *pdev)
|
||||
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)
|
||||
|
@ -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" },
|
||||
{ },
|
||||
};
|
||||
|
@ -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
437
drivers/hwmon/ltc2991.c
Normal 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, ®vals, 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, ®_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, ®_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, ®_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 = <c2991_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, <c2991_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,
|
||||
<c2991_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");
|
@ -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;
|
||||
|
@ -312,14 +312,12 @@ static int max197_probe(struct platform_device *pdev)
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -285,7 +285,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
|
||||
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,
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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], ®);
|
||||
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], ®);
|
||||
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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -1586,14 +1586,12 @@ static int pc87360_probe(struct platform_device *pdev)
|
||||
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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1115,14 +1115,12 @@ static int pc87427_probe(struct platform_device *pdev)
|
||||
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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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", },
|
||||
{ },
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
275
drivers/hwmon/powerz.c
Normal 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");
|
@ -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 @@ static int sch5627_update_in(struct sch5627_data *data)
|
||||
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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -1017,7 +1017,7 @@ static int sht15_probe(struct platform_device *pdev)
|
||||
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);
|
||||
|
@ -709,7 +709,7 @@ static int sis5595_probe(struct platform_device *pdev)
|
||||
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,
|
||||
|
@ -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) {
|
||||
|
@ -291,7 +291,7 @@ static int env_probe(struct platform_device *op)
|
||||
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);
|
||||
|
@ -182,7 +182,7 @@ static int via_cputemp_probe(struct platform_device *pdev)
|
||||
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 {
|
||||
|
@ -786,14 +786,12 @@ static int via686a_probe(struct platform_device *pdev)
|
||||
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[] = {
|
||||
|
@ -1208,14 +1208,12 @@ static int vt1211_probe(struct platform_device *pdev)
|
||||
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)
|
||||
|
@ -892,7 +892,7 @@ static int vt8231_probe(struct platform_device *pdev)
|
||||
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[] = {
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -751,7 +751,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user