diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml new file mode 100644 index 000000000000..011e5b65c79c --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml @@ -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 + +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; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml index 2dc8b07b4d3b..f60e06ab7d0a 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml @@ -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; }; }; ... diff --git a/Documentation/devicetree/bindings/hwmon/ina3221.txt b/Documentation/devicetree/bindings/hwmon/ina3221.txt deleted file mode 100644 index fa63b6171407..000000000000 --- a/Documentation/devicetree/bindings/hwmon/ina3221.txt +++ /dev/null @@ -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>; - }; -}; diff --git a/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt index 8523777f560c..18095ba87a5a 100644 --- a/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt +++ b/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt @@ -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. diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/infineon,tda38640.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,tda38640.yaml new file mode 100644 index 000000000000..ded1c115764b --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,tda38640.yaml @@ -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 + +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>; + }; + }; diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml index 8648877d2d01..378d1f6aeeb3 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml @@ -26,6 +26,7 @@ properties: - ti,ina226 - ti,ina230 - ti,ina231 + - ti,ina237 - ti,ina238 reg: diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina3221.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina3221.yaml new file mode 100644 index 000000000000..5f10f1207d69 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ti,ina3221.yaml @@ -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 + - Guenter Roeck + +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>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/regulator/mps,mpq2286.yaml b/Documentation/devicetree/bindings/regulator/mps,mpq2286.yaml new file mode 100644 index 000000000000..1296f9b30862 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/mps,mpq2286.yaml @@ -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 + +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; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 430a814f64a5..9a1443ec3aaa 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -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 diff --git a/Documentation/hwmon/adt7475.rst b/Documentation/hwmon/adt7475.rst index ef3ea1ea9bc1..f90f769d82d6 100644 --- a/Documentation/hwmon/adt7475.rst +++ b/Documentation/hwmon/adt7475.rst @@ -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 diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst index 94dc2d93d180..cb073c79479c 100644 --- a/Documentation/hwmon/aquacomputer_d5next.rst +++ b/Documentation/hwmon/aquacomputer_d5next.rst @@ -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. diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 7e3cd5b6686f..0bf99ba406dd 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -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 diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 88dadea85cfc..72f4e6065bae 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -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 diff --git a/Documentation/hwmon/ltc2991.rst b/Documentation/hwmon/ltc2991.rst new file mode 100644 index 000000000000..15d8b4d7e471 --- /dev/null +++ b/Documentation/hwmon/ltc2991.rst @@ -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 + + +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 +=============== ================= diff --git a/Documentation/hwmon/max31827.rst b/Documentation/hwmon/max31827.rst index b0971d05b8a4..9a1055a007cf 100644 --- a/Documentation/hwmon/max31827.rst +++ b/Documentation/hwmon/max31827.rst @@ -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 diff --git a/Documentation/hwmon/nct6683.rst b/Documentation/hwmon/nct6683.rst index 2e1408d174bd..3e7f6ee779c2 100644 --- a/Documentation/hwmon/nct6683.rst +++ b/Documentation/hwmon/nct6683.rst @@ -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 =============== =============================================== diff --git a/Documentation/hwmon/powerz.rst b/Documentation/hwmon/powerz.rst new file mode 100644 index 000000000000..317084e0b76b --- /dev/null +++ b/Documentation/hwmon/powerz.rst @@ -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 + +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. diff --git a/Documentation/hwmon/sch5627.rst b/Documentation/hwmon/sch5627.rst index ecb4fc84d045..8639dff234fc 100644 --- a/Documentation/hwmon/sch5627.rst +++ b/Documentation/hwmon/sch5627.rst @@ -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. diff --git a/MAINTAINERS b/MAINTAINERS index 9277814f30d0..bcc6a0533412 100644 --- a/MAINTAINERS +++ b/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 +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/powerz.rst +F: drivers/hwmon/powerz.c + CHECKPATCH M: Andy Whitcroft M: Joe Perches @@ -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 +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á L: linux-iio@vger.kernel.org @@ -19441,6 +19456,7 @@ F: drivers/net/ethernet/sfc/ SFCTEMP HWMON DRIVER M: Emil Renner Berthing +M: Hal Feng L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ec38c8892158..cf27523eed5a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -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" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4ac9452b5430..e84bd9685b5c 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -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 diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index a7cae6568155..93653ea05430 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1428,7 +1428,7 @@ abituguru_probe_error: return res; } -static int abituguru_remove(struct platform_device *pdev) +static void abituguru_remove(struct platform_device *pdev) { int i; struct abituguru_data *data = platform_get_drvdata(pdev); @@ -1439,8 +1439,6 @@ static int abituguru_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) device_remove_file(&pdev->dev, &abituguru_sysfs_attr[i].dev_attr); - - return 0; } static struct abituguru_data *abituguru_update_device(struct device *dev) @@ -1533,7 +1531,7 @@ static struct platform_driver abituguru_driver = { .pm = pm_sleep_ptr(&abituguru_pm), }, .probe = abituguru_probe, - .remove = abituguru_remove, + .remove_new = abituguru_remove, }; static int __init abituguru_detect(void) diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index afb21f73032d..4501f0e49efb 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -1061,7 +1061,7 @@ abituguru3_probe_error: return res; } -static int abituguru3_remove(struct platform_device *pdev) +static void abituguru3_remove(struct platform_device *pdev) { int i; struct abituguru3_data *data = platform_get_drvdata(pdev); @@ -1072,7 +1072,6 @@ static int abituguru3_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++) device_remove_file(&pdev->dev, &abituguru3_sysfs_attr[i].dev_attr); - return 0; } static struct abituguru3_data *abituguru3_update_device(struct device *dev) @@ -1153,7 +1152,7 @@ static struct platform_driver abituguru3_driver = { .pm = pm_sleep_ptr(&abituguru3_pm), }, .probe = abituguru3_probe, - .remove = abituguru3_remove, + .remove_new = abituguru3_remove, }; static int __init abituguru3_dmi_detect(void) diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 03acadc3a6cb..4224ffb30483 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -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; diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 023807859be7..4fdd2e12427b 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -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 * Copyright 2022 Jack Doan @@ -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) }, { } }; diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 51f9c2db403e..36f9e38000d5 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -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)", diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c index 5fd136baf1cd..19b9bf3d75ef 100644 --- a/drivers/hwmon/axi-fan-control.c +++ b/drivers/hwmon/axi-fan-control.c @@ -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 = { diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index eba94f68585a..ba82d1e79c13 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -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) diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index ed6c5df94fdf..2bd7ae8100d7 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -479,7 +479,7 @@ exit_regulator: return err; } -static int da9052_hwmon_remove(struct platform_device *pdev) +static void da9052_hwmon_remove(struct platform_device *pdev) { struct da9052_hwmon *hwmon = platform_get_drvdata(pdev); @@ -487,13 +487,11 @@ static int da9052_hwmon_remove(struct platform_device *pdev) da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon); regulator_disable(hwmon->tsiref); } - - return 0; } static struct platform_driver da9052_hwmon_driver = { .probe = da9052_hwmon_probe, - .remove = da9052_hwmon_remove, + .remove_new = da9052_hwmon_remove, .driver = { .name = "da9052-hwmon", }, diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index cdbf3dff9172..3dcef221041d 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2710,14 +2710,12 @@ exit_remove_files: return err; } -static int dme1737_isa_remove(struct platform_device *pdev) +static void dme1737_isa_remove(struct platform_device *pdev) { struct dme1737_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); dme1737_remove_files(&pdev->dev); - - return 0; } static struct platform_driver dme1737_isa_driver = { @@ -2725,7 +2723,7 @@ static struct platform_driver dme1737_isa_driver = { .name = "dme1737", }, .probe = dme1737_isa_probe, - .remove = dme1737_isa_remove, + .remove_new = dme1737_isa_remove, }; /* --------------------------------------------------------------------- diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 7f20edb0677c..243c570dee4c 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1480,7 +1480,7 @@ exit_remove_files: return err; } -static int f71805f_remove(struct platform_device *pdev) +static void f71805f_remove(struct platform_device *pdev) { struct f71805f_data *data = platform_get_drvdata(pdev); int i; @@ -1490,8 +1490,6 @@ static int f71805f_remove(struct platform_device *pdev) for (i = 0; i < 4; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); - - return 0; } static struct platform_driver f71805f_driver = { @@ -1499,7 +1497,7 @@ static struct platform_driver f71805f_driver = { .name = DRVNAME, }, .probe = f71805f_probe, - .remove = f71805f_remove, + .remove_new = f71805f_remove, }; static int __init f71805f_device_add(unsigned short address, diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 27207ec6f7fe..7c941d320a18 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -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) diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c index ac574e46d069..01ea9a3062bc 100644 --- a/drivers/hwmon/hs3001.c +++ b/drivers/hwmon/hs3001.c @@ -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, diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 783fa936e4d1..ff48913fe6bf 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -555,7 +555,7 @@ err: return res; } -static int i5k_amb_remove(struct platform_device *pdev) +static void i5k_amb_remove(struct platform_device *pdev) { int i; struct i5k_amb_data *data = platform_get_drvdata(pdev); @@ -568,7 +568,6 @@ static int i5k_amb_remove(struct platform_device *pdev) iounmap(data->amb_mmio); release_mem_region(data->amb_base, data->amb_len); kfree(data); - return 0; } static struct platform_driver i5k_amb_driver = { @@ -576,7 +575,7 @@ static struct platform_driver i5k_amb_driver = { .name = DRVNAME, }, .probe = i5k_amb_probe, - .remove = i5k_amb_remove, + .remove_new = i5k_amb_remove, }; static int __init i5k_amb_init(void) diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index f519c22d3907..ca9f5d2c811b 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -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" }, { }, }; diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 5ab944056ec0..5ffdc94db436 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -6,6 +6,7 @@ * Andrew F. Davis */ +#include #include #include #include @@ -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; diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c new file mode 100644 index 000000000000..bd63c61129a9 --- /dev/null +++ b/drivers/hwmon/ltc2991.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Analog Devices, Inc. + * Author: Antoniu Miclaus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Analog Devices LTC2991 HWMON Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index 589bcd07ce7f..001799bc28ed 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -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; diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c index 56add579e32f..bb30403f81ca 100644 --- a/drivers/hwmon/max197.c +++ b/drivers/hwmon/max197.c @@ -312,14 +312,12 @@ error: return ret; } -static int max197_remove(struct platform_device *pdev) +static void max197_remove(struct platform_device *pdev) { struct max197_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); - - return 0; } static const struct platform_device_id max197_device_ids[] = { @@ -334,7 +332,7 @@ static struct platform_driver max197_driver = { .name = "max197", }, .probe = max197_probe, - .remove = max197_remove, + .remove_new = max197_remove, .id_table = max197_device_ids, }; module_platform_driver(max197_driver); diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 602f4e4f81ff..fd1fed1a797c 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -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; diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index ff147e5e1b8c..67471c9cd4d4 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -285,7 +285,7 @@ out_err_create_16chans: return ret; } -static int mc13783_adc_remove(struct platform_device *pdev) +static void mc13783_adc_remove(struct platform_device *pdev) { struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; @@ -299,8 +299,6 @@ static int mc13783_adc_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); - - return 0; } static const struct platform_device_id mc13783_adc_idtable[] = { @@ -317,7 +315,7 @@ static const struct platform_device_id mc13783_adc_idtable[] = { MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable); static struct platform_driver mc13783_adc_driver = { - .remove = mc13783_adc_remove, + .remove_new = mc13783_adc_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index f673f7d07941..3f3f7a88413e 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -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; diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index b5b81bd83bb1..d928eb8ae5a3 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -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); diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 81bf03dad6bb..0adeeab7ee03 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -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; } diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 10ed3f4335d4..4702e4edc662 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -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) { diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index 96521363b696..b5993c79c09e 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -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); diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index a4adc8bd531f..926ea1fe133c 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1586,14 +1586,12 @@ error: return err; } -static int pc87360_remove(struct platform_device *pdev) +static void pc87360_remove(struct platform_device *pdev) { struct pc87360_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); pc87360_remove_files(&pdev->dev); - - return 0; } /* @@ -1604,7 +1602,7 @@ static struct platform_driver pc87360_driver = { .name = DRIVER_NAME, }, .probe = pc87360_probe, - .remove = pc87360_remove, + .remove_new = pc87360_remove, }; /* diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index eaab83d879fb..7bca04eb4ee4 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -1115,14 +1115,12 @@ exit_remove_files: return err; } -static int pc87427_remove(struct platform_device *pdev) +static void pc87427_remove(struct platform_device *pdev) { struct pc87427_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); pc87427_remove_files(&pdev->dev); - - return 0; } @@ -1131,7 +1129,7 @@ static struct platform_driver pc87427_driver = { .name = DRVNAME, }, .probe = pc87427_probe, - .remove = pc87427_remove, + .remove_new = pc87427_remove, }; static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data) diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index f9aa576495a5..5d13bbfc8f47 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -3,6 +3,7 @@ * Copyright (C) 2017 IBM Corp. */ +#include #include #include #include @@ -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; diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index 26ba50633100..b9bb469e2d8f 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -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)); diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 6c62f01da7c6..67487867c70f 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -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", }, { }, }; diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index b0832a4c690d..fb442fae7b3e 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -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); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index 450b0273fb59..09cd114b1736 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -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[] = { diff --git a/drivers/hwmon/powerz.c b/drivers/hwmon/powerz.c new file mode 100644 index 000000000000..cfb635f94d66 --- /dev/null +++ b/drivers/hwmon/powerz.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Thomas Weißschuh + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("ChargerLAB POWER-Z USB-C tester"); diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 1bbda3b05532..1891d4d75aa9 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -6,9 +6,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include #include #include +#include #include +#include #include #include #include @@ -32,6 +36,10 @@ #define SCH5627_REG_PRIMARY_ID 0x3f #define SCH5627_REG_CTRL 0x40 +#define SCH5627_CTRL_START BIT(0) +#define SCH5627_CTRL_LOCK BIT(1) +#define SCH5627_CTRL_VBAT BIT(4) + #define SCH5627_NO_TEMPS 8 #define SCH5627_NO_FANS 4 #define SCH5627_NO_IN 5 @@ -67,11 +75,9 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { "VCC", "VTT", "VBAT", "VTR", "V_IN" }; struct sch5627_data { + struct regmap *regmap; unsigned short addr; u8 control; - u8 temp_max[SCH5627_NO_TEMPS]; - u8 temp_crit[SCH5627_NO_TEMPS]; - u16 fan_min[SCH5627_NO_FANS]; struct mutex update_lock; unsigned long last_battery; /* In jiffies */ @@ -86,6 +92,36 @@ struct sch5627_data { u16 in[SCH5627_NO_IN]; }; +static const struct regmap_range sch5627_tunables_ranges[] = { + regmap_reg_range(0x57, 0x57), + regmap_reg_range(0x59, 0x59), + regmap_reg_range(0x5B, 0x5B), + regmap_reg_range(0x5D, 0x5D), + regmap_reg_range(0x5F, 0x5F), + regmap_reg_range(0x61, 0x69), + regmap_reg_range(0x96, 0x9B), + regmap_reg_range(0xA0, 0xA3), + regmap_reg_range(0x184, 0x184), + regmap_reg_range(0x186, 0x186), + regmap_reg_range(0x1A8, 0x1A9), +}; + +static const struct regmap_access_table sch5627_tunables_table = { + .yes_ranges = sch5627_tunables_ranges, + .n_yes_ranges = ARRAY_SIZE(sch5627_tunables_ranges), +}; + +static const struct regmap_config sch5627_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .wr_table = &sch5627_tunables_table, + .rd_table = &sch5627_tunables_table, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .can_sleep = true, +}; + static int sch5627_update_temp(struct sch5627_data *data) { int ret = 0; @@ -147,7 +183,8 @@ static int sch5627_update_in(struct sch5627_data *data) /* Trigger a Vbat voltage measurement every 5 minutes */ if (time_after(jiffies, data->last_battery + 300 * HZ)) { - sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10); + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, + data->control | SCH5627_CTRL_VBAT); data->last_battery = jiffies; } @@ -171,38 +208,6 @@ abort: return ret; } -static int sch5627_read_limits(struct sch5627_data *data) -{ - int i, val; - - for (i = 0; i < SCH5627_NO_TEMPS; i++) { - /* - * Note what SMSC calls ABS, is what lm_sensors calls max - * (aka high), and HIGH is what lm_sensors calls crit. - */ - val = sch56xx_read_virtual_reg(data->addr, - SCH5627_REG_TEMP_ABS[i]); - if (val < 0) - return val; - data->temp_max[i] = val; - - val = sch56xx_read_virtual_reg(data->addr, - SCH5627_REG_TEMP_HIGH[i]); - if (val < 0) - return val; - data->temp_crit[i] = val; - } - for (i = 0; i < SCH5627_NO_FANS; i++) { - val = sch56xx_read_virtual_reg16(data->addr, - SCH5627_REG_FAN_MIN[i]); - if (val < 0) - return val; - data->fan_min[i] = val; - } - - return 0; -} - static int reg_to_temp(u16 reg) { return (reg * 625) / 10 - 64000; @@ -223,11 +228,65 @@ static int reg_to_rpm(u16 reg) return 5400540 / reg; } +static u8 sch5627_temp_limit_to_reg(long value) +{ + long limit = (value / 1000) + 64; + + return clamp_val(limit, 0, U8_MAX); +} + +static u16 sch5627_rpm_to_reg(long value) +{ + long pulses; + + if (value <= 0) + return U16_MAX - 1; + + pulses = 5400540 / value; + + return clamp_val(pulses, 1, U16_MAX - 1); +} + static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel) { - if (type == hwmon_pwm && attr == hwmon_pwm_auto_channels_temp) - return 0644; + const struct sch5627_data *data = drvdata; + + /* Once the lock bit is set, the virtual registers become read-only + * until the next power cycle. + */ + if (data->control & SCH5627_CTRL_LOCK) + return 0444; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_max: + case hwmon_temp_crit: + return 0644; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_min: + return 0644; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_auto_channels_temp: + return 0644; + default: + break; + } + break; + default: + break; + } return 0444; } @@ -236,24 +295,37 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at long *val) { struct sch5627_data *data = dev_get_drvdata(dev); - int ret; + int ret, value; switch (type) { case hwmon_temp: - ret = sch5627_update_temp(data); - if (ret < 0) - return ret; switch (attr) { case hwmon_temp_input: + ret = sch5627_update_temp(data); + if (ret < 0) + return ret; + *val = reg_to_temp(data->temp[channel]); return 0; case hwmon_temp_max: - *val = reg_to_temp_limit(data->temp_max[channel]); + ret = regmap_read(data->regmap, SCH5627_REG_TEMP_ABS[channel], &value); + if (ret < 0) + return ret; + + *val = reg_to_temp_limit((u8)value); return 0; case hwmon_temp_crit: - *val = reg_to_temp_limit(data->temp_crit[channel]); + ret = regmap_read(data->regmap, SCH5627_REG_TEMP_HIGH[channel], &value); + if (ret < 0) + return ret; + + *val = reg_to_temp_limit((u8)value); return 0; case hwmon_temp_fault: + ret = sch5627_update_temp(data); + if (ret < 0) + return ret; + *val = (data->temp[channel] == 0); return 0; default: @@ -261,23 +333,35 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at } break; case hwmon_fan: - ret = sch5627_update_fan(data); - if (ret < 0) - return ret; switch (attr) { case hwmon_fan_input: + ret = sch5627_update_fan(data); + if (ret < 0) + return ret; + ret = reg_to_rpm(data->fan[channel]); if (ret < 0) return ret; + *val = ret; return 0; case hwmon_fan_min: - ret = reg_to_rpm(data->fan_min[channel]); + ret = sch56xx_regmap_read16(data->regmap, SCH5627_REG_FAN_MIN[channel], + &value); if (ret < 0) return ret; + + ret = reg_to_rpm((u16)value); + if (ret < 0) + return ret; + *val = ret; return 0; case hwmon_fan_fault: + ret = sch5627_update_fan(data); + if (ret < 0) + return ret; + *val = (data->fan[channel] == 0xffff); return 0; default: @@ -287,15 +371,11 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at case hwmon_pwm: switch (attr) { case hwmon_pwm_auto_channels_temp: - mutex_lock(&data->update_lock); - ret = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel]); - mutex_unlock(&data->update_lock); - + ret = regmap_read(data->regmap, SCH5627_REG_PWM_MAP[channel], &value); if (ret < 0) return ret; - *val = ret; - + *val = value; return 0; default: break; @@ -345,9 +425,33 @@ static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 a long val) { struct sch5627_data *data = dev_get_drvdata(dev); - int ret; + u16 fan; + u8 temp; switch (type) { + case hwmon_temp: + temp = sch5627_temp_limit_to_reg(val); + + switch (attr) { + case hwmon_temp_max: + return regmap_write(data->regmap, SCH5627_REG_TEMP_ABS[channel], temp); + case hwmon_temp_crit: + return regmap_write(data->regmap, SCH5627_REG_TEMP_HIGH[channel], temp); + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_min: + fan = sch5627_rpm_to_reg(val); + + return sch56xx_regmap_write16(data->regmap, SCH5627_REG_FAN_MIN[channel], + fan); + default: + break; + } + break; case hwmon_pwm: switch (attr) { case hwmon_pwm_auto_channels_temp: @@ -355,12 +459,7 @@ static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 a if (val > U8_MAX || val < 0) return -EINVAL; - mutex_lock(&data->update_lock); - ret = sch56xx_write_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel], - val); - mutex_unlock(&data->update_lock); - - return ret; + return regmap_write(data->regmap, SCH5627_REG_PWM_MAP[channel], val); default: break; } @@ -422,7 +521,7 @@ static int sch5627_probe(struct platform_device *pdev) { struct sch5627_data *data; struct device *hwmon_dev; - int err, build_code, build_id, hwmon_rev, val; + int build_code, build_id, hwmon_rev, val; data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data), GFP_KERNEL); @@ -483,24 +582,21 @@ static int sch5627_probe(struct platform_device *pdev) return val; data->control = val; - if (!(data->control & 0x01)) { + if (!(data->control & SCH5627_CTRL_START)) { pr_err("hardware monitoring not enabled\n"); return -ENODEV; } + + data->regmap = devm_regmap_init_sch56xx(&pdev->dev, &data->update_lock, data->addr, + &sch5627_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + /* Trigger a Vbat voltage measurement, so that we get a valid reading the first time we read Vbat */ - sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, - data->control | 0x10); + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | SCH5627_CTRL_VBAT); data->last_battery = jiffies; - /* - * Read limits, we do this only once as reading a register on - * the sch5627 is quite expensive (and they don't change). - */ - err = sch5627_read_limits(data); - if (err) - return err; - pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", build_code, build_id, hwmon_rev); @@ -518,6 +614,30 @@ static int sch5627_probe(struct platform_device *pdev) return 0; } +static int sch5627_suspend(struct device *dev) +{ + struct sch5627_data *data = dev_get_drvdata(dev); + + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + + return 0; +} + +static int sch5627_resume(struct device *dev) +{ + struct sch5627_data *data = dev_get_drvdata(dev); + + regcache_cache_only(data->regmap, false); + /* We must not access the virtual registers when the lock bit is set */ + if (data->control & SCH5627_CTRL_LOCK) + return regcache_drop_region(data->regmap, 0, U16_MAX); + + return regcache_sync(data->regmap); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(sch5627_dev_pm_ops, sch5627_suspend, sch5627_resume); + static const struct platform_device_id sch5627_device_id[] = { { .name = "sch5627", @@ -529,6 +649,7 @@ MODULE_DEVICE_TABLE(platform, sch5627_device_id); static struct platform_driver sch5627_driver = { .driver = { .name = DRVNAME, + .pm = pm_sleep_ptr(&sch5627_dev_pm_ops), }, .probe = sch5627_probe, .id_table = sch5627_device_id, diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 269757bc3a9e..6e6d54158474 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -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, }; diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index de3a0886c2f7..71941b1bb573 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -7,10 +7,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include -#include +#include #include #include #include @@ -21,10 +20,7 @@ #include #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); diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h index e907d9da0dd5..7479a549a026 100644 --- a/drivers/hwmon/sch56xx-common.h +++ b/drivers/hwmon/sch56xx-common.h @@ -5,9 +5,15 @@ ***************************************************************************/ #include +#include 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); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 32a41fc56fc9..494f9655f44f 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1017,7 +1017,7 @@ err_release_reg: return ret; } -static int sht15_remove(struct platform_device *pdev) +static void sht15_remove(struct platform_device *pdev) { struct sht15_data *data = platform_get_drvdata(pdev); int ret; @@ -1033,8 +1033,6 @@ static int sht15_remove(struct platform_device *pdev) regulator_unregister_notifier(data->reg, &data->nb); regulator_disable(data->reg); } - - return 0; } static const struct platform_device_id sht15_device_ids[] = { @@ -1053,7 +1051,7 @@ static struct platform_driver sht15_driver = { .of_match_table = of_match_ptr(sht15_dt_match), }, .probe = sht15_probe, - .remove = sht15_remove, + .remove_new = sht15_remove, .id_table = sht15_device_ids, }; module_platform_driver(sht15_driver); diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 0a0479501e11..641be1f7f9cd 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -709,7 +709,7 @@ exit_remove_files: return err; } -static int sis5595_remove(struct platform_device *pdev) +static void sis5595_remove(struct platform_device *pdev) { struct sis5595_data *data = platform_get_drvdata(pdev); @@ -717,8 +717,6 @@ static int sis5595_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &sis5595_group); sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4); sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1); - - return 0; } static const struct pci_device_id sis5595_pci_ids[] = { @@ -790,7 +788,7 @@ static struct platform_driver sis5595_driver = { .name = DRIVER_NAME, }, .probe = sis5595_probe, - .remove = sis5595_remove, + .remove_new = sis5595_remove, }; static int sis5595_pci_probe(struct pci_dev *dev, diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index 9a180b1030c9..8a7cf08733c6 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -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) { diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c index 3b580f229887..9823afb0675a 100644 --- a/drivers/hwmon/ultra45_env.c +++ b/drivers/hwmon/ultra45_env.c @@ -291,7 +291,7 @@ out_iounmap: goto out; } -static int env_remove(struct platform_device *op) +static void env_remove(struct platform_device *op) { struct env *p = platform_get_drvdata(op); @@ -300,8 +300,6 @@ static int env_remove(struct platform_device *op) hwmon_device_unregister(p->hwmon_dev); of_iounmap(&op->resource[0], p->regs, REG_SIZE); } - - return 0; } static const struct of_device_id env_match[] = { @@ -319,7 +317,7 @@ static struct platform_driver env_driver = { .of_match_table = env_match, }, .probe = env_probe, - .remove = env_remove, + .remove_new = env_remove, }; module_platform_driver(env_driver); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index e5d18dac8ee7..5abe95b683c0 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -182,7 +182,7 @@ exit_remove: return err; } -static int via_cputemp_remove(struct platform_device *pdev) +static void via_cputemp_remove(struct platform_device *pdev) { struct via_cputemp_data *data = platform_get_drvdata(pdev); @@ -190,7 +190,6 @@ static int via_cputemp_remove(struct platform_device *pdev) if (data->vrm) device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); - return 0; } static struct platform_driver via_cputemp_driver = { @@ -198,7 +197,7 @@ static struct platform_driver via_cputemp_driver = { .name = DRVNAME, }, .probe = via_cputemp_probe, - .remove = via_cputemp_remove, + .remove_new = via_cputemp_remove, }; struct pdev_entry { diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index 407933d6e425..3a002ad3c005 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -786,14 +786,12 @@ exit_remove_files: return err; } -static int via686a_remove(struct platform_device *pdev) +static void via686a_remove(struct platform_device *pdev) { struct via686a_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &via686a_group); - - return 0; } static struct platform_driver via686a_driver = { @@ -801,7 +799,7 @@ static struct platform_driver via686a_driver = { .name = DRIVER_NAME, }, .probe = via686a_probe, - .remove = via686a_remove, + .remove_new = via686a_remove, }; static const struct pci_device_id via686a_pci_ids[] = { diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index fcd4be7a5a85..2f3890463e18 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -1208,14 +1208,12 @@ EXIT_DEV_REMOVE_SILENT: return err; } -static int vt1211_remove(struct platform_device *pdev) +static void vt1211_remove(struct platform_device *pdev) { struct vt1211_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); vt1211_remove_sysfs(pdev); - - return 0; } static struct platform_driver vt1211_driver = { @@ -1223,7 +1221,7 @@ static struct platform_driver vt1211_driver = { .name = DRVNAME, }, .probe = vt1211_probe, - .remove = vt1211_remove, + .remove_new = vt1211_remove, }; static int __init vt1211_device_add(unsigned short address) diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 16bc16d33cd1..dcdd14ccd115 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -892,7 +892,7 @@ exit_remove_files: return err; } -static int vt8231_remove(struct platform_device *pdev) +static void vt8231_remove(struct platform_device *pdev) { struct vt8231_data *data = platform_get_drvdata(pdev); int i; @@ -906,8 +906,6 @@ static int vt8231_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]); sysfs_remove_group(&pdev->dev.kobj, &vt8231_group); - - return 0; } @@ -916,7 +914,7 @@ static struct platform_driver vt8231_driver = { .name = DRIVER_NAME, }, .probe = vt8231_probe, - .remove = vt8231_remove, + .remove_new = vt8231_remove, }; static const struct pci_device_id vt8231_pci_ids[] = { diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index b638d672ac45..2fc9b718e2ab 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -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, diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index b33f382f238d..cba5ec432e6d 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -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 */ diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index 1ccdd61b6d13..5e0759a70f6d 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -751,7 +751,7 @@ out_mbox_free: return rc; } -static int xgene_hwmon_remove(struct platform_device *pdev) +static void xgene_hwmon_remove(struct platform_device *pdev) { struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev); @@ -762,8 +762,6 @@ static int xgene_hwmon_remove(struct platform_device *pdev) mbox_free_channel(ctx->mbox_chan); else pcc_mbox_free_channel(ctx->pcc_chan); - - return 0; } static const struct of_device_id xgene_hwmon_of_match[] = { @@ -774,7 +772,7 @@ MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match); static struct platform_driver xgene_hwmon_driver = { .probe = xgene_hwmon_probe, - .remove = xgene_hwmon_remove, + .remove_new = xgene_hwmon_remove, .driver = { .name = "xgene-slimpro-hwmon", .of_match_table = xgene_hwmon_of_match,