diff --git a/Documentation/ABI/testing/sysfs-class-hwmon b/Documentation/ABI/testing/sysfs-class-hwmon index cfd0d0bab483..b185bdfc7186 100644 --- a/Documentation/ABI/testing/sysfs-class-hwmon +++ b/Documentation/ABI/testing/sysfs-class-hwmon @@ -27,6 +27,20 @@ Description: Some devices have a variable update rate or interval. This attribute can be used to change it to the desired value. +What: /sys/class/hwmon/hwmonX/update_interval_us +Description: + The interval at which the chip will update readings, + expressed in microseconds. + Unit: microsecond + + RW + + Some devices have a variable update rate or interval and + require finer-than-millisecond control. + This attribute can be used to change it to the desired value. + Drivers implementing this attribute should also implement + update_interval for millisecond-based userspace interfaces. + What: /sys/class/hwmon/hwmonX/inY_min Description: Voltage min value. diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml new file mode 100644 index 000000000000..05e2132ad4d8 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml @@ -0,0 +1,272 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/adi,ltc4283.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LTC4283 Negative Voltage Hot Swap Controller + +maintainers: + - Nuno Sá + +description: | + The LTC4283 negative voltage hot swap controller drives an external N-channel + MOSFET to allow a board to be safely inserted and removed from a live + backplane. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf + +properties: + compatible: + enum: + - adi,ltc4283 + + reg: + maxItems: 1 + + adi,rsense-nano-ohms: + description: Value of the sense resistor. + + adi,current-limit-sense-microvolt: + description: + The current limit sense voltage of the chip is adjustable between + 15mV and 30mV in 1mV steps. This effectively limits the current + on the load. + minimum: 15000 + maximum: 30000 + default: 15000 + + adi,current-limit-foldback-factor: + description: + Specifies the foldback factor for the current limit. The current limit + can be reduced (folded back) to one of four preset levels. The value + represents the percentage of the current limit sense voltage to use + during foldback. A value of 100 means no foldback. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [10, 20, 50, 100] + default: 100 + + adi,cooling-delay-ms: + description: + Cooling time to apply after an overcurrent fault, FET bad or + external fault. + enum: [512, 1002, 2005, 4100, 8190, 16400, 32800, 65600] + default: 512 + + adi,fet-bad-timer-delay-ms: + description: + FET bad timer delay. After a FET bad status condition is detected, + this timer is started. If the condition persists for the + specified time, the FET is turned off and a fault is logged. + enum: [256, 512, 1002, 2005] + default: 256 + + adi,power-good-reset-on-fet: + description: + If set, resets the power good status when the MOSFET is turned off. + Otherwise, it resets when a low output voltage is detected. + type: boolean + + adi,fet-turn-off-disable: + description: + If set, the MOSFET is not turned off when a FET fault is detected. + type: boolean + + adi,tmr-pull-down-disable: + description: Disables 2uA pull-down current on the TMR pin. + type: boolean + + adi,dvdt-inrush-control-disable: + description: + Disables dV/dt inrush control during startup. In dV/dt mode, the inrush + current is limited by controlling a constant output voltage ramp rate. + When disabled, the inrush control mechanism is active current limiting. + type: boolean + + adi,fault-log-enable: + description: + If set, enables logging fault registers and ADC data into EEPROM upon a + fault. + type: boolean + + adi,vpower-drns-enable: + description: + If set, enables the attenuated MOSFET drain voltage to be monitored. This + effectively means that the MOSFET power is monitored. If not set, the + attenuated input voltage (and hence input power) is monitored. + type: boolean + + adi,external-fault-fet-off-enable: + description: Turns MOSFET off following an external fault. + type: boolean + + adi,undervoltage-retry-disable: + description: Do not retry to turn on the MOSFET after an undervoltage fault. + type: boolean + + adi,overvoltage-retry-disable: + description: Do not retry to turn on the MOSFET after an overvoltage fault. + type: boolean + + adi,external-fault-retry-enable: + description: Retry to turn on the MOSFET after an external fault. + type: boolean + + adi,overcurrent-retries: + description: Configures auto-retry following an Overcurrent fault. + $ref: /schemas/types.yaml#/definitions/string + enum: [latch-off, "1", "7", unlimited] + default: latch-off + + adi,fet-bad-retries: + description: + Configures auto-retry following a FET bad fault and a consequent MOSFET + turn off. + $ref: /schemas/types.yaml#/definitions/string + enum: [latch-off, "1", "7", unlimited] + default: latch-off + + adi,pgio1-func: + description: Configures the function of the PGIO1 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_power_good, power_good, gpio] + default: inverted_power_good + + adi,pgio2-func: + description: Configures the function of the PGIO2 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_power_good, power_good, gpio, active_current_limiting] + default: inverted_power_good + + adi,pgio3-func: + description: Configures the function of the PGIO3 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_power_good_input, power_good_input, gpio] + default: inverted_power_good_input + + adi,pgio4-func: + description: Configures the function of the PGIO4 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_external_fault, external_fault, gpio] + default: inverted_external_fault + + adi,gpio-on-adio1: + description: If set, the ADIO1 pin is used as a GPIO. + type: boolean + + adi,gpio-on-adio2: + description: If set, the ADIO2 pin is used as a GPIO. + type: boolean + + adi,gpio-on-adio3: + description: If set, the ADIO3 pin is used as a GPIO. + type: boolean + + adi,gpio-on-adio4: + description: If set, the ADIO4 pin is used as a GPIO. + type: boolean + + gpio-controller: true + + '#gpio-cells': + const: 2 + +dependencies: + adi,gpio-on-adio1: + - gpio-controller + - '#gpio-cells' + adi,gpio-on-adio2: + - gpio-controller + - '#gpio-cells' + adi,gpio-on-adio3: + - gpio-controller + - '#gpio-cells' + adi,gpio-on-adio4: + - gpio-controller + - '#gpio-cells' + adi,external-fault-retry-enable: + - adi,pgio4-func + adi,external-fault-fet-off-enable: + - adi,pgio4-func + +required: + - compatible + - reg + - adi,rsense-nano-ohms + +allOf: + - if: + properties: + adi,pgio1-func: + const: gpio + required: + - adi,pgio1-func + then: + required: + - gpio-controller + - '#gpio-cells' + + - if: + properties: + adi,pgio2-func: + const: gpio + required: + - adi,pgio2-func + then: + required: + - gpio-controller + - '#gpio-cells' + + - if: + properties: + adi,pgio3-func: + const: gpio + required: + - adi,pgio3-func + then: + required: + - gpio-controller + - '#gpio-cells' + + - if: + properties: + adi,pgio4-func: + const: gpio + required: + - adi,pgio4-func + then: + properties: + adi,external-fault-retry-enable: false + adi,external-fault-fet-off-enable: false + required: + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + swap-controller@15 { + compatible = "adi,ltc4283"; + reg = <0x15>; + + adi,rsense-nano-ohms = <500>; + adi,current-limit-sense-microvolt = <25000>; + adi,current-limit-foldback-factor = <10>; + adi,cooling-delay-ms = <8190>; + adi,fet-bad-timer-delay-ms = <512>; + + adi,external-fault-fet-off-enable; + adi,pgio4-func = "external_fault"; + + adi,gpio-on-adio1; + adi,pgio1-func = "gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/hwmon/lm75.yaml b/Documentation/devicetree/bindings/hwmon/lm75.yaml index 0b9fda81e3ec..b48bf3fd721f 100644 --- a/Documentation/devicetree/bindings/hwmon/lm75.yaml +++ b/Documentation/devicetree/bindings/hwmon/lm75.yaml @@ -54,6 +54,13 @@ properties: interrupts: maxItems: 1 + ti,alert-polarity-active-high: + description: Alert pin is asserted based on the value of alert polarity + bit of configuration register. Default value is normal (0 which maps to + active-low). The other value is inverted (1 which maps to active-high). + Specify this property to set the alert polarity to active-high. + $ref: /schemas/types.yaml#/definitions/flag + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml b/Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml new file mode 100644 index 000000000000..1a273621db82 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml @@ -0,0 +1,193 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/microchip,emc1812.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip EMC1812/13/14/15/33 multichannel temperature sensor + +maintainers: + - Marius Cristea + +description: | + The Microchip EMC1812/13/14/15/33 is a high-accuracy 2-wire multichannel + low-voltage remote diode temperature monitor. + + The datasheet can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + + EMC1812 has one external remote temperature monitoring channel + EMC1813 has two external remote temperature monitoring channels + EMC1814 has three external remote temperature monitoring channels and + channels 2 and 3 support anti parallel diode + EMC1815 has four external remote temperature monitoring channels and + channels 1/2 and 3/4 support anti parallel diode + EMC1833 has two external remote temperature monitoring channels and + channels 1 and 2 support anti parallel diode + +properties: + compatible: + enum: + - microchip,emc1812 + - microchip,emc1813 + - microchip,emc1814 + - microchip,emc1815 + - microchip,emc1833 + + reg: + maxItems: 1 + + interrupts: + items: + - description: alert-therm2 asserts when the ALERT limit is exceeded. + - description: therm-addr asserts when the THERM limit is exceeded. + minItems: 1 + + interrupt-names: + items: + - const: alert-therm2 + - const: therm-addr + minItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + microchip,enable-anti-parallel: + description: + Enable anti-parallel diode mode operation. EMC1814, EMC1815 and EMC1833 + support reading two external diodes in anti-parallel connection on the + same set of pins. Disabling APD functionality to implement substrate + diodes on devices that support APD eliminates the benefit of APD + (two diodes on one channel). + type: boolean + + microchip,parasitic-res-on-channel1-2: + description: + Indicates that the chip and the diodes/transistors are sufficiently + far apart that a parasitic resistance is added to the wires, which can + affect the measurements. Due to the availability of only a single + configuration bit in hardware, channels 1 and 2 are affected together. + If channel 2 is not available in hardware, this setting affects only + channel 1. + type: boolean + + microchip,parasitic-res-on-channel3-4: + description: + Indicates that the chip and the diodes/transistors are sufficiently + far apart that a parasitic resistance is added to the wires, which can + affect the measurements. Due to the availability of only a single + configuration bit in hardware, channels 3 and 4 are affected together. + If channel 4 is not available in hardware, this setting affects only + channel 3. + type: boolean + + vdd-supply: true + +patternProperties: + "^channel@[0-4]$": + description: | + Represents the temperature channels. + 0: Internal sensor + 1-4: External remote diodes + type: object + + properties: + reg: + maxItems: 1 + + label: + description: Unique name to identify which channel this is. + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + +allOf: + # EMC1812: 1 Internal, 1 External Channels, No APD, + # parasitic-res-on-channel1-2: for channel 1 + - if: + properties: + compatible: + const: microchip,emc1812 + then: + properties: + microchip,enable-anti-parallel: false + microchip,parasitic-res-on-channel3-4: false + patternProperties: + "^channel@[2-4]$": false + + # EMC1813: 1 Internal, 2 External Channels, No APD, + # parasitic-res-on-channel1-2: on both channel 1 & 2 + - if: + properties: + compatible: + const: microchip,emc1813 + then: + properties: + microchip,enable-anti-parallel: false + microchip,parasitic-res-on-channel3-4: false + patternProperties: + "^channel@[3-4]$": false + + # EMC1833: 1 Internal, 2 External Channels, Supports APD, + # parasitic-res-on-channel1-2: on both channel 1 & 2 + - if: + properties: + compatible: + const: microchip,emc1833 + then: + properties: + microchip,parasitic-res-on-channel3-4: false + patternProperties: + "^channel@[3-4]$": false + + # EMC1814: 1 Internal, 3 External Channels, Supports APD, + # parasitic-res-on-channel1-2: on both channel 1 & 2 + # parasitic-res-on-channel3-4: for channel 3 + - if: + properties: + compatible: + const: microchip,emc1814 + then: + properties: + channel@4: false + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@4c { + compatible = "microchip,emc1813"; + reg = <0x4c>; + + #address-cells = <1>; + #size-cells = <0>; + + microchip,parasitic-res-on-channel1-2; + + vdd-supply = <&vdd>; + + channel@1 { + reg = <1>; + label = "External CH1 Temperature"; + }; + + channel@2 { + reg = <2>; + label = "External CH2 Temperature"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml b/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml index d3f06ebc19fa..8c2548539d7f 100644 --- a/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml +++ b/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml @@ -54,6 +54,12 @@ patternProperties: The fan number used to determine the associated PWM channel. maxItems: 1 + fan-shutdown-percent: + description: + PWM duty cycle in percent applied to the fan during shutdown. + minimum: 0 + maximum: 100 + required: - reg @@ -80,12 +86,14 @@ examples: fan@0 { reg = <0x0>; pwms = <&fan_controller 26000 PWM_POLARITY_INVERTED 1>; + fan-shutdown-percent = <100>; #cooling-cells = <2>; }; fan@1 { reg = <0x1>; pwms = <&fan_controller 26000 0 1>; + fan-shutdown-percent = <50>; #cooling-cells = <2>; }; diff --git a/Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt b/Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt deleted file mode 100644 index 0863e067c85b..000000000000 --- a/Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt +++ /dev/null @@ -1,20 +0,0 @@ -Bindings for the fan / temperature monitor microcontroller used on -the Zyxel NSA 320 and several subsequent models. - -Required properties: -- compatible : "zyxel,nsa320-mcu" -- data-gpios : The GPIO pin connected to the data line on the MCU -- clk-gpios : The GPIO pin connected to the clock line on the MCU -- act-gpios : The GPIO pin connected to the active line on the MCU - -Example: - - hwmon { - compatible = "zyxel,nsa320-mcu"; - pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>; - pinctrl-names = "default"; - - data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; - clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>; - act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; - }; diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml new file mode 100644 index 000000000000..1625dd59417f --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20830.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX20830 Step-Down Switching Regulator with PMBus + +maintainers: + - Alexis Czezar Torreno + +description: | + The MAX20830 is a fully integrated step-down DC-DC switching regulator with + PMBus interface. It provides 2.7V to 16V input, 0.4V to 5.8V adjustable + output, and up to 30A output current. It allows monitoring of input/output + voltage, output current and temperature through the PMBus serial interface. + Datasheet: + https://www.analog.com/en/products/max20830.html + +allOf: + - $ref: /schemas/regulator/regulator.yaml# + +properties: + compatible: + const: adi,max20830 + + reg: + maxItems: 1 + + vddh-supply: + description: + Phandle to the regulator that provides the VDDH power supply. + + avdd-supply: + description: + Phandle to the regulator that provides the AVDD power supply. + + ldoin-supply: + description: + Optional 2.5V to 5.5V LDO input supply. + + pwr-good-gpios: + description: + GPIO connected to the power-good status output pin. + maxItems: 1 + +required: + - compatible + - reg + - vddh-supply + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@30 { + compatible = "adi,max20830"; + reg = <0x30>; + vddh-supply = <&vddh>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml new file mode 100644 index 000000000000..dd238265d462 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20860a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX20860A Step-Down Converter + +maintainers: + - Sanman Pradhan + +description: | + The MAX20860A is a fully integrated step-down DC-DC switching regulator + with PMBus interface for monitoring input/output voltage, output current + and temperature. + + Datasheet: https://www.analog.com/en/products/max20860a.html + +allOf: + - $ref: /schemas/regulator/regulator.yaml# + +properties: + compatible: + const: adi,max20860a + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@40 { + compatible = "adi,max20860a"; + reg = <0x40>; + }; + }; diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml index 72bc3a5e7139..4a949c53f7ae 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml @@ -5,23 +5,31 @@ $id: http://devicetree.org/schemas/hwmon/pmbus/infineon,xdp720.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Infineon XDP720 Digital eFuse Controller +title: Infineon XDP720 / XDP730 Digital eFuse Controllers maintainers: - Ashish Yadav description: | - The XDP720 is an eFuse with integrated current sensor and digital - controller. It provides accurate system telemetry (V, I, P, T) and - reports analog current at the IMON pin for post-processing. + The XDP720 and XDP730 are PMBus-compliant digital eFuse controllers + with an integrated current sensor. They provide accurate system + telemetry (V, I, P, T) and report analog current at the IMON pin for + post-processing. - Datasheet: - https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf + Both parts share the same PMBus register map and direct-format + coefficients; they differ in the GIMON gain step exposed via the + TELEMETRY_AVG register (bit 10) and in the VDD_VIN pin number + (XDP720: pin 9, XDP730: pin 20). + + Datasheets: + - XDP720: https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf + - XDP730: https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp730-001-datasheet-en.pdf properties: compatible: enum: - infineon,xdp720 + - infineon,xdp730 reg: maxItems: 1 @@ -33,9 +41,9 @@ properties: vdd-vin-supply: description: - Supply for the VDD_VIN pin (pin 9), the IC controller power supply. - Typically connected to the input bus (VIN) through a 100 ohm / 100 nF - RC filter. + Supply for the VDD_VIN pin (XDP720 pin 9, XDP730 pin 20), the IC + controller power supply. Typically connected to the input bus + (VIN) through a 100 ohm / 100 nF RC filter. required: - compatible @@ -50,7 +58,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - hwmon@11 { + efuse@11 { compatible = "infineon,xdp720"; reg = <0x11>; vdd-vin-supply = <&vdd_vin>; diff --git a/Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml b/Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml new file mode 100644 index 000000000000..a111f8125e09 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/zyxel,nsa320-mcu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ZyXEL NSA320 MCU + +maintainers: + - Adam Baker + - Guenter Roeck + +description: + The ZyXEL NSA320 uses a dedicated microcontroller to manage system-critical + functions like fan speed and power monitoring. It is connected to the SoC + via a GPIO-based serial protocol. + +properties: + compatible: + const: zyxel,nsa320-mcu + + data-gpios: + maxItems: 1 + description: GPIO pin connected to the data line on the MCU. + + clk-gpios: + maxItems: 1 + description: GPIO pin connected to the clock line on the MCU. + + act-gpios: + maxItems: 1 + description: GPIO pin connected to the active line on the MCU. + +required: + - compatible + - data-gpios + - clk-gpios + - act-gpios + +additionalProperties: false + +examples: + - | + #include + + hwmon { + compatible = "zyxel,nsa320-mcu"; + pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>; + pinctrl-names = "default"; + + data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>; + act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; + }; diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 23fd4513933a..1e870bb1bb6e 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -99,6 +99,8 @@ properties: - delta,dps800 # Delta Electronics DPS920AB 920W 54V Power Supply - delta,dps920ab + # 600W Non-isolated 1/8th Brick DC/DC Power Modules + - delta,e50sn12051 # 1/4 Brick DC/DC Regulated Power Module - delta,q54sj108a2 # 1300W 1/4 Brick DC/DC Regulated Power Module @@ -192,6 +194,8 @@ properties: - jedec,spd5118 # Linear Technology LTC2488 - lineartechnology,ltc2488 + # Regulated 12V, 860W, Digital DC/DC Power Module + - luxshare,lx1308 # 5 Bit Programmable, Pulse-Width Modulator - maxim,ds1050 # 10 kOhm digital potentiometer with I2C interface @@ -336,6 +340,8 @@ properties: - mps,mp29612 # Monolithic Power Systems Inc. multi-phase controller mp29816 - mps,mp29816 + # Monolithic Power Systems Inc. multi-phase controller mp2985 + - mps,mp2985 # Monolithic Power Systems Inc. multi-phase controller mp2993 - mps,mp2993 # Monolithic Power Systems Inc. hot-swap protection device @@ -352,6 +358,8 @@ properties: - mps,mp9941 # Monolithic Power Systems Inc. digital step-down converter mp9945 - mps,mp9945 + # Murata D1U74T-W power supply unit + - murata,d1u74t # Temperature sensor with integrated fan control - national,lm63 # Temperature sensor with integrated fan control diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 28784d66ae7b..d31198268fee 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -981,6 +981,8 @@ patternProperties: description: Shenzhen Luckfox Technology Co., Ltd. "^lunzn,.*": description: Shenzhen Lunzn Technology Co., Ltd. + "^luxshare,.*": + description: Luxshare-ICT Co., Ltd. "^luxul,.*": description: Lagrand | AV "^lwn,.*": diff --git a/Documentation/hwmon/adt7411.rst b/Documentation/hwmon/adt7411.rst index 57ad16fb216a..28b6f3cb6b86 100644 --- a/Documentation/hwmon/adt7411.rst +++ b/Documentation/hwmon/adt7411.rst @@ -30,11 +30,36 @@ Check the datasheet for details. sysfs-Interface --------------- -================ ================= -in0_input vdd voltage input -in[1-8]_input analog 1-8 input -temp1_input temperature input -================ ================= +================ ================================= +in0_input vdd voltage input +in0_min vdd low limit +in0_max vdd high limit +in0_alarm vdd alarm +in[1-8]_input analog 1-8 input +in[1-8]_min analog input low limit +in[1-8]_max analog input high limit +in[1-8]_alarm analog input alarm +temp1_input internal temperature input +temp1_min internal temperature low limit +temp1_max internal temperature high limit +temp1_min_alarm internal temperature low alarm +temp1_max_alarm internal temperature high alarm +================ ================================= + +If the external temperature sensor is enabled, the following attributes are +also available: + +================ ================================================ +temp2_input external temperature input +temp2_min external temperature low limit +temp2_max external temperature high limit +temp2_min_alarm external temperature low alarm +temp2_max_alarm external temperature high alarm +temp2_fault external temperature sensor fault +================ ================================================ + +If the external temperature sensor is enabled, analog inputs in1 and in2 are +not available. Besides standard interfaces, this driver adds (0 = off, 1 = on): @@ -47,4 +72,4 @@ Besides standard interfaces, this driver adds (0 = off, 1 = on): Notes ----- -SPI, external temperature sensor and limit registers are not supported yet. +SPI is not supported yet. diff --git a/Documentation/hwmon/arctic_fan_controller.rst b/Documentation/hwmon/arctic_fan_controller.rst new file mode 100644 index 000000000000..b5be88ae464d --- /dev/null +++ b/Documentation/hwmon/arctic_fan_controller.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver arctic_fan_controller +===================================== + +Supported devices: + +* ARCTIC Fan Controller (USB HID, VID 0x3904, PID 0xF001) + +Author: Aureo Serrano de Souza + +Description +----------- + +This driver provides hwmon support for the ARCTIC Fan Controller, a USB +Custom HID device with 10 fan channels. The device sends IN reports about +once per second containing current RPM values (bytes 11-30, 10 x uint16 LE). +Fan speed control is manual-only: the device does not change PWM +autonomously; it only applies a new duty cycle when it receives an OUT +report from the host. + +After the device applies an OUT report, it sends back a 2-byte ACK IN +report (Report ID 0x02, byte 1 = 0x00 on success) confirming the command +was applied. + +Usage notes +----------- + +Since it is a USB device, hotplug is supported. The device is autodetected. + +The device does not support GET_REPORT, so the driver cannot read back the +current hardware PWM state at probe time. The cached PWM values (readable +via pwm[1-10]) start at 0 and reflect only values that have been +successfully written. Because each OUT report carries all 10 channel values, +writing a single channel also sends the cached values for all other channels. +Users should set all channels to the desired values before relying on the +cached state. + +On system suspend, the device may lose power and reset its PWM channels to +hardware defaults. The driver clears its cached duty values on resume so +that reads reflect the unknown hardware state rather than stale pre-suspend +values. Userspace is responsible for re-applying the desired duty cycles +after resume. + +Sysfs entries +------------- + +================ ============================================================== +fan[1-10]_input Fan speed in RPM (read-only). Updated from IN reports at ~1 Hz. +pwm[1-10] PWM duty cycle (0-255). Write: sends an OUT report setting the + duty cycle (scaled from 0-255 to 0-100% for the device); + the cached value is updated only after the device ACKs the + command with a success status. Read: returns the last + successfully written value; initialized to 0 at driver load + and after resume (hardware state unknown). +================ ============================================================== diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 9ad3f0a57f55..77a709517437 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -29,9 +29,12 @@ Supported boards: * ROG MAXIMUS XI HERO * ROG MAXIMUS XI HERO (WI-FI) * ROG MAXIMUS Z690 FORMULA + * ROG MAXIMUS Z790 EXTREME * ROG STRIX B550-E GAMING * ROG STRIX B550-I GAMING + * ROG STRIX B650E-E GAMING WIFI * ROG STRIX B650E-I GAMING WIFI + * ROG STRIX B850-E GAMING WIFI * ROG STRIX B850-I GAMING WIFI * ROG STRIX X470-F GAMING * ROG STRIX X470-I GAMING diff --git a/Documentation/hwmon/coretemp.rst b/Documentation/hwmon/coretemp.rst index 7a5fbb37b0f3..f63b21f24d42 100644 --- a/Documentation/hwmon/coretemp.rst +++ b/Documentation/hwmon/coretemp.rst @@ -22,8 +22,7 @@ Supported chips: Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programming Guide - - http://softwarecommunity.intel.com/Wiki/Mobility/720.htm + (https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html) Author: Rudolf Marek @@ -51,12 +50,13 @@ All Sysfs entries are named with their core_id (represented here by 'X'). ================= ======================================================== tempX_input Core temperature (in millidegrees Celsius). -tempX_max All cooling devices should be turned on (on Core2). +tempX_max Maximum recommended operating temperature (Tcontrol). + All cooling devices should be turned on. tempX_crit Maximum junction temperature (in millidegrees Celsius). tempX_crit_alarm Set when Out-of-spec bit is set, never clears. Correct CPU operation is no longer guaranteed. tempX_label Contains string "Core X", where X is processor - number. For Package temp, this will be "Physical id Y", + number. For Package temp, this will be "Package id Y", where Y is the package number. ================= ======================================================== diff --git a/Documentation/hwmon/d1u74t.rst b/Documentation/hwmon/d1u74t.rst new file mode 100644 index 000000000000..3a9eedbda483 --- /dev/null +++ b/Documentation/hwmon/d1u74t.rst @@ -0,0 +1,81 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver d1u74t +==================== + +Supported chips: + + * Murata D1U74T + + Prefix: 'd1u74t' + + Addresses scanned: - + + Datasheet: Publicly available at the Murata website + +Authors: + Abdurrahman Hussain + + +Description +----------- + +This driver implements support for Murata D1U74T Power Supply with +PMBus support. + +The driver is a client driver to the core PMBus driver. +Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for +details. + + +Sysfs entries +------------- + +======================= ====================================================== +curr1_label "iin" +curr1_input Measured input current +curr1_alarm Input current alarm +curr1_rated_max Maximum rated input current + +curr2_label "iout1" +curr2_input Measured output current +curr2_max Maximum output current +curr2_max_alarm Output current high alarm +curr2_crit Critical high output current +curr2_crit_alarm Output current critical high alarm +curr2_rated_max Maximum rated output current + +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in1_rated_min Minimum rated input voltage +in1_rated_max Maximum rated input voltage + +in2_label "vout1" +in2_input Measured output voltage +in2_alarm Output voltage alarm +in2_rated_min Minimum rated output voltage +in2_rated_max Maximum rated output voltage + +power1_label "pin" +power1_input Measured input power +power1_alarm Input power alarm +power1_rated_max Maximum rated input power + +temp[1-3]_input Measured temperature +temp[1-3]_max Maximum temperature +temp[1-3]_max_alarm Maximum temperature alarm +temp[1-3]_rated_max Maximum rated temperature + +fan1_alarm Fan 1 warning +fan1_fault Fan 1 fault +fan1_input Fan 1 speed in RPM +fan1_target Fan 1 target +======================= ====================================================== diff --git a/Documentation/hwmon/e50sn12051.rst b/Documentation/hwmon/e50sn12051.rst new file mode 100644 index 000000000000..cbbfa7895d82 --- /dev/null +++ b/Documentation/hwmon/e50sn12051.rst @@ -0,0 +1,81 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver e50sn12051 +======================== + +Supported chips: + + * Delta E50SN12051 + + Prefix: 'e50sn12051' + + Addresses scanned: - + + Datasheet: + + Provided by Delta upon request and NDA + +Description +----------- + +E50SN12051 is a 600W non-isolated 1/8th brick DC-DC power module. + +This driver supports the E50SN12051 PMBus compliant monitor device. + +The device provides measurements for: + +* Input voltage +* Output current +* Output voltage +* Temperature + +The driver is based on the PMBus core and exposes standard hwmon +sysfs attributes. + +Sysfs attributes +---------------- + +======================= ======================================================= +curr1_label "iout1" +curr1_crit Critical maximum current. + From IOUT_OC_FAULT_LIMIT register. +curr1_crit_alarm Current critical high alarm. + From IOUT_OC_FAULT status. +curr1_input Measured current. + From READ_IOUT register. +curr1_max Maximum current. + From IOUT_OC_WARN_LIMIT register. +curr1_max_alarm Current high alarm. + From IOUT_OC_WARN_LIMIT status. + +in1_label "vin" +in1_alarm Input voltage alarm. +in1_input Measured voltage. + From READ_VIN register. + +in2_label "vout1" +in2_crit Critical maximum voltage. + From VOUT_OV_FAULT_LIMIT register. +in2_crit_alarm Voltage critical high alarm. + From VOLTAGE_OV_FAULT status. +in2_input Measured voltage. + From READ_VOUT register. +in2_max Maximum voltage. + From VOUT_OV_WARN_LIMIT register. +in2_max_alarm Voltage high alarm. + From VOLTAGE_OV_WARNING status. + +temp1_crit Critical high temperature. + From OT_FAULT_LIMIT register. +temp1_crit_alarm Module temperature critical high alarm. + Set by comparing READ_TEMPERATURE_1 with OT_FAULT_LIMIT + if TEMP_OT_FAULT status is set. +temp1_input Measured module's hot spot temperature. + From READ_TEMPERATURE_1 register. +temp1_max Maximum temperature. + From OT_WARN_LIMIT register. +temp1_max_alarm Module temperature high alarm. + Set by comparing READ_TEMPERATURE_1 with OT_WARN_LIMIT if + TEMP_OT_WARNING status is set. +======================= ======================================================= + diff --git a/Documentation/hwmon/emc1812.rst b/Documentation/hwmon/emc1812.rst new file mode 100644 index 000000000000..0b4fbcaaea71 --- /dev/null +++ b/Documentation/hwmon/emc1812.rst @@ -0,0 +1,67 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver emc1812 +===================== + +Supported chips: + + * Microchip EMC1812, EMC1813, EMC1814, EMC1815, EMC1833 + + Prefix: 'emc1812' + + Datasheets: + + - https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + +Author: + Marius Cristea + + +Description +----------- + +The Microchip EMC181x/33 chips contain up to 4 remote temperature sensors +and one internal. +- The EMC1812 is a single channel remote temperature sensor. +- The EMC1813 and EMC1833 are dual channel remote temperature sensor. The +remote channels for this selection of devices can support substrate diodes, +discrete diode-connected transistors or CPU/GPU thermal diodes. +- The EMC1814 is a three channel remote temperature sensor that supports +Anti-Parallel Diode (APD) only on one channel. For the channel that does not +support APD functionality, substrate diodes, discrete diode-connected +transistors or CPU/GPU thermal diodes are supported. For the channel that +supports APD, only discrete diode-connected transistors may be implemented. +However, if APD is disabled on the EMC1814, then the channel that supports +APD will be functional with substrate diodes, discrete diode-connected +transistors and CPU/GPU thermal diodes. +- The EMC1815 is a four channel remote temperature sensor. + +The EMC1815 and EMC1833 support APD on all channels. When APD is enabled, +the channels support only diode-connected transistors. If APD is disabled, +then the channels will support substrate transistors, discrete diode-connected +transistors and CPU/GPU thermal diodes. + +Note: Disabling APD functionality to implement substrate diodes on devices +that support APD eliminates the benefit of APD (two diodes on one channel). + +The chips implement three limits for each sensor: low (tempX_min), high +(tempX_max) and critical (tempX_crit). The chips also implement an +hysteresis mechanism which applies to all limits. The relative difference +is stored in a single register on the chip, which means that the relative +difference between the limit and its hysteresis is always the same for +all three limits. + +This implementation detail implies the following: + +* When setting a limit, its hysteresis will automatically follow, the + difference staying unchanged. For example, if the old critical limit was + 80 degrees C, and the hysteresis was 75 degrees C, and you change the + critical limit to 90 degrees C, then the hysteresis will automatically + change to 85 degrees C. +* The hysteresis values can't be set independently. We decided to make + only tempX_crit_hyst writable, while all other hysteresis attributes + are read-only. Setting tempX_crit_hyst writes the difference between + tempX_crit_hyst and tempX_crit into the chip, and the same relative + hysteresis applies automatically to all other limits. +* The limits should be set before the hysteresis. At power up the device + starts with 10 degree hysteresis. diff --git a/Documentation/hwmon/htu31.rst b/Documentation/hwmon/htu31.rst index ccde84264643..9ab774dcf65d 100644 --- a/Documentation/hwmon/htu31.rst +++ b/Documentation/hwmon/htu31.rst @@ -35,3 +35,10 @@ temp1_input: temperature input humidity1_input: humidity input heater_enable: heater control =================== ================= + +debugfs-Interface +----------------- + +=================== ========================================= +serial_number: unique serial number of the sensor +=================== ========================================= diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst index 1d7f1397a827..9fcde32a140d 100644 --- a/Documentation/hwmon/hwmon-kernel-api.rst +++ b/Documentation/hwmon/hwmon-kernel-api.rst @@ -85,9 +85,10 @@ removal. When using ``[devm_]hwmon_device_register_with_info()`` to register the hardware monitoring device, accesses using the associated access functions are serialised by the hardware monitoring core. If a driver needs locking -for other functions such as interrupt handlers or for attributes which are -fully implemented in the driver, hwmon_lock() and hwmon_unlock() can be used -to ensure that calls to those functions are serialized. +for other functions such as interrupt handlers, attributes which are fully +implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock() +can be used to ensure that calls to those functions are serialized. Those +functions also support guard() and scoped_guard() variants. Using devm_hwmon_device_register_with_info() -------------------------------------------- diff --git a/Documentation/hwmon/ina238.rst b/Documentation/hwmon/ina238.rst index 43950d1ec551..a75b79e17d9d 100644 --- a/Documentation/hwmon/ina238.rst +++ b/Documentation/hwmon/ina238.rst @@ -106,4 +106,8 @@ energy1_input Energy measurement (uJ) temp1_input Die temperature measurement (mC) temp1_max Maximum die temperature threshold (mC) temp1_max_alarm Maximum die temperature alarm + +samples ADC averaging count (1, 4, 16, 64, 128, 256, 512, 1024) +update_interval Total ADC conversion cycle time including averaging (ms) +update_interval_us Total ADC conversion cycle time including averaging (us) ======================= ======================================================= diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 8b655e5d6b68..4aa910569c31 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -43,6 +43,7 @@ Hardware Monitoring Kernel Drivers amc6821 aps-379 aquacomputer_d5next + arctic_fan_controller asb100 asc7621 aspeed-g6-pwm-tach @@ -60,6 +61,7 @@ Hardware Monitoring Kernel Drivers corsair-psu cros_ec_hwmon crps + d1u74t da9052 da9055 dell-smm-hwmon @@ -68,7 +70,9 @@ Hardware Monitoring Kernel Drivers drivetemp ds1621 ds620 + e50sn12051 emc1403 + emc1812 emc2103 emc2305 emc6w201 @@ -145,7 +149,9 @@ Hardware Monitoring Kernel Drivers ltc4260 ltc4261 ltc4282 + ltc4283 ltc4286 + lx1308 macsmc-hwmon max127 max15301 @@ -158,6 +164,8 @@ Hardware Monitoring Kernel Drivers max197 max20730 max20751 + max20830 + max20860a max31722 max31730 max31760 @@ -185,6 +193,7 @@ Hardware Monitoring Kernel Drivers mp2925 mp29502 mp2975 + mp2985 mp2993 mp5023 mp5920 diff --git a/Documentation/hwmon/lm75.rst b/Documentation/hwmon/lm75.rst index 4269da04508e..ca46754e028b 100644 --- a/Documentation/hwmon/lm75.rst +++ b/Documentation/hwmon/lm75.rst @@ -181,3 +181,19 @@ is supported by this driver, other specific enhancements are not. The LM77 is not supported, contrary to what we pretended for a long time. Both chips are simply not compatible, value encoding differs. + +sysfs-Interface +--------------- + +The following list includes the sysfs attributes that the driver provides, their +permissions and a short description: + +=============================== ======= =========================================== +Name Perm Description +=============================== ======= =========================================== +temp1_input RO Temperature input +temp1_label RO Descriptive name for the sensor +temp1_max RW Maximum temperature +temp1_max_hyst RW Maximum hysteresis temperature +update_interval RW Update conversions interval in milliseconds +=============================== ======= =========================================== diff --git a/Documentation/hwmon/ltc4283.rst b/Documentation/hwmon/ltc4283.rst new file mode 100644 index 000000000000..a650c595bc8f --- /dev/null +++ b/Documentation/hwmon/ltc4283.rst @@ -0,0 +1,267 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel drivers ltc4283 +========================================== + +Supported chips: + + * Analog Devices LTC4283 + + Prefix: 'ltc4283' + + Addresses scanned: - + + Datasheet: + + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf + +Author: Nuno Sá + +Description +___________ + +The LTC4283 negative voltage hot swap controller drives an external N-channel +MOSFET to allow a board to be safely inserted and removed from a live backplane. +The device features programmable current limit with foldback and independently +adjustable inrush current to optimize the MOSFET safe operating area (SOA). The +SOA timer limits MOSFET temperature rise for reliable protection against +overstresses. An I2C interface and onboard gear-shift ADC allow monitoring of +board current, voltage, power, energy, and fault status. Additional features +respond to input UV/OV, interrupt the host when a fault has occurred, notify +when output power is good, detect insertion of a board, turn off the MOSFET +if an external supply monitor fails to indicate power good within a timeout +period, and auto-reboot after a programmable delay following a host commanded +turn-off. + +Sysfs entries +_____________ + +The following attributes are supported. Limits are read-write and all the other +attributes are read-only. Note that the VADIOx channels might not be available +if the ADIO pins are used as GPIOs (naturally also affects the respective +differential channels). + +======================= ========================================== +in0_lcrit_alarm Critical Undervoltage alarm +in0_crit_alarm Critical Overvoltage alarm +in0_reset_history Clears Under and Overvoltage fault logs. +in0_label Channel label (VIN) + +in1_input Output voltage (mV). +in1_min Undervoltage threshold +in1_max Overvoltage threshold +in1_lowest Lowest measured voltage +in1_highest Highest measured voltage +in1_reset_history Write 1 to reset history. +in1_min_alarm Undervoltage alarm +in1_max_alarm Overvoltage alarm +in1_label Channel label (VPWR) + +in2_input Output voltage (mV). +in2_min Undervoltage threshold +in2_max Overvoltage threshold +in2_lowest Lowest measured voltage +in2_highest Highest measured voltage +in2_reset_history Write 1 to reset history. +in2_min_alarm Undervoltage alarm +in2_max_alarm Overvoltage alarm +in2_enable Enable/Disable monitoring. +in2_label Channel label (VADI1) + +in3_input Output voltage (mV). +in3_min Undervoltage threshold +in3_max Overvoltage threshold +in3_lowest Lowest measured voltage +in3_highest Highest measured voltage +in3_reset_history Write 1 to reset history. +in3_min_alarm Undervoltage alarm +in3_max_alarm Overvoltage alarm +in3_enable Enable/Disable monitoring. +in3_label Channel label (VADI2) + +in4_input Output voltage (mV). +in4_min Undervoltage threshold +in4_max Overvoltage threshold +in4_lowest Lowest measured voltage +in4_highest Highest measured voltage +in4_reset_history Write 1 to reset history. +in4_min_alarm Undervoltage alarm +in4_max_alarm Overvoltage alarm +in4_enable Enable/Disable monitoring. +in4_label Channel label (VADI3) + +in5_input Output voltage (mV). +in5_min Undervoltage threshold +in5_max Overvoltage threshold +in5_lowest Lowest measured voltage +in5_highest Highest measured voltage +in5_reset_history Write 1 to reset history. +in5_min_alarm Undervoltage alarm +in5_max_alarm Overvoltage alarm +in5_enable Enable/Disable monitoring. +in5_label Channel label (VADI4) + +in6_input Output voltage (mV). +in6_min Undervoltage threshold +in6_max Overvoltage threshold +in6_lowest Lowest measured voltage +in6_highest Highest measured voltage +in6_reset_history Write 1 to reset history. +in6_min_alarm Undervoltage alarm +in6_max_alarm Overvoltage alarm +in6_enable Enable/Disable monitoring. +in6_label Channel label (VADIO1) + +in7_input Output voltage (mV). +in7_min Undervoltage threshold +in7_max Overvoltage threshold +in7_lowest Lowest measured voltage +in7_highest Highest measured voltage +in7_reset_history Write 1 to reset history. +in7_min_alarm Undervoltage alarm +in7_max_alarm Overvoltage alarm +in7_enable Enable/Disable monitoring. +in7_label Channel label (VADIO2) + +in8_input Output voltage (mV). +in8_min Undervoltage threshold +in8_max Overvoltage threshold +in8_lowest Lowest measured voltage +in8_highest Highest measured voltage +in8_reset_history Write 1 to reset history. +in8_min_alarm Undervoltage alarm +in8_max_alarm Overvoltage alarm +in8_enable Enable/Disable monitoring. +in8_label Channel label (VADIO3) + +in9_input Output voltage (mV). +in9_min Undervoltage threshold +in9_max Overvoltage threshold +in9_lowest Lowest measured voltage +in9_highest Highest measured voltage +in9_reset_history Write 1 to reset history. +in9_min_alarm Undervoltage alarm +in9_max_alarm Overvoltage alarm +in9_enable Enable/Disable monitoring. +in9_label Channel label (VADIO4) + +in10_input Output voltage (mV). +in10_min Undervoltage threshold +in10_max Overvoltage threshold +in10_lowest Lowest measured voltage +in10_highest Highest measured voltage +in10_reset_history Write 1 to reset history. +in10_min_alarm Undervoltage alarm +in10_max_alarm Overvoltage alarm +in10_enable Enable/Disable monitoring. +in10_label Channel label (DRNS) + +in11_input Output voltage (mV). +in11_min Undervoltage threshold +in11_max Overvoltage threshold +in11_lowest Lowest measured voltage +in11_highest Highest measured voltage +in11_reset_history Write 1 to reset history. + Also clears fet bad and short fault logs. +in11_min_alarm Undervoltage alarm +in11_max_alarm Overvoltage alarm +in11_enable Enable/Disable monitoring +in11_fault Failure in the MOSFET. Either bad or shorted FET. +in11_label Channel label (DRAIN) + +in12_input Output voltage (mV). +in12_min Undervoltage threshold +in12_max Overvoltage threshold +in12_lowest Lowest measured voltage +in12_highest Highest measured voltage +in12_reset_history Write 1 to reset history. +in12_min_alarm Undervoltage alarm +in12_max_alarm Overvoltage alarm +in12_enable Enable/Disable monitoring. +in12_label Channel label (ADIN2-ADIN1) + +in13_input Output voltage (mV). +in13_min Undervoltage threshold +in13_max Overvoltage threshold +in13_lowest Lowest measured voltage +in13_highest Highest measured voltage +in13_reset_history Write 1 to reset history. +in13_min_alarm Undervoltage alarm +in13_max_alarm Overvoltage alarm +in13_enable Enable/Disable monitoring. +in13_label Channel label (ADIN4-ADIN3) + +in14_input Output voltage (mV). +in14_min Undervoltage threshold +in14_max Overvoltage threshold +in14_lowest Lowest measured voltage +in14_highest Highest measured voltage +in14_reset_history Write 1 to reset history. +in14_min_alarm Undervoltage alarm +in14_max_alarm Overvoltage alarm +in14_enable Enable/Disable monitoring. +in14_label Channel label (ADIO2-ADIO1) + +in15_input Output voltage (mV). +in15_min Undervoltage threshold +in15_max Overvoltage threshold +in15_lowest Lowest measured voltage +in15_highest Highest measured voltage +in15_reset_history Write 1 to reset history. +in15_min_alarm Undervoltage alarm +in15_max_alarm Overvoltage alarm +in15_enable Enable/Disable monitoring. +in15_label Channel label (ADIO4-ADIO3) + +curr1_input Sense current (mA) +curr1_min Undercurrent threshold +curr1_max Overcurrent threshold +curr1_lowest Lowest measured current +curr1_highest Highest measured current +curr1_reset_history Write 1 to reset curr1 history. + Also clears overcurrent fault logs. +curr1_min_alarm Undercurrent alarm +curr1_max_alarm Overcurrent alarm +curr1_crit_alarm Critical Overcurrent alarm +curr1_label Channel label (ISENSE) + +power1_input Power (in uW) +power1_min Low power threshold +power1_max High power threshold +power1_input_lowest Historical minimum power use +power1_input_highest Historical maximum power use +power1_reset_history Write 1 to reset power1 history. + Also clears power fault logs. +power1_min_alarm Low power alarm +power1_max_alarm High power alarm +power1_label Channel label (Power) + +energy1_input Measured energy over time (in microJoule) +energy1_enable Enable/Disable Energy accumulation +======================= ========================================== + +DebugFs entries +_______________ + +The chip also has a fault log register where failures can be logged. Hence, +as these are logging events, we give access to them in debugfs. Note that +even if some failure is detected in these logs, it does necessarily mean +that the failure is still present. As mentioned in the proper Sysfs entries, +these logs can be cleared by writing in the proper reset_history attribute. + +.. warning:: The debugfs interface is subject to change without notice + and is only available when the kernel is compiled with + ``CONFIG_DEBUG_FS`` defined. + +``/sys/kernel/debug/i2c/i2c-[X]/[X]-addr/`` +contains the following attributes: + +======================= ========================================== +power1_failed_fault_log Set to 1 by a power1 fault occurring. +power1_good_input_fault_log Set to 1 by a power1 good input fault occurring at PGIO3. +in11_fet_short_fault_log Set to 1 when a FET-short fault occurs. +in11_fet_bad_fault_log Set to 1 when a FET-BAD fault occurs. +in0_lcrit_fault_log Set to 1 by a VIN undervoltage fault occurring. +in0_crit_fault_log Set to 1 by a VIN overvoltage fault occurring. +curr1_crit_fault_log Set to 1 by an overcurrent fault occurring. +======================= ========================================== diff --git a/Documentation/hwmon/lx1308.rst b/Documentation/hwmon/lx1308.rst new file mode 100644 index 000000000000..c1b72e1647c5 --- /dev/null +++ b/Documentation/hwmon/lx1308.rst @@ -0,0 +1,90 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver lx1308 +==================== + +Supported chips: + + * Luxshare LX1308 + + Prefixes: 'lx1308' + + Addresses scanned: - + + Datasheet: Datasheet is not publicly available. + +Author: Brian Chiang + + +Description +----------- + +The LX1308 is a high-efficiency, non-isolated, regulated 12V, 860W, +digital DC/DC power module. The module operates from a 40V to 60V DC +primary bus and provides a 12V regulated output voltage. It can deliver +up to 860W continuous and 1300W in transient. + +The module has slow OCP and fast OCP. If the module output current is higher +than slow OCP set point and the lasting time is also longer than the delay, +the module will shut down and retry 3 time, if the fault still exists then +module enter latch mode. + +If the module output current is higher than fast OCP set point then it shut +down and enter latch mode. + +The driver is a client driver to the core PMBus driver. +Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for +details. + + +Sysfs entries +------------- + +======================= ====================================================== +curr1_alarm Input current alarm +curr1_input Input current (IIN) +curr1_label "iin" +curr2_crit Output over current fault threshold (slow OCP, 60ms delay) +curr2_crit_alarm Output over current fault alarm +curr2_input Output current (IOUT) +curr2_label "iout1" +curr2_max Output over current warning threshold (slow OCP, 60ms delay) +curr2_max_alarm Output over current warning alarm +in1_crit Input over voltage fault threshold +in1_crit_alarm Input over voltage fault alarm +in1_input Input voltage (VIN) +in1_label "vin" +in1_lcrit Input under voltage fault threshold +in1_lcrit_alarm Input under voltage fault alarm +in1_max Input over voltage warning threshold +in1_max_alarm Input over voltage warning alarm +in1_min Input under voltage warning threshold +in1_min_alarm Input under voltage warning alarm +in2_crit Output over voltage fault threshold +in2_crit_alarm Output over voltage fault alarm +in2_input Output voltage (VOUT) +in2_label "vout1" +in2_lcrit Output under voltage fault threshold +in2_lcrit_alarm Output under voltage fault alarm +in2_max Output over voltage warning threshold +in2_max_alarm Output over voltage warning alarm +in2_min Output under voltage warning threshold +in2_min_alarm Output under voltage warning alarm +power1_alarm Input power alarm +power1_input Input power (PIN) +power1_label "pin" +power2_input Output power (POUT) +power2_label "pout1" +temp1_crit Over temperature fault threshold +temp1_crit_alarm Over temperature fault alarm +temp1_input Module hot spot temperature +temp1_max Over temperature warning threshold +temp1_max_alarm Over temperature warning alarm +======================= ====================================================== diff --git a/Documentation/hwmon/max20830.rst b/Documentation/hwmon/max20830.rst new file mode 100644 index 000000000000..936e409dcc5c --- /dev/null +++ b/Documentation/hwmon/max20830.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver max20830 +====================== + +Supported chips: + + * Analog Devices MAX20830 + + Prefix: 'max20830' + + Addresses scanned: - + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20830.pdf + +Author: + + - Alexis Czezar Torreno + + +Description +----------- + +This driver supports hardware monitoring for Analog Devices MAX20830 +Step-Down Switching Regulator with PMBus Interface. + +The MAX20830 is a 2.7V to 16V, 30A fully integrated step-down DC-DC switching +regulator. Through the PMBus interface, the device can monitor input/output +voltages, output current and temperature. + +The driver is a client driver to the core PMBus driver. Please see +Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + +Sysfs entries +------------- + +================= ======================================== +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in2_label "vout1" +in2_input Measured output voltage +in2_alarm Output voltage alarm +curr1_label "iout1" +curr1_input Measured output current +curr1_alarm Output current alarm +temp1_input Measured temperature +temp1_alarm Chip temperature alarm +================= ======================================== diff --git a/Documentation/hwmon/max20860a.rst b/Documentation/hwmon/max20860a.rst new file mode 100644 index 000000000000..ea6d2228fafc --- /dev/null +++ b/Documentation/hwmon/max20860a.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver max20860a +======================= + +Supported chips: + + * Analog Devices MAX20860A + + Prefix: 'max20860a' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/max20860a.html + +Author: + + - Syed Arif + - Sanman Pradhan + + +Description +----------- + +This driver supports hardware monitoring for Analog Devices MAX20860A +Step-Down Switching Regulator with PMBus Interface. + +The MAX20860A is a fully integrated step-down DC-DC switching regulator. +Through the PMBus interface, the device can monitor input/output voltages, +output current and temperature. + +The driver is a client driver to the core PMBus driver. Please see +Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate +the devices explicitly. + +Sysfs entries +------------- + +================= ======================================== +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in2_label "vout1" +in2_input Measured output voltage +in2_alarm Output voltage alarm +curr1_label "iout1" +curr1_input Measured output current +curr1_alarm Output current alarm +temp1_input Measured temperature +temp1_alarm Chip temperature alarm +temp2_input Measured temperature (secondary) +================= ======================================== diff --git a/Documentation/hwmon/max31730.rst b/Documentation/hwmon/max31730.rst index 1c5a32b64187..0936ba2eac24 100644 --- a/Documentation/hwmon/max31730.rst +++ b/Documentation/hwmon/max31730.rst @@ -1,4 +1,4 @@ -Kernel driver max31790 +Kernel driver max31730 ====================== Supported chips: diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst index d6d4fbc863d9..e7421f4dbf38 100644 --- a/Documentation/hwmon/max34440.rst +++ b/Documentation/hwmon/max34440.rst @@ -19,6 +19,14 @@ Supported chips: Datasheet: - + * ADI ADPM12250 + + Prefixes: 'adpm12250' + + Addresses scanned: - + + Datasheet: - + * Maxim MAX34440 Prefixes: 'max34440' @@ -87,11 +95,11 @@ This driver supports multiple devices: hardware monitoring for Maxim MAX34440 PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and -MAX34461; PMBus DC/DC Power Module ADPM12160, and ADPM12200. The MAX34451 -supports monitoring voltage or current of 12 channels based on GIN pins. The -MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage -channels. The ADPM12160, and ADPM12200 also monitors both input and output -of voltage and current. +MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. The +MAX34451 supports monitoring voltage or current of 12 channels based on GIN +pins. The MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 +voltage channels. The ADPM12160, ADPM12200, and ADPM12250 also monitors both +input and output of voltage and current. The driver is a client driver to the core PMBus driver. Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. @@ -149,7 +157,7 @@ in[1-6]_reset_history Write any value to reset history. .. note:: - MAX34446 only supports in[1-4]. - - ADPM12160, and ADPM12200 only supports in[1-2]. Label is "vin1" + - ADPM12160, ADPM12200, and ADPM12250 only supports in[1-2]. Label is "vin1" and "vout1" respectively. Curr @@ -172,8 +180,9 @@ curr[1-6]_reset_history Write any value to reset history. - in6 and curr6 attributes only exist for MAX34440. - MAX34446 only supports curr[1-4]. - - For ADPM12160, and ADPM12200, curr[1] is "iin1" and curr[2-6] - are "iout[1-5]". + - For ADPM12160, ADPM12200, and ADPM12250, curr[1] is "iin1" + - For ADPM12160, and ADPM12200 curr[2-6] are "iout[1-5]". + - For ADPM12250, curr[2-4] are "iout[1-3]". Power ~~~~~ @@ -209,7 +218,7 @@ temp[1-8]_reset_history Write any value to reset history. .. note:: - temp7 and temp8 attributes only exist for MAX34440. - MAX34446 only supports temp[1-3]. - - ADPM12160, and ADPM12200 only supports temp[1]. + - ADPM12160, ADPM12200, and ADPM12250 only supports temp[1]. .. note:: diff --git a/Documentation/hwmon/mp2985.rst b/Documentation/hwmon/mp2985.rst new file mode 100644 index 000000000000..87a39c8a300c --- /dev/null +++ b/Documentation/hwmon/mp2985.rst @@ -0,0 +1,147 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mp2985 +==================== + +Supported chips: + + * MPS mp2985 + + Prefix: 'mp2985' + +Author: + + Wensheng Wang + +Description +----------- + +This driver implements support for Monolithic Power Systems, Inc. (MPS) +MP2985 Dual Loop Digital Multi-phase Controller. + +Device compliant with: + +- PMBus rev 1.3 interface. + +The driver exports the following attributes via the 'sysfs' files +for input voltage: + +**in1_input** + +**in1_label** + +**in1_crit** + +**in1_crit_alarm** + +**in1_lcrit** + +**in1_lcrit_alarm** + +**in1_max** + +**in1_max_alarm** + +**in1_min** + +**in1_min_alarm** + +The driver provides the following attributes for output voltage: + +**in2_input** + +**in2_label** + +**in2_crit** + +**in2_crit_alarm** + +**in2_lcrit** + +**in2_lcrit_alarm** + +**in3_input** + +**in3_label** + +**in3_crit** + +**in3_crit_alarm** + +**in3_lcrit** + +**in3_lcrit_alarm** + +The driver provides the following attributes for input current: + +**curr1_input** + +**curr1_label** + +The driver provides the following attributes for output current: + +**curr2_input** + +**curr2_label** + +**curr2_crit** + +**curr2_crit_alarm** + +**curr2_max** + +**curr2_max_alarm** + +**curr3_input** + +**curr3_label** + +**curr3_crit** + +**curr3_crit_alarm** + +**curr3_max** + +**curr3_max_alarm** + +The driver provides the following attributes for input power: + +**power1_input** + +**power1_label** + +**power2_input** + +**power2_label** + +The driver provides the following attributes for output power: + +**power3_input** + +**power3_label** + +**power4_input** + +**power4_label** + +The driver provides the following attributes for temperature: + +**temp1_input** + +**temp1_crit** + +**temp1_crit_alarm** + +**temp1_max** + +**temp1_max_alarm** + +**temp2_input** + +**temp2_crit** + +**temp2_crit_alarm** + +**temp2_max** + +**temp2_max_alarm** diff --git a/Documentation/hwmon/nct7802.rst b/Documentation/hwmon/nct7802.rst index 8b7365a7cb32..366050ea595c 100644 --- a/Documentation/hwmon/nct7802.rst +++ b/Documentation/hwmon/nct7802.rst @@ -24,6 +24,22 @@ speed sensors. Smart Fan™ speed control is available via pwmX_auto_point attributes. +Sysfs Attributes +---------------- + +Sysfs attributes unique to this chip are documented below. For common +attributes, see Documentation/hwmon/sysfs-interface.rst. + +step_up_time + Time interval between successive duty cycle increases + when in Smart Fan mode. Specified in milliseconds and + rounded to intervals of 100 in the range 100-25500. + +step_down_time + Time interval between successive duty cycle decreases + when in Smart Fan mode. Specified in milliseconds and + rounded to intervals of 100 in the range 100-25500. + Tested Boards and BIOS Versions ------------------------------- diff --git a/Documentation/hwmon/pmbus.rst b/Documentation/hwmon/pmbus.rst index a8e01a5b96da..23c42c312274 100644 --- a/Documentation/hwmon/pmbus.rst +++ b/Documentation/hwmon/pmbus.rst @@ -3,17 +3,18 @@ Kernel driver pmbus Supported chips: - * Flex BMR310, BMR453, BMR454, BMR456, BMR457, BMR458, BMR480, - BMR490, BMR491, BMR492 + * Flex BMR310, BMR316, BMR321, BMR350, BMR351, BMR453, BMR454, + BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, BMR492 - Prefixes: 'bmr310', 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480', + Prefixes: 'bmr310', 'bmr316', 'bmr321', 'bmr350', 'bmr351', + 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480', 'bmr490', 'bmr491', 'bmr492' Addresses scanned: - Datasheets: - https://flexpowermodules.com/products + https://flex.com/products/power-modules/product-selector * ON Semiconductor ADP4000, NCP4200, NCP4208 diff --git a/Documentation/hwmon/raspberrypi-hwmon.rst b/Documentation/hwmon/raspberrypi-hwmon.rst index 8038ade36490..db315184b861 100644 --- a/Documentation/hwmon/raspberrypi-hwmon.rst +++ b/Documentation/hwmon/raspberrypi-hwmon.rst @@ -20,6 +20,17 @@ undervoltage conditions. Sysfs entries ------------- -======================= ================== +======================= ====================================================== +in0_input Core voltage in millivolts +in1_input SDRAM controller voltage in millivolts +in2_input SDRAM I/O voltage in millivolts +in3_input SDRAM PHY voltage in millivolts +in0_label "core" +in1_label "sdram_c" +in2_label "sdram_i" +in3_label "sdram_p" in0_lcrit_alarm Undervoltage alarm -======================= ================== +======================= ====================================================== + +The voltage inputs and labels are only exposed if the firmware reports support +for the corresponding voltage ID. diff --git a/Documentation/hwmon/sysfs-interface.rst b/Documentation/hwmon/sysfs-interface.rst index f76e9f8cc1ad..94e1bbce172a 100644 --- a/Documentation/hwmon/sysfs-interface.rst +++ b/Documentation/hwmon/sysfs-interface.rst @@ -106,6 +106,10 @@ Global attributes `update_interval` The interval at which the chip will update readings. +`update_interval_us` + The interval at which the chip will update readings, + expressed in microseconds for finer resolution. + ******** Voltages diff --git a/MAINTAINERS b/MAINTAINERS index f6770a5d528e..0609d6e99c77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2077,6 +2077,13 @@ S: Maintained F: drivers/net/arcnet/ F: include/uapi/linux/if_arcnet.h +ARCTIC FAN CONTROLLER DRIVER +M: Aureo Serrano de Souza +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/arctic_fan_controller.rst +F: drivers/hwmon/arctic_fan_controller.c + ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS) M: Arnd Bergmann M: Krzysztof Kozlowski @@ -15251,6 +15258,15 @@ F: Documentation/devicetree/bindings/hwmon/adi,ltc4282.yaml F: Documentation/hwmon/ltc4282.rst F: drivers/hwmon/ltc4282.c +LTC4283 HARDWARE MONITOR AND GPIO DRIVER +M: Nuno Sá +L: linux-gpio@vger.kernel.org +L: linux-hwmon@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml +F: drivers/gpio/gpio-ltc4283.c +F: drivers/hwmon/ltc4283.c + LTC4286 HARDWARE MONITOR DRIVER M: Delphine CC Chiu L: linux-hwmon@vger.kernel.org @@ -15683,6 +15699,23 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml F: Documentation/hwmon/max17616.rst F: drivers/hwmon/pmbus/max17616.c +MAX20830 HARDWARE MONITOR DRIVER +M: Alexis Czezar Torreno +L: linux-hwmon@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml +F: Documentation/hwmon/max20830.rst +F: drivers/hwmon/pmbus/max20830.c + +MAX20860A HARDWARE MONITOR DRIVER +M: Sanman Pradhan +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml +F: Documentation/hwmon/max20860a.rst +F: drivers/hwmon/pmbus/max20860a.c + MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram L: linux-media@vger.kernel.org @@ -17370,6 +17403,14 @@ S: Supported F: Documentation/devicetree/bindings/interrupt-controller/microchip,sama7g5-eic.yaml F: drivers/irqchip/irq-mchp-eic.c +MICROCHIP EMC1812 DRIVER +M: Marius Cristea +L: linux-hwmon@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml +F: Documentation/hwmon/emc1812.rst +F: drivers/hwmon/emc1812.c + MICROCHIP I2C DRIVER M: Codrin Ciubotariu L: linux-i2c@vger.kernel.org @@ -18091,6 +18132,13 @@ S: Maintained F: Documentation/hwmon/mp29502.rst F: drivers/hwmon/pmbus/mp29502.c +MPS MP2985 DRIVER +M: Wensheng Wang +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/mp2985.rst +F: drivers/hwmon/pmbus/mp2985.c + MPS MP2993 DRIVER M: Noah Wang L: linux-hwmon@vger.kernel.org @@ -18267,6 +18315,13 @@ F: drivers/mux/ F: include/dt-bindings/mux/ F: include/linux/mux/ +MURATA D1U74T PSU DRIVER +M: Abdurrahman Hussain +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/d1u74t.rst +F: drivers/hwmon/pmbus/d1u74t.c + MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER M: Bin Liu L: linux-usb@vger.kernel.org diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bbd0ebe2f131..28cf6d2e83c2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1793,6 +1793,21 @@ config GPIO_WM8994 endmenu +menu "Auxiliary Bus GPIO drivers" + depends on AUXILIARY_BUS + +config GPIO_LTC4283 + tristate "Analog Devices LTC4283 GPIO support" + depends on SENSORS_LTC4283 + help + If you say yes here you want the GPIO function available in Analog + Devices LTC4283 Negative Voltage Hot Swap Controller. + + This driver can also be built as a module. If so, the module will + be called gpio-ltc4283. + +endmenu + menu "PCI GPIO expanders" depends on PCI diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8ec03c9aec20..4d0e900402fc 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_LTC4283) += gpio-ltc4283.o obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o diff --git a/drivers/gpio/gpio-ltc4283.c b/drivers/gpio/gpio-ltc4283.c new file mode 100644 index 000000000000..6609443c5d62 --- /dev/null +++ b/drivers/gpio/gpio-ltc4283.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices LTC4283 GPIO driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTC4283_PINS_MAX 8 +#define LTC4283_PGIOX_START_NR 4 +#define LTC4283_INPUT_STATUS 0x02 +#define LTC4283_PGIO_CONFIG 0x10 +#define LTC4283_PGIO_CFG_MASK(pin) \ + GENMASK(((pin) - LTC4283_PGIOX_START_NR) * 2 + 1, (((pin) - LTC4283_PGIOX_START_NR) * 2)) +#define LTC4283_PGIO_CONFIG_2 0x11 + +#define LTC4283_ADIO_CONFIG 0x12 +/* starts at bit 4 */ +#define LTC4283_ADIOX_CONFIG_MASK(pin) BIT((pin) + 4) +#define LTC4283_PGIO_DIR_IN 3 +#define LTC4283_PGIO_DIR_OUT 2 + +struct ltc4283_gpio { + struct gpio_chip gpio_chip; + struct regmap *regmap; +}; + +static int ltc4283_pgio_get_direction(const struct ltc4283_gpio *st, unsigned int off) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, LTC4283_PGIO_CONFIG, &val); + if (ret) + return ret; + + val = field_get(LTC4283_PGIO_CFG_MASK(off), val); + if (val == LTC4283_PGIO_DIR_IN) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int ltc4283_gpio_get_direction(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + unsigned int val; + int ret; + + if (off >= LTC4283_PGIOX_START_NR) + return ltc4283_pgio_get_direction(st, off); + + ret = regmap_read(st->regmap, LTC4283_ADIO_CONFIG, &val); + if (ret) + return ret; + + if (val & LTC4283_ADIOX_CONFIG_MASK(off)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int ltc4283_gpio_direction_set(const struct ltc4283_gpio *st, + unsigned int off, bool input) +{ + if (off >= LTC4283_PGIOX_START_NR) { + unsigned int val = LTC4283_PGIO_DIR_OUT; + + if (input) + val = LTC4283_PGIO_DIR_IN; + + val = field_prep(LTC4283_PGIO_CFG_MASK(off), val); + return regmap_update_bits(st->regmap, LTC4283_PGIO_CONFIG, + LTC4283_PGIO_CFG_MASK(off), val); + } + + return regmap_update_bits(st->regmap, LTC4283_ADIO_CONFIG, + LTC4283_ADIOX_CONFIG_MASK(off), + field_prep(LTC4283_ADIOX_CONFIG_MASK(off), input)); +} + +static int __ltc4283_gpio_set_value(const struct ltc4283_gpio *st, + unsigned int off, int val) +{ + u32 reg = off < LTC4283_PGIOX_START_NR ? LTC4283_ADIO_CONFIG : LTC4283_PGIO_CONFIG_2; + + return regmap_update_bits(st->regmap, reg, BIT(off), + field_prep(BIT(off), !!val)); +} + +static int ltc4283_gpio_direction_input(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + + return ltc4283_gpio_direction_set(st, off, true); +} + +static int ltc4283_gpio_direction_output(struct gpio_chip *gc, unsigned int off, int val) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + int ret; + + ret = ltc4283_gpio_direction_set(st, off, false); + if (ret) + return ret; + + return __ltc4283_gpio_set_value(st, off, val); +} + +static int ltc4283_gpio_get_value(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + unsigned int val, reg; + int ret, dir; + + dir = ltc4283_gpio_get_direction(gc, off); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_IN) { + ret = regmap_read(st->regmap, LTC4283_INPUT_STATUS, &val); + if (ret) + return ret; + + /* ADIO1 is at bit 3. */ + if (off < LTC4283_PGIOX_START_NR) + return !!(val & BIT(3 - off)); + + /* PGIO1 is at bit 7. */ + return !!(val & BIT(7 - (off - LTC4283_PGIOX_START_NR))); + } + + if (off < LTC4283_PGIOX_START_NR) + reg = LTC4283_ADIO_CONFIG; + else + reg = LTC4283_PGIO_CONFIG_2; + + ret = regmap_read(st->regmap, reg, &val); + if (ret) + return ret; + + return !!(val & BIT(off)); +} + +static int ltc4283_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + + return __ltc4283_gpio_set_value(st, off, val); +} + +static int ltc4283_init_valid_mask(struct gpio_chip *gc, unsigned long *valid_mask, + unsigned int ngpios) +{ + unsigned long *mask = dev_get_platdata(gc->parent); + + bitmap_copy(valid_mask, mask, ngpios); + return 0; +} + +static int ltc4283_gpio_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct ltc4283_gpio *st; + struct gpio_chip *gc; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->regmap = dev_get_regmap(dev->parent, NULL); + if (!st->regmap) + return dev_err_probe(dev, -ENODEV, + "Failed to get regmap\n"); + + gc = &st->gpio_chip; + gc->parent = dev; + gc->get_direction = ltc4283_gpio_get_direction; + gc->direction_input = ltc4283_gpio_direction_input; + gc->direction_output = ltc4283_gpio_direction_output; + gc->get = ltc4283_gpio_get_value; + gc->set = ltc4283_gpio_set_value; + gc->init_valid_mask = ltc4283_init_valid_mask; + gc->can_sleep = true; + + gc->base = -1; + gc->ngpio = LTC4283_PINS_MAX; + gc->label = adev->name; + gc->owner = THIS_MODULE; + + return devm_gpiochip_add_data(dev, &st->gpio_chip, st); +} + +static const struct auxiliary_device_id ltc4283_aux_id_table[] = { + { "ltc4283.gpio" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ltc4283_aux_id_table); + +static struct auxiliary_driver ltc4283_gpio_driver = { + .probe = ltc4283_gpio_probe, + .id_table = ltc4283_aux_id_table, +}; +module_auxiliary_driver(ltc4283_gpio_driver); + +MODULE_AUTHOR("Nuno Sá "); +MODULE_DESCRIPTION("GPIO LTC4283 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc..e4c4f2b09732 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -388,6 +388,18 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_ARCTIC_FAN_CONTROLLER + tristate "ARCTIC Fan Controller" + depends on USB_HID + help + If you say yes here you get support for the ARCTIC Fan Controller, + a USB HID device (VID 0x3904, PID 0xF001) with 10 fan channels. + The driver exposes fan speed (RPM) and PWM control via the hwmon + sysfs interface. + + This driver can also be built as a module. If so, the module + will be called arctic_fan_controller. + config SENSORS_ARM_SCMI tristate "ARM SCMI Sensors" depends on ARM_SCMI_PROTOCOL @@ -1157,6 +1169,18 @@ config SENSORS_LTC4282 This driver can also be built as a module. If so, the module will be called ltc4282. +config SENSORS_LTC4283 + tristate "Analog Devices LTC4283" + depends on I2C + select REGMAP_I2C + select AUXILIARY_BUS + help + If you say yes here you get support for Analog Devices LTC4283 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4283. + config SENSORS_LTQ_CPUTEMP bool "Lantiq cpu temperature sensor driver" depends on SOC_XWAY @@ -2044,6 +2068,17 @@ config SENSORS_EMC1403 Threshold values can be configured using sysfs. Data from the different diodes are accessible via sysfs. +config SENSORS_EMC1812 + tristate "Microchip Technology EMC1812 driver" + depends on I2C + select REGMAP_I2C + help + If you say yes here to build support for Microchip Technology's + EMC181X/33 Multichannel Low-Voltage Remote Diode Sensor Family. + + This driver can also be built as a module. If so, the module + will be called emc1812. + config SENSORS_EMC2103 tristate "SMSC EMC2103" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 982ee2c6f9de..d5c8bc05123a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_AHT10) += aht10.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o +obj-$(CONFIG_SENSORS_ARCTIC_FAN_CONTROLLER) += arctic_fan_controller.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o @@ -72,6 +73,7 @@ obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o +obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o @@ -147,6 +149,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o +obj-$(CONFIG_SENSORS_LTC4283) += ltc4283.o obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index f0b17e59827f..c2df631ae93a 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -205,8 +205,8 @@ static int ad7414_probe(struct i2c_client *client) } static const struct i2c_device_id ad7414_id[] = { - { "ad7414" }, - {} + { .name = "ad7414" }, + { } }; MODULE_DEVICE_TABLE(i2c, ad7414_id); diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index 7a132accdf8a..a0c9cf5e12cc 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -281,9 +281,9 @@ static int ad7418_probe(struct i2c_client *client) } static const struct i2c_device_id ad7418_id[] = { - { "ad7416", ad7416 }, - { "ad7417", ad7417 }, - { "ad7418", ad7418 }, + { .name = "ad7416", .driver_data = ad7416 }, + { .name = "ad7417", .driver_data = ad7417 }, + { .name = "ad7418", .driver_data = ad7418 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7418_id); diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index 5e805d4ee76a..e45adc0b84d1 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -480,7 +480,7 @@ static int adc128_probe(struct i2c_client *client) } static const struct i2c_device_id adc128_id[] = { - { "adc128d818" }, + { .name = "adc128d818" }, { } }; MODULE_DEVICE_TABLE(i2c, adc128_id); diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 389382d54752..ccac7ba601e9 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -548,8 +548,8 @@ static int adm1025_probe(struct i2c_client *client) } static const struct i2c_device_id adm1025_id[] = { - { "adm1025", adm1025 }, - { "ne1619", ne1619 }, + { .name = "adm1025", .driver_data = adm1025 }, + { .name = "ne1619", .driver_data = ne1619 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1025_id); diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index c38c932e5d2a..3ad82c01f81d 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1857,7 +1857,7 @@ static int adm1026_probe(struct i2c_client *client) } static const struct i2c_device_id adm1026_id[] = { - { "adm1026" }, + { .name = "adm1026" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1026_id); diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 71eea8ae51b9..6cb0a238e059 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -382,7 +382,7 @@ static int adm1029_probe(struct i2c_client *client) } static const struct i2c_device_id adm1029_id[] = { - { "adm1029" }, + { .name = "adm1029" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1029_id); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 343118532cdb..24d0d4146c87 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -1055,8 +1055,8 @@ static int adm1031_probe(struct i2c_client *client) } static const struct i2c_device_id adm1031_id[] = { - { "adm1030", adm1030 }, - { "adm1031", adm1031 }, + { .name = "adm1030", .driver_data = adm1030 }, + { .name = "adm1031", .driver_data = adm1031 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1031_id); diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c index 7888afe8dafd..dc4d8a214d1b 100644 --- a/drivers/hwmon/adm1177.c +++ b/drivers/hwmon/adm1177.c @@ -246,8 +246,8 @@ static int adm1177_probe(struct i2c_client *client) } static const struct i2c_device_id adm1177_id[] = { - {"adm1177"}, - {} + { .name = "adm1177" }, + { } }; MODULE_DEVICE_TABLE(i2c, adm1177_id); diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 86f6044b5bd0..586650e8d043 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -794,9 +794,9 @@ static int adm9240_probe(struct i2c_client *client) } static const struct i2c_device_id adm9240_id[] = { - { "adm9240", adm9240 }, - { "ds1780", ds1780 }, - { "lm81", lm81 }, + { .name = "adm9240", .driver_data = adm9240 }, + { .name = "ds1780", .driver_data = ds1780 }, + { .name = "lm81", .driver_data = lm81 }, { } }; MODULE_DEVICE_TABLE(i2c, adm9240_id); diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 7f43565ca284..149cfcec78dc 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -176,8 +176,8 @@ static int ads7828_probe(struct i2c_client *client) } static const struct i2c_device_id ads7828_device_ids[] = { - { "ads7828", ads7828 }, - { "ads7830", ads7830 }, + { .name = "ads7828", .driver_data = ads7828 }, + { .name = "ads7830", .driver_data = ads7830 }, { } }; MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c index 9ee3ce01f130..d1cbff0e8572 100644 --- a/drivers/hwmon/ads7871.c +++ b/drivers/hwmon/ads7871.c @@ -56,7 +56,6 @@ #include #include #include -#include #include #include @@ -64,8 +63,19 @@ struct ads7871_data { struct spi_device *spi; + u8 tx_buf[2] ____cacheline_aligned; }; +static umode_t ads7871_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_in && attr == hwmon_in_input) + return 0444; + + return 0; +} + static int ads7871_read_reg8(struct spi_device *spi, int reg) { int ret; @@ -86,29 +96,31 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg) return le16_to_cpu((__force __le16)ret); } -static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) +static int ads7871_write_reg8(struct ads7871_data *pdata, int reg, u8 val) { - u8 tmp[2] = {reg, val}; - return spi_write(spi, tmp, sizeof(tmp)); + pdata->tx_buf[0] = reg; + pdata->tx_buf[1] = val; + + return spi_write(pdata->spi, pdata->tx_buf, 2); } -static ssize_t voltage_show(struct device *dev, struct device_attribute *da, - char *buf) +static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { struct ads7871_data *pdata = dev_get_drvdata(dev); struct spi_device *spi = pdata->spi; - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - int ret, val, i = 0; - uint8_t channel, mux_cnv; + int ret, raw_val, i = 0; + u8 mux_cnv; - channel = attr->index; + if (type != hwmon_in || attr != hwmon_in_input) + return -EOPNOTSUPP; /* * TODO: add support for conversions * other than single ended with a gain of 1 */ /*MUX_M3_BM forces single ended*/ /*This is also where the gain of the PGA would be set*/ - ret = ads7871_write_reg8(spi, REG_GAIN_MUX, + ret = ads7871_write_reg8(pdata, REG_GAIN_MUX, (MUX_CNV_BM | MUX_M3_BM | channel)); if (ret < 0) return ret; @@ -116,6 +128,7 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da, ret = ads7871_read_reg8(spi, REG_GAIN_MUX); if (ret < 0) return ret; + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); /* * on 400MHz arm9 platform the conversion @@ -131,39 +144,37 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da, } if (mux_cnv == 0) { - val = ads7871_read_reg16(spi, REG_LS_BYTE); - if (val < 0) - return val; - /*result in volts*10000 = (val/8192)*2.5*10000*/ - val = ((val >> 2) * 25000) / 8192; - return sysfs_emit(buf, "%d\n", val); + raw_val = ads7871_read_reg16(spi, REG_LS_BYTE); + if (raw_val < 0) + return raw_val; + + /* + * Use (s16) to ensure the sign bit is preserved during the shift. + * Report millivolts (2.5V = 2500mV). + */ + *val = ((s16)raw_val >> 2) * 2500 / 8192; + return 0; } return -ETIMEDOUT; } -static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 5); -static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 6); -static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 7); - -static struct attribute *ads7871_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, +static const struct hwmon_channel_info * const ads7871_info[] = { + 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), NULL }; -ATTRIBUTE_GROUPS(ads7871); +static const struct hwmon_ops ads7871_hwmon_ops = { + .is_visible = ads7871_is_visible, + .read = ads7871_read, +}; + +static const struct hwmon_chip_info ads7871_chip_info = { + .ops = &ads7871_hwmon_ops, + .info = ads7871_info, +}; static int ads7871_probe(struct spi_device *spi) { @@ -178,11 +189,17 @@ static int ads7871_probe(struct spi_device *spi) spi->bits_per_word = 8; spi_setup(spi); - ads7871_write_reg8(spi, REG_SER_CONTROL, 0); - ads7871_write_reg8(spi, REG_AD_CONTROL, 0); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->spi = spi; + + ads7871_write_reg8(pdata, REG_SER_CONTROL, 0); + ads7871_write_reg8(pdata, REG_AD_CONTROL, 0); val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM); - ads7871_write_reg8(spi, REG_OSC_CONTROL, val); + ads7871_write_reg8(pdata, REG_OSC_CONTROL, val); ret = ads7871_read_reg8(spi, REG_OSC_CONTROL); dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret); @@ -193,15 +210,10 @@ static int ads7871_probe(struct spi_device *spi) if (val != ret) return -ENODEV; - pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdata->spi = spi; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias, - pdata, - ads7871_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias, + pdata, + &ads7871_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 73b196a78f3a..0aa7ce0a04be 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -89,10 +89,10 @@ static int adt7410_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adt7410_ids[] = { - { "adt7410" }, - { "adt7420" }, - { "adt7422" }, - {} + { .name = "adt7410" }, + { .name = "adt7420" }, + { .name = "adt7422" }, + { } }; MODULE_DEVICE_TABLE(i2c, adt7410_ids); diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index b9991a69e6c6..598f31647544 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -169,11 +169,11 @@ static ssize_t adt7411_set_bit(struct device *dev, if (ret || flag > 1) return -EINVAL; - hwmon_lock(dev); - ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); - /* force update */ - data->next_update = jiffies; - hwmon_unlock(dev); + scoped_guard(hwmon_lock, dev) { + ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); + /* force update */ + data->next_update = jiffies; + } return ret < 0 ? ret : count; } @@ -670,7 +670,7 @@ static int adt7411_probe(struct i2c_client *client) } static const struct i2c_device_id adt7411_id[] = { - { "adt7411" }, + { .name = "adt7411" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7411_id); diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 174dfee47f7a..31cf9e3bb04f 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1809,15 +1810,22 @@ static int adt7462_probe(struct i2c_client *client) } static const struct i2c_device_id adt7462_id[] = { - { "adt7462" }, + { .name = "adt7462" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7462_id); +static const struct of_device_id adt7462_of_match[] = { + { .compatible = "onnn,adt7462" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adt7462_of_match); + static struct i2c_driver adt7462_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7462", + .of_match_table = adt7462_of_match, }, .probe = adt7462_probe, .id_table = adt7462_id, diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index dbee6926fa05..664349756dc2 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1296,7 +1296,7 @@ static void adt7470_remove(struct i2c_client *client) } static const struct i2c_device_id adt7470_id[] = { - { "adt7470" }, + { .name = "adt7470" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7470_id); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 8cefa14e1633..7241fc73d21a 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -165,15 +167,15 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { adt7473, adt7475, adt7476, adt7490 }; static const struct i2c_device_id adt7475_id[] = { - { "adt7473", adt7473 }, - { "adt7475", adt7475 }, - { "adt7476", adt7476 }, - { "adt7490", adt7490 }, + { .name = "adt7473", .driver_data = adt7473 }, + { .name = "adt7475", .driver_data = adt7475 }, + { .name = "adt7476", .driver_data = adt7476 }, + { .name = "adt7490", .driver_data = adt7490 }, { } }; MODULE_DEVICE_TABLE(i2c, adt7475_id); -static const struct of_device_id __maybe_unused adt7475_of_match[] = { +static const struct of_device_id adt7475_of_match[] = { { .compatible = "adi,adt7473", .data = (void *)adt7473 @@ -1995,7 +1997,7 @@ static struct i2c_driver adt7475_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7475", - .of_match_table = of_match_ptr(adt7475_of_match), + .of_match_table = adt7475_of_match, }, .probe = adt7475_probe, .id_table = adt7475_id, diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index 66955395d058..6c263cb57766 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -55,10 +55,10 @@ enum aht10_variant { aht10, aht20, dht20}; static const struct i2c_device_id aht10_id[] = { - { "aht10", aht10 }, - { "aht20", aht20 }, - { "dht20", dht20 }, - { }, + { .name = "aht10", .driver_data = aht10 }, + { .name = "aht20", .driver_data = aht20 }, + { .name = "dht20", .driver_data = dht20 }, + { } }; MODULE_DEVICE_TABLE(i2c, aht10_id); diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 8e5926b06070..5e6611d6e1bd 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -1083,7 +1083,7 @@ static int amc6821_probe(struct i2c_client *client) } static const struct i2c_device_id amc6821_id[] = { - { "amc6821" }, + { .name = "amc6821" }, { } }; diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 24f3e86d0ebf..90a14a7f2c4c 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1305,6 +1305,7 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = { }, { .ident = NULL } }; +MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); static int __init applesmc_init(void) { @@ -1416,4 +1417,3 @@ module_exit(applesmc_exit); MODULE_AUTHOR("Nicolas Boichat"); MODULE_DESCRIPTION("Apple SMC"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); diff --git a/drivers/hwmon/arctic_fan_controller.c b/drivers/hwmon/arctic_fan_controller.c new file mode 100644 index 000000000000..dbe84cd93c08 --- /dev/null +++ b/drivers/hwmon/arctic_fan_controller.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux hwmon driver for ARCTIC Fan Controller + * + * USB Custom HID device with 10 fan channels. + * Exposes fan RPM (input) and PWM (0-255) via hwmon. Device pushes IN reports + * at ~1 Hz; no GET_REPORT. OUT reports set PWM duty (bytes 1-10, 0-100%). + * PWM is manual-only: the device does not change duty autonomously, only + * when it receives an OUT report from the host. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARCTIC_VID 0x3904 +#define ARCTIC_PID 0xF001 +#define ARCTIC_NUM_FANS 10 +#define ARCTIC_OUTPUT_REPORT_ID 0x01 +#define ARCTIC_REPORT_LEN 32 +#define ARCTIC_RPM_OFFSET 11 /* bytes 11-30: 10 x uint16 LE */ +/* ACK report: device sends Report ID 0x02, 2 bytes (ID + status) after applying OUT report */ +#define ARCTIC_ACK_REPORT_ID 0x02 +#define ARCTIC_ACK_REPORT_LEN 2 +/* + * Time to wait for ACK report after send. + * Measured over 500 iterations: max ~563 ms. Keep 1 s as margin. + */ +#define ARCTIC_ACK_TIMEOUT_MS 1000 + +struct arctic_fan_data { + struct hid_device *hdev; + struct device *hwmon_dev; /* stored for explicit unregister in remove() */ + spinlock_t in_report_lock; /* protects fan_rpm, ack_status, write_pending, pwm_duty */ + struct completion in_report_received; /* ACK (ID 0x02) received in raw_event */ + int ack_status; /* 0 = OK, negative errno on device error */ + bool write_pending; /* true while an OUT report ACK is in flight */ + u32 fan_rpm[ARCTIC_NUM_FANS]; + u8 pwm_duty[ARCTIC_NUM_FANS]; /* 0-255 matching sysfs range; converted to 0-100 on send */ + /* + * OUT report buffer passed to hid_hw_output_report(). Embedded in the + * devm_kzalloc'd struct so it is heap-allocated and passes + * usb_hcd_map_urb_for_dma(). Exclusively accessed by write(), which + * the hwmon core serializes. + */ + __dma_from_device_group_begin(); + u8 buf[ARCTIC_REPORT_LEN]; + __dma_from_device_group_end(); +}; + +/* + * Parse RPM values from the periodic status report (10 x uint16 LE at rpm_off). + * pwm_duty is not updated from the report: the device is manual-only, so the + * host cache is the authoritative source for PWM. + * Called from raw_event which may run in IRQ context; must not sleep. + */ +static void arctic_fan_parse_report(struct arctic_fan_data *priv, u8 *buf, + int len, int rpm_off) +{ + unsigned long flags; + int i; + + if (len < rpm_off + 20) + return; + + spin_lock_irqsave(&priv->in_report_lock, flags); + for (i = 0; i < ARCTIC_NUM_FANS; i++) + priv->fan_rpm[i] = get_unaligned_le16(&buf[rpm_off + i * 2]); + spin_unlock_irqrestore(&priv->in_report_lock, flags); +} + +/* + * raw_event: IN reports. + * + * Status report: Report ID 0x01, 32 bytes: + * byte 0 = report ID, bytes 1-10 = PWM 0-100%, bytes 11-30 = 10 x RPM uint16 LE. + * Device pushes these at ~1 Hz; no GET_REPORT. + * + * ACK report: Report ID 0x02, 2 bytes: + * byte 0 = 0x02, byte 1 = status (0x00 = OK, 0x01 = ERROR). + * Sent once after accepting and applying an OUT report (ID 0x01). + */ +static int arctic_fan_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + unsigned long flags; + + hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d\n", report->id, size); + + if (report->id == ARCTIC_ACK_REPORT_ID && size == ARCTIC_ACK_REPORT_LEN) { + spin_lock_irqsave(&priv->in_report_lock, flags); + /* + * Only deliver if a write is in flight. This prevents a + * late-arriving ACK from a timed-out write from erroneously + * satisfying a subsequent write's completion wait. + */ + if (priv->write_pending) { + priv->ack_status = data[1] == 0x00 ? 0 : -EIO; + complete(&priv->in_report_received); + } + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + + if (report->id != ARCTIC_OUTPUT_REPORT_ID || size != ARCTIC_REPORT_LEN) { + hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d ignored\n", + report->id, size); + return 0; + } + + arctic_fan_parse_report(priv, data, size, ARCTIC_RPM_OFFSET); + return 0; +} + +static umode_t arctic_fan_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_fan && attr == hwmon_fan_input) + return 0444; + if (type == hwmon_pwm && attr == hwmon_pwm_input) + return 0644; + return 0; +} + +static int arctic_fan_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct arctic_fan_data *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (type == hwmon_fan && attr == hwmon_fan_input) { + spin_lock_irqsave(&priv->in_report_lock, flags); + *val = priv->fan_rpm[channel]; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + if (type == hwmon_pwm && attr == hwmon_pwm_input) { + spin_lock_irqsave(&priv->in_report_lock, flags); + *val = priv->pwm_duty[channel]; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + return -EINVAL; +} + +static int arctic_fan_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct arctic_fan_data *priv = dev_get_drvdata(dev); + u8 new_duty = (u8)clamp_val(val, 0, 255); + unsigned long flags; + unsigned long t; + int i, ret; + + /* + * Build the buffer and arm write_pending under in_report_lock so that + * reset_resume() cannot clear pwm_duty[] between the pwm_duty[] read + * and the buffer write, and raw_event() cannot deliver a stale ACK + * from a previous write into this write's completion. + * + * priv->buf is heap-allocated (embedded in the devm_kzalloc'd struct), + * satisfying usb_hcd_map_urb_for_dma(). Exclusively accessed by + * write() which the hwmon core serializes. + * + * pwm_duty[channel] is committed only after a positive device ACK so a + * failed or timed-out write does not corrupt the cached state. + * + * Residual theoretical race: if write A times out (write_pending + * cleared), write B sets write_pending = true, and a late ACK from + * write A—delayed beyond ARCTIC_ACK_TIMEOUT_MS—arrives during write + * B's pending window, it would falsely satisfy write B's completion. + * This cannot be prevented in driver code without protocol support + * (for example, a correlation ID echoed in the device ACK report). + * In testing, observed ACK latency stayed below the 1 s timeout + * (maximum ~563 ms over 500 iterations). + * + * The wait is non-interruptible so that a signal cannot cause write() + * to return early while the OUT report is already in flight; an + * interruptible early return would create the same late-ACK window + * without even the timeout guard. + * Serialized by the hwmon core: only one arctic_fan_write() at a time. + * Use irqsave to match the IRQ context in which raw_event may run. + */ + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->buf[0] = ARCTIC_OUTPUT_REPORT_ID; + for (i = 0; i < ARCTIC_NUM_FANS; i++) { + u8 d = i == channel ? new_duty : priv->pwm_duty[i]; + + priv->buf[1 + i] = DIV_ROUND_CLOSEST((unsigned int)d * 100, 255); + } + priv->ack_status = -ETIMEDOUT; + priv->write_pending = true; + reinit_completion(&priv->in_report_received); + spin_unlock_irqrestore(&priv->in_report_lock, flags); + + ret = hid_hw_output_report(priv->hdev, priv->buf, ARCTIC_REPORT_LEN); + if (ret < 0) { + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->write_pending = false; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return ret; + } + + t = wait_for_completion_timeout(&priv->in_report_received, + msecs_to_jiffies(ARCTIC_ACK_TIMEOUT_MS)); + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->write_pending = false; + /* Commit inside the lock so reset_resume() cannot race with this write */ + if (t && priv->ack_status == 0) + priv->pwm_duty[channel] = new_duty; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + + if (!t) + return -ETIMEDOUT; + return priv->ack_status; /* 0=OK, -EIO=device error */ +} + +static const struct hwmon_ops arctic_fan_ops = { + .is_visible = arctic_fan_is_visible, + .read = arctic_fan_read, + .write = arctic_fan_write, +}; + +static const struct hwmon_channel_info *arctic_fan_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_chip_info arctic_fan_chip_info = { + .ops = &arctic_fan_ops, + .info = arctic_fan_info, +}; + +static int arctic_fan_reset_resume(struct hid_device *hdev) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + unsigned long flags; + + /* + * The device resets its PWM channels to hardware defaults on power + * loss during suspend. Clear the cached duty values so they reflect + * the unknown hardware state, consistent with probe-time behaviour + * (the device has no GET_REPORT support). Hold in_report_lock so + * this does not race with a concurrent pwm read or write callback. + */ + spin_lock_irqsave(&priv->in_report_lock, flags); + memset(priv->pwm_duty, 0, sizeof(priv->pwm_duty)); + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; +} + +static int arctic_fan_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct arctic_fan_data *priv; + int ret; + + if (!hid_is_usb(hdev)) + return -ENODEV; + + ret = hid_parse(hdev); + if (ret) + return ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hdev = hdev; + spin_lock_init(&priv->in_report_lock); + init_completion(&priv->in_report_received); + hid_set_drvdata(hdev, priv); + + ret = hid_hw_start(hdev, HID_CONNECT_DRIVER); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto out_stop; + + /* + * Start IO before registering with hwmon. If IO were started after + * hwmon registration, a sysfs write arriving in that narrow window + * would send an OUT report but the ACK could not be delivered (the HID + * core discards events until io_started), causing a spurious timeout. + */ + hid_device_io_start(hdev); + + /* + * Use the non-devm variant and store the pointer so remove() can + * call hwmon_device_unregister() before tearing down the HID + * transport. devm_hwmon_device_register_with_info() would defer + * unregistration until after remove() returns, leaving a window + * where a concurrent sysfs write could call hid_hw_output_report() + * on an already-stopped device (use-after-free). + */ + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "arctic_fan", + priv, &arctic_fan_chip_info, + NULL); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto out_close; + } + + return 0; + +out_close: + hid_device_io_stop(hdev); + hid_hw_close(hdev); +out_stop: + hid_hw_stop(hdev); + return ret; +} + +static void arctic_fan_remove(struct hid_device *hdev) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + + /* + * Unregister hwmon before stopping the HID transport. This removes + * the sysfs files and waits for any in-progress write() callback to + * return, so no hwmon op can call hid_hw_output_report() after + * hid_hw_stop() frees the underlying USB resources. + * Matches the pattern used by nzxt-smart2 and aquacomputer_d5next. + * + * The HID core clears hdev->io_started before invoking ->remove(), + * so hid_device_io_stop() is not called here; doing so would emit + * a spurious "io already stopped" warning. + */ + hwmon_device_unregister(priv->hwmon_dev); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id arctic_fan_id_table[] = { + { HID_USB_DEVICE(ARCTIC_VID, ARCTIC_PID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, arctic_fan_id_table); + +static struct hid_driver arctic_fan_driver = { + .name = "arctic_fan", + .id_table = arctic_fan_id_table, + .probe = arctic_fan_probe, + .remove = arctic_fan_remove, + .raw_event = arctic_fan_raw_event, + .reset_resume = arctic_fan_reset_resume, +}; + +module_hid_driver(arctic_fan_driver); + +MODULE_AUTHOR("Aureo Serrano de Souza "); +MODULE_DESCRIPTION("HID hwmon driver for ARCTIC Fan Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 14e7737866c2..5f5c94505166 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -213,7 +213,7 @@ static struct asb100_data *asb100_update_device(struct device *dev); static void asb100_init_client(struct i2c_client *client); static const struct i2c_device_id asb100_id[] = { - { "asb100" }, + { .name = "asb100" }, { } }; MODULE_DEVICE_TABLE(i2c, asb100_id); diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 87e186700849..e8145bb13ab3 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -1179,9 +1179,9 @@ static void asc7621_remove(struct i2c_client *client) } static const struct i2c_device_id asc7621_id[] = { - {"asc7621", asc7621}, - {"asc7621a", asc7621a}, - {}, + { .name = "asc7621", .driver_data = asc7621 }, + { .name = "asc7621a", .driver_data = asc7621a }, + { } }; MODULE_DEVICE_TABLE(i2c, asc7621_id); diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index b5d97a27f80d..29a23484cbe7 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -277,7 +277,7 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { [ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31), [ec_sensor_temp_mb] = - EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32), + 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] = @@ -404,6 +404,12 @@ static const struct ec_sensor_info sensors_family_intel_700[] = { [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; /* Shortcuts for common combinations */ @@ -514,6 +520,13 @@ static const struct ec_board_info board_info_maximus_z690_formula = { .family = family_intel_600_series, }; +static const struct ec_board_info board_info_maximus_z790_extreme = { + .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW, + .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, + .family = family_intel_700_series, +}; + static const struct ec_board_info board_info_prime_x470_pro = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | @@ -621,6 +634,14 @@ static const struct ec_board_info board_info_strix_b550_i_gaming = { .family = family_amd_500_series, }; +static const struct ec_board_info board_info_strix_b650e_e_gaming = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_600_series, +}; + static const struct ec_board_info board_info_strix_b650e_i_gaming = { .sensors = SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_IN_CPU_CORE, @@ -628,6 +649,14 @@ static const struct ec_board_info board_info_strix_b650e_i_gaming = { .family = family_amd_600_series, }; +static const struct ec_board_info board_info_strix_b850_e_gaming_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_800_series, +}; + static const struct ec_board_info board_info_strix_b850_i_gaming_wifi = { .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_MB | SENSOR_TEMP_VRM, @@ -862,12 +891,18 @@ static const struct dmi_system_id dmi_table[] = { &board_info_maximus_x_hero), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z690 FORMULA", &board_info_maximus_z690_formula), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME", + &board_info_maximus_z790_extreme), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", &board_info_strix_b550_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", &board_info_strix_b550_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-E GAMING WIFI", + &board_info_strix_b650e_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-I GAMING WIFI", &board_info_strix_b650e_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-E GAMING WIFI", + &board_info_strix_b850_e_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-I GAMING WIFI", &board_info_strix_b850_i_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING", diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 1c7e9a98b757..f5de4894f0a2 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -278,7 +278,7 @@ static int atxp1_probe(struct i2c_client *client) }; static const struct i2c_device_id atxp1_id[] = { - { "atxp1" }, + { .name = "atxp1" }, { } }; MODULE_DEVICE_TABLE(i2c, atxp1_id); diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index 645b8c2e704e..4aecf463180f 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -736,14 +736,14 @@ static void cc2_remove(struct i2c_client *client) } static const struct i2c_device_id cc2_id[] = { - { "cc2d23" }, - { "cc2d23s" }, - { "cc2d25" }, - { "cc2d25s" }, - { "cc2d33" }, - { "cc2d33s" }, - { "cc2d35" }, - { "cc2d35s" }, + { .name = "cc2d23" }, + { .name = "cc2d23s" }, + { .name = "cc2d25" }, + { .name = "cc2d25s" }, + { .name = "cc2d33" }, + { .name = "cc2d33s" }, + { .name = "cc2d35" }, + { .name = "cc2d35s" }, { } }; MODULE_DEVICE_TABLE(i2c, cc2_id); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 70711a7cca12..6215ea49faaa 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -39,7 +39,6 @@ static int force_tjmax; module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); -#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */ enum coretemp_attr_index { @@ -136,14 +135,14 @@ static const struct tjmax_model tjmax_model_table[] = { * which are covered by tjmax_table */ { INTEL_ATOM_BONNELL_MID, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) - * Note: TjMax for E6xxT is 110C, but CPU type - * is undetectable by software + * Note: TjMax for E6xxT is 110C, but CPU + * type is undetectable by software */ { INTEL_ATOM_SALTWELL_MID, ANY, 90000 }, /* Atom Medfield (Z2460) */ { INTEL_ATOM_SALTWELL_TABLET, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */ { INTEL_ATOM_SALTWELL, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) - * Also matches S12x0 (stepping 9), covered by - * PCI table + * Also matches S12x0 (stepping 9), covered + * by PCI table */ { INTEL_ATOM_SILVERMONT, 9, 110000 }, /* Atom Bay Trail E38xx (embedded) */ { INTEL_ATOM_SILVERMONT, ANY, 90000 }, /* Atom Bay Trail Z37xx (tablet) */ @@ -201,6 +200,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) { const struct tjmax_model *tm = &tjmax_model_table[i]; + if (c->x86_vfm == tm->vfm && (tm->stepping_mask == ANY || tm->stepping_mask == c->x86_stepping)) @@ -223,8 +223,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) err = rdmsrq_safe_on_cpu(id, 0x17, &val); if (err) { dev_warn(dev, - "Unable to access MSR 0x17, assuming desktop" - " CPU\n"); + "Unable to access MSR 0x17, assuming desktop CPU\n"); usemsr_ee = 0; } else if (c->x86_vfm < INTEL_CORE2_PENRYN && !(val & 0x10000000)) { @@ -258,8 +257,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) err = rdmsrq_safe_on_cpu(id, 0xee, &val); if (err) { dev_warn(dev, - "Unable to access MSR 0xEE, for Tjmax, left" - " at default\n"); + "Unable to access MSR 0xEE, for Tjmax, left at default\n"); } else if (val & 0x40000000) { tjmax = tjmax_ee; } @@ -342,7 +340,7 @@ static int max_zones __read_mostly; static struct platform_device **zone_devices; static ssize_t show_label(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct platform_data *pdata = dev_get_drvdata(dev); struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]); @@ -354,7 +352,7 @@ static ssize_t show_label(struct device *dev, } static ssize_t show_crit_alarm(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct msr val; struct temp_data *tdata = container_of(devattr, struct temp_data, @@ -368,7 +366,7 @@ static ssize_t show_crit_alarm(struct device *dev, } static ssize_t show_tjmax(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TJMAX]); int tjmax; @@ -381,7 +379,7 @@ static ssize_t show_tjmax(struct device *dev, } static ssize_t show_ttarget(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TTARGET]); int ttarget; @@ -396,7 +394,7 @@ static ssize_t show_ttarget(struct device *dev, } static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct msr val; struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]); @@ -424,7 +422,6 @@ static ssize_t show_temp(struct device *dev, static int create_core_attrs(struct temp_data *tdata, struct device *dev) { - int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, @@ -432,6 +429,7 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev) static const char *const suffixes[TOTAL_ATTRS] = { "label", "crit_alarm", "input", "crit", "max" }; + int i; for (i = 0; i < tdata->attr_size; i++) { /* @@ -453,7 +451,6 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev) return sysfs_create_group(&dev->kobj, &tdata->attr_group); } - static int chk_ucode_version(unsigned int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); @@ -485,13 +482,7 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag) struct temp_data *tdata; if (!pdata->core_data) { - /* - * TODO: - * The information of actual possible cores in a package is broken for now. - * Will replace hardcoded NUM_REAL_CORES with actual per package core count - * when this information becomes available. - */ - pdata->nr_cores = NUM_REAL_CORES; + pdata->nr_cores = topology_num_cores_per_package(); pdata->core_data = kzalloc_objs(struct temp_data *, pdata->nr_cores); if (!pdata->core_data) @@ -781,7 +772,8 @@ static int coretemp_cpu_offline(unsigned int cpu) } return 0; } -static const struct x86_cpu_id __initconst coretemp_ids[] = { + +static const struct x86_cpu_id coretemp_ids[] __initconst = { X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), {} }; diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index 77dd9f28962d..ea24056ae646 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -654,9 +654,10 @@ static int cros_ec_hwmon_resume(struct platform_device *pdev) } static const struct platform_device_id cros_ec_hwmon_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; +MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); static struct platform_driver cros_ec_hwmon_driver = { .driver.name = DRV_NAME, @@ -667,7 +668,6 @@ static struct platform_driver cros_ec_hwmon_driver = { }; module_platform_driver(cros_ec_hwmon_driver); -MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); MODULE_DESCRIPTION("ChromeOS EC Hardware Monitoring Driver"); MODULE_AUTHOR("Thomas Weißschuh + * + * Datasheet can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* EMC1812 Registers Addresses */ +#define EMC1812_STATUS_ADDR 0x02 +#define EMC1812_CONFIG_LO_ADDR 0x03 + +#define EMC1812_CFG_ADDR 0x09 +#define EMC1812_CONV_ADDR 0x0A +#define EMC1812_INT_DIODE_HIGH_LIMIT_ADDR 0x0B +#define EMC1812_INT_DIODE_LOW_LIMIT_ADDR 0x0C +#define EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR 0x0D +#define EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR 0x0E +#define EMC1812_ONE_SHOT_ADDR 0x0F + +#define EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR 0x13 +#define EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR 0x14 +#define EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR 0x15 +#define EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR 0x16 +#define EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR 0x17 +#define EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR 0x18 +#define EMC1812_EXT1_THERM_LIMIT_ADDR 0x19 +#define EMC1812_EXT2_THERM_LIMIT_ADDR 0x1A +#define EMC1812_EXT_DIODE_FAULT_STATUS_ADDR 0x1B + +#define EMC1812_DIODE_FAULT_MASK_ADDR 0x1F +#define EMC1812_INT_DIODE_THERM_LIMIT_ADDR 0x20 +#define EMC1812_THRM_HYS_ADDR 0x21 +#define EMC1812_CONSEC_ALERT_ADDR 0x22 + +#define EMC1812_EXT1_BETA_CONFIG_ADDR 0x25 +#define EMC1812_EXT2_BETA_CONFIG_ADDR 0x26 +#define EMC1812_EXT1_IDEALITY_FACTOR_ADDR 0x27 +#define EMC1812_EXT2_IDEALITY_FACTOR_ADDR 0x28 + +#define EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR 0x2C +#define EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR 0x2D +#define EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR 0x2E +#define EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR 0x2F +#define EMC1812_EXT3_THERM_LIMIT_ADDR 0x30 +#define EMC1812_EXT3_IDEALITY_FACTOR_ADDR 0x31 + +#define EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR 0x34 +#define EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR 0x35 +#define EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR 0x36 +#define EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR 0x37 +#define EMC1812_EXT4_THERM_LIMIT_ADDR 0x38 +#define EMC1812_EXT4_IDEALITY_FACTOR_ADDR 0x39 +#define EMC1812_HIGH_LIMIT_STATUS_ADDR 0x3A +#define EMC1812_LOW_LIMIT_STATUS_ADDR 0x3B +#define EMC1812_THERM_LIMIT_STATUS_ADDR 0x3C +#define EMC1812_ROC_GAIN_ADDR 0x3D +#define EMC1812_ROC_CONFIG_ADDR 0x3E +#define EMC1812_ROC_STATUS_ADDR 0x3F +#define EMC1812_R1_RESH_ADDR 0x40 +#define EMC1812_R1_LIMH_ADDR 0x41 +#define EMC1812_R1_LIML_ADDR 0x42 +#define EMC1812_R1_SMPL_ADDR 0x43 +#define EMC1812_R2_RESH_ADDR 0x44 +#define EMC1812_R2_3_RESL_ADDR 0x45 +#define EMC1812_R2_LIMH_ADDR 0x46 +#define EMC1812_R2_LIML_ADDR 0x47 +#define EMC1812_R2_SMPL_ADDR 0x48 +#define EMC1812_PER_MAXTH_1_ADDR 0x49 +#define EMC1812_PER_MAXT1L_ADDR 0x4A +#define EMC1812_PER_MAXTH_2_ADDR 0x4B +#define EMC1812_PER_MAXT2_3L_ADDR 0x4C +#define EMC1812_GBL_MAXT1H_ADDR 0x4D +#define EMC1812_GBL_MAXT1L_ADDR 0x4E +#define EMC1812_GBL_MAXT2H_ADDR 0x4F +#define EMC1812_GBL_MAXT2L_ADDR 0x50 +#define EMC1812_FILTER_SEL_ADDR 0x51 + +#define EMC1812_INT_HIGH_BYTE_ADDR 0x60 +#define EMC1812_INT_LOW_BYTE_ADDR 0x61 +#define EMC1812_EXT1_HIGH_BYTE_ADDR 0x62 +#define EMC1812_EXT1_LOW_BYTE_ADDR 0x63 +#define EMC1812_EXT2_HIGH_BYTE_ADDR 0x64 +#define EMC1812_EXT2_LOW_BYTE_ADDR 0x65 +#define EMC1812_EXT3_HIGH_BYTE_ADDR 0x66 +#define EMC1812_EXT3_LOW_BYTE_ADDR 0x67 +#define EMC1812_EXT4_HIGH_BYTE_ADDR 0x68 +#define EMC1812_EXT4_LOW_BYTE_ADDR 0x69 +#define EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR 0x6A +#define EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR 0x6B +#define EMC1812_HOTTEST_STATUS_ADDR 0x6C +#define EMC1812_HOTTEST_CFG_ADDR 0x6D + +#define EMC1812_PRODUCT_ID_ADDR 0xFD +#define EMC1812_MANUFACTURER_ID_ADDR 0xFE +#define EMC1812_REVISION_ADDR 0xFF + +/* EMC1812 Config Bits */ +#define EMC1812_CFG_MSKAL BIT(7) +#define EMC1812_CFG_RS BIT(6) +#define EMC1812_CFG_ATTHM BIT(5) +#define EMC1812_CFG_RECD12 BIT(4) +#define EMC1812_CFG_RECD34 BIT(3) +#define EMC1812_CFG_RANGE BIT(2) +#define EMC1812_CFG_DA_ENA BIT(1) +#define EMC1812_CFG_APDD BIT(0) + +/* EMC1812 Status Bits */ +#define EMC1812_STATUS_ROCF BIT(7) +#define EMC1812_STATUS_HOTCHG BIT(6) +#define EMC1812_STATUS_BUSY BIT(5) +#define EMC1812_STATUS_HIGH BIT(4) +#define EMC1812_STATUS_LOW BIT(3) +#define EMC1812_STATUS_FAULT BIT(2) +#define EMC1812_STATUS_ETHRM BIT(1) +#define EMC1812_STATUS_ITHRM BIT(0) + +#define EMC1812_BETA_LOCK_VAL 0x0F + +#define EMC1812_TEMP_CH_ADDR(index) (EMC1812_INT_HIGH_BYTE_ADDR + 2 * (index)) + +#define EMC1812_FILTER_MASK_LEN 2 + +#define EMC1812_PID 0x81 +#define EMC1813_PID 0x87 +#define EMC1814_PID 0x84 +#define EMC1815_PID 0x85 +#define EMC1833_PID 0x83 + +/* The maximum number of channels a member of the family can have */ +#define EMC1812_MAX_NUM_CHANNELS 5 +#define EMC1812_TEMP_OFFSET 64 + +#define EMC1812_DEFAULT_IDEALITY_FACTOR 0x12 + +/* Constants and default values */ +#define EMC1812_HIGH_LIMIT_DEFAULT (85 + EMC1812_TEMP_OFFSET) + +#define EMC1812_TEMP_MASK (HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | \ + HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST | \ + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | \ + HWMON_T_CRIT_ALARM | HWMON_T_LABEL) + +static const struct hwmon_channel_info * const emc1812_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + EMC1812_TEMP_MASK, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT), + NULL +}; + +/** + * struct emc1812_features - features of a emc1812 instance + * @name: chip's name + * @phys_channels: number of physical channels supported by the chip + * @has_ext2_beta_reg: the EXT2_BETA register is available on the chip + */ +struct emc1812_features { + const char *name; + u8 phys_channels; + bool has_ext2_beta_reg; +}; + +static const struct emc1812_features emc1833_chip_config = { + .name = "emc1833", + .phys_channels = 3, + .has_ext2_beta_reg = true, +}; + +static const struct emc1812_features emc1812_chip_config = { + .name = "emc1812", + .phys_channels = 2, + .has_ext2_beta_reg = false, +}; + +static const struct emc1812_features emc1813_chip_config = { + .name = "emc1813", + .phys_channels = 3, + .has_ext2_beta_reg = true, +}; + +static const struct emc1812_features emc1814_chip_config = { + .name = "emc1814", + .phys_channels = 4, + .has_ext2_beta_reg = false, +}; + +static const struct emc1812_features emc1815_chip_config = { + .name = "emc1815", + .phys_channels = 5, + .has_ext2_beta_reg = false, +}; + +enum emc1812_limit_type {temp_min, temp_max}; + +static const u8 emc1812_temp_map[] = { + [hwmon_temp_min] = temp_min, + [hwmon_temp_max] = temp_max, +}; + +static const u8 emc1812_ideality_regs[] = { + [0] = 0xff, + [1] = EMC1812_EXT1_IDEALITY_FACTOR_ADDR, + [2] = EMC1812_EXT2_IDEALITY_FACTOR_ADDR, + [3] = EMC1812_EXT3_IDEALITY_FACTOR_ADDR, + [4] = EMC1812_EXT4_IDEALITY_FACTOR_ADDR, +}; + +static const u8 emc1812_temp_crit_regs[] = { + [0] = EMC1812_INT_DIODE_THERM_LIMIT_ADDR, + [1] = EMC1812_EXT1_THERM_LIMIT_ADDR, + [2] = EMC1812_EXT2_THERM_LIMIT_ADDR, + [3] = EMC1812_EXT3_THERM_LIMIT_ADDR, + [4] = EMC1812_EXT4_THERM_LIMIT_ADDR, +}; + +static const u8 emc1812_limit_regs[][2] = { + [0] = { + [temp_min] = EMC1812_INT_DIODE_LOW_LIMIT_ADDR, + [temp_max] = EMC1812_INT_DIODE_HIGH_LIMIT_ADDR, + }, + [1] = { + [temp_min] = EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [2] = { + [temp_min] = EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [3] = { + [temp_min] = EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [4] = { + [temp_min] = EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, +}; + +static const u8 emc1812_limit_regs_low[][2] = { + [0] = { + [temp_min] = 0xff, + [temp_max] = 0xff, + }, + [1] = { + [temp_min] = EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [2] = { + [temp_min] = EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [3] = { + [temp_min] = EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [4] = { + [temp_min] = EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR, + }, +}; + +/* Lookup table for temperature conversion times in msec */ +static const u16 emc1812_conv_time[] = { + 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 62, 31, 16 +}; + +/** + * struct emc1812_data - information about chip parameters + * @labels: labels of the channels + * @active_ch_mask: active channels + * @chip: pointer to structure holding chip features + * @regmap: device register map + * @recd34_en: state of Resistance Error Correction (REC) on channels 3 and 4 + * @recd12_en: state of Resistance Error Correction (REC) on channels 1 and 2 + * @apdd_en: state of anti-parallel diode mode + */ +struct emc1812_data { + const char *labels[EMC1812_MAX_NUM_CHANNELS]; + unsigned long active_ch_mask; + const struct emc1812_features *chip; + struct regmap *regmap; + bool recd34_en; + bool recd12_en; + bool apdd_en; +}; + +/* emc1812 regmap configuration */ +static const struct regmap_range emc1812_regmap_writable_ranges[] = { + regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR), + regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, EMC1812_EXT2_THERM_LIMIT_ADDR), + regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR), + regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_EXT4_IDEALITY_FACTOR_ADDR), + regmap_reg_range(EMC1812_ROC_GAIN_ADDR, EMC1812_ROC_CONFIG_ADDR), + regmap_reg_range(EMC1812_R1_LIMH_ADDR, EMC1812_R1_SMPL_ADDR), + regmap_reg_range(EMC1812_R2_LIMH_ADDR, EMC1812_R2_SMPL_ADDR), + regmap_reg_range(EMC1812_FILTER_SEL_ADDR, EMC1812_FILTER_SEL_ADDR), + regmap_reg_range(EMC1812_HOTTEST_CFG_ADDR, EMC1812_HOTTEST_CFG_ADDR), +}; + +static const struct regmap_access_table emc1812_regmap_wr_table = { + .yes_ranges = emc1812_regmap_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(emc1812_regmap_writable_ranges), +}; + +static const struct regmap_range emc1812_regmap_rd_ranges[] = { + regmap_reg_range(EMC1812_STATUS_ADDR, EMC1812_CONFIG_LO_ADDR), + regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR), + regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, + EMC1812_EXT_DIODE_FAULT_STATUS_ADDR), + regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR), + regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_FILTER_SEL_ADDR), + regmap_reg_range(EMC1812_INT_HIGH_BYTE_ADDR, EMC1812_HOTTEST_CFG_ADDR), + regmap_reg_range(EMC1812_PRODUCT_ID_ADDR, EMC1812_REVISION_ADDR), +}; + +static const struct regmap_access_table emc1812_regmap_rd_table = { + .yes_ranges = emc1812_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(emc1812_regmap_rd_ranges), +}; + +static bool emc1812_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case EMC1812_STATUS_ADDR: + case EMC1812_EXT_DIODE_FAULT_STATUS_ADDR: + case EMC1812_DIODE_FAULT_MASK_ADDR: + case EMC1812_EXT1_BETA_CONFIG_ADDR: + case EMC1812_EXT2_BETA_CONFIG_ADDR: + case EMC1812_HIGH_LIMIT_STATUS_ADDR: + case EMC1812_LOW_LIMIT_STATUS_ADDR: + case EMC1812_THERM_LIMIT_STATUS_ADDR: + case EMC1812_ROC_STATUS_ADDR: + case EMC1812_PER_MAXTH_1_ADDR: + case EMC1812_PER_MAXT1L_ADDR: + case EMC1812_PER_MAXTH_2_ADDR: + case EMC1812_PER_MAXT2_3L_ADDR: + case EMC1812_GBL_MAXT1H_ADDR: + case EMC1812_GBL_MAXT1L_ADDR: + case EMC1812_GBL_MAXT2H_ADDR: + case EMC1812_GBL_MAXT2L_ADDR: + case EMC1812_INT_HIGH_BYTE_ADDR: + case EMC1812_INT_LOW_BYTE_ADDR: + case EMC1812_EXT1_HIGH_BYTE_ADDR: + case EMC1812_EXT1_LOW_BYTE_ADDR: + case EMC1812_EXT2_HIGH_BYTE_ADDR: + case EMC1812_EXT2_LOW_BYTE_ADDR: + case EMC1812_EXT3_HIGH_BYTE_ADDR: + case EMC1812_EXT3_LOW_BYTE_ADDR: + case EMC1812_EXT4_HIGH_BYTE_ADDR: + case EMC1812_EXT4_LOW_BYTE_ADDR: + case EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR: + case EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR: + case EMC1812_HOTTEST_STATUS_ADDR: + return true; + default: + return false; + } +} + +static const struct regmap_config emc1812_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &emc1812_regmap_rd_table, + .wr_table = &emc1812_regmap_wr_table, + .volatile_reg = emc1812_is_volatile_reg, + .max_register = EMC1812_REVISION_ADDR, + .cache_type = REGCACHE_MAPLE, +}; + +static umode_t emc1812_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct emc1812_data *data = _data; + + switch (type) { + case hwmon_temp: + /* Don't show channels which are not enabled */ + if (!(data->active_ch_mask & BIT(channel))) + return 0; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + return 0644; + case hwmon_temp_crit_alarm: + case hwmon_temp_input: + case hwmon_temp_fault: + case hwmon_temp_max_alarm: + case hwmon_temp_max_hyst: + case hwmon_temp_min_alarm: + return 0444; + case hwmon_temp_label: + if (data->labels[channel]) + return 0444; + return 0; + default: + return 0; + } + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + default: + return 0; + } + default: + return 0; + } +}; + +static int emc1812_get_temp(struct emc1812_data *data, int channel, long *val) +{ + __be16 tmp_be16; + int ret; + + ret = regmap_bulk_read(data->regmap, EMC1812_TEMP_CH_ADDR(channel), + &tmp_be16, sizeof(tmp_be16)); + if (ret) + return ret; + + /* Range is always -64 to 191.875°C */ + *val = ((be16_to_cpu(tmp_be16) >> 5) - (EMC1812_TEMP_OFFSET << 3)) * 125; + + return 0; +} + +static int emc1812_get_crit_limit_temp(struct emc1812_data *data, int channel, long *val) +{ + unsigned int tmp; + int ret; + + /* Critical register is 8bits long and keeps only integer part of temperature */ + ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &tmp); + if (ret) + return ret; + + *val = tmp; + /* Range is always -64 to 191°C */ + *val = (*val - EMC1812_TEMP_OFFSET) * 1000; + + return 0; +} + +static int emc1812_get_limit_temp(struct emc1812_data *data, int ch, + enum emc1812_limit_type type, long *val) +{ + unsigned int regvalh; + unsigned int regvall = 0; + int ret; + + ret = regmap_read(data->regmap, emc1812_limit_regs[ch][type], ®valh); + if (ret < 0) + return ret; + + if (ch) { + ret = regmap_read(data->regmap, emc1812_limit_regs_low[ch][type], ®vall); + if (ret < 0) + return ret; + } + + /* Range is always -64 to 191.875°C */ + *val = ((regvalh << 3) | (regvall >> 5)); + *val = (*val - (EMC1812_TEMP_OFFSET << 3)) * 125; + + return 0; +} + +static int emc1812_read_reg(struct device *dev, struct emc1812_data *data, u32 attr, + int channel, long *val) +{ + unsigned int hyst; + int ret; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return emc1812_get_limit_temp(data, channel, emc1812_temp_map[attr], val); + case hwmon_temp_crit: + return emc1812_get_crit_limit_temp(data, channel, val); + case hwmon_temp_input: + return emc1812_get_temp(data, channel, val); + case hwmon_temp_max_hyst: + ret = emc1812_get_limit_temp(data, channel, temp_max, val); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst); + if (ret < 0) + return ret; + + *val -= (long)hyst * 1000; + + return 0; + case hwmon_temp_crit_hyst: + ret = emc1812_get_crit_limit_temp(data, channel, val); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst); + if (ret < 0) + return ret; + + *val -= (long)hyst * 1000; + + return 0; + case hwmon_temp_min_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_LOW_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_max_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_HIGH_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_crit_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_THERM_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_fault: + *val = regmap_test_bits(data->regmap, EMC1812_EXT_DIODE_FAULT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + unsigned int convrate; + int ret; + + switch (type) { + case hwmon_temp: + return emc1812_read_reg(dev, data, attr, channel, val); + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, EMC1812_CONV_ADDR, &convrate); + if (ret < 0) + return ret; + + if (convrate > 10) + convrate = 4; + + *val = DIV_ROUND_CLOSEST(16000, 1 << convrate); + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + + if (channel >= data->chip->phys_channels) + return -EOPNOTSUPP; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = data->labels[channel]; + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_set_hyst(struct emc1812_data *data, int channel, int val) +{ + unsigned int limit; + int hyst, ret; + + /* Critical register is 8bits long and keeps only integer part of temperature */ + ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &limit); + if (ret) + return ret; + + hyst = clamp_val((int)limit - val, 0, 255); + + ret = regmap_write(data->regmap, EMC1812_THRM_HYS_ADDR, hyst); + + return ret; +} + +static int emc1812_set_temp(struct emc1812_data *data, int channel, + enum emc1812_limit_type map, int val) +{ + unsigned int valh, vall; + u8 regh, regl; + int ret; + + regh = emc1812_limit_regs[channel][map]; + regl = emc1812_limit_regs_low[channel][map]; + + if (channel) { + val = DIV_ROUND_CLOSEST(val, 125); + valh = (val >> 3) & 0xff; + vall = (val & 0x07) << 5; + } else { + /* Temperature limit for internal channel is stored on 8bits */ + valh = DIV_ROUND_CLOSEST(val, 1000); + valh = clamp_val(valh, 0, 255); + } + + ret = regmap_write(data->regmap, regh, valh); + if (ret < 0) + return ret; + + if (channel) + ret = regmap_write(data->regmap, regl, vall); + + return ret; +} + +static int emc1812_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + unsigned int interval, tmp; + + switch (type) { + case hwmon_temp: + /* Range should be -64000 to 191875°C + (EMC1812_TEMP_OFFSET * 1000) */ + val = clamp_val(val, -64000, 191875); + val = val + (EMC1812_TEMP_OFFSET * 1000); + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return emc1812_set_temp(data, channel, emc1812_temp_map[attr], val); + case hwmon_temp_crit: + /* Critical temperature limit is stored on 8bits */ + val = DIV_ROUND_CLOSEST(val, 1000); + tmp = clamp_val(val, 0, 255); + return regmap_write(data->regmap, emc1812_temp_crit_regs[channel], tmp); + case hwmon_temp_crit_hyst: + /* Critical temperature hysteresis is stored on 8bits */ + val = DIV_ROUND_CLOSEST(val, 1000); + tmp = clamp_val(val, 0, 255); + return emc1812_set_hyst(data, channel, tmp); + default: + return -EOPNOTSUPP; + } + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + interval = clamp_val(val, 0, 16000); + tmp = find_closest_descending(interval, emc1812_conv_time, + ARRAY_SIZE(emc1812_conv_time)); + return regmap_write(data->regmap, EMC1812_CONV_ADDR, tmp); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_init(struct emc1812_data *priv) +{ + int i, ret; + u8 val; + + ret = regmap_write(priv->regmap, EMC1812_THRM_HYS_ADDR, 0x0A); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_CONSEC_ALERT_ADDR, 0x70); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_FILTER_SEL_ADDR, 0); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_HOTTEST_CFG_ADDR, 0); + if (ret) + return ret; + + /* Enables the beta compensation factor auto-detection function for beta1 and beta2 */ + ret = regmap_write(priv->regmap, EMC1812_EXT1_BETA_CONFIG_ADDR, + EMC1812_BETA_LOCK_VAL); + if (ret) + return ret; + + if (priv->chip->has_ext2_beta_reg) { + ret = regmap_write(priv->regmap, EMC1812_EXT2_BETA_CONFIG_ADDR, + EMC1812_BETA_LOCK_VAL); + if (ret) + return ret; + } + + for (i = 0; i < priv->chip->phys_channels; i++) { + if (!test_bit(i, &priv->active_ch_mask)) + continue; + + /* Update the max temperature limit for extended temperature range. */ + ret = emc1812_set_temp(priv, i, emc1812_temp_map[hwmon_temp_max], + EMC1812_HIGH_LIMIT_DEFAULT * 1000); + if (ret) + return ret; + + /* Update the critical temperature limit for extended temperature range. */ + ret = regmap_write(priv->regmap, emc1812_temp_crit_regs[i], + EMC1812_HIGH_LIMIT_DEFAULT); + if (ret) + return ret; + + /* Set the ideality factor */ + if (i > 0) { + ret = regmap_write(priv->regmap, emc1812_ideality_regs[i], + EMC1812_DEFAULT_IDEALITY_FACTOR); + if (ret) + return ret; + } + } + + /* + * Set default values in registers. APDD, RECD12 and RECD34 are active on 0. + * Set the device to be in Run (Active) state and converting on all + * channels. + * Don't change conversion rate. After reset, default is 4 conversions/seconds. + * The temperature measurement range is -64°C to +191.875°C. + * Set ALERT/THERM2 pin to be in comparator mode (When the ALERT/THERM2 pin is + * asserted in comparator mode, the corresponding High Limit Status bits are set. + * Reading these bits does not clear them until the ALERT/THERM2 pin is deasserted. + * Once the ALERT/THERM2 pin is deasserted, the status bits are automatically + * cleared.). + */ + val = FIELD_PREP(EMC1812_CFG_MSKAL, 0) | + FIELD_PREP(EMC1812_CFG_RS, 0) | + FIELD_PREP(EMC1812_CFG_ATTHM, 1) | + FIELD_PREP(EMC1812_CFG_RECD12, !priv->recd12_en) | + FIELD_PREP(EMC1812_CFG_RECD34, !priv->recd34_en) | + FIELD_PREP(EMC1812_CFG_RANGE, 1) | + FIELD_PREP(EMC1812_CFG_DA_ENA, 0) | + FIELD_PREP(EMC1812_CFG_APDD, !priv->apdd_en); + + return regmap_write(priv->regmap, EMC1812_CFG_ADDR, val); +} + +static int emc1812_parse_fw_config(struct emc1812_data *data, struct device *dev) +{ + unsigned int reg_nr = 0; + int ret; + + /* To be able to load the driver in case we don't have device tree */ + if (!dev_fwnode(dev)) { + data->active_ch_mask = BIT(data->chip->phys_channels) - 1; + return 0; + } + + data->apdd_en = device_property_read_bool(dev, "microchip,enable-anti-parallel"); + data->recd12_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel1-2"); + data->recd34_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel3-4"); + + /* Internal temperature channel is always active */ + data->labels[reg_nr] = "internal_diode"; + set_bit(reg_nr, &data->active_ch_mask); + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®_nr); + if (ret || reg_nr >= data->chip->phys_channels) + return dev_err_probe(dev, -EINVAL, + "The index is higher then the chip supports\n"); + /* Mark channel as active */ + set_bit(reg_nr, &data->active_ch_mask); + + fwnode_property_read_string(child, "label", &data->labels[reg_nr]); + } + + return 0; +} + +static int emc1812_chip_identify(struct emc1812_data *data, struct i2c_client *client) +{ + const struct emc1812_features *chip; + struct device *dev = &client->dev; + unsigned int tmp; + int ret; + + ret = regmap_read(data->regmap, EMC1812_PRODUCT_ID_ADDR, &tmp); + if (ret) + return ret; + + switch (tmp) { + case EMC1812_PID: + data->chip = &emc1812_chip_config; + break; + case EMC1813_PID: + data->chip = &emc1813_chip_config; + break; + case EMC1814_PID: + data->chip = &emc1814_chip_config; + break; + case EMC1815_PID: + data->chip = &emc1815_chip_config; + break; + case EMC1833_PID: + data->chip = &emc1833_chip_config; + break; + default: + /* + * If failed to identify the hardware based on internal registers, + * try using fallback compatible in device tree to deal with some + * newer part number. + */ + chip = i2c_get_match_data(client); + if (!chip) + return -ENODEV; + + dev_warn(dev, "Unrecognized hardware ID 0x%x, using %s from devicetree data\n", + tmp, chip->name); + + data->chip = chip; + + return 0; + } + + return 0; +} + +static const struct hwmon_ops emc1812_ops = { + .is_visible = emc1812_is_visible, + .read = emc1812_read, + .read_string = emc1812_read_string, + .write = emc1812_write, +}; + +static const struct hwmon_chip_info emc1812_chip_info = { + .ops = &emc1812_ops, + .info = emc1812_info, +}; + +static int emc1812_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct emc1812_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &emc1812_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Cannot initialize register map\n"); + + ret = emc1812_chip_identify(data, client); + if (ret) + return dev_err_probe(dev, ret, "Chip identification fails\n"); + + ret = emc1812_parse_fw_config(data, dev); + if (ret) + return ret; + + ret = emc1812_init(data); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize device\n"); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &emc1812_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id emc1812_id[] = { + { .name = "emc1812", .driver_data = (kernel_ulong_t)&emc1812_chip_config }, + { .name = "emc1813", .driver_data = (kernel_ulong_t)&emc1813_chip_config }, + { .name = "emc1814", .driver_data = (kernel_ulong_t)&emc1814_chip_config }, + { .name = "emc1815", .driver_data = (kernel_ulong_t)&emc1815_chip_config }, + { .name = "emc1833", .driver_data = (kernel_ulong_t)&emc1833_chip_config }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc1812_id); + +static const struct of_device_id emc1812_of_match[] = { + { + .compatible = "microchip,emc1812", + .data = &emc1812_chip_config + }, + { + .compatible = "microchip,emc1813", + .data = &emc1813_chip_config + }, + { + .compatible = "microchip,emc1814", + .data = &emc1814_chip_config + }, + { + .compatible = "microchip,emc1815", + .data = &emc1815_chip_config + }, + { + .compatible = "microchip,emc1833", + .data = &emc1833_chip_config + }, + { } +}; +MODULE_DEVICE_TABLE(of, emc1812_of_match); + +static struct i2c_driver emc1812_driver = { + .driver = { + .name = "emc1812", + .of_match_table = emc1812_of_match, + }, + .probe = emc1812_probe, + .id_table = emc1812_id, +}; +module_i2c_driver(emc1812_driver); + +MODULE_AUTHOR("Marius Cristea "); +MODULE_DESCRIPTION("EMC1812/13/14/15/33 high-accuracy remote diode temperature monitor Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 9b8e925af030..27dc149a3ed9 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -624,7 +624,7 @@ emc2103_probe(struct i2c_client *client) } static const struct i2c_device_id emc2103_ids[] = { - { "emc2103" }, + { .name = "emc2103" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, emc2103_ids); diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 2505e9fac499..eef3b021671b 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -32,6 +32,7 @@ #define EMC2305_REG_DRIVE_PWM_OUT 0x2b #define EMC2305_OPEN_DRAIN 0x0 #define EMC2305_PUSH_PULL 0x1 +#define EMC2305_PWM_SHUTDOWN_UNSET -1 #define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) @@ -59,10 +60,10 @@ enum emc230x_product_id { }; static const struct i2c_device_id emc2305_ids[] = { - { "emc2305" }, - { "emc2303" }, - { "emc2302" }, - { "emc2301" }, + { .name = "emc2305" }, + { .name = "emc2303" }, + { .name = "emc2302" }, + { .name = "emc2301" }, { } }; MODULE_DEVICE_TABLE(i2c, emc2305_ids); @@ -104,6 +105,7 @@ struct emc2305_cdev_data { * @pwm_output_mask: PWM output mask * @pwm_polarity_mask: PWM polarity mask * @pwm_separate: separate PWM settings for every channel + * @pwm_shutdown: Set shutdown PWM. * @pwm_min: array of minimum PWM per channel * @pwm_freq: array of PWM frequency per channel * @cdev_data: array of cooling devices data @@ -116,6 +118,7 @@ struct emc2305_data { u8 pwm_output_mask; u8 pwm_polarity_mask; bool pwm_separate; + s16 pwm_shutdown[EMC2305_PWM_MAX]; u8 pwm_min[EMC2305_PWM_MAX]; u16 pwm_freq[EMC2305_PWM_MAX]; struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; @@ -539,6 +542,7 @@ static int emc2305_of_parse_pwm_child(struct device *dev, struct device_node *child, struct emc2305_data *data) { u32 ch; + u32 pwm_shutdown_percent; int ret; struct of_phandle_args args; @@ -548,6 +552,12 @@ static int emc2305_of_parse_pwm_child(struct device *dev, return ret; } + if (ch >= data->pwm_num) { + dev_err(dev, "invalid reg %u for node %pOF (valid range 0-%u)\n", ch, child, + data->pwm_num - 1); + return -EINVAL; + } + ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", 0, &args); if (ret) @@ -579,6 +589,16 @@ static int emc2305_of_parse_pwm_child(struct device *dev, } of_node_put(args.np); + + ret = of_property_read_u32(child, "fan-shutdown-percent", + &pwm_shutdown_percent); + + if (!ret) { + pwm_shutdown_percent = clamp(pwm_shutdown_percent, 0, 100); + data->pwm_shutdown[ch] = + DIV_ROUND_CLOSEST(pwm_shutdown_percent * EMC2305_FAN_MAX, 100); + } + return 0; } @@ -612,6 +632,7 @@ static int emc2305_probe(struct i2c_client *client) int ret; int i; int pwm_childs; + u32 ch; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -631,6 +652,9 @@ static int emc2305_probe(struct i2c_client *client) if (ret) return ret; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_shutdown[i] = EMC2305_PWM_SHUTDOWN_UNSET; + pwm_childs = emc2305_probe_childs_from_dt(dev); pdata = dev_get_platdata(&client->dev); @@ -680,12 +704,18 @@ static int emc2305_probe(struct i2c_client *client) if (IS_REACHABLE(CONFIG_THERMAL)) { /* Parse and check for the available PWM child nodes */ if (pwm_childs > 0) { - i = 0; for_each_child_of_node_scoped(dev->of_node, child) { - ret = emc2305_set_single_tz(dev, child, i); + ret = of_property_read_u32(child, "reg", &ch); + if (ret || ch >= data->pwm_num) + continue; + + /* + * emc2305_set_single_tz() uses 0 for the common cooling + * device and 1..pwm_num for individual fan channels. + */ + ret = emc2305_set_single_tz(dev, child, ch + 1); if (ret != 0) return ret; - i++; } } else { ret = emc2305_set_tz(dev); @@ -714,6 +744,23 @@ static int emc2305_probe(struct i2c_client *client) return 0; } +static void emc2305_shutdown(struct i2c_client *client) +{ + int i; + int ret; + struct emc2305_data *data = i2c_get_clientdata(client); + + for (i = 0; i < data->pwm_num; i++) { + if (data->pwm_shutdown[i] != EMC2305_PWM_SHUTDOWN_UNSET) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), + data->pwm_shutdown[i]); + if (ret < 0) + dev_warn(&client->dev, + "Failed to set shutdown PWM for ch %d\n", i); + } + } +} + static const struct of_device_id of_emc2305_match_table[] = { { .compatible = "microchip,emc2305", }, {}, @@ -726,6 +773,7 @@ static struct i2c_driver emc2305_driver = { .of_match_table = of_emc2305_match_table, }, .probe = emc2305_probe, + .shutdown = emc2305_shutdown, .id_table = emc2305_ids, }; diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index 1100c6e5daa7..c13ea8748683 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -464,7 +464,7 @@ static int emc6w201_probe(struct i2c_client *client) } static const struct i2c_device_id emc6w201_id[] = { - { "emc6w201" }, + { .name = "emc6w201" }, { } }; MODULE_DEVICE_TABLE(i2c, emc6w201_id); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 7e867f132420..cc3a9612f2c0 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -877,9 +877,9 @@ static int f75375_detect(struct i2c_client *client, } static const struct i2c_device_id f75375_id[] = { - { "f75373", f75373 }, - { "f75375", f75375 }, - { "f75387", f75387 }, + { .name = "f75373", .driver_data = f75373 }, + { .name = "f75375", .driver_data = f75375 }, + { .name = "f75387", .driver_data = f75387 }, { } }; MODULE_DEVICE_TABLE(i2c, f75375_id); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 1211fa2259e5..019fc32bf318 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -225,13 +225,13 @@ static struct fschmd_data *fschmd_update_device(struct device *dev); */ static const struct i2c_device_id fschmd_id[] = { - { "fscpos", fscpos }, - { "fscher", fscher }, - { "fscscy", fscscy }, - { "fschrc", fschrc }, - { "fschmd", fschmd }, - { "fschds", fschds }, - { "fscsyl", fscsyl }, + { .name = "fscpos", .driver_data = fscpos }, + { .name = "fscher", .driver_data = fscher }, + { .name = "fscscy", .driver_data = fscscy }, + { .name = "fschrc", .driver_data = fschrc }, + { .name = "fschmd", .driver_data = fschmd }, + { .name = "fschds", .driver_data = fschds }, + { .name = "fscsyl", .driver_data = fscsyl }, { } }; MODULE_DEVICE_TABLE(i2c, fschmd_id); diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 08dcc6a7fb62..06be120a349d 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -49,7 +49,7 @@ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; static const struct i2c_device_id fts_id[] = { - { "ftsteutates" }, + { .name = "ftsteutates" }, { } }; MODULE_DEVICE_TABLE(i2c, fts_id); diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 39ae8f826417..e2166ee76286 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -197,7 +197,7 @@ static int g760a_probe(struct i2c_client *client) } static const struct i2c_device_id g760a_id[] = { - { "g760a" }, + { .name = "g760a" }, { } }; MODULE_DEVICE_TABLE(i2c, g760a_id); diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 4fa3aa1271da..407cf7af4fdd 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -44,9 +44,9 @@ #define DRVNAME "g762" static const struct i2c_device_id g762_id[] = { - { "g761" }, - { "g762" }, - { "g763" }, + { .name = "g761" }, + { .name = "g762" }, + { .name = "g763" }, { } }; MODULE_DEVICE_TABLE(i2c, g762_id); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 9c68bc013950..742c130cec7f 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -642,7 +642,7 @@ static int gl518_probe(struct i2c_client *client) } static const struct i2c_device_id gl518_id[] = { - { "gl518sm" }, + { .name = "gl518sm" }, { } }; MODULE_DEVICE_TABLE(i2c, gl518_id); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 972f4f8caa2b..f4fe39912ee2 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -885,7 +885,7 @@ static int gl520_probe(struct i2c_client *client) } static const struct i2c_device_id gl520_id[] = { - { "gl520sm" }, + { .name = "gl520sm" }, { } }; MODULE_DEVICE_TABLE(i2c, gl520_id); diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 80de5f20781e..ed212615b2fe 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -40,12 +40,11 @@ enum FAN_PWM_ENABLE { AUTOMATIC = 2, }; -static struct { +struct gpd_fan_data { enum FAN_PWM_ENABLE pwm_enable; u8 pwm_value; - const struct gpd_fan_drvdata *drvdata; -} gpd_driver_priv; +}; struct gpd_fan_drvdata { const char *board_name; // Board name for module param comparison @@ -249,10 +248,10 @@ static const struct gpd_fan_drvdata *gpd_module_drvdata[] = { }; // Helper functions to handle EC read/write -static void gpd_ecram_read(u16 offset, u8 *val) +static void gpd_ecram_read(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 *val) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -270,10 +269,10 @@ static void gpd_ecram_read(u16 offset, u8 *val) *val = inb(data_port); } -static void gpd_ecram_write(u16 offset, u8 value) +static void gpd_ecram_write(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 value) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -291,198 +290,202 @@ static void gpd_ecram_write(u16 offset, u8 value) outb(value, data_port); } -static int gpd_generic_read_rpm(void) +static int gpd_generic_read_rpm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 high, low; - gpd_ecram_read(drvdata->rpm_read, &high); - gpd_ecram_read(drvdata->rpm_read + 1, &low); + gpd_ecram_read(drvdata, drvdata->rpm_read, &high); + gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low); return (u16)high << 8 | low; } -static int gpd_wm2_read_rpm(void) +static int gpd_wm2_read_rpm(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; + for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { u8 PWMCTR; - gpd_ecram_read(pwm_ctr_offset, &PWMCTR); + gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR); if (PWMCTR != 0xB8) - gpd_ecram_write(pwm_ctr_offset, 0xB8); + gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8); } - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); } // Read value for fan1_input -static int gpd_read_rpm(void) +static int gpd_read_rpm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win4_6800u: case win_mini: case duo: case mpc2: - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); case win_max_2: - return gpd_wm2_read_rpm(); + return gpd_wm2_read_rpm(data); } return 0; } -static int gpd_wm2_read_pwm(void) +static int gpd_wm2_read_pwm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 var; - gpd_ecram_read(drvdata->pwm_write, &var); + gpd_ecram_read(drvdata, drvdata->pwm_write, &var); + + // EC PWM register valid range is 1 ~ pwm_max; 0 is an invalid state + if (unlikely(!var)) + return -EIO; // Match gpd_generic_write_pwm(u8) below return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); } // Read value for pwm1 -static int gpd_read_pwm(void) +static int gpd_read_pwm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case duo: case win4_6800u: case mpc2: - switch (gpd_driver_priv.pwm_enable) { + switch (data->pwm_enable) { case DISABLE: return 255; case MANUAL: - return gpd_driver_priv.pwm_value; + return data->pwm_value; case AUTOMATIC: return -EOPNOTSUPP; } break; case win_max_2: - return gpd_wm2_read_pwm(); + return gpd_wm2_read_pwm(data); } return 0; } // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. -static inline u8 gpd_cast_pwm_range(u8 val) +static inline u8 gpd_cast_pwm_range(const struct gpd_fan_drvdata *drvdata, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; - return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; } -static void gpd_generic_write_pwm(u8 val) +static void gpd_generic_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); } -static void gpd_duo_write_pwm(u8 val) +static void gpd_duo_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); - gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); + gpd_ecram_write(drvdata, drvdata->pwm_write + 1, pwm_reg); } // Write value for pwm1 -static int gpd_write_pwm(u8 val) +static int gpd_write_pwm(struct gpd_fan_data *data, u8 val) { - if (gpd_driver_priv.pwm_enable != MANUAL) + if (data->pwm_enable != MANUAL) return -EPERM; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case duo: - gpd_duo_write_pwm(val); + gpd_duo_write_pwm(data, val); break; case win_mini: case win4_6800u: case win_max_2: case mpc2: - gpd_generic_write_pwm(val); + gpd_generic_write_pwm(data, val); break; } return 0; } -static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_win_mini_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_generic_write_pwm(255); + gpd_generic_write_pwm(data, 255); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); + gpd_generic_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_duo_write_pwm(255); + gpd_duo_write_pwm(data, 255); break; case MANUAL: - gpd_duo_write_pwm(gpd_driver_priv.pwm_value); + gpd_duo_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_wm2_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; switch (enable) { case DISABLE: - gpd_generic_write_pwm(255); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, 255); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, data->pwm_value); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case AUTOMATIC: - gpd_ecram_write(drvdata->manual_control_enable, 0); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0); break; } } // Write value for pwm1_enable -static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { if (enable == MANUAL) // Set pwm_value to max firstly when switching to manual mode, in // consideration of device safety. - gpd_driver_priv.pwm_value = 255; + data->pwm_value = 255; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case win4_6800u: case mpc2: - gpd_win_mini_set_pwm_enable(enable); + gpd_win_mini_set_pwm_enable(data, enable); break; case duo: - gpd_duo_set_pwm_enable(enable); + gpd_duo_set_pwm_enable(data, enable); break; case win_max_2: - gpd_wm2_set_pwm_enable(enable); + gpd_wm2_set_pwm_enable(data, enable); break; } } @@ -505,15 +508,16 @@ static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata, return 0; } -static int gpd_fan_hwmon_read(__always_unused struct device *dev, +static int gpd_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long *val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); int ret; if (type == hwmon_fan) { if (attr == hwmon_fan_input) { - ret = gpd_read_rpm(); + ret = gpd_read_rpm(data); if (ret < 0) return ret; @@ -524,10 +528,10 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, } else if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: - *val = gpd_driver_priv.pwm_enable; + *val = data->pwm_enable; return 0; case hwmon_pwm_input: - ret = gpd_read_pwm(); + ret = gpd_read_pwm(data); if (ret < 0) return ret; @@ -540,27 +544,29 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, return -EOPNOTSUPP; } -static int gpd_fan_hwmon_write(__always_unused struct device *dev, +static int gpd_fan_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); + if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: if (!in_range(val, 0, 3)) return -EINVAL; - gpd_driver_priv.pwm_enable = val; + data->pwm_enable = val; - gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); + gpd_set_pwm_enable(data, data->pwm_enable); return 0; case hwmon_pwm_input: if (!in_range(val, 0, 256)) return -EINVAL; - gpd_driver_priv.pwm_value = val; + data->pwm_value = val; - return gpd_write_pwm(val); + return gpd_write_pwm(data, val); } } @@ -584,26 +590,37 @@ static struct hwmon_chip_info gpd_fan_chip_info = { .info = gpd_fan_hwmon_channel_info }; -static void gpd_win4_init_ec(void) +static void gpd_win4_init_ec(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 chip_id, chip_ver; - gpd_ecram_read(0x2000, &chip_id); + gpd_ecram_read(drvdata, 0x2000, &chip_id); if (chip_id == 0x55) { - gpd_ecram_read(0x1060, &chip_ver); - gpd_ecram_write(0x1060, chip_ver | 0x80); + gpd_ecram_read(drvdata, 0x1060, &chip_ver); + gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80); } } -static void gpd_init_ec(void) +static void gpd_init_ec(struct gpd_fan_data *data) { // The buggy firmware won't initialize EC properly on boot. // Before its initialization, reading RPM will always return 0, // and writing PWM will have no effect. // Initialize it manually on driver load. - if (gpd_driver_priv.drvdata->board == win4_6800u) - gpd_win4_init_ec(); + if (data->drvdata->board == win4_6800u) + gpd_win4_init_ec(data); +} + +static void gpd_fan_reset_hardware(void *pdata) +{ + struct gpd_fan_data *data = pdata; + + if (data) { + data->pwm_enable = AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } } static int gpd_fan_probe(struct platform_device *pdev) @@ -611,7 +628,10 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct resource *region; const struct resource *res; - const struct device *hwdev; + struct device *hwdev; + struct gpd_fan_data *data; + const struct gpd_fan_drvdata *match; + int ret; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -624,29 +644,39 @@ static int gpd_fan_probe(struct platform_device *pdev) return dev_err_probe(dev, -EBUSY, "Failed to request region\n"); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + match = dev_get_platdata(dev); + if (!match) + return -EINVAL; + + data->drvdata = match; + data->pwm_enable = AUTOMATIC; + data->pwm_value = 255; + + dev_set_drvdata(dev, data); + + gpd_init_ec(data); + + ret = devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data); + if (ret) + return ret; + hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, - NULL, + data, &gpd_fan_chip_info, NULL); if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - - gpd_init_ec(); - return 0; } -static void gpd_fan_remove(__always_unused struct platform_device *pdev) -{ - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_set_pwm_enable(AUTOMATIC); -} - static struct platform_driver gpd_fan_driver = { .probe = gpd_fan_probe, - .remove = gpd_fan_remove, .driver = { .name = KBUILD_MODNAME, }, @@ -668,6 +698,7 @@ static int __init gpd_fan_init(void) if (!match) { const struct dmi_system_id *dmi_match = dmi_first_match(dmi_table); + if (dmi_match) match = dmi_match->driver_data; } @@ -675,10 +706,6 @@ static int __init gpd_fan_init(void) if (!match) return -ENODEV; - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_driver_priv.pwm_value = 255; - gpd_driver_priv.drvdata = match; - struct resource gpd_fan_resources[] = { { .start = match->addr_port, @@ -690,10 +717,11 @@ static int __init gpd_fan_init(void) gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver, gpd_fan_probe, gpd_fan_resources, - 1, NULL, 0); + 1, + match, sizeof(*match)); if (IS_ERR(gpd_fan_platform_device)) { - pr_warn("Failed to create platform device\n"); + pr_err("Failed to create platform device\n"); return PTR_ERR(gpd_fan_platform_device); } diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c index 85af8299150a..7984be1e706d 100644 --- a/drivers/hwmon/hih6130.c +++ b/drivers/hwmon/hih6130.c @@ -233,7 +233,7 @@ static int hih6130_probe(struct i2c_client *client) /* Device ID table */ static const struct i2c_device_id hih6130_id[] = { - { "hih6130" }, + { .name = "hih6130" }, { } }; MODULE_DEVICE_TABLE(i2c, hih6130_id); diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c index 50c6c15f8b18..b263cbeca074 100644 --- a/drivers/hwmon/hs3001.c +++ b/drivers/hwmon/hs3001.c @@ -169,8 +169,8 @@ static const struct hwmon_chip_info hs3001_chip_info = { /* device ID table */ static const struct i2c_device_id hs3001_ids[] = { - { "hs3001" }, - { }, + { .name = "hs3001" }, + { } }; MODULE_DEVICE_TABLE(i2c, hs3001_ids); diff --git a/drivers/hwmon/htu31.c b/drivers/hwmon/htu31.c index 7521a371aa6c..5d2cdbdb773b 100644 --- a/drivers/hwmon/htu31.c +++ b/drivers/hwmon/htu31.c @@ -322,7 +322,7 @@ static int htu31_probe(struct i2c_client *client) } static const struct i2c_device_id htu31_id[] = { - { "htu31" }, + { .name = "htu31" }, { } }; MODULE_DEVICE_TABLE(i2c, htu31_id); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index c16e6da0690c..55a9a3ddd4aa 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -573,6 +573,7 @@ static const char * const hwmon_chip_attrs[] = { [hwmon_chip_curr_reset_history] = "curr_reset_history", [hwmon_chip_power_reset_history] = "power_reset_history", [hwmon_chip_update_interval] = "update_interval", + [hwmon_chip_update_interval_us] = "update_interval_us", [hwmon_chip_alarms] = "alarms", [hwmon_chip_samples] = "samples", [hwmon_chip_curr_samples] = "curr_samples", diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index a116f1600e81..39aca2cdf27f 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -569,7 +569,7 @@ static void ina209_remove(struct i2c_client *client) } static const struct i2c_device_id ina209_id[] = { - { "ina209" }, + { .name = "ina209" }, { } }; MODULE_DEVICE_TABLE(i2c, ina209_id); diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index ff67b03189f7..3632bbca607e 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -15,6 +15,7 @@ #include #include #include +#include /* INA238 register definitions */ #define INA238_CONFIG 0x0 @@ -49,6 +50,17 @@ #define INA238_DIAG_ALERT_BUSUL BIT(3) #define INA238_DIAG_ALERT_POL BIT(2) +/* INA238_ADC_CONFIG register field masks and shifts */ +#define INA238_ADC_CONFIG_MODE_MASK GENMASK(15, 12) +#define INA238_ADC_CONFIG_VBUSCT_SHIFT 9 +#define INA238_ADC_CONFIG_VBUSCT_MASK GENMASK(11, 9) +#define INA238_ADC_CONFIG_VSHCT_SHIFT 6 +#define INA238_ADC_CONFIG_VSHCT_MASK GENMASK(8, 6) +#define INA238_ADC_CONFIG_VTCT_SHIFT 3 +#define INA238_ADC_CONFIG_VTCT_MASK GENMASK(5, 3) +#define INA238_ADC_CONFIG_AVG_SHIFT 0 +#define INA238_ADC_CONFIG_AVG_MASK GENMASK(2, 0) + #define INA238_REGISTERS 0x20 #define INA238_RSHUNT_DEFAULT 2500 /* uOhm */ @@ -101,6 +113,21 @@ static const struct regmap_config ina238_regmap_config = { .val_bits = 16, }; +/* Lookup table for conversion times in usec for INA238 family */ +static const u16 ina238_conv_time[] = { + 50, 84, 150, 280, 540, 1052, 2074, 4120, +}; + +/* Lookup table for conversion times in usec for SQ52206 */ +static const u16 sq52206_conv_time[] = { + 66, 118, 310, 566, 1070, 2090, 4140, 8230, +}; + +/* Lookup table for number of samples used in averaging mode */ +static const int ina238_avg_samples[] = { + 1, 4, 16, 64, 128, 256, 512, 1024, +}; + enum ina238_ids { ina228, ina237, ina238, ina700, ina780, sq52206 }; struct ina238_config { @@ -112,6 +139,7 @@ struct ina238_config { u32 power_calculate_factor; /* fixed parameter for power calculation, from datasheet */ u32 bus_voltage_lsb; /* bus voltage LSB, in nV */ int current_lsb; /* current LSB, in uA */ + const u16 *conv_time; /* conversion time lookup table */ }; struct ina238_data { @@ -124,6 +152,7 @@ struct ina238_data { int current_lsb; /* current LSB, in uA */ int power_lsb; /* power LSB, in uW */ int energy_lsb; /* energy LSB, in uJ */ + u16 adc_config; /* cached ADC_CONFIG register value */ }; static const struct ina238_config ina238_config[] = { @@ -135,6 +164,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 16, + .conv_time = ina238_conv_time, }, [ina237] = { .has_20bit_voltage_current = false, @@ -144,6 +174,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, + .conv_time = ina238_conv_time, }, [ina238] = { .has_20bit_voltage_current = false, @@ -153,6 +184,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, + .conv_time = ina238_conv_time, }, [ina700] = { .has_20bit_voltage_current = false, @@ -163,6 +195,7 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, .current_lsb = 480, + .conv_time = ina238_conv_time, }, [ina780] = { .has_20bit_voltage_current = false, @@ -173,6 +206,7 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, .current_lsb = 2400, + .conv_time = ina238_conv_time, }, [sq52206] = { .has_20bit_voltage_current = false, @@ -182,6 +216,7 @@ static const struct ina238_config ina238_config[] = { .config_default = SQ52206_CONFIG_DEFAULT, .bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB, .temp_resolution = 16, + .conv_time = sq52206_conv_time, }, }; @@ -261,6 +296,111 @@ static int ina228_read_voltage(struct ina238_data *data, int channel, long *val) return 0; } +/* Converting ADC_CONFIG register value to update_interval in usec */ +static inline u32 ina238_reg_to_interval_us(struct ina238_data *data) +{ + const u16 *ct = data->config->conv_time; + u32 vbusct = ct[(data->adc_config & INA238_ADC_CONFIG_VBUSCT_MASK) >> + INA238_ADC_CONFIG_VBUSCT_SHIFT]; + u32 vshct = ct[(data->adc_config & INA238_ADC_CONFIG_VSHCT_MASK) >> + INA238_ADC_CONFIG_VSHCT_SHIFT]; + u32 vtct = ct[(data->adc_config & INA238_ADC_CONFIG_VTCT_MASK) >> + INA238_ADC_CONFIG_VTCT_SHIFT]; + + return vbusct + vshct + vtct; +} + +static inline u32 ina238_samples(struct ina238_data *data) +{ + return ina238_avg_samples[(data->adc_config & INA238_ADC_CONFIG_AVG_MASK) >> + INA238_ADC_CONFIG_AVG_SHIFT]; +} + +/* Converting update_interval(_us) to a per-field conversion time in usec. + * interval_us is the total ADC cycle time including averaging in microseconds. + * All three conversion fields (VBUSCT, VSHCT, VTCT) are set equal, so the + * per-field time is interval_us / (samples * 3). + */ +static inline u32 ina238_interval_us_to_conv_time(u32 interval_us, u32 samples) +{ + return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3); +} + +/* Write a per-field conversion time (in usec) to the ADC_CONFIG register */ +static int ina238_write_conv_time(struct ina238_data *data, u32 conv_time_us) +{ + u16 adc_config; + int idx, ret; + + idx = find_closest(conv_time_us, data->config->conv_time, + ARRAY_SIZE(ina238_conv_time)); + adc_config = (data->adc_config & + ~(INA238_ADC_CONFIG_VBUSCT_MASK | + INA238_ADC_CONFIG_VSHCT_MASK | + INA238_ADC_CONFIG_VTCT_MASK)) | + ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; +} + +static int ina238_read_chip(struct device *dev, u32 attr, long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_samples: + *val = ina238_samples(data); + return 0; + case hwmon_chip_update_interval: + /* Return in msec */ + *val = DIV_ROUND_CLOSEST(ina238_reg_to_interval_us(data) * + ina238_samples(data), 1000); + return 0; + case hwmon_chip_update_interval_us: + /* Return in usec */ + *val = ina238_reg_to_interval_us(data) * ina238_samples(data); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int ina238_write_chip(struct device *dev, u32 attr, long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + u16 adc_config; + int idx, ret; + + switch (attr) { + case hwmon_chip_samples: + idx = find_closest(val, ina238_avg_samples, + ARRAY_SIZE(ina238_avg_samples)); + adc_config = (data->adc_config & ~INA238_ADC_CONFIG_AVG_MASK) | + (idx << INA238_ADC_CONFIG_AVG_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; + case hwmon_chip_update_interval: + /* Convert ms to us before passing to the shared helper */ + val = clamp_val(val, 0, INT_MAX / 1000) * 1000; + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); + case hwmon_chip_update_interval_us: + val = clamp_val(val, 0, INT_MAX); + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); + default: + return -EOPNOTSUPP; + } +} + static int ina238_read_in(struct device *dev, u32 attr, int channel, long *val) { @@ -587,6 +727,8 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { switch (type) { + case hwmon_chip: + return ina238_read_chip(dev, attr, val); case hwmon_in: return ina238_read_in(dev, attr, channel, val); case hwmon_curr: @@ -607,6 +749,8 @@ static int ina238_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { switch (type) { + case hwmon_chip: + return ina238_write_chip(dev, attr, val); case hwmon_in: return ina238_write_in(dev, attr, channel, val); case hwmon_curr: @@ -629,6 +773,15 @@ static umode_t ina238_is_visible(const void *drvdata, bool has_energy = data->config->has_energy; switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_samples: + case hwmon_chip_update_interval: + case hwmon_chip_update_interval_us: + return 0644; + default: + return 0; + } case hwmon_in: switch (attr) { case hwmon_in_input: @@ -692,6 +845,9 @@ static umode_t ina238_is_visible(const void *drvdata, HWMON_I_MIN | HWMON_I_MIN_ALARM) static const struct hwmon_channel_info * const ina238_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL | + HWMON_C_UPDATE_INTERVAL_US), HWMON_CHANNEL_INFO(in, /* 0: shunt voltage */ INA238_HWMON_IN_CONFIG, @@ -798,8 +954,8 @@ static int ina238_probe(struct i2c_client *client) } /* Setup ADC_CONFIG register */ - ret = regmap_write(data->regmap, INA238_ADC_CONFIG, - INA238_ADC_CONFIG_DEFAULT); + data->adc_config = INA238_ADC_CONFIG_DEFAULT; + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, data->adc_config); if (ret < 0) { dev_err(dev, "error configuring the device: %d\n", ret); return -ENODEV; @@ -837,12 +993,12 @@ static int ina238_probe(struct i2c_client *client) } static const struct i2c_device_id ina238_id[] = { - { "ina228", ina228 }, - { "ina237", ina237 }, - { "ina238", ina238 }, - { "ina700", ina700 }, - { "ina780", ina780 }, - { "sq52206", sq52206 }, + { .name = "ina228", .driver_data = ina228 }, + { .name = "ina237", .driver_data = ina237 }, + { .name = "ina238", .driver_data = ina238 }, + { .name = "ina700", .driver_data = ina700 }, + { .name = "ina780", .driver_data = ina780 }, + { .name = "sq52206", .driver_data = sq52206 }, { } }; MODULE_DEVICE_TABLE(i2c, ina238_id); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 613ffb622b7c..c4742e84b999 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -875,11 +875,11 @@ static ssize_t shunt_resistor_store(struct device *dev, if (status < 0) return status; - hwmon_lock(dev); - status = ina2xx_set_shunt(data, val); - hwmon_unlock(dev); - if (status < 0) - return status; + scoped_guard(hwmon_lock, dev) { + status = ina2xx_set_shunt(data, val); + if (status < 0) + return status; + } return count; } @@ -1000,14 +1000,14 @@ static int ina2xx_probe(struct i2c_client *client) } static const struct i2c_device_id ina2xx_id[] = { - { "ina219", ina219 }, - { "ina220", ina219 }, - { "ina226", ina226 }, - { "ina230", ina226 }, - { "ina231", ina226 }, - { "ina234", ina234 }, - { "ina260", ina260 }, - { "sy24655", sy24655 }, + { .name = "ina219", .driver_data = ina219 }, + { .name = "ina220", .driver_data = ina219 }, + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, + { .name = "ina234", .driver_data = ina234 }, + { .name = "ina260", .driver_data = ina260 }, + { .name = "sy24655", .driver_data = sy24655 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 5ecc68dcf169..3ab5de3a1110 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -1002,7 +1002,7 @@ static const struct of_device_id ina3221_of_match_table[] = { MODULE_DEVICE_TABLE(of, ina3221_of_match_table); static const struct i2c_device_id ina3221_ids[] = { - { "ina3221" }, + { .name = "ina3221" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ina3221_ids); diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index aa01a4bedc21..e85d42a45113 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -773,6 +773,7 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); static struct platform_driver intel_m10bmc_hwmon_driver = { .probe = m10bmc_hwmon_probe, @@ -783,7 +784,6 @@ static struct platform_driver intel_m10bmc_hwmon_driver = { }; module_platform_driver(intel_m10bmc_hwmon_driver); -MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c index 96fcfbfff48f..0c60d2ef28f5 100644 --- a/drivers/hwmon/isl28022.c +++ b/drivers/hwmon/isl28022.c @@ -475,7 +475,7 @@ static int isl28022_probe(struct i2c_client *client) } static const struct i2c_device_id isl28022_ids[] = { - { "isl28022" }, + { .name = "isl28022" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, isl28022_ids); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5fd310662ee4..87edb1b6048b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1412,6 +1412,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + if (val < 0) + val = 0; + err = it87_lock(data); if (err) return err; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 6549dc543781..77fece680358 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -579,7 +579,7 @@ static const struct dev_pm_ops jc42_dev_pm_ops = { #endif /* CONFIG_PM */ static const struct i2c_device_id jc42_id[] = { - { "jc42" }, + { .name = "jc42" }, { } }; MODULE_DEVICE_TABLE(i2c, jc42_id); diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c index 64a335a64a2e..2553e49f8401 100644 --- a/drivers/hwmon/lineage-pem.c +++ b/drivers/hwmon/lineage-pem.c @@ -502,8 +502,8 @@ static int pem_probe(struct i2c_client *client) } static const struct i2c_device_id pem_id[] = { - {"lineage_pem"}, - {} + { .name = "lineage_pem" }, + { } }; MODULE_DEVICE_TABLE(i2c, pem_id); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 30500b4d2221..e2a429e579ac 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -92,6 +92,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 #define LM96163_REG_CONFIG_ENHANCED 0x45 +#define LM63_PWM_BASE_FAST_HZ 180000 +#define LM63_PWM_BASE_SLOW_HZ 700 + #define LM63_MAX_CONVRATE 9 #define LM63_MAX_CONVRATE_HZ 32 @@ -455,6 +458,91 @@ static ssize_t pwm1_enable_store(struct device *dev, return count; } +static ssize_t pwm1_freq_show(struct device *dev, + struct device_attribute *dummy, char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + unsigned int base, freq; + + mutex_lock(&data->update_lock); + base = (data->config_fan & 0x08) ? + LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ; + freq = data->pwm1_freq; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", base / freq); +} + +/* + * Pick the closest CONFIG_FAN.SCS + PFR for the requested frequency. + * PWM_FREQ writes need the LUT unlocked, same as set_pwm1(). LUT PWM + * bytes are register-relative; rewrite them after a frequency change + * if duty cycles must be preserved. + */ +static ssize_t pwm1_freq_store(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr; + bool slow_clock; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val == 0) + return -EINVAL; + + pfr_fast = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_FAST_HZ, val), + 1UL, 31UL); + pfr_slow = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_SLOW_HZ, val), + 1UL, 31UL); + err_fast = abs_diff(LM63_PWM_BASE_FAST_HZ / pfr_fast, val); + err_slow = abs_diff(LM63_PWM_BASE_SLOW_HZ / pfr_slow, val); + + if (err_slow < err_fast) { + slow_clock = true; + pfr = pfr_slow; + } else { + slow_clock = false; + pfr = pfr_fast; + } + + mutex_lock(&data->update_lock); + ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + data->config_fan = ret; + + if (!(data->config_fan & 0x20)) { /* register is read-only */ + mutex_unlock(&data->update_lock); + return -EPERM; + } + + if (data->kind == lm96163) { + ret = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + data->pwm_highres = !slow_clock && pfr == 8 && (ret & 0x10); + } + + if (slow_clock) + data->config_fan |= 0x08; + else + data->config_fan &= ~0x08; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan); + i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr); + data->pwm1_freq = pfr; + mutex_unlock(&data->update_lock); + return count; +} + /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, @@ -629,6 +717,47 @@ static ssize_t show_lut_temp_hyst(struct device *dev, return sprintf(buf, "%d\n", temp); } +/* + * The LM63 has a single hysteresis register shared by all LUT entries. + * Expose it as a chip-wide hysteresis amount in millidegrees; the + * per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and + * show the resulting absolute trip-down temperature for each entry. + */ +static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev, + struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + u8 hyst; + + mutex_lock(&data->update_lock); + hyst = data->lut_temp_hyst; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", TEMP8_FROM_REG(hyst)); +} + +static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->lut_temp_hyst = HYST_TO_REG(val); + i2c_smbus_write_byte_data(client, LM63_REG_LUT_TEMP_HYST, + data->lut_temp_hyst); + mutex_unlock(&data->update_lock); + return count; +} + /* * And now the other way around, user-space provides an absolute * hysteresis value and we have to store a relative one @@ -764,6 +893,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); static DEVICE_ATTR_RW(pwm1_enable); +static DEVICE_ATTR_RW(pwm1_freq); +static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 1); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, @@ -869,6 +1000,8 @@ static DEVICE_ATTR_RW(update_interval); static struct attribute *lm63_attributes[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1_freq.attr, + &dev_attr_pwm1_auto_point_temp_hyst.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, @@ -1152,9 +1285,9 @@ static int lm63_probe(struct i2c_client *client) */ static const struct i2c_device_id lm63_id[] = { - { "lm63", lm63 }, - { "lm64", lm64 }, - { "lm96163", lm96163 }, + { .name = "lm63", .driver_data = lm63 }, + { .name = "lm64", .driver_data = lm64 }, + { .name = "lm96163", .driver_data = lm96163 }, { } }; MODULE_DEVICE_TABLE(i2c, lm63_id); diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 581b01572e1b..63ee67481a16 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -220,7 +220,7 @@ lm73_probe(struct i2c_client *client) } static const struct i2c_device_id lm73_ids[] = { - { "lm73" }, + { .name = "lm73" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm73_ids); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index c283443e363b..104149a03bad 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -14,7 +14,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -123,7 +124,9 @@ struct lm75_data { static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; -#define LM75_SAMPLE_CLEAR_MASK (3 << 5) +#define LM75_ALERT_POLARITY_HIGH_8_BIT (BIT(2)) +#define LM75_ALERT_POLARITY_HIGH_16_BIT (BIT(2) << 8) +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) /* The structure below stores the configuration values of the supported devices. * In case of being supported multiple configurations, the default one must @@ -539,6 +542,8 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, if (config_data->params->num_sample_times > 1) return 0644; return 0444; + default: + break; } break; case hwmon_temp: @@ -555,6 +560,8 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, if (config_data->params->alarm) return 0444; break; + default: + break; } break; default: @@ -725,6 +732,7 @@ static void lm75_remove(void *data) static int lm75_generic_probe(struct device *dev, const char *name, enum lm75_type kind, int irq, struct regmap *regmap) { + u16 clr_mask, pol_mask, set_mask; struct device *hwmon_dev; struct lm75_data *data; int status, err; @@ -762,8 +770,18 @@ static int lm75_generic_probe(struct device *dev, const char *name, return err; data->orig_conf = status; - err = lm75_write_config(data, data->params->set_mask, - data->params->clr_mask); + /* Enforce polarity active-low (default) or active-high (devicetree) */ + if (!data->params->config_reg_16bits) + pol_mask = LM75_ALERT_POLARITY_HIGH_8_BIT; + else + pol_mask = LM75_ALERT_POLARITY_HIGH_16_BIT; + + clr_mask = data->params->clr_mask | pol_mask; + set_mask = data->params->set_mask & ~pol_mask; + if (device_property_read_bool(dev, "ti,alert-polarity-active-high")) + set_mask |= pol_mask; + + err = lm75_write_config(data, set_mask, clr_mask); if (err) return err; @@ -816,37 +834,37 @@ static int lm75_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id lm75_i2c_ids[] = { - { "adt75", adt75, }, - { "as6200", as6200, }, - { "at30ts74", at30ts74, }, - { "ds1775", ds1775, }, - { "ds75", ds75, }, - { "ds7505", ds7505, }, - { "g751", g751, }, - { "lm75", lm75, }, - { "lm75a", lm75a, }, - { "lm75b", lm75b, }, - { "max6625", max6625, }, - { "max6626", max6626, }, - { "max31725", max31725, }, - { "max31726", max31725, }, - { "mcp980x", mcp980x, }, - { "p3t1750", p3t1750, }, - { "p3t1755", p3t1755, }, - { "pct2075", pct2075, }, - { "stds75", stds75, }, - { "stlm75", stlm75, }, - { "tcn75", tcn75, }, - { "tmp100", tmp100, }, - { "tmp101", tmp101, }, - { "tmp105", tmp105, }, - { "tmp112", tmp112, }, - { "tmp175", tmp175, }, - { "tmp275", tmp275, }, - { "tmp75", tmp75, }, - { "tmp75b", tmp75b, }, - { "tmp75c", tmp75c, }, - { "tmp1075", tmp1075, }, + { .name = "adt75", .driver_data = adt75 }, + { .name = "as6200", .driver_data = as6200 }, + { .name = "at30ts74", .driver_data = at30ts74 }, + { .name = "ds1775", .driver_data = ds1775 }, + { .name = "ds75", .driver_data = ds75 }, + { .name = "ds7505", .driver_data = ds7505 }, + { .name = "g751", .driver_data = g751 }, + { .name = "lm75", .driver_data = lm75 }, + { .name = "lm75a", .driver_data = lm75a }, + { .name = "lm75b", .driver_data = lm75b }, + { .name = "max6625", .driver_data = max6625 }, + { .name = "max6626", .driver_data = max6626 }, + { .name = "max31725", .driver_data = max31725 }, + { .name = "max31726", .driver_data = max31725 }, + { .name = "mcp980x", .driver_data = mcp980x }, + { .name = "p3t1750", .driver_data = p3t1750 }, + { .name = "p3t1755", .driver_data = p3t1755 }, + { .name = "pct2075", .driver_data = pct2075 }, + { .name = "stds75", .driver_data = stds75 }, + { .name = "stlm75", .driver_data = stlm75 }, + { .name = "tcn75", .driver_data = tcn75 }, + { .name = "tmp100", .driver_data = tmp100 }, + { .name = "tmp101", .driver_data = tmp101 }, + { .name = "tmp105", .driver_data = tmp105 }, + { .name = "tmp112", .driver_data = tmp112 }, + { .name = "tmp175", .driver_data = tmp175 }, + { .name = "tmp275", .driver_data = tmp275 }, + { .name = "tmp75", .driver_data = tmp75 }, + { .name = "tmp75b", .driver_data = tmp75b }, + { .name = "tmp75c", .driver_data = tmp75c }, + { .name = "tmp1075", .driver_data = tmp1075 }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_i2c_ids); @@ -882,7 +900,7 @@ static int lm75_i3c_probe(struct i3c_device *i3cdev) return lm75_generic_probe(dev, id_data->name, id_data->type, 0, regmap); } -static const struct of_device_id __maybe_unused lm75_of_match[] = { +static const struct of_device_id lm75_of_match[] = { { .compatible = "adi,adt75", .data = (void *)adt75 @@ -1135,7 +1153,7 @@ static struct i2c_driver lm75_i2c_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm75", - .of_match_table = of_match_ptr(lm75_of_match), + .of_match_table = lm75_of_match, .pm = LM75_DEV_PM_OPS, }, .probe = lm75_i2c_probe, diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 80f7a6a3f9a2..96c5c2584d37 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -337,7 +337,7 @@ static int lm77_probe(struct i2c_client *client) } static const struct i2c_device_id lm77_id[] = { - { "lm77" }, + { .name = "lm77" }, { } }; MODULE_DEVICE_TABLE(i2c, lm77_id); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 9378a47bf5af..e4834f9eca6a 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -649,8 +649,8 @@ static int lm78_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id lm78_i2c_id[] = { - { "lm78", lm78 }, - { "lm79", lm79 }, + { .name = "lm78", .driver_data = lm78 }, + { .name = "lm79", .driver_data = lm79 }, { } }; MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 63c7831bd3e1..94f3dabaaa6f 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -622,8 +622,8 @@ static int lm80_probe(struct i2c_client *client) */ static const struct i2c_device_id lm80_id[] = { - { "lm80", 0 }, - { "lm96080", 1 }, + { .name = "lm80" }, + { .name = "lm96080" }, { } }; MODULE_DEVICE_TABLE(i2c, lm80_id); diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index f800fe2ef18b..8d49df8c9314 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -443,8 +443,8 @@ static int lm83_probe(struct i2c_client *client) */ static const struct i2c_device_id lm83_id[] = { - { "lm83", lm83 }, - { "lm82", lm82 }, + { .name = "lm83", .driver_data = lm83 }, + { .name = "lm82", .driver_data = lm82 }, { } }; MODULE_DEVICE_TABLE(i2c, lm83_id); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 1c244ed75122..c56e164d61c1 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1618,18 +1618,18 @@ static int lm85_probe(struct i2c_client *client) } static const struct i2c_device_id lm85_id[] = { - { "adm1027", adm1027 }, - { "adt7463", adt7463 }, - { "adt7468", adt7468 }, - { "lm85", lm85 }, - { "lm85b", lm85 }, - { "lm85c", lm85 }, - { "lm96000", lm96000 }, - { "emc6d100", emc6d100 }, - { "emc6d101", emc6d100 }, - { "emc6d102", emc6d102 }, - { "emc6d103", emc6d103 }, - { "emc6d103s", emc6d103s }, + { .name = "adm1027", .driver_data = adm1027 }, + { .name = "adt7463", .driver_data = adt7463 }, + { .name = "adt7468", .driver_data = adt7468 }, + { .name = "lm85", .driver_data = lm85 }, + { .name = "lm85b", .driver_data = lm85 }, + { .name = "lm85c", .driver_data = lm85 }, + { .name = "lm96000", .driver_data = lm96000 }, + { .name = "emc6d100", .driver_data = emc6d100 }, + { .name = "emc6d101", .driver_data = emc6d100 }, + { .name = "emc6d102", .driver_data = emc6d102 }, + { .name = "emc6d103", .driver_data = emc6d103 }, + { .name = "emc6d103s", .driver_data = emc6d103s }, { } }; MODULE_DEVICE_TABLE(i2c, lm85_id); diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 37bf2d1d3d09..c09074f44708 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -981,8 +981,8 @@ static int lm87_probe(struct i2c_client *client) */ static const struct i2c_device_id lm87_id[] = { - { "lm87" }, - { "adm1024" }, + { .name = "lm87" }, + { .name = "adm1024" }, { } }; MODULE_DEVICE_TABLE(i2c, lm87_id); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1eeb608e5903..4b9c0ccdf260 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -243,54 +243,54 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, */ static const struct i2c_device_id lm90_id[] = { - { "adm1020", max1617 }, - { "adm1021", max1617 }, - { "adm1023", adm1023 }, - { "adm1032", adm1032 }, - { "adt7421", adt7461a }, - { "adt7461", adt7461 }, - { "adt7461a", adt7461a }, - { "adt7481", adt7481 }, - { "adt7482", adt7481 }, - { "adt7483a", adt7481 }, - { "g781", g781 }, - { "gl523sm", max1617 }, - { "lm84", lm84 }, - { "lm86", lm90 }, - { "lm89", lm90 }, - { "lm90", lm90 }, - { "lm99", lm99 }, - { "max1617", max1617 }, - { "max6642", max6642 }, - { "max6646", max6646 }, - { "max6647", max6646 }, - { "max6648", max6648 }, - { "max6649", max6646 }, - { "max6654", max6654 }, - { "max6657", max6657 }, - { "max6658", max6657 }, - { "max6659", max6659 }, - { "max6680", max6680 }, - { "max6681", max6680 }, - { "max6690", max6654 }, - { "max6692", max6648 }, - { "max6695", max6696 }, - { "max6696", max6696 }, - { "mc1066", max1617 }, - { "nct1008", adt7461a }, - { "nct210", nct210 }, - { "nct214", nct72 }, - { "nct218", nct72 }, - { "nct72", nct72 }, - { "nct7716", nct7716 }, - { "nct7717", nct7717 }, - { "nct7718", nct7718 }, - { "ne1618", ne1618 }, - { "w83l771", w83l771 }, - { "sa56004", sa56004 }, - { "thmc10", max1617 }, - { "tmp451", tmp451 }, - { "tmp461", tmp461 }, + { .name = "adm1020", .driver_data = max1617 }, + { .name = "adm1021", .driver_data = max1617 }, + { .name = "adm1023", .driver_data = adm1023 }, + { .name = "adm1032", .driver_data = adm1032 }, + { .name = "adt7421", .driver_data = adt7461a }, + { .name = "adt7461", .driver_data = adt7461 }, + { .name = "adt7461a", .driver_data = adt7461a }, + { .name = "adt7481", .driver_data = adt7481 }, + { .name = "adt7482", .driver_data = adt7481 }, + { .name = "adt7483a", .driver_data = adt7481 }, + { .name = "g781", .driver_data = g781 }, + { .name = "gl523sm", .driver_data = max1617 }, + { .name = "lm84", .driver_data = lm84 }, + { .name = "lm86", .driver_data = lm90 }, + { .name = "lm89", .driver_data = lm90 }, + { .name = "lm90", .driver_data = lm90 }, + { .name = "lm99", .driver_data = lm99 }, + { .name = "max1617", .driver_data = max1617 }, + { .name = "max6642", .driver_data = max6642 }, + { .name = "max6646", .driver_data = max6646 }, + { .name = "max6647", .driver_data = max6646 }, + { .name = "max6648", .driver_data = max6648 }, + { .name = "max6649", .driver_data = max6646 }, + { .name = "max6654", .driver_data = max6654 }, + { .name = "max6657", .driver_data = max6657 }, + { .name = "max6658", .driver_data = max6657 }, + { .name = "max6659", .driver_data = max6659 }, + { .name = "max6680", .driver_data = max6680 }, + { .name = "max6681", .driver_data = max6680 }, + { .name = "max6690", .driver_data = max6654 }, + { .name = "max6692", .driver_data = max6648 }, + { .name = "max6695", .driver_data = max6696 }, + { .name = "max6696", .driver_data = max6696 }, + { .name = "mc1066", .driver_data = max1617 }, + { .name = "nct1008", .driver_data = adt7461a }, + { .name = "nct210", .driver_data = nct210 }, + { .name = "nct214", .driver_data = nct72 }, + { .name = "nct218", .driver_data = nct72 }, + { .name = "nct72", .driver_data = nct72 }, + { .name = "nct7716", .driver_data = nct7716 }, + { .name = "nct7717", .driver_data = nct7717 }, + { .name = "nct7718", .driver_data = nct7718 }, + { .name = "ne1618", .driver_data = ne1618 }, + { .name = "w83l771", .driver_data = w83l771 }, + { .name = "sa56004", .driver_data = sa56004 }, + { .name = "thmc10", .driver_data = max1617 }, + { .name = "tmp451", .driver_data = tmp451 }, + { .name = "tmp461", .driver_data = tmp461 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -1226,13 +1226,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) static int lm90_update_alarms(struct lm90_data *data, bool force) { - int err; - - hwmon_lock(data->hwmon_dev); - err = lm90_update_alarms_locked(data, force); - hwmon_unlock(data->hwmon_dev); - - return err; + guard(hwmon_lock)(data->hwmon_dev); + return lm90_update_alarms_locked(data, force); } static void lm90_alert_work(struct work_struct *__work) @@ -2598,9 +2593,9 @@ static void lm90_stop_work(void *_data) { struct lm90_data *data = _data; - hwmon_lock(data->hwmon_dev); - data->shutdown = true; - hwmon_unlock(data->hwmon_dev); + scoped_guard(hwmon_lock, data->hwmon_dev) { + data->shutdown = true; + } cancel_delayed_work_sync(&data->alert_work); cancel_work_sync(&data->report_work); } @@ -2946,17 +2941,17 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, */ struct lm90_data *data = i2c_get_clientdata(client); - hwmon_lock(data->hwmon_dev); - if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && - (data->current_alarms & data->alert_alarms)) { - if (!(data->config & 0x80)) { - dev_dbg(&client->dev, "Disabling ALERT#\n"); - lm90_update_confreg(data, data->config | 0x80); + scoped_guard(hwmon_lock, data->hwmon_dev) { + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_update_confreg(data, data->config | 0x80); + } + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } - schedule_delayed_work(&data->alert_work, - max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } - hwmon_unlock(data->hwmon_dev); } else { dev_dbg(&client->dev, "Everything OK\n"); } diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 91a6b7525bb6..4aadbabc27bb 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -405,10 +405,10 @@ static int lm92_probe(struct i2c_client *client) * Module and driver stuff */ -/* .driver_data is limit register resolution */ +/* .driver_data is limit register resolution */ static const struct i2c_device_id lm92_id[] = { - { "lm92", 13 }, - { "max6635", 9 }, + { .name = "lm92", .driver_data = 13 }, + { .name = "max6635", .driver_data = 9 }, { } }; MODULE_DEVICE_TABLE(i2c, lm92_id); diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index be4853fad80f..d58a3c2a8593 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -2624,8 +2624,8 @@ static int lm93_probe(struct i2c_client *client) } static const struct i2c_device_id lm93_id[] = { - { "lm93" }, - { "lm94" }, + { .name = "lm93" }, + { .name = "lm94" }, { } }; MODULE_DEVICE_TABLE(i2c, lm93_id); diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 387b3ba81dbf..74f280b90e3e 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -532,8 +532,8 @@ static int lm95234_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95234_id[] = { - { "lm95233", lm95233 }, - { "lm95234", lm95234 }, + { .name = "lm95233", .driver_data = lm95233 }, + { .name = "lm95234", .driver_data = lm95234 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95234_id); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 456381b0938e..0cb0edb3845d 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -441,8 +441,8 @@ static int lm95241_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95241_id[] = { - { "lm95231" }, - { "lm95241" }, + { .name = "lm95231" }, + { .name = "lm95241" }, { } }; MODULE_DEVICE_TABLE(i2c, lm95241_id); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 9ed300c6b5f7..11553391f54b 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -546,8 +546,8 @@ static int lm95245_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95245_id[] = { - { "lm95235" }, - { "lm95245" }, + { .name = "lm95235" }, + { .name = "lm95245" }, { } }; MODULE_DEVICE_TABLE(i2c, lm95245_id); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index 3e0e0e0687bd..b521100afe53 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -508,7 +508,7 @@ static int ltc2945_probe(struct i2c_client *client) } static const struct i2c_device_id ltc2945_id[] = { - {"ltc2945"}, + { .name = "ltc2945" }, { } }; diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c index 176d710706dd..e07d33983d5c 100644 --- a/drivers/hwmon/ltc2947-i2c.c +++ b/drivers/hwmon/ltc2947-i2c.c @@ -27,8 +27,8 @@ static int ltc2947_probe(struct i2c_client *i2c) } static const struct i2c_device_id ltc2947_id[] = { - {"ltc2947"}, - {} + { .name = "ltc2947" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2947_id); diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index f1c1933c52cf..a2725e4b2f21 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -259,8 +259,8 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ltc2990_i2c_id[] = { - { "ltc2990" }, - {} + { .name = "ltc2990" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id); diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c index 6d5d4cb846da..bc8d803e8cc9 100644 --- a/drivers/hwmon/ltc2991.c +++ b/drivers/hwmon/ltc2991.c @@ -409,8 +409,8 @@ static const struct of_device_id ltc2991_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2991_of_match); static const struct i2c_device_id ltc2991_i2c_id[] = { - { "ltc2991" }, - {} + { .name = "ltc2991" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2991_i2c_id); diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index 2617c4538af9..43b6029c0840 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -948,8 +948,8 @@ static const struct of_device_id ltc2992_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2992_of_match); static const struct i2c_device_id ltc2992_i2c_id[] = { - {"ltc2992"}, - {} + { .name = "ltc2992" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2992_i2c_id); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index fa66eda78efe..fa7c57aae5f5 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -188,7 +188,7 @@ static int ltc4151_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4151_id[] = { - { "ltc4151" }, + { .name = "ltc4151" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4151_id); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index cce452711cec..3d439fbbbef9 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -245,7 +245,7 @@ static int ltc4215_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4215_id[] = { - { "ltc4215" }, + { .name = "ltc4215" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4215_id); diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c index f7eb007fd766..4fc69be5f2bc 100644 --- a/drivers/hwmon/ltc4222.c +++ b/drivers/hwmon/ltc4222.c @@ -200,7 +200,7 @@ static int ltc4222_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4222_id[] = { - {"ltc4222"}, + { .name = "ltc4222" }, { } }; diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index e8131a48bda7..d87aa9c32c87 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -461,7 +461,7 @@ static int ltc4245_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4245_id[] = { - { "ltc4245" }, + { .name = "ltc4245" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4245_id); diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c index 9750dc9aa336..37a85125d619 100644 --- a/drivers/hwmon/ltc4260.c +++ b/drivers/hwmon/ltc4260.c @@ -163,7 +163,7 @@ static int ltc4260_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4260_id[] = { - {"ltc4260"}, + { .name = "ltc4260" }, { } }; diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 2cd218a6a3be..a2e52ca0b06e 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -222,8 +222,8 @@ static int ltc4261_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4261_id[] = { - {"ltc4261"}, - {} + { .name = "ltc4261" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc4261_id); diff --git a/drivers/hwmon/ltc4283.c b/drivers/hwmon/ltc4283.c new file mode 100644 index 000000000000..d8931c9a4685 --- /dev/null +++ b/drivers/hwmon/ltc4283.c @@ -0,0 +1,1795 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices LTC4283 I2C Negative Voltage Hot Swap Controller (HWMON) + * + * Copyright 2025 Analog Devices Inc. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define LTC4283_SYSTEM_STATUS 0x00 +#define LTC4283_FAULT_STATUS 0x03 +#define LTC4283_OV_MASK BIT(0) +#define LTC4283_UV_MASK BIT(1) +#define LTC4283_OC_MASK BIT(2) +#define LTC4283_FET_BAD_MASK BIT(3) +#define LTC4283_FET_SHORT_MASK BIT(6) +#define LTC4283_FAULT_LOG 0x04 +#define LTC4283_OV_FAULT_MASK BIT(0) +#define LTC4283_UV_FAULT_MASK BIT(1) +#define LTC4283_OC_FAULT_MASK BIT(2) +#define LTC4283_FET_BAD_FAULT_MASK BIT(3) +#define LTC4283_PGI_FAULT_MASK BIT(4) +#define LTC4283_PWR_FAIL_FAULT_MASK BIT(5) +#define LTC4283_FET_SHORT_FAULT_MASK BIT(6) +#define LTC4283_ADC_ALM_LOG_1 0x05 +#define LTC4283_POWER_LOW_ALM BIT(0) +#define LTC4283_POWER_HIGH_ALM BIT(1) +#define LTC4283_SENSE_LOW_ALM BIT(4) +#define LTC4283_SENSE_HIGH_ALM BIT(5) +#define LTC4283_ADC_ALM_LOG_2 0x06 +#define LTC4283_ADC_ALM_LOG_3 0x07 +#define LTC4283_ADC_ALM_LOG_4 0x08 +#define LTC4283_ADC_ALM_LOG_5 0x09 +#define LTC4283_CONTROL_1 0x0a +#define LTC4283_RW_PAGE_MASK BIT(0) +#define LTC4283_PIGIO2_ACLB_MASK BIT(2) +#define LTC4283_PWRGD_RST_CTRL_MASK BIT(3) +#define LTC4283_FET_BAD_OFF_MASK BIT(4) +#define LTC4283_THERM_TMR_MASK BIT(5) +#define LTC4283_DVDT_MASK BIT(6) +#define LTC4283_CONTROL_2 0x0b +#define LTC4283_OV_RETRY_MASK BIT(0) +#define LTC4283_UV_RETRY_MASK BIT(1) +#define LTC4283_OC_RETRY_MASK GENMASK(3, 2) +#define LTC4283_FET_BAD_RETRY_MASK GENMASK(5, 4) +#define LTC4283_EXT_FAULT_RETRY_MASK BIT(7) +#define LTC4283_RESERVED_OC 0x0c +#define LTC4283_CONFIG_1 0x0d +#define LTC4283_FB_MASK GENMASK(3, 2) +#define LTC4283_ILIM_MASK GENMASK(7, 4) +#define LTC4283_CONFIG_2 0x0e +#define LTC4283_COOLING_DL_MASK GENMASK(3, 1) +#define LTC4283_FTBD_DL_MASK GENMASK(5, 4) +#define LTC4283_CONFIG_3 0x0f +#define LTC4283_VPWR_DRNS_MASK BIT(6) +#define LTC4283_EXTFLT_TURN_OFF_MASK BIT(7) +#define LTC4283_PGIO_CONFIG 0x10 +#define LTC4283_PGIO1_CFG_MASK GENMASK(1, 0) +#define LTC4283_PGIO2_CFG_MASK GENMASK(3, 2) +#define LTC4283_PGIO3_CFG_MASK GENMASK(5, 4) +#define LTC4283_PGIO4_CFG_MASK GENMASK(7, 6) +#define LTC4283_PGIO_CONFIG_2 0x11 +#define LTC4283_ADC_MASK GENMASK(2, 0) +#define LTC4283_ADC_SELECT(c) (0x13 + (c) / 8) +#define LTC4283_ADC_SELECT_MASK(c) BIT((c) % 8) +#define LTC4283_SENSE_MIN_TH 0x1b +#define LTC4283_SENSE_MAX_TH 0x1c +#define LTC4283_VPWR_MIN_TH 0x1d +#define LTC4283_VPWR_MAX_TH 0x1e +#define LTC4283_POWER_MIN_TH 0x1f +#define LTC4283_POWER_MAX_TH 0x20 +#define LTC4283_ADC_2_MIN_TH(c) (0x21 + (c) * 2) +#define LTC4283_ADC_2_MAX_TH(c) (0x22 + (c) * 2) +#define LTC4283_ADC_2_MIN_TH_DIFF(c) (0x39 + (c) * 2) +#define LTC4283_ADC_2_MAX_TH_DIFF(c) (0x3a + (c) * 2) +#define LTC4283_SENSE 0x41 +#define LTC4283_SENSE_MIN 0x42 +#define LTC4283_SENSE_MAX 0x43 +#define LTC4283_VPWR 0x44 +#define LTC4283_VPWR_MIN 0x45 +#define LTC4283_VPWR_MAX 0x46 +#define LTC4283_POWER 0x47 +#define LTC4283_POWER_MIN 0x48 +#define LTC4283_POWER_MAX 0x49 +#define LTC4283_RESERVED_68 0x68 +#define LTC4283_RESERVED_6D 0x6D +/* get channels from ADC 2 */ +#define LTC4283_ADC_2(c) (0x4a + (c) * 3) +#define LTC4283_ADC_2_MIN(c) (0x4b + (c) * 3) +#define LTC4283_ADC_2_MAX(c) (0x4c + (c) * 3) +#define LTC4283_ADC_2_DIFF(c) (0x6e + (c) * 3) +#define LTC4283_ADC_2_MIN_DIFF(c) (0x6f + (c) * 3) +#define LTC4283_ADC_2_MAX_DIFF(c) (0x70 + (c) * 3) +#define LTC4283_ENERGY 0x7a +#define LTC4283_METER_CONTROL 0x84 +#define LTC4283_INTEGRATE_I_MASK BIT(0) +#define LTC4283_METER_HALT_MASK BIT(6) +#define LTC4283_RESERVED_86 0x86 +#define LTC4283_RESERVED_8F 0x8F +#define LTC4283_FAULT_LOG_CTRL 0x90 +#define LTC4283_FAULT_LOG_EN_MASK BIT(7) +#define LTC4283_RESERVED_91 0x91 +#define LTC4283_RESERVED_A1 0xA1 +#define LTC4283_RESERVED_A3 0xA3 +#define LTC4283_RESERVED_AC 0xAC +#define LTC4283_POWER_PLAY_MSB 0xE7 +#define LTC4283_POWER_PLAY_LSB 0xE8 +#define LTC4283_RESERVED_F1 0xF1 +#define LTC4283_RESERVED_FF 0xFF + +/* also applies for differential channels */ +#define LTC4283_ADC1_FS_uV 32768 +#define LTC4283_ADC2_FS_mV 2048 +#define LTC4283_TCONV_uS 64103 +#define LTC4283_VILIM_MIN_uV 15000 +#define LTC4283_VILIM_MAX_uV 30000 +#define LTC4283_VILIM_RANGE \ + (LTC4283_VILIM_MAX_uV - LTC4283_VILIM_MIN_uV + 1) + +#define LTC4283_PGIO_FUNC_GPIO 2 +#define LTC4283_PGIO2_FUNC_ACLB 3 + +/* + * Maximum value for rsense in nano ohms. The reasoning for this value is that + * it's the max value for which multiplying by 256 does not overflow long on + * 32bits. For the minimum value, is a sane minimum rsense for which power_max + * does not overflow 32bits. + */ +#define LTC4283_MAX_RSENSE 1677721599 +#define LTC4283_MIN_RSENSE 50000 + +/* voltage channels */ +enum { + LTC4283_CHAN_VIN, + LTC4283_CHAN_VPWR, + LTC4283_CHAN_ADI_1, + LTC4283_CHAN_ADI_2, + LTC4283_CHAN_ADI_3, + LTC4283_CHAN_ADI_4, + LTC4283_CHAN_ADIO_1, + LTC4283_CHAN_ADIO_2, + LTC4283_CHAN_ADIO_3, + LTC4283_CHAN_ADIO_4, + LTC4283_CHAN_DRNS, + LTC4283_CHAN_DRAIN, + /* differential channels */ + LTC4283_CHAN_ADIN12, + LTC4283_CHAN_ADIN34, + LTC4283_CHAN_ADIO12, + LTC4283_CHAN_ADIO34, + LTC4283_CHAN_MAX +}; + +/* Just for ease of use on the regmap */ +#define LTC4283_ADIO34_MAX \ + LTC4283_ADC_2_MAX_DIFF(LTC4283_CHAN_ADIO34 - LTC4283_CHAN_ADIN12) + +struct ltc4283_hwmon { + struct regmap *map; + struct i2c_client *client; + unsigned long gpio_mask; + unsigned long ch_enable_mask; + /* in microwatt */ + unsigned long power_max; + /* in millivolt */ + u32 vsense_max; + /* in tenths of microohm*/ + u32 rsense; + bool energy_en; + bool ext_fault; +}; + +static int ltc4283_read_voltage_word(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long *val) +{ + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(__raw * fs, BIT(16)); + return 0; +} + +static int ltc4283_read_voltage_byte(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long *val) +{ + int ret; + u32 in; + + ret = regmap_read(st->map, reg, &in); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(in * fs, BIT(8)); + return 0; +} + +static u32 ltc4283_in_reg(u32 attr, u32 channel) +{ + switch (attr) { + case hwmon_in_input: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_highest: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MAX; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MAX(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MAX_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_lowest: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MIN; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_max: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MAX_TH; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + default: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MIN_TH; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + } +} + +static int ltc4283_read_in_vals(const struct ltc4283_hwmon *st, + u32 attr, u32 channel, long *val) +{ + u32 reg = ltc4283_in_reg(attr, channel); + int ret; + + if (channel < LTC4283_CHAN_ADIN12) { + if (attr != hwmon_in_max && attr != hwmon_in_min) + return ltc4283_read_voltage_word(st, reg, + LTC4283_ADC2_FS_mV, + val); + + return ltc4283_read_voltage_byte(st, reg, + LTC4283_ADC2_FS_mV, val); + } + + if (attr != hwmon_in_max && attr != hwmon_in_min) + ret = ltc4283_read_voltage_word(st, reg, + LTC4283_ADC1_FS_uV, val); + else + ret = ltc4283_read_voltage_byte(st, reg, + LTC4283_ADC1_FS_uV, val); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(*val, MILLI); + return 0; +} + +static int ltc4283_read_alarm(struct ltc4283_hwmon *st, u32 reg, + u32 mask, long *val) +{ + u32 alarm; + int ret; + + ret = regmap_read(st->map, reg, &alarm); + if (ret) + return ret; + + *val = !!(alarm & mask); + + /* If not status/fault logs, clear the alarm after reading it. */ + if (reg != LTC4283_FAULT_STATUS && reg != LTC4283_FAULT_LOG) + return regmap_write(st->map, reg, alarm & ~mask); + + return 0; +} + +static int ltc4283_read_in_alarm(struct ltc4283_hwmon *st, u32 channel, + bool max_alm, long *val) +{ + if (channel == LTC4283_CHAN_VPWR) + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + BIT(2 + max_alm), val); + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_ADI_4) { + u32 bit = (channel - LTC4283_CHAN_ADI_1) * 2; + /* + * Lower channels go to higher bits. We also want to go +1 down + * in the min_alarm case. + */ + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_2, + BIT(7 - bit - !max_alm), val); + } + + if (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) { + u32 bit = (channel - LTC4283_CHAN_ADIO_1) * 2; + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_3, + BIT(7 - bit - !max_alm), val); + } + + if (channel >= LTC4283_CHAN_ADIN12 && channel <= LTC4283_CHAN_ADIO34) { + u32 bit = (channel - LTC4283_CHAN_ADIN12) * 2; + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_5, + BIT(7 - bit - !max_alm), val); + } + + if (channel == LTC4283_CHAN_DRNS) + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, + BIT(6 + max_alm), val); + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, BIT(4 + max_alm), + val); +} + +static int ltc4283_read_in(struct ltc4283_hwmon *st, u32 attr, u32 channel, + long *val) +{ + switch (attr) { + case hwmon_in_input: + if (!test_bit(channel, &st->ch_enable_mask)) + return -ENODATA; + + return ltc4283_read_in_vals(st, attr, channel, val); + case hwmon_in_highest: + case hwmon_in_lowest: + case hwmon_in_max: + case hwmon_in_min: + return ltc4283_read_in_vals(st, attr, channel, val); + case hwmon_in_max_alarm: + return ltc4283_read_in_alarm(st, channel, true, val); + case hwmon_in_min_alarm: + return ltc4283_read_in_alarm(st, channel, false, val); + case hwmon_in_crit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_OV_MASK, val); + case hwmon_in_lcrit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_UV_MASK, val); + case hwmon_in_fault: + /* + * We report failure if we detect either a fer_bad or a + * fet_short in the status register. + */ + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_FET_BAD_MASK | LTC4283_FET_SHORT_MASK, val); + case hwmon_in_enable: + *val = test_bit(channel, &st->ch_enable_mask); + return 0; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ltc4283_read_current_word(const struct ltc4283_hwmon *st, u32 reg, + long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI; + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + *val = DIV64_U64_ROUND_CLOSEST(__raw * temp, + BIT_ULL(16) * st->rsense); + + return 0; +} + +static int ltc4283_read_current_byte(const struct ltc4283_hwmon *st, u32 reg, + long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI; + u32 curr; + int ret; + + ret = regmap_read(st->map, reg, &curr); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST_ULL(curr * temp, BIT(8) * st->rsense); + return 0; +} + +static int ltc4283_read_curr(struct ltc4283_hwmon *st, u32 attr, long *val) +{ + switch (attr) { + case hwmon_curr_input: + return ltc4283_read_current_word(st, LTC4283_SENSE, val); + case hwmon_curr_highest: + return ltc4283_read_current_word(st, LTC4283_SENSE_MAX, val); + case hwmon_curr_lowest: + return ltc4283_read_current_word(st, LTC4283_SENSE_MIN, val); + case hwmon_curr_max: + return ltc4283_read_current_byte(st, LTC4283_SENSE_MAX_TH, val); + case hwmon_curr_min: + return ltc4283_read_current_byte(st, LTC4283_SENSE_MIN_TH, val); + case hwmon_curr_max_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_SENSE_HIGH_ALM, val); + case hwmon_curr_min_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_SENSE_LOW_ALM, val); + case hwmon_curr_crit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_OC_MASK, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_read_power_word(const struct ltc4283_hwmon *st, + u32 reg, long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + /* + * Power is given by: + * P = CODE(16b) * 32.768mV * 2.048V / (2^16 * Rsense) + */ + *val = DIV64_U64_ROUND_CLOSEST(temp * __raw, BIT_ULL(16) * st->rsense); + + return 0; +} + +static int ltc4283_read_power_byte(const struct ltc4283_hwmon *st, + u32 reg, long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u32 power; + int ret; + + ret = regmap_read(st->map, reg, &power); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST_ULL(power * temp, BIT(8) * st->rsense); + + return 0; +} + +static int ltc4283_read_power(struct ltc4283_hwmon *st, u32 attr, long *val) +{ + switch (attr) { + case hwmon_power_input: + return ltc4283_read_power_word(st, LTC4283_POWER, val); + case hwmon_power_input_highest: + return ltc4283_read_power_word(st, LTC4283_POWER_MAX, val); + case hwmon_power_input_lowest: + return ltc4283_read_power_word(st, LTC4283_POWER_MIN, val); + case hwmon_power_max_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_POWER_HIGH_ALM, val); + case hwmon_power_min_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_POWER_LOW_ALM, val); + case hwmon_power_max: + return ltc4283_read_power_byte(st, LTC4283_POWER_MAX_TH, val); + case hwmon_power_min: + return ltc4283_read_power_byte(st, LTC4283_POWER_MIN_TH, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_read_energy(struct ltc4283_hwmon *st, u32 attr, s64 *val) +{ + u64 temp = LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV, energy; + u8 raw[8] = {}; + int ret; + + if (!st->energy_en) + return -ENODATA; + + ret = i2c_smbus_read_i2c_block_data(st->client, LTC4283_ENERGY, 6, raw); + if (ret < 0) + return ret; + if (ret != 6) + return -EIO; + + energy = get_unaligned_be64(raw) >> 16; + + /* + * The formula for energy is given by: + * E = CODE(48b) * 32.768mV * 2.048V * Tconv / 2^24 * Rsense + * + * As Rsense can have tenths of micro-ohm resolution, we need to + * multiply by DECA to get microjoule. + */ + + /* + * Use mul_u64_u64_div_u64() to handle the 128-bit intermediate + * product of energy (up to 48 bits) * temp * Tconv without overflow. + * Multiply rsense by CENTI to convert from tenths-of-microohm back + * to nanoohm so the result comes out in microjoule. + */ + energy = mul_u64_u64_div_u64(energy, temp * LTC4283_TCONV_uS, + BIT_ULL(24) * st->rsense * CENTI); + + *val = energy; + return 0; +} + +static int ltc4283_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct ltc4283_hwmon *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + return ltc4283_read_in(st, attr, channel, val); + case hwmon_curr: + return ltc4283_read_curr(st, attr, val); + case hwmon_power: + return ltc4283_read_power(st, attr, val); + case hwmon_energy: + *val = st->energy_en; + return 0; + case hwmon_energy64: + return ltc4283_read_energy(st, attr, (s64 *)val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_power_byte(const struct ltc4283_hwmon *st, u32 reg, + long val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u32 __raw; + + val = clamp_val(val, 0, st->power_max); + __raw = DIV64_U64_ROUND_CLOSEST(val * BIT_ULL(8) * st->rsense, temp); + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_write_power_word(const struct ltc4283_hwmon *st, + u32 reg, unsigned long val) +{ + u64 divisor = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u16 __raw; + + __raw = mul_u64_u64_div_u64(val, st->rsense * BIT_ULL(16), divisor); + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_reset_power_hist(struct ltc4283_hwmon *st) +{ + int ret; + + ret = ltc4283_write_power_word(st, LTC4283_POWER_MIN, st->power_max); + if (ret) + return ret; + + ret = ltc4283_write_power_word(st, LTC4283_POWER_MAX, 0); + if (ret) + return ret; + + /* Clear possible power faults. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_PWR_FAIL_FAULT_MASK | LTC4283_PGI_FAULT_MASK); +} + +static int ltc4283_write_power(struct ltc4283_hwmon *st, u32 attr, long val) +{ + switch (attr) { + case hwmon_power_max: + return ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, val); + case hwmon_power_min: + return ltc4283_write_power_byte(st, LTC4283_POWER_MIN_TH, val); + case hwmon_power_reset_history: + return ltc4283_reset_power_hist(st); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_in_history(struct ltc4283_hwmon *st, u32 reg, + long lowest, u32 fs) +{ + u32 __raw; + int ret; + + __raw = DIV_ROUND_CLOSEST(BIT(16) * lowest, fs); + if (__raw == BIT(16)) + __raw = U16_MAX; + + ret = regmap_write(st->map, reg, __raw); + if (ret) + return ret; + + return regmap_write(st->map, reg + 1, 0); +} + +static int ltc4283_write_in_byte(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long val) +{ + u32 __raw; + + val = clamp_val(val, 0, fs); + __raw = DIV_ROUND_CLOSEST(val * BIT(8), fs); + if (__raw == BIT(8)) + __raw = U8_MAX; + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_reset_in_hist(struct ltc4283_hwmon *st, u32 channel) +{ + u32 reg, fs; + int ret; + + /* + * Make sure to clear possible under/over voltage faults. Otherwise the + * chip won't latch on again. + */ + if (channel == LTC4283_CHAN_VIN) + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_OV_FAULT_MASK | LTC4283_UV_FAULT_MASK); + + if (channel == LTC4283_CHAN_VPWR) + return ltc4283_write_in_history(st, LTC4283_VPWR_MIN, + LTC4283_ADC2_FS_mV, + LTC4283_ADC2_FS_mV); + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) { + fs = LTC4283_ADC2_FS_mV; + reg = LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1); + } else { + fs = LTC4283_ADC1_FS_uV; + reg = LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12); + } + + ret = ltc4283_write_in_history(st, reg, fs, fs); + if (ret) + return ret; + if (channel != LTC4283_CHAN_DRAIN) + return 0; + + /* Then, let's also clear possible fet faults. Same as above. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_FET_BAD_FAULT_MASK | LTC4283_FET_SHORT_FAULT_MASK); +} + +static int ltc4283_write_in_en(struct ltc4283_hwmon *st, u32 channel, bool en) +{ + unsigned int bit, adc_idx = channel - LTC4283_CHAN_ADI_1; + unsigned int reg = LTC4283_ADC_SELECT(adc_idx); + int ret; + + bit = LTC4283_ADC_SELECT_MASK(adc_idx); + if (channel > LTC4283_CHAN_DRAIN) + /* Account for two reserved fields after DRAIN. */ + bit <<= 2; + + if (en) + ret = regmap_set_bits(st->map, reg, bit); + else + ret = regmap_clear_bits(st->map, reg, bit); + if (ret) + return ret; + + __assign_bit(channel, &st->ch_enable_mask, en); + return 0; +} + +static int ltc4283_write_minmax(struct ltc4283_hwmon *st, long val, + u32 channel, bool is_max) +{ + u32 reg; + + if (channel == LTC4283_CHAN_VPWR) { + if (is_max) + return ltc4283_write_in_byte(st, LTC4283_VPWR_MAX_TH, + LTC4283_ADC2_FS_mV, val); + + return ltc4283_write_in_byte(st, LTC4283_VPWR_MIN_TH, + LTC4283_ADC2_FS_mV, val); + } + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) { + if (is_max) { + reg = LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1); + return ltc4283_write_in_byte(st, reg, + LTC4283_ADC2_FS_mV, val); + } + + reg = LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC2_FS_mV, val); + } + + /* Clamp before multiplying to avoid overflow on any arch. */ + val = clamp_val(val, 0, LONG_MAX / MILLI); + + if (is_max) { + reg = LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, + val * MILLI); + } + + reg = LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, val * MILLI); +} + +static int ltc4283_write_in(struct ltc4283_hwmon *st, u32 attr, long val, + int channel) +{ + switch (attr) { + case hwmon_in_max: + return ltc4283_write_minmax(st, val, channel, true); + case hwmon_in_min: + return ltc4283_write_minmax(st, val, channel, false); + case hwmon_in_reset_history: + return ltc4283_reset_in_hist(st, channel); + case hwmon_in_enable: + return ltc4283_write_in_en(st, channel, !!val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_curr_byte(const struct ltc4283_hwmon *st, + u32 reg, long val) +{ + u32 temp = LTC4283_ADC1_FS_uV * DECA * MILLI; + u32 reg_val, isense_max; + + isense_max = DIV_ROUND_CLOSEST(st->vsense_max * MICRO * DECA, st->rsense); + val = clamp_val(val, 0, isense_max); + reg_val = DIV_ROUND_CLOSEST_ULL(val * BIT_ULL(8) * st->rsense, temp); + + return regmap_write(st->map, reg, reg_val); +} + +static int ltc4283_write_curr_history(struct ltc4283_hwmon *st) +{ + int ret; + + ret = ltc4283_write_in_history(st, LTC4283_SENSE_MIN, + st->vsense_max * MILLI, + LTC4283_ADC1_FS_uV); + if (ret) + return ret; + + /* Now, let's also clear possible overcurrent logs. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_OC_FAULT_MASK); +} + +static int ltc4283_write_curr(struct ltc4283_hwmon *st, u32 attr, long val) +{ + switch (attr) { + case hwmon_curr_max: + return ltc4283_write_curr_byte(st, LTC4283_SENSE_MAX_TH, val); + case hwmon_curr_min: + return ltc4283_write_curr_byte(st, LTC4283_SENSE_MIN_TH, val); + case hwmon_curr_reset_history: + return ltc4283_write_curr_history(st); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_energy_enable_set(struct ltc4283_hwmon *st, long val) +{ + int ret; + + /* Setting the bit halts the meter. */ + val = !!val; + ret = regmap_update_bits(st->map, LTC4283_METER_CONTROL, + LTC4283_METER_HALT_MASK, + FIELD_PREP(LTC4283_METER_HALT_MASK, !val)); + if (ret) + return ret; + + st->energy_en = val; + + return 0; +} + +static int ltc4283_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct ltc4283_hwmon *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_power: + return ltc4283_write_power(st, attr, val); + case hwmon_in: + return ltc4283_write_in(st, attr, val, channel); + case hwmon_curr: + return ltc4283_write_curr(st, attr, val); + case hwmon_energy: + return ltc4283_energy_enable_set(st, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t ltc4283_in_is_visible(const struct ltc4283_hwmon *st, + u32 attr, int channel) +{ + /* If ADIO is set as a GPIO, don´t make it visible. */ + if (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) { + /* ADIOX pins come at index 0 in the gpio mask. */ + channel -= LTC4283_CHAN_ADIO_1; + if (test_bit(channel, &st->gpio_mask)) + return 0; + } + + /* Also take care of differential channels. */ + if (channel >= LTC4283_CHAN_ADIO12 && channel <= LTC4283_CHAN_ADIO34) { + channel -= LTC4283_CHAN_ADIO12; + /* If one channel in the pair is used, make it invisible. */ + if (test_bit(channel * 2, &st->gpio_mask) || + test_bit(channel * 2 + 1, &st->gpio_mask)) + return 0; + } + + switch (attr) { + case hwmon_in_input: + case hwmon_in_highest: + case hwmon_in_lowest: + case hwmon_in_max_alarm: + case hwmon_in_min_alarm: + case hwmon_in_label: + case hwmon_in_lcrit_alarm: + case hwmon_in_crit_alarm: + case hwmon_in_fault: + return 0444; + case hwmon_in_max: + case hwmon_in_min: + case hwmon_in_enable: + return 0644; + case hwmon_in_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_curr_is_visible(u32 attr) +{ + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_highest: + case hwmon_curr_lowest: + case hwmon_curr_max_alarm: + case hwmon_curr_min_alarm: + case hwmon_curr_crit_alarm: + case hwmon_curr_label: + return 0444; + case hwmon_curr_max: + case hwmon_curr_min: + return 0644; + case hwmon_curr_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_power_is_visible(u32 attr) +{ + switch (attr) { + case hwmon_power_input: + case hwmon_power_input_highest: + case hwmon_power_input_lowest: + case hwmon_power_label: + case hwmon_power_max_alarm: + case hwmon_power_min_alarm: + return 0444; + case hwmon_power_max: + case hwmon_power_min: + return 0644; + case hwmon_power_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_in: + return ltc4283_in_is_visible(data, attr, channel); + case hwmon_curr: + return ltc4283_curr_is_visible(attr); + case hwmon_power: + return ltc4283_power_is_visible(attr); + case hwmon_energy: + /* hwmon_energy_enable */ + return 0644; + case hwmon_energy64: + /* hwmon_energy_input */ + return 0444; + default: + return 0; + } +} + +static const char * const ltc4283_in_strs[] = { + "VIN", "VPWR", "VADI1", "VADI2", "VADI3", "VADI4", "VADIO1", "VADIO2", + "VADIO3", "VADIO4", "DRNS", "DRAIN", "ADIN2-ADIN1", "ADIN4-ADIN3", + "ADIO2-ADIO1", "ADIO4-ADIO3" +}; + +static int ltc4283_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_in: + *str = ltc4283_in_strs[channel]; + return 0; + case hwmon_curr: + *str = "ISENSE"; + return 0; + case hwmon_power: + *str = "Power"; + return 0; + default: + return -EOPNOTSUPP; + } +} + +/* + * Set max limits for ISENSE and Power as that depends on the max voltage on + * rsense that is defined in ILIM_ADJUST. This is specially important for power + * because for some rsense and vfsout values, if we allow the default raw 255 + * value, that would overflow long in 32bit archs when reading back the max + * power limit. + */ +static int ltc4283_set_max_limits(struct ltc4283_hwmon *st, struct device *dev) +{ + u32 temp = st->vsense_max * DECA * MICRO; + int ret; + + ret = ltc4283_write_in_byte(st, LTC4283_SENSE_MAX_TH, LTC4283_ADC1_FS_uV, + st->vsense_max * MILLI); + if (ret) + return ret; + + /* Power is given by ISENSE * Vout. */ + st->power_max = DIV_ROUND_CLOSEST(temp, st->rsense) * LTC4283_ADC2_FS_mV; + return ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, st->power_max); +} + +static int ltc4283_parse_array_prop(const struct ltc4283_hwmon *st, + struct device *dev, const char *prop, + const u32 *vals, u32 n_vals) +{ + u32 prop_val; + int ret; + u32 i; + + ret = device_property_read_u32(dev, prop, &prop_val); + if (ret) + return n_vals; + + for (i = 0; i < n_vals; i++) { + if (prop_val != vals[i]) + continue; + + return i; + } + + return dev_err_probe(dev, -EINVAL, + "Invalid %s property value %u\n", prop, prop_val); +} + +static int ltc4283_get_defaults(struct ltc4283_hwmon *st) +{ + u32 reg_val, ilm_adjust, c; + int ret; + + ret = regmap_read(st->map, LTC4283_METER_CONTROL, ®_val); + if (ret) + return ret; + + st->energy_en = !FIELD_GET(LTC4283_METER_HALT_MASK, reg_val); + + ret = regmap_read(st->map, LTC4283_CONFIG_1, ®_val); + if (ret) + return ret; + + ilm_adjust = FIELD_GET(LTC4283_ILIM_MASK, reg_val); + st->vsense_max = LTC4283_VILIM_MIN_uV / MILLI + ilm_adjust; + + ret = regmap_read(st->map, LTC4283_PGIO_CONFIG, ®_val); + if (ret) + return ret; + + /* Can be latter overwritten in ltc4283_pgio_config() */ + if (FIELD_GET(LTC4283_PGIO4_CFG_MASK, reg_val) < LTC4283_PGIO_FUNC_GPIO) + st->ext_fault = true; + + /* VPWR and VIN are always enabled */ + __set_bit(LTC4283_CHAN_VIN, &st->ch_enable_mask); + __set_bit(LTC4283_CHAN_VPWR, &st->ch_enable_mask); + for (c = LTC4283_CHAN_ADI_1; c < LTC4283_CHAN_MAX; c++) { + u32 chan = c - LTC4283_CHAN_ADI_1, bit; + + ret = regmap_read(st->map, LTC4283_ADC_SELECT(chan), ®_val); + if (ret) + return ret; + + bit = LTC4283_ADC_SELECT_MASK(chan); + if (c > LTC4283_CHAN_DRAIN) + /* account for two reserved fields after DRAIN */ + bit <<= 2; + + if (!(bit & reg_val)) + continue; + + __set_bit(c, &st->ch_enable_mask); + } + + return 0; +} + +static const char * const ltc4283_pgio1_funcs[] = { + "inverted_power_good", "power_good", "gpio" +}; + +static const char * const ltc4283_pgio2_funcs[] = { + "inverted_power_good", "power_good", "gpio", "active_current_limiting" +}; + +static const char * const ltc4283_pgio3_funcs[] = { + "inverted_power_good_input", "power_good_input", "gpio" +}; + +static const char * const ltc4283_pgio4_funcs[] = { + "inverted_external_fault", "external_fault", "gpio" +}; + +enum { + LTC4283_PIN_ADIO1, + LTC4283_PIN_ADIO2, + LTC4283_PIN_ADIO3, + LTC4283_PIN_ADIO4, + LTC4283_PIN_PGIO1, + LTC4283_PIN_PGIO2, + LTC4283_PIN_PGIO3, + LTC4283_PIN_PGIO4, +}; + +static int ltc4283_pgio_config(struct ltc4283_hwmon *st, struct device *dev) +{ + int ret, func; + + func = device_property_match_property_string(dev, "adi,pgio1-func", + ltc4283_pgio1_funcs, + ARRAY_SIZE(ltc4283_pgio1_funcs)); + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio1-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO1, &st->gpio_mask); + /* If GPIO, default to an input pin. */ + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO1_CFG_MASK, + FIELD_PREP(LTC4283_PGIO1_CFG_MASK, func)); + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio2-func", + ltc4283_pgio2_funcs, + ARRAY_SIZE(ltc4283_pgio2_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio2-func property\n"); + if (func >= 0) { + if (func != LTC4283_PGIO2_FUNC_ACLB) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO2, &st->gpio_mask); + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO2_CFG_MASK, + FIELD_PREP(LTC4283_PGIO2_CFG_MASK, func)); + } else { + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, + LTC4283_PIGIO2_ACLB_MASK); + } + + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio3-func", + ltc4283_pgio3_funcs, + ARRAY_SIZE(ltc4283_pgio3_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio3-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO3, &st->gpio_mask); + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO3_CFG_MASK, + FIELD_PREP(LTC4283_PGIO3_CFG_MASK, func)); + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio4-func", + ltc4283_pgio4_funcs, + ARRAY_SIZE(ltc4283_pgio4_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio4-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO4, &st->gpio_mask); + func++; + st->ext_fault = false; + } else { + st->ext_fault = true; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO4_CFG_MASK, + FIELD_PREP(LTC4283_PGIO4_CFG_MASK, func)); + if (ret) + return ret; + } + + return 0; +} + +static int ltc4283_adio_config(struct ltc4283_hwmon *st, struct device *dev, + const char *prop, u32 pin) +{ + u32 adc_idx; + int ret; + + if (!device_property_read_bool(dev, prop)) + return 0; + + adc_idx = LTC4283_CHAN_ADIO_1 - LTC4283_CHAN_ADI_1 + pin; + ret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(adc_idx), + LTC4283_ADC_SELECT_MASK(adc_idx)); + if (ret) + return ret; + + __set_bit(pin, &st->gpio_mask); + return 0; +} + +static int ltc4283_pin_config(struct ltc4283_hwmon *st, struct device *dev) +{ + int ret; + + ret = ltc4283_pgio_config(st, dev); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio1", LTC4283_PIN_ADIO1); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio2", LTC4283_PIN_ADIO2); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio3", LTC4283_PIN_ADIO3); + if (ret) + return ret; + + return ltc4283_adio_config(st, dev, "adi,gpio-on-adio4", LTC4283_PIN_ADIO4); +} + +static const char * const ltc4283_oc_fet_retry[] = { + "latch-off", "1", "7", "unlimited" +}; + +static const u32 ltc4283_fb_factor[] = { + 100, 50, 20, 10 +}; + +static const u32 ltc4283_cooling_dl[] = { + 512, 1002, 2005, 4100, 8190, 16400, 32800, 65600 +}; + +static const u32 ltc4283_fet_bad_delay[] = { + 256, 512, 1002, 2005 +}; + +static int ltc4283_setup(struct ltc4283_hwmon *st, struct device *dev) +{ + u32 val; + int ret; + + /* The part has an eeprom so let's get the needed defaults from it */ + ret = ltc4283_get_defaults(st); + if (ret) + return ret; + + /* + * Default to LTC4283_MIN_RSENSE so we can probe without FW properties. + */ + st->rsense = LTC4283_MIN_RSENSE; + ret = device_property_read_u32(dev, "adi,rsense-nano-ohms", + &st->rsense); + if (!ret) { + if (st->rsense < LTC4283_MIN_RSENSE || st->rsense > LTC4283_MAX_RSENSE) + return dev_err_probe(dev, -EINVAL, + "adi,rsense-nano-ohms(%u) too small or too large [%u %u]\n", + st->rsense, LTC4283_MIN_RSENSE, LTC4283_MAX_RSENSE); + } + + /* + * The resolution for rsense is tenths of micro (eg: 62.5 uOhm) which + * means we need nano in the bindings. However, to make things easier to + * handle (with respect to overflows) we divide it by 100 as we don't + * really need the last two digits. + */ + st->rsense /= CENTI; + + ret = device_property_read_u32(dev, "adi,current-limit-sense-microvolt", + &st->vsense_max); + if (!ret) { + u32 reg_val; + + if (!in_range(st->vsense_max, LTC4283_VILIM_MIN_uV, + LTC4283_VILIM_RANGE)) { + return dev_err_probe(dev, -EINVAL, + "adi,current-limit-sense-microvolt (%u) out of range [%u %u]\n", + st->vsense_max, LTC4283_VILIM_MIN_uV, + LTC4283_VILIM_MAX_uV); + } + + st->vsense_max /= MILLI; + reg_val = FIELD_PREP(LTC4283_ILIM_MASK, + st->vsense_max - LTC4283_VILIM_MIN_uV / MILLI); + ret = regmap_update_bits(st->map, LTC4283_CONFIG_1, + LTC4283_ILIM_MASK, reg_val); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,current-limit-foldback-factor", + ltc4283_fb_factor, ARRAY_SIZE(ltc4283_fb_factor)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_fb_factor)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_1, LTC4283_FB_MASK, + FIELD_PREP(LTC4283_FB_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,cooling-delay-ms", + ltc4283_cooling_dl, ARRAY_SIZE(ltc4283_cooling_dl)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_cooling_dl)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_COOLING_DL_MASK, + FIELD_PREP(LTC4283_COOLING_DL_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,fet-bad-timer-delay-ms", + ltc4283_fet_bad_delay, ARRAY_SIZE(ltc4283_fet_bad_delay)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_fet_bad_delay)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_FTBD_DL_MASK, + FIELD_PREP(LTC4283_FTBD_DL_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_set_max_limits(st, dev); + if (ret) + return ret; + + ret = ltc4283_pin_config(st, dev); + if (ret) + return ret; + + if (device_property_read_bool(dev, "adi,power-good-reset-on-fet")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_PWRGD_RST_CTRL_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,fet-turn-off-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_FET_BAD_OFF_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,tmr-pull-down-disable")) { + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, + LTC4283_THERM_TMR_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,dvdt-inrush-control-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_DVDT_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,undervoltage-retry-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_2, + LTC4283_UV_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,overvoltage-retry-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_2, + LTC4283_OV_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,external-fault-retry-enable")) { + if (!st->ext_fault) + return dev_err_probe(dev, -EINVAL, + "adi,external-fault-retry-enable set but PGIO4 not configured\n"); + ret = regmap_set_bits(st->map, LTC4283_CONTROL_2, + LTC4283_EXT_FAULT_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,fault-log-enable")) { + ret = regmap_set_bits(st->map, LTC4283_FAULT_LOG_CTRL, + LTC4283_FAULT_LOG_EN_MASK); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,overcurrent-retries", + ltc4283_oc_fet_retry, + ARRAY_SIZE(ltc4283_oc_fet_retry)); + /* We still want to catch when an invalid string is given. */ + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "adi,overcurrent-retries invalid value\n"); + if (ret >= 0) { + ret = regmap_update_bits(st->map, LTC4283_CONTROL_2, + LTC4283_OC_RETRY_MASK, + FIELD_PREP(LTC4283_OC_RETRY_MASK, ret)); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,fet-bad-retries", + ltc4283_oc_fet_retry, + ARRAY_SIZE(ltc4283_oc_fet_retry)); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "adi,fet-bad-retries invalid value\n"); + if (ret >= 0) { + ret = regmap_update_bits(st->map, LTC4283_CONTROL_2, + LTC4283_FET_BAD_RETRY_MASK, + FIELD_PREP(LTC4283_FET_BAD_RETRY_MASK, ret)); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,external-fault-fet-off-enable")) { + if (!st->ext_fault) + return dev_err_probe(dev, -EINVAL, + "adi,external-fault-fet-off-enable set but PGIO4 not configured\n"); + ret = regmap_set_bits(st->map, LTC4283_CONFIG_3, + LTC4283_EXTFLT_TURN_OFF_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,vpower-drns-enable")) { + u32 chan = LTC4283_CHAN_DRNS - LTC4283_CHAN_ADI_1; + + __clear_bit(LTC4283_CHAN_DRNS, &st->ch_enable_mask); + /* + * Then, let's by default disable DRNS from ADC2 given that it + * is already being monitored by the VPWR channel. One can still + * enable it later on if needed. + */ + ret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(chan), + LTC4283_ADC_SELECT_MASK(chan)); + if (ret) + return ret; + + val = 1; + } else { + val = 0; + } + + ret = regmap_update_bits(st->map, LTC4283_CONFIG_3, + LTC4283_VPWR_DRNS_MASK, + FIELD_PREP(LTC4283_VPWR_DRNS_MASK, val)); + if (ret) + return ret; + + /* Make sure the ADC has 12bit resolution since we're assuming that. */ + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG_2, + LTC4283_ADC_MASK, + FIELD_PREP(LTC4283_ADC_MASK, 3)); + if (ret) + return ret; + + /* Energy reads (which are 6 byte block reads) rely on page access */ + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, LTC4283_RW_PAGE_MASK); + if (ret) + return ret; + + /* + * Make sure we are integrating power as we only support reporting + * consumed energy. + */ + return regmap_clear_bits(st->map, LTC4283_METER_CONTROL, + LTC4283_INTEGRATE_I_MASK); +} + +static const struct hwmon_channel_info * const ltc4283_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_LCRIT_ALARM | HWMON_I_CRIT_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_RESET_HISTORY | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_FAULT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | + HWMON_C_MAX | HWMON_C_MIN | HWMON_C_MIN_ALARM | + HWMON_C_MAX_ALARM | HWMON_C_CRIT_ALARM | + HWMON_C_RESET_HISTORY | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | + HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN | + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | + HWMON_P_RESET_HISTORY | HWMON_P_LABEL), + HWMON_CHANNEL_INFO(energy, + HWMON_E_ENABLE), + HWMON_CHANNEL_INFO(energy64, + HWMON_E_INPUT), + NULL +}; + +static const struct hwmon_ops ltc4283_ops = { + .read = ltc4283_read, + .write = ltc4283_write, + .is_visible = ltc4283_is_visible, + .read_string = ltc4283_read_labels, +}; + +static const struct hwmon_chip_info ltc4283_chip_info = { + .ops = <c4283_ops, + .info = ltc4283_info, +}; + +static int ltc4283_show_fault_log(void *arg, u64 *val, u32 mask) +{ + struct ltc4283_hwmon *st = arg; + long alarm; + int ret; + + ret = ltc4283_read_alarm(st, LTC4283_FAULT_LOG, mask, &alarm); + if (ret) + return ret; + + *val = alarm; + + return 0; +} + +static int ltc4283_show_in0_lcrit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_UV_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_lcrit_fault_log, + ltc4283_show_in0_lcrit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_in0_crit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_OV_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_crit_fault_log, + ltc4283_show_in0_crit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_fet_bad_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_FET_BAD_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_bad_fault_log, + ltc4283_show_fet_bad_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_fet_short_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_FET_SHORT_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_short_fault_log, + ltc4283_show_fet_short_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_curr1_crit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_OC_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_curr1_crit_fault_log, + ltc4283_show_curr1_crit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_power1_failed_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_PWR_FAIL_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_failed_fault_log, + ltc4283_show_power1_failed_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_power1_good_input_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_PGI_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_good_input_fault_log, + ltc4283_show_power1_good_input_fault_log, NULL, "%llu\n"); + +static void ltc4283_debugfs_init(struct ltc4283_hwmon *st, struct i2c_client *i2c) +{ + debugfs_create_file_unsafe("in0_crit_fault_log", 0400, i2c->debugfs, st, + <c4283_in0_crit_fault_log); + debugfs_create_file_unsafe("in0_lcrit_fault_log", 0400, i2c->debugfs, st, + <c4283_in0_lcrit_fault_log); + debugfs_create_file_unsafe("in11_fet_bad_fault_log", 0400, i2c->debugfs, st, + <c4283_fet_bad_fault_log); + debugfs_create_file_unsafe("in11_fet_short_fault_log", 0400, i2c->debugfs, st, + <c4283_fet_short_fault_log); + debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, i2c->debugfs, st, + <c4283_curr1_crit_fault_log); + debugfs_create_file_unsafe("power1_failed_fault_log", 0400, i2c->debugfs, st, + <c4283_power1_failed_fault_log); + debugfs_create_file_unsafe("power1_good_input_fault_log", 0400, i2c->debugfs, + st, <c4283_power1_good_input_fault_log); +} + +static bool ltc4283_is_word_reg(unsigned int reg) +{ + return reg >= LTC4283_SENSE && reg <= LTC4283_ADIO34_MAX; +} + +static int ltc4283_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + int ret; + + if (ltc4283_is_word_reg(reg)) + ret = i2c_smbus_read_word_swapped(client, reg); + else + ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int ltc4283_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + + if (ltc4283_is_word_reg(reg)) + return i2c_smbus_write_word_swapped(client, reg, val); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static const struct regmap_bus ltc4283_regmap_bus = { + .reg_read = ltc4283_reg_read, + .reg_write = ltc4283_reg_write, +}; + +static bool ltc4283_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC4283_SYSTEM_STATUS ... LTC4283_FAULT_STATUS: + return false; + case LTC4283_RESERVED_OC: + return false; + case LTC4283_RESERVED_86 ... LTC4283_RESERVED_8F: + return false; + case LTC4283_RESERVED_91 ... LTC4283_RESERVED_A1: + return false; + case LTC4283_RESERVED_A3: + return false; + case LTC4283_RESERVED_AC: + return false; + case LTC4283_POWER_PLAY_MSB ... LTC4283_POWER_PLAY_LSB: + return false; + case LTC4283_RESERVED_F1 ... LTC4283_RESERVED_FF: + return false; + default: + return true; + } +} + +static const struct regmap_config ltc4283_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xFF, + .writeable_reg = ltc4283_writable_reg, +}; + +static int ltc4283_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev, *hwmon; + struct auxiliary_device *adev; + struct ltc4283_hwmon *st; + int ret, id; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + st->client = client; + st->map = devm_regmap_init(dev, <c4283_regmap_bus, client, + <c4283_regmap_config); + if (IS_ERR(st->map)) + return dev_err_probe(dev, PTR_ERR(st->map), + "Failed to create regmap\n"); + + ret = ltc4283_setup(st, dev); + if (ret) + return ret; + + hwmon = devm_hwmon_device_register_with_info(dev, "ltc4283", st, + <c4283_chip_info, NULL); + + if (IS_ERR(hwmon)) + return PTR_ERR(hwmon); + + ltc4283_debugfs_init(st, client); + + if (!st->gpio_mask) + return 0; + + id = (client->adapter->nr << 10) | client->addr; + adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, "gpio", + &st->gpio_mask, id); + if (!adev) + return dev_err_probe(dev, -ENODEV, "Failed to add GPIO device\n"); + + return 0; +} + +static const struct of_device_id ltc4283_of_match[] = { + { .compatible = "adi,ltc4283" }, + { } +}; + +static const struct i2c_device_id ltc4283_i2c_id[] = { + { "ltc4283" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4283_i2c_id); + +static struct i2c_driver ltc4283_driver = { + .driver = { + .name = "ltc4283", + .of_match_table = ltc4283_of_match, + }, + .probe = ltc4283_probe, + .id_table = ltc4283_i2c_id, +}; +module_i2c_driver(ltc4283_driver); + +MODULE_AUTHOR("Nuno Sá "); +MODULE_DESCRIPTION("LTC4283 Hot Swap Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c index 5102d86d2619..b294e86d52d1 100644 --- a/drivers/hwmon/max127.c +++ b/drivers/hwmon/max127.c @@ -312,7 +312,7 @@ static int max127_probe(struct i2c_client *client) } static const struct i2c_device_id max127_id[] = { - { "max127" }, + { .name = "max127" }, { } }; MODULE_DEVICE_TABLE(i2c, max127_id); diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 43fbb9b26b10..f8b421754220 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -592,12 +592,12 @@ static int max16065_probe(struct i2c_client *client) } static const struct i2c_device_id max16065_id[] = { - { "max16065", max16065 }, - { "max16066", max16066 }, - { "max16067", max16067 }, - { "max16068", max16068 }, - { "max16070", max16070 }, - { "max16071", max16071 }, + { .name = "max16065", .driver_data = max16065 }, + { .name = "max16066", .driver_data = max16066 }, + { .name = "max16067", .driver_data = max16067 }, + { .name = "max16068", .driver_data = max16068 }, + { .name = "max16070", .driver_data = max16070 }, + { .name = "max16071", .driver_data = max16071 }, { } }; diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 9b6d03cff4df..77ef39e8ae7e 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -366,7 +366,7 @@ static int max1619_probe(struct i2c_client *client) } static const struct i2c_device_id max1619_id[] = { - { "max1619" }, + { .name = "max1619" }, { } }; MODULE_DEVICE_TABLE(i2c, max1619_id); diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index a8197a86f559..32548f56409c 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -293,9 +293,9 @@ static int max1668_probe(struct i2c_client *client) } static const struct i2c_device_id max1668_id[] = { - { "max1668", 5 }, - { "max1805", 3 }, - { "max1989", 5 }, + { .name = "max1668", .driver_data = 5 }, + { .name = "max1805", .driver_data = 3 }, + { .name = "max1989", .driver_data = 5 }, { } }; MODULE_DEVICE_TABLE(i2c, max1668_id); diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c index f0048ff37607..9b6ab050db1b 100644 --- a/drivers/hwmon/max197.c +++ b/drivers/hwmon/max197.c @@ -321,8 +321,8 @@ static void max197_remove(struct platform_device *pdev) } static const struct platform_device_id max197_device_ids[] = { - { "max197", max197 }, - { "max199", max199 }, + { .name = "max197", .driver_data = max197 }, + { .name = "max199", .driver_data = max199 }, { } }; MODULE_DEVICE_TABLE(platform, max197_device_ids); diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c index 2f4b419b6c9e..d132be2d6487 100644 --- a/drivers/hwmon/max31730.c +++ b/drivers/hwmon/max31730.c @@ -345,7 +345,7 @@ max31730_probe(struct i2c_client *client) } static const struct i2c_device_id max31730_ids[] = { - { "max31730" }, + { .name = "max31730" }, { } }; MODULE_DEVICE_TABLE(i2c, max31730_ids); diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c index 127e31ca3c87..8d3be064b025 100644 --- a/drivers/hwmon/max31760.c +++ b/drivers/hwmon/max31760.c @@ -555,7 +555,7 @@ static const struct of_device_id max31760_of_match[] = { MODULE_DEVICE_TABLE(of, max31760_of_match); static const struct i2c_device_id max31760_id[] = { - {"max31760"}, + { .name = "max31760" }, { } }; MODULE_DEVICE_TABLE(i2c, max31760_id); diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 4f6171a17d9f..db8a0f6f1829 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -517,7 +517,7 @@ static int max31790_probe(struct i2c_client *client) } static const struct i2c_device_id max31790_id[] = { - { "max31790" }, + { .name = "max31790" }, { } }; MODULE_DEVICE_TABLE(i2c, max31790_id); diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 9b2e56c040df..6a7048fdd8ab 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -463,9 +463,9 @@ static struct attribute *max31827_attrs[] = { ATTRIBUTE_GROUPS(max31827); static const struct i2c_device_id max31827_i2c_ids[] = { - { "max31827", max31827 }, - { "max31828", max31828 }, - { "max31829", max31829 }, + { .name = "max31827", .driver_data = max31827 }, + { .name = "max31828", .driver_data = max31828 }, + { .name = "max31829", .driver_data = max31829 }, { } }; MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids); diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c index 4316dcdd03fc..100acf357b5f 100644 --- a/drivers/hwmon/max6620.c +++ b/drivers/hwmon/max6620.c @@ -474,7 +474,7 @@ static int max6620_probe(struct i2c_client *client) } static const struct i2c_device_id max6620_id[] = { - { "max6620" }, + { .name = "max6620" }, { } }; MODULE_DEVICE_TABLE(i2c, max6620_id); diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c index a7066f3a0bb4..e86ec6d237ca 100644 --- a/drivers/hwmon/max6621.c +++ b/drivers/hwmon/max6621.c @@ -537,7 +537,7 @@ static int max6621_probe(struct i2c_client *client) } static const struct i2c_device_id max6621_id[] = { - { MAX6621_DRV_NAME }, + { .name = MAX6621_DRV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, max6621_id); diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 163d31f17bd4..dd5f4b3b128d 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -778,7 +778,7 @@ static int max6639_resume(struct device *dev) } static const struct i2c_device_id max6639_id[] = { - {"max6639"}, + { .name = "max6639" }, { } }; diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 3466edd7d501..90c37f5647bc 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -807,8 +807,8 @@ static int max6650_probe(struct i2c_client *client) } static const struct i2c_device_id max6650_id[] = { - { "max6650", 1 }, - { "max6651", 4 }, + { .name = "max6650", .driver_data = 1 }, + { .name = "max6651", .driver_data = 4 }, { } }; MODULE_DEVICE_TABLE(i2c, max6650_id); diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index dd906cf491ca..c864093f015c 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -564,16 +564,16 @@ static int max6697_probe(struct i2c_client *client) } static const struct i2c_device_id max6697_id[] = { - { "max6581", max6581 }, - { "max6602", max6602 }, - { "max6622", max6622 }, - { "max6636", max6636 }, - { "max6689", max6689 }, - { "max6693", max6693 }, - { "max6694", max6694 }, - { "max6697", max6697 }, - { "max6698", max6698 }, - { "max6699", max6699 }, + { .name = "max6581", .driver_data = max6581 }, + { .name = "max6602", .driver_data = max6602 }, + { .name = "max6622", .driver_data = max6622 }, + { .name = "max6636", .driver_data = max6636 }, + { .name = "max6689", .driver_data = max6689 }, + { .name = "max6693", .driver_data = max6693 }, + { .name = "max6694", .driver_data = max6694 }, + { .name = "max6697", .driver_data = max6697 }, + { .name = "max6698", .driver_data = max6698 }, + { .name = "max6699", .driver_data = max6699 }, { } }; MODULE_DEVICE_TABLE(i2c, max6697_id); diff --git a/drivers/hwmon/mc34vr500.c b/drivers/hwmon/mc34vr500.c index 84458e4533d8..8cb9d5c09033 100644 --- a/drivers/hwmon/mc34vr500.c +++ b/drivers/hwmon/mc34vr500.c @@ -235,8 +235,8 @@ static int mc34vr500_probe(struct i2c_client *client) } static const struct i2c_device_id mc34vr500_id[] = { - { "mc34vr500" }, - { }, + { .name = "mc34vr500" }, + { } }; MODULE_DEVICE_TABLE(i2c, mc34vr500_id); diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index bcddf6804d3a..d6b0ebf27941 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -177,8 +177,8 @@ static int mcp3021_probe(struct i2c_client *client) } static const struct i2c_device_id mcp3021_id[] = { - { "mcp3021", mcp3021 }, - { "mcp3221", mcp3221 }, + { .name = "mcp3021", .driver_data = mcp3021 }, + { .name = "mcp3221", .driver_data = mcp3221 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp3021_id); diff --git a/drivers/hwmon/mcp9982.c b/drivers/hwmon/mcp9982.c index 26c69e3388ab..9e19e2697e25 100644 --- a/drivers/hwmon/mcp9982.c +++ b/drivers/hwmon/mcp9982.c @@ -92,19 +92,19 @@ static const struct hwmon_channel_info * const mcp9985_info[] = { HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST), + HWMON_T_CRIT_HYST | HWMON_T_FAULT), HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), NULL @@ -369,7 +369,8 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at /* * In Standby State the conversion cycle must be initated manually in - * order to read fresh temperature values and the status of the alarms. + * order to read fresh temperature values, the status of the alarms and + * fault information. */ if (!priv->run_state) { switch (type) { @@ -379,6 +380,7 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at case hwmon_temp_max_alarm: case hwmon_temp_min_alarm: case hwmon_temp_crit_alarm: + case hwmon_temp_fault: ret = regmap_write(priv->regmap, MCP9982_ONE_SHOT_ADDR, 1); if (ret) return ret; @@ -402,6 +404,11 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at } switch (type) { + /* + * Because the ALERT/THERM pin is set in Therm(Comparator) mode, + * the external diode fault status, high limit status and low + * limit status registers do not clear the bits after reading. + */ case hwmon_temp: switch (attr) { case hwmon_temp_input: @@ -510,6 +517,13 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at *val -= hyst * 1000; + return 0; + case hwmon_temp_fault: + *val = regmap_test_bits(priv->regmap, MCP9982_EXT_FAULT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + return 0; default: return -EINVAL; @@ -681,6 +695,7 @@ static umode_t mcp9982_is_visible(const void *_data, enum hwmon_sensor_types typ case hwmon_temp_max_alarm: case hwmon_temp_max_hyst: case hwmon_temp_crit_alarm: + case hwmon_temp_fault: return 0444; case hwmon_temp_min: case hwmon_temp_max: diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 0581770380cc..e1c36c95affb 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -177,12 +177,13 @@ superio_exit(int ioreg) #define NCT6683_CUSTOMER_ID_MSI3 0x207 #define NCT6683_CUSTOMER_ID_MSI4 0x20d #define NCT6683_CUSTOMER_ID_AMD 0x162b -#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c +#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c #define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b #define NCT6683_CUSTOMER_ID_ASROCK3 0x1631 #define NCT6683_CUSTOMER_ID_ASROCK4 0x163e #define NCT6683_CUSTOMER_ID_ASROCK5 0x1621 #define NCT6683_CUSTOMER_ID_ASROCK6 0x1633 +#define NCT6683_CUSTOMER_ID_ASROCK7 0x163d #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -1248,6 +1249,8 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_ASROCK6: break; + case NCT6683_CUSTOMER_ID_ASROCK7: + break; default: if (!force) return -ENODEV; diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c index ba71d776a291..07783910c058 100644 --- a/drivers/hwmon/nct6775-i2c.c +++ b/drivers/hwmon/nct6775-i2c.c @@ -93,19 +93,19 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match); static const struct i2c_device_id nct6775_i2c_id[] = { - { "nct6106", nct6106 }, - { "nct6116", nct6116 }, - { "nct6775", nct6775 }, - { "nct6776", nct6776 }, - { "nct6779", nct6779 }, - { "nct6791", nct6791 }, - { "nct6792", nct6792 }, - { "nct6793", nct6793 }, - { "nct6795", nct6795 }, - { "nct6796", nct6796 }, - { "nct6797", nct6797 }, - { "nct6798", nct6798 }, - { "nct6799", nct6799 }, + { .name = "nct6106", .driver_data = nct6106 }, + { .name = "nct6116", .driver_data = nct6116 }, + { .name = "nct6775", .driver_data = nct6775 }, + { .name = "nct6776", .driver_data = nct6776 }, + { .name = "nct6779", .driver_data = nct6779 }, + { .name = "nct6791", .driver_data = nct6791 }, + { .name = "nct6792", .driver_data = nct6792 }, + { .name = "nct6793", .driver_data = nct6793 }, + { .name = "nct6795", .driver_data = nct6795 }, + { .name = "nct6796", .driver_data = nct6796 }, + { .name = "nct6797", .driver_data = nct6797 }, + { .name = "nct6798", .driver_data = nct6798 }, + { .name = "nct6799", .driver_data = nct6799 }, { } }; MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id); diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 8c9351da12c6..10d1a41fe211 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -47,6 +47,8 @@ static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { #define REG_PWM(x) (0x60 + (x)) #define REG_SMARTFAN_EN(x) (0x64 + (x) / 2) #define SMARTFAN_EN_SHIFT(x) ((x) % 2 * 4) +#define REG_SMARTFAN_STEP_UP_TIME 0x6e +#define REG_SMARTFAN_STEP_DOWN_TIME 0x6f #define REG_VENDOR_ID 0xfd #define REG_CHIP_ID 0xfe #define REG_VERSION_ID 0xff @@ -560,6 +562,77 @@ beep_store(struct device *dev, struct device_attribute *attr, const char *buf, return err ? : count; } +static ssize_t step_time_show(struct device *dev, struct device_attribute *attr, + char *buf, bool step_up) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg, val; + int ret; + + if (step_up) + reg = REG_SMARTFAN_STEP_UP_TIME; + else + reg = REG_SMARTFAN_STEP_DOWN_TIME; + + ret = regmap_read(data->regmap, reg, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", val * 100); /* Convert from ds to ms */ +} + +static ssize_t step_up_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return step_time_show(dev, attr, buf, true); +} + +static ssize_t step_down_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return step_time_show(dev, attr, buf, false); +} + +static ssize_t step_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count, bool step_up) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + unsigned int reg; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + /* Clamp range, and convert from ms to ds */ + val = DIV_ROUND_CLOSEST(clamp_val(val, 100, 25500), 100); + + if (step_up) + reg = REG_SMARTFAN_STEP_UP_TIME; + else + reg = REG_SMARTFAN_STEP_DOWN_TIME; + + ret = regmap_write(data->regmap, reg, val); + + return ret ? : count; +} + +static ssize_t step_up_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return step_time_store(dev, attr, buf, count, true); +} + +static ssize_t step_down_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return step_time_store(dev, attr, buf, count, false); +} + static SENSOR_DEVICE_ATTR_RW(temp1_type, temp_type, 0); static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0x01, REG_TEMP_LSB); static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 0x31, 0); @@ -975,12 +1048,30 @@ static const struct attribute_group nct7802_auto_point_group = { .attrs = nct7802_auto_point_attrs, }; +/* 7.2.102 0x6E FANCTL Step Up Time Register */ +static SENSOR_DEVICE_ATTR_RW(step_up_time, step_up_time, 0); + +/* 7.2.103 0x6F FANCTL Step Down Time Register */ +static SENSOR_DEVICE_ATTR_RW(step_down_time, step_down_time, 0); + +static struct attribute *nct7802_step_time_attrs[] = { + &sensor_dev_attr_step_up_time.dev_attr.attr, + &sensor_dev_attr_step_down_time.dev_attr.attr, + + NULL +}; + +static const struct attribute_group nct7802_step_time_group = { + .attrs = nct7802_step_time_attrs, +}; + static const struct attribute_group *nct7802_groups[] = { &nct7802_temp_group, &nct7802_in_group, &nct7802_fan_group, &nct7802_pwm_group, &nct7802_auto_point_group, + &nct7802_step_time_group, NULL }; @@ -1193,7 +1284,7 @@ static const unsigned short nct7802_address_list[] = { }; static const struct i2c_device_id nct7802_idtable[] = { - { "nct7802" }, + { .name = "nct7802" }, { } }; MODULE_DEVICE_TABLE(i2c, nct7802_idtable); diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 2fa091720c79..976b8a008e44 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -1146,8 +1146,8 @@ static int nct7904_probe(struct i2c_client *client) } static const struct i2c_device_id nct7904_id[] = { - {"nct7904"}, - {} + { .name = "nct7904" }, + { } }; MODULE_DEVICE_TABLE(i2c, nct7904_id); diff --git a/drivers/hwmon/nsa320-hwmon.c b/drivers/hwmon/nsa320-hwmon.c index 18076ba7fc14..5c99acc09677 100644 --- a/drivers/hwmon/nsa320-hwmon.c +++ b/drivers/hwmon/nsa320-hwmon.c @@ -153,6 +153,7 @@ static const struct of_device_id of_nsa320_hwmon_match[] = { { .compatible = "zyxel,nsa320-mcu", }, { }, }; +MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match); static int nsa320_hwmon_probe(struct platform_device *pdev) { @@ -197,7 +198,6 @@ static struct platform_driver nsa320_hwmon_driver = { module_platform_driver(nsa320_hwmon_driver); -MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match); MODULE_AUTHOR("Peter Schildmann "); MODULE_AUTHOR("Adam Baker "); MODULE_DESCRIPTION("NSA320 Hardware Monitoring"); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index d6b48178343d..6f82a6c49393 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -52,18 +52,18 @@ enum { }; static const struct platform_device_id ntc_thermistor_id[] = { - [NTC_B57330V2103] = { "b57330v2103", TYPE_B57330V2103 }, - [NTC_B57891S0103] = { "b57891s0103", TYPE_B57891S0103 }, - [NTC_NCP03WB473] = { "ncp03wb473", TYPE_NCPXXWB473 }, - [NTC_NCP03WF104] = { "ncp03wf104", TYPE_NCPXXWF104 }, - [NTC_NCP15WB473] = { "ncp15wb473", TYPE_NCPXXWB473 }, - [NTC_NCP15WL333] = { "ncp15wl333", TYPE_NCPXXWL333 }, - [NTC_NCP15XH103] = { "ncp15xh103", TYPE_NCPXXXH103 }, - [NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 }, - [NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 }, - [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, - [NTC_NCP18WM474] = { "ncp18wm474", TYPE_NCPXXWM474 }, - [NTC_LAST] = { }, + [NTC_B57330V2103] = { .name = "b57330v2103", .driver_data = TYPE_B57330V2103 }, + [NTC_B57891S0103] = { .name = "b57891s0103", .driver_data = TYPE_B57891S0103 }, + [NTC_NCP03WB473] = { .name = "ncp03wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP03WF104] = { .name = "ncp03wf104", .driver_data = TYPE_NCPXXWF104 }, + [NTC_NCP15WB473] = { .name = "ncp15wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP15WL333] = { .name = "ncp15wl333", .driver_data = TYPE_NCPXXWL333 }, + [NTC_NCP15XH103] = { .name = "ncp15xh103", .driver_data = TYPE_NCPXXXH103 }, + [NTC_NCP18WB473] = { .name = "ncp18wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP21WB473] = { .name = "ncp21wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_SSG1404001221] = { .name = "ssg1404_001221", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP18WM474] = { .name = "ncp18wm474", .driver_data = TYPE_NCPXXWM474 }, + [NTC_LAST] = { } }; MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index 167d2fe4d543..c0220ebfd4b6 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -285,7 +285,7 @@ static int pcf8591_read_channel(struct device *dev, int channel) } static const struct i2c_device_id pcf8591_id[] = { - { "pcf8591" }, + { .name = "pcf8591" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8591_id); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 8f4bff375ecb..c8cda160b5f8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -113,6 +113,15 @@ config SENSORS_CRPS This driver can also be built as a module. If so, the module will be called crps. +config SENSORS_D1U74T + tristate "Murata D1U74T Power Supply" + help + If you say yes here you get hardware monitoring support for the Murata + D1U74T Power Supply. + + This driver can also be built as a module. If so, the module will + be called d1u74t. + config SENSORS_DELTA_AHE50DC_FAN tristate "Delta AHE-50DC fan control module" help @@ -161,6 +170,15 @@ config SENSORS_DPS920AB This driver can also be built as a module. If so, the module will be called dps920ab. +config SENSORS_E50SN12051 + tristate "Delta E50SN12051 Power Modules" + help + If you say yes here you get hardware monitoring support for Delta + E50SN12051 Power Modules. + + This driver can also be built as a module. If so, the module will + be called e50sn12051. + config SENSORS_INA233 tristate "Texas Instruments INA233 and compatibles" help @@ -311,6 +329,16 @@ config SENSORS_LTC4286 If you say yes here you get hardware monitoring support for Analog Devices LTC4286. +config SENSORS_LX1308 + tristate "Luxshare LX1308 DC/DC Power Module" + help + If you say yes here you get hardware monitoring support for + Luxshare LX1308, a high-efficiency 12V 860W DC/DC power module + with PMBus interface. + + This driver can also be built as a module. If so, the module will + be called lx1308. + config SENSORS_MAX15301 tristate "Maxim MAX15301" help @@ -365,6 +393,34 @@ config SENSORS_MAX20751 This driver can also be built as a module. If so, the module will be called max20751. +config SENSORS_MAX20830 + tristate "Analog Devices MAX20830" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX20830. + + This driver can also be built as a module. If so, the module will + be called max20830. + +config SENSORS_MAX20860A + tristate "Analog Devices MAX20860A" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX20860A step-down converter. + + This driver can also be built as a module. If so, the module will + be called max20860a. + +config SENSORS_MAX20860A_REGULATOR + bool "Regulator support for MAX20860A" + depends on SENSORS_MAX20860A && REGULATOR + help + If you say yes here you get regulator support for Analog Devices + MAX20860A step-down converter. + + This enables the MAX20860A to be used as a regulator device, + providing voltage control through the regulator framework. + config SENSORS_MAX31785 tristate "Maxim MAX31785 and compatibles" help @@ -456,6 +512,15 @@ config SENSORS_MP2975 This driver can also be built as a module. If so, the module will be called mp2975. +config SENSORS_MP2985 + tristate "MPS MP2985" + help + If you say yes here you get hardware monitoring support for MPS + MP2985 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2985. + config SENSORS_MP2993 tristate "MPS MP2993" help @@ -715,7 +780,7 @@ config SENSORS_XDP720 tristate "Infineon XDP720 family" help If you say yes here you get hardware monitoring support for Infineon - XDP720. + XDP720 and XDP730 Digital eFuse Controllers. This driver can also be built as a module. If so, the module will be called xdp720. diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 7129b62bc00f..ffc05f493213 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_HAC300S) += hac300s.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o +obj-$(CONFIG_SENSORS_E50SN12051) += e50sn12051.o obj-$(CONFIG_SENSORS_INA233) += ina233.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o @@ -30,12 +31,15 @@ obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o +obj-$(CONFIG_SENSORS_LX1308) += lx1308.o obj-$(CONFIG_SENSORS_MAX15301) += max15301.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX16601) += max16601.o obj-$(CONFIG_SENSORS_MAX17616) += max17616.o obj-$(CONFIG_SENSORS_MAX20730) += max20730.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o +obj-$(CONFIG_SENSORS_MAX20830) += max20830.o +obj-$(CONFIG_SENSORS_MAX20860A) += max20860a.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o @@ -46,6 +50,7 @@ obj-$(CONFIG_SENSORS_MP2891) += mp2891.o obj-$(CONFIG_SENSORS_MP2925) += mp2925.o obj-$(CONFIG_SENSORS_MP29502) += mp29502.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o +obj-$(CONFIG_SENSORS_MP2985) += mp2985.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_MP5920) += mp5920.o @@ -76,3 +81,4 @@ obj-$(CONFIG_SENSORS_XDPE1A2G7B) += xdpe1a2g7b.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o obj-$(CONFIG_SENSORS_CRPS) += crps.o +obj-$(CONFIG_SENSORS_D1U74T) += d1u74t.o diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c index 9f07fb4abaff..d283005d92ae 100644 --- a/drivers/hwmon/pmbus/acbel-fsg032.c +++ b/drivers/hwmon/pmbus/acbel-fsg032.c @@ -49,8 +49,8 @@ static void acbel_fsg032_init_debugfs(struct i2c_client *client) } static const struct i2c_device_id acbel_fsg032_id[] = { - { "acbel_fsg032" }, - {} + { .name = "acbel_fsg032" }, + { } }; static struct pmbus_driver_info acbel_fsg032_info = { diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 6f6ad7b20e9a..22ccb721dd51 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -21,12 +21,14 @@ #include #include +#define ADM1266_IC_DEVICE_REV 0xAE #define ADM1266_BLACKBOX_CONFIG 0xD3 #define ADM1266_PDIO_CONFIG 0xD4 #define ADM1266_READ_STATE 0xD9 #define ADM1266_READ_BLACKBOX 0xDE #define ADM1266_SET_RTC 0xDF #define ADM1266_GPIO_CONFIG 0xE1 +#define ADM1266_POWERUP_COUNTER 0xE4 #define ADM1266_BLACKBOX_INFO 0xE6 #define ADM1266_PDIO_STATUS 0xE9 #define ADM1266_GPIO_STATUS 0xEA @@ -345,6 +347,154 @@ static int adm1266_state_read(struct seq_file *s, void *pdata) return 0; } +/* + * IC_DEVICE_REV (0xAE) returns an 8-byte block (datasheet Rev. D, Table 80): + * [2:0] firmware revision major.minor.patch + * [5:3] bootloader revision major.minor.patch + * [7:6] silicon revision two ASCII characters + */ +static int adm1266_firmware_revision_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_IC_DEVICE_REV, buf); + if (ret < 0) + return ret; + if (ret < 3) + return -EIO; + + seq_printf(s, "%u.%u.%u\n", buf[0], buf[1], buf[2]); + + return 0; +} + +/* + * POWERUP_COUNTER (0xE4) is a 2-byte little-endian non-volatile counter + * that increments on every device power cycle (datasheet Rev. D, Table + * 93). It saturates at 65535 and cannot be reset by the host. Each + * blackbox record embeds the counter value at record time, so this live + * read is mainly useful for matching a record back to its boot. + */ +static int adm1266_powerup_counter_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_POWERUP_COUNTER, buf); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + seq_printf(s, "%u\n", buf[0] | (buf[1] << 8)); + + return 0; +} + +/* + * Clearing the blackbox is required when the device is configured in + * single-recording mode (BLACKBOX_CONFIG[0] = 0): once the 32-record + * buffer is full the device stops recording until cleared. + * + * The clear is issued as a 2-byte block-write to READ_BLACKBOX with + * payload {0xFE, 0x00} per the datasheet. READ_BLACKBOX is also used + * by adm1266_nvmem_read_blackbox() to walk records one at a time; + * both paths run under pmbus_lock so the clear cannot interleave + * mid-iteration and corrupt the read sequence. + */ +static ssize_t adm1266_clear_blackbox_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct i2c_client *client = file->private_data; + u8 payload[2] = { 0xFE, 0x00 }; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_write_block_data(client, ADM1266_READ_BLACKBOX, + sizeof(payload), payload); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations adm1266_clear_blackbox_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = adm1266_clear_blackbox_write, + .llseek = noop_llseek, +}; + +/* + * SET_RTC (0xDF) is a 6-byte block (datasheet Rev. D, Table 84): + * bytes [1:0] - fractional seconds (1/65536 s, written as zero) + * bytes [5:2] - seconds since 1970-01-01 UTC, little-endian + * + * The driver seeds it once at probe via adm1266_rtc_set(). Over a + * long uptime the chip's counter drifts away from host wall-clock, + * so expose it via debugfs: + * + * read -- returns the chip's current seconds counter, which lets + * userspace observe host-vs-chip drift. + * write -- the kernel re-reads ktime_get_real_seconds() and writes + * it to SET_RTC. The write payload is ignored; userspace + * does not get to supply its own timestamp value, so + * there is no way to push a wrong time into the chip. + * + * A small userspace agent (chrony hook, systemd-timesyncd script, + * or a periodic cron job) can write to this file to keep the + * timestamp embedded in each blackbox record aligned with + * wall-clock across long uptimes. + */ +static int adm1266_rtc_get(void *data, u64 *val) +{ + struct i2c_client *client = data; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + u32 seconds = 0; + int ret, i; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_SET_RTC, buf); + if (ret < 0) + return ret; + if (ret < 6) + return -EIO; + + for (i = 0; i < 4; i++) + seconds |= (u32)buf[2 + i] << (i * 8); + + *val = seconds; + + return 0; +} + +static int adm1266_rtc_set(void *data, u64 val) +{ + struct i2c_client *client = data; + time64_t kt = ktime_get_real_seconds(); + u8 write_buf[6] = { 0 }; + int i; + + /* User-supplied @val is ignored on purpose; the kernel owns the + * time source so userspace cannot push a wrong value into the chip. + */ + for (i = 0; i < 4; i++) + write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; + + guard(pmbus_lock)(client); + return i2c_smbus_write_block_data(client, ADM1266_SET_RTC, + sizeof(write_buf), write_buf); +} +DEFINE_DEBUGFS_ATTRIBUTE(adm1266_rtc_fops, + adm1266_rtc_get, adm1266_rtc_set, "%llu\n"); + static void adm1266_init_debugfs(struct adm1266_data *data) { struct dentry *root; @@ -357,6 +507,14 @@ static void adm1266_init_debugfs(struct adm1266_data *data) debugfs_create_devm_seqfile(&data->client->dev, "sequencer_state", data->debugfs_dir, adm1266_state_read); + debugfs_create_devm_seqfile(&data->client->dev, "firmware_revision", data->debugfs_dir, + adm1266_firmware_revision_read); + debugfs_create_devm_seqfile(&data->client->dev, "powerup_counter", data->debugfs_dir, + adm1266_powerup_counter_read); + debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client, + &adm1266_clear_blackbox_fops); + debugfs_create_file("rtc", 0600, data->debugfs_dir, data->client, + &adm1266_rtc_fops); } static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) @@ -446,23 +604,6 @@ static int adm1266_config_nvmem(struct adm1266_data *data) return 0; } -static int adm1266_set_rtc(struct adm1266_data *data) -{ - time64_t kt; - char write_buf[6]; - int i; - - kt = ktime_get_real_seconds(); - - memset(write_buf, 0, sizeof(write_buf)); - - for (i = 0; i < 4; i++) - write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; - - return i2c_smbus_write_block_data(data->client, ADM1266_SET_RTC, sizeof(write_buf), - write_buf); -} - static int adm1266_probe(struct i2c_client *client) { struct adm1266_data *data; @@ -482,14 +623,14 @@ static int adm1266_probe(struct i2c_client *client) crc8_populate_msb(pmbus_crc_table, 0x7); mutex_init(&data->buf_mutex); - ret = adm1266_set_rtc(data); - if (ret < 0) - return ret; - ret = pmbus_do_probe(client, &data->info); if (ret) return ret; + ret = adm1266_rtc_set(client, 0); + if (ret < 0) + return ret; + ret = adm1266_config_nvmem(data); if (ret < 0) return ret; @@ -510,7 +651,7 @@ static const struct of_device_id adm1266_of_match[] = { MODULE_DEVICE_TABLE(of, adm1266_of_match); static const struct i2c_device_id adm1266_id[] = { - { "adm1266" }, + { .name = "adm1266" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1266_id); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index bc2a6a07dc3e..2e5963fb1e12 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -478,16 +478,16 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) } static const struct i2c_device_id adm1275_id[] = { - { "adm1075", adm1075 }, - { "adm1272", adm1272 }, - { "adm1273", adm1273 }, - { "adm1275", adm1275 }, - { "adm1276", adm1276 }, - { "adm1278", adm1278 }, - { "adm1281", adm1281 }, - { "adm1293", adm1293 }, - { "adm1294", adm1294 }, - { "mc09c", sq24905c }, + { .name = "adm1075", .driver_data = adm1075 }, + { .name = "adm1272", .driver_data = adm1272 }, + { .name = "adm1273", .driver_data = adm1273 }, + { .name = "adm1275", .driver_data = adm1275 }, + { .name = "adm1276", .driver_data = adm1276 }, + { .name = "adm1278", .driver_data = adm1278 }, + { .name = "adm1281", .driver_data = adm1281 }, + { .name = "adm1293", .driver_data = adm1293 }, + { .name = "adm1294", .driver_data = adm1294 }, + { .name = "mc09c", .driver_data = sq24905c }, { } }; MODULE_DEVICE_TABLE(i2c, adm1275_id); diff --git a/drivers/hwmon/pmbus/aps-379.c b/drivers/hwmon/pmbus/aps-379.c index 7d46cd647e20..3ec0940ae564 100644 --- a/drivers/hwmon/pmbus/aps-379.c +++ b/drivers/hwmon/pmbus/aps-379.c @@ -100,8 +100,8 @@ static struct pmbus_driver_info aps_379_info = { }; static const struct i2c_device_id aps_379_id[] = { - { "aps-379", 0 }, - {}, + { .name = "aps-379" }, + { } }; MODULE_DEVICE_TABLE(i2c, aps_379_id); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index 6499556f735b..9e3dc9d29c56 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -106,9 +106,9 @@ static int pfe_pmbus_probe(struct i2c_client *client) } static const struct i2c_device_id pfe_device_id[] = { - {"pfe1100", pfe1100}, - {"pfe3000", pfe3000}, - {} + { .name = "pfe1100", .driver_data = pfe1100 }, + { .name = "pfe3000", .driver_data = pfe3000 }, + { } }; MODULE_DEVICE_TABLE(i2c, pfe_device_id); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index 6c3875ba37a0..e364dcb59dd4 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -147,9 +147,9 @@ static struct pmbus_driver_info bpa_rs600_info = { }; static const struct i2c_device_id bpa_rs600_id[] = { - { "bpa-rs600", bpa_rs600 }, - { "bpd-rs600", bpd_rs600 }, - {}, + { .name = "bpa-rs600", .driver_data = bpa_rs600 }, + { .name = "bpd-rs600", .driver_data = bpd_rs600 }, + { } }; MODULE_DEVICE_TABLE(i2c, bpa_rs600_id); diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c index 164b33fed312..266ec8947519 100644 --- a/drivers/hwmon/pmbus/crps.c +++ b/drivers/hwmon/pmbus/crps.c @@ -10,8 +10,8 @@ #include "pmbus.h" static const struct i2c_device_id crps_id[] = { - { "intel_crps185" }, - {} + { .name = "intel_crps185" }, + { } }; MODULE_DEVICE_TABLE(i2c, crps_id); diff --git a/drivers/hwmon/pmbus/d1u74t.c b/drivers/hwmon/pmbus/d1u74t.c new file mode 100644 index 000000000000..752df3af9c1a --- /dev/null +++ b/drivers/hwmon/pmbus/d1u74t.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2026 Nexthop Systems. + */ + +#include +#include +#include +#include +#include + +#include "pmbus.h" + +static const struct i2c_device_id d1u74t_id[] = { + { "d1u74t" }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, d1u74t_id); + +static struct pmbus_driver_info d1u74t_info = { + .pages = 1, + /* PSU uses default linear data format. */ + .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_FAN12 | + PMBUS_HAVE_STATUS_FAN12, +}; + +static int d1u74t_probe(struct i2c_client *client) +{ + char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + struct device *dev = &client->dev; + int rc; + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (rc < 0) + return dev_err_probe(dev, rc, "Failed to read PMBUS_MFR_ID\n"); + + if (rc != 9 || strncmp(buf, "Murata-PS", 9)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, + "Unsupported Manufacturer ID '%s'\n", + buf); + } + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rc < 0) + return dev_err_probe(dev, rc, + "Failed to read PMBUS_MFR_MODEL\n"); + + if (rc < 8 || strncmp(buf, "D1U74T-W", 8)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, "Model '%s' not supported\n", + buf); + } + + rc = pmbus_do_probe(client, &d1u74t_info); + if (rc) + return dev_err_probe(dev, rc, "Failed to probe\n"); + + return 0; +} + +static const struct of_device_id d1u74t_of_match[] = { + { + .compatible = "murata,d1u74t", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, d1u74t_of_match); + +static struct i2c_driver d1u74t_driver = { + .driver = { + .name = "d1u74t", + .of_match_table = d1u74t_of_match, + }, + .probe = d1u74t_probe, + .id_table = d1u74t_id, +}; + +module_i2c_driver(d1u74t_driver); + +MODULE_AUTHOR("Abdurrahman Hussain"); +MODULE_DESCRIPTION("PMBus driver for Murata D1U74T-W power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c index 3850eaea75da..2df655b20ea5 100644 --- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c +++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c @@ -103,7 +103,7 @@ static int ahe50dc_fan_probe(struct i2c_client *client) } static const struct i2c_device_id ahe50dc_fan_id[] = { - { "ahe50dc_fan" }, + { .name = "ahe50dc_fan" }, { } }; MODULE_DEVICE_TABLE(i2c, ahe50dc_fan_id); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index 325111a955e6..0d2901c314c2 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -191,8 +191,8 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = { MODULE_DEVICE_TABLE(of, dps920ab_of_match); static const struct i2c_device_id dps920ab_device_id[] = { - { "dps920ab" }, - {} + { .name = "dps920ab" }, + { } }; MODULE_DEVICE_TABLE(i2c, dps920ab_device_id); diff --git a/drivers/hwmon/pmbus/e50sn12051.c b/drivers/hwmon/pmbus/e50sn12051.c new file mode 100644 index 000000000000..efb4d62b2603 --- /dev/null +++ b/drivers/hwmon/pmbus/e50sn12051.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for E50SN12051 + */ + +#include +#include +#include +#include "pmbus.h" + +static struct pmbus_driver_info e50sn12051_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, +}; + +static const struct i2c_device_id e50sn12051_id[] = { { "e50sn12051", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, e50sn12051_id); + +static const struct of_device_id e50sn12051_of_match[] = { + { .compatible = "delta,e50sn12051" }, + {}, +}; +MODULE_DEVICE_TABLE(of, e50sn12051_of_match); + +static int e50sn12051_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &e50sn12051_info); +} + +static struct i2c_driver e50sn12051_driver = { + .driver = { + .name = "e50sn12051", + .of_match_table = e50sn12051_of_match, + }, + .probe = e50sn12051_probe, + + .id_table = e50sn12051_id, +}; + +module_i2c_driver(e50sn12051_driver); + +MODULE_AUTHOR("Kevin Chang "); +MODULE_DESCRIPTION("PMBus driver for E50SN12051"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c index cad4d2330003..44cf2db9364b 100644 --- a/drivers/hwmon/pmbus/fsp-3y.c +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -271,8 +271,8 @@ static int fsp3y_probe(struct i2c_client *client) } static const struct i2c_device_id fsp3y_id[] = { - {"ym2151e", ym2151e}, - {"yh5151e", yh5151e}, + { .name = "ym2151e", .driver_data = ym2151e }, + { .name = "yh5151e", .driver_data = yh5151e }, { } }; MODULE_DEVICE_TABLE(i2c, fsp3y_id); diff --git a/drivers/hwmon/pmbus/hac300s.c b/drivers/hwmon/pmbus/hac300s.c index a073db1cfe2e..761e53890ebc 100644 --- a/drivers/hwmon/pmbus/hac300s.c +++ b/drivers/hwmon/pmbus/hac300s.c @@ -112,8 +112,8 @@ static const struct of_device_id hac300s_of_match[] = { MODULE_DEVICE_TABLE(of, hac300s_of_match); static const struct i2c_device_id hac300s_id[] = { - {"hac300s", 0}, - {} + { .name = "hac300s" }, + { } }; MODULE_DEVICE_TABLE(i2c, hac300s_id); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 6c7256d997f4..aad94bcb9ceb 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -472,10 +472,10 @@ static struct pmbus_platform_data ibm_cffps_pdata = { }; static const struct i2c_device_id ibm_cffps_id[] = { - { "ibm_cffps1", cffps1 }, - { "ibm_cffps2", cffps2 }, - { "ibm_cffps", cffps_unknown }, - {} + { .name = "ibm_cffps1", .driver_data = cffps1 }, + { .name = "ibm_cffps2", .driver_data = cffps2 }, + { .name = "ibm_cffps", .driver_data = cffps_unknown }, + { } }; MODULE_DEVICE_TABLE(i2c, ibm_cffps_id); diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c index 652087589c55..c72aa258b7c6 100644 --- a/drivers/hwmon/pmbus/ina233.c +++ b/drivers/hwmon/pmbus/ina233.c @@ -168,8 +168,8 @@ static int ina233_probe(struct i2c_client *client) } static const struct i2c_device_id ina233_id[] = { - {"ina233", 0}, - {} + { .name = "ina233" }, + { } }; MODULE_DEVICE_TABLE(i2c, ina233_id); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index 074e0f164ee1..57c0fc16909c 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -197,8 +197,8 @@ static int ipsps_probe(struct i2c_client *client) } static const struct i2c_device_id ipsps_id[] = { - { "ipsps1" }, - {} + { .name = "ipsps1" }, + { } }; MODULE_DEVICE_TABLE(i2c, ipsps_id); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 46d8f334d49a..b2120fc76f40 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -126,8 +126,8 @@ static int ir35221_probe(struct i2c_client *client) } static const struct i2c_device_id ir35221_id[] = { - {"ir35221"}, - {} + { .name = "ir35221" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir35221_id); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c index 34ce15fc708b..0dce4c3f666f 100644 --- a/drivers/hwmon/pmbus/ir36021.c +++ b/drivers/hwmon/pmbus/ir36021.c @@ -51,8 +51,8 @@ static int ir36021_probe(struct i2c_client *client) } static const struct i2c_device_id ir36021_id[] = { - { "ir36021" }, - {}, + { .name = "ir36021" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir36021_id); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index 7b4188e8bf48..47ce88e9d13a 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -53,11 +53,11 @@ static int ir38064_probe(struct i2c_client *client) } static const struct i2c_device_id ir38064_id[] = { - {"ir38060"}, - {"ir38064"}, - {"ir38164"}, - {"ir38263"}, - {} + { .name = "ir38060" }, + { .name = "ir38064" }, + { .name = "ir38164" }, + { .name = "ir38263" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir38064_id); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index 43674c64841d..1694b96d7abf 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -44,8 +44,8 @@ static int irps5401_probe(struct i2c_client *client) } static const struct i2c_device_id irps5401_id[] = { - {"irps5401"}, - {} + { .name = "irps5401" }, + { } }; MODULE_DEVICE_TABLE(i2c, irps5401_id); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 21d047b577a4..2f7f825bfb69 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -409,54 +409,54 @@ static int isl68137_probe(struct i2c_client *client) } static const struct i2c_device_id raa_dmpvr_id[] = { - {"isl68137", raa_dmpvr1_2rail}, - {"isl68220", raa_dmpvr2_2rail}, - {"isl68221", raa_dmpvr2_3rail}, - {"isl68222", raa_dmpvr2_2rail}, - {"isl68223", raa_dmpvr2_2rail}, - {"isl68224", raa_dmpvr2_3rail}, - {"isl68225", raa_dmpvr2_2rail}, - {"isl68226", raa_dmpvr2_3rail}, - {"isl68227", raa_dmpvr2_1rail}, - {"isl68229", raa_dmpvr2_3rail}, - {"isl68233", raa_dmpvr2_2rail}, - {"isl68239", raa_dmpvr2_3rail}, + { .name = "isl68137", .driver_data = raa_dmpvr1_2rail }, + { .name = "isl68220", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68221", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68222", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68223", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68224", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68225", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68226", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68227", .driver_data = raa_dmpvr2_1rail }, + { .name = "isl68229", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68233", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68239", .driver_data = raa_dmpvr2_3rail }, - {"isl69222", raa_dmpvr2_2rail}, - {"isl69223", raa_dmpvr2_3rail}, - {"isl69224", raa_dmpvr2_2rail}, - {"isl69225", raa_dmpvr2_2rail}, - {"isl69227", raa_dmpvr2_3rail}, - {"isl69228", raa_dmpvr2_3rail}, - {"isl69234", raa_dmpvr2_2rail}, - {"isl69236", raa_dmpvr2_2rail}, - {"isl69239", raa_dmpvr2_3rail}, - {"isl69242", raa_dmpvr2_2rail}, - {"isl69243", raa_dmpvr2_1rail}, - {"isl69247", raa_dmpvr2_2rail}, - {"isl69248", raa_dmpvr2_2rail}, - {"isl69254", raa_dmpvr2_2rail}, - {"isl69255", raa_dmpvr2_2rail}, - {"isl69256", raa_dmpvr2_2rail}, - {"isl69259", raa_dmpvr2_2rail}, - {"isl69260", raa_dmpvr2_2rail}, - {"isl69268", raa_dmpvr2_2rail}, - {"isl69269", raa_dmpvr2_3rail}, - {"isl69298", raa_dmpvr2_2rail}, + { .name = "isl69222", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69223", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69224", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69225", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69227", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69228", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69234", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69236", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69239", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69242", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69243", .driver_data = raa_dmpvr2_1rail }, + { .name = "isl69247", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69248", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69254", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69255", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69256", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69259", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69260", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69268", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69269", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69298", .driver_data = raa_dmpvr2_2rail }, - {"raa228000", raa_dmpvr2_hv}, - {"raa228004", raa_dmpvr2_hv}, - {"raa228006", raa_dmpvr2_hv}, - {"raa228228", raa_dmpvr2_2rail_nontc}, - {"raa228244", raa_dmpvr2_2rail_nontc}, - {"raa228246", raa_dmpvr2_2rail_nontc}, - {"raa228942", raa_dmpvr2_2rail_nontc}, - {"raa228943", raa_dmpvr2_2rail_nontc}, - {"raa229001", raa_dmpvr2_2rail}, - {"raa229004", raa_dmpvr2_2rail}, - {"raa229141", raa_dmpvr2_2rail_pmbus}, - {"raa229621", raa_dmpvr2_2rail}, - {} + { .name = "raa228000", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228004", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228006", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228228", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228244", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228246", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228942", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228943", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa229001", .driver_data = raa_dmpvr2_2rail }, + { .name = "raa229004", .driver_data = raa_dmpvr2_2rail }, + { .name = "raa229141", .driver_data = raa_dmpvr2_2rail_pmbus }, + { .name = "raa229621", .driver_data = raa_dmpvr2_2rail }, + { } }; MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index dd7275a67a0a..2be50cf1bbde 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -132,23 +132,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 10742, - .b = 1552, - .R = -2, - }, - [PSC_CURRENT_IN_L] = { .m = 5456, .b = 2118, .R = -2, }, + [PSC_CURRENT_IN_L] = { + .m = 10742, + .b = 1552, + .R = -2, + }, [PSC_POWER] = { - .m = 1204, - .b = 8524, + .m = 612, + .b = 11202, .R = -3, }, [PSC_POWER_L] = { - .m = 612, - .b = 11202, + .m = 1204, + .b = 8524, .R = -3, }, [PSC_TEMPERATURE] = { @@ -167,23 +167,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 10753, - .b = -1200, - .R = -2, - }, - [PSC_CURRENT_IN_L] = { .m = 5405, .b = -600, .R = -2, }, + [PSC_CURRENT_IN_L] = { + .m = 10753, + .b = -1200, + .R = -2, + }, [PSC_POWER] = { - .m = 1204, - .b = -6000, + .m = 605, + .b = -8000, .R = -3, }, [PSC_POWER_L] = { - .m = 605, - .b = -8000, + .m = 1204, + .b = -6000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -202,23 +202,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 15076, - .b = -504, - .R = -2, - }, - [PSC_CURRENT_IN_L] = { .m = 7645, .b = 100, .R = -2, }, + [PSC_CURRENT_IN_L] = { + .m = 15076, + .b = -504, + .R = -2, + }, [PSC_POWER] = { - .m = 1701, - .b = -4000, + .m = 861, + .b = -965, .R = -3, }, [PSC_POWER_L] = { - .m = 861, - .b = -965, + .m = 1701, + .b = -4000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -442,11 +442,11 @@ static const struct regulator_desc lm25066_reg_desc[] = { #endif static const struct i2c_device_id lm25066_id[] = { - {"lm25056", lm25056}, - {"lm25066", lm25066}, - {"lm5064", lm5064}, - {"lm5066", lm5066}, - {"lm5066i", lm5066i}, + { .name = "lm25056", .driver_data = lm25056 }, + { .name = "lm25066", .driver_data = lm25066 }, + { .name = "lm5064", .driver_data = lm5064 }, + { .name = "lm5066", .driver_data = lm5066 }, + { .name = "lm5066i", .driver_data = lm5066i }, { } }; MODULE_DEVICE_TABLE(i2c, lm25066_id); @@ -519,18 +519,20 @@ static int lm25066_probe(struct i2c_client *client) info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; - info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; - info->R[PSC_POWER] = coeff[PSC_POWER].R; if (config & LM25066_DEV_SETUP_CL) { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].R; info->m[PSC_POWER] = coeff[PSC_POWER_L].m; info->b[PSC_POWER] = coeff[PSC_POWER_L].b; + info->R[PSC_POWER] = coeff[PSC_POWER_L].R; } else { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; info->m[PSC_POWER] = coeff[PSC_POWER].m; info->b[PSC_POWER] = coeff[PSC_POWER].b; + info->R[PSC_POWER] = coeff[PSC_POWER].R; } /* diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c index 3704dbe7b54a..ed932ddb4f77 100644 --- a/drivers/hwmon/pmbus/lt3074.c +++ b/drivers/hwmon/pmbus/lt3074.c @@ -95,8 +95,8 @@ static int lt3074_probe(struct i2c_client *client) } static const struct i2c_device_id lt3074_id[] = { - { "lt3074", 0 }, - {} + { .name = "lt3074" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt3074_id); diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c index 9d6d50f39bd6..f8971a786f25 100644 --- a/drivers/hwmon/pmbus/lt7182s.c +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -168,8 +168,8 @@ static int lt7182s_probe(struct i2c_client *client) } static const struct i2c_device_id lt7182s_id[] = { - { "lt7182s" }, - {} + { .name = "lt7182s" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt7182s_id); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index d69a5e675e80..10877b0867fd 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -538,36 +538,36 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { - {"lt7170", lt7170}, - {"lt7171", lt7171}, - {"ltc2972", ltc2972}, - {"ltc2974", ltc2974}, - {"ltc2975", ltc2975}, - {"ltc2977", ltc2977}, - {"ltc2978", ltc2978}, - {"ltc2979", ltc2979}, - {"ltc2980", ltc2980}, - {"ltc3880", ltc3880}, - {"ltc3882", ltc3882}, - {"ltc3883", ltc3883}, - {"ltc3884", ltc3884}, - {"ltc3886", ltc3886}, - {"ltc3887", ltc3887}, - {"ltc3889", ltc3889}, - {"ltc7132", ltc7132}, - {"ltc7841", ltc7841}, - {"ltc7880", ltc7880}, - {"ltm2987", ltm2987}, - {"ltm4664", ltm4664}, - {"ltm4673", ltm4673}, - {"ltm4675", ltm4675}, - {"ltm4676", ltm4676}, - {"ltm4677", ltm4677}, - {"ltm4678", ltm4678}, - {"ltm4680", ltm4680}, - {"ltm4686", ltm4686}, - {"ltm4700", ltm4700}, - {} + { .name = "lt7170", .driver_data = lt7170 }, + { .name = "lt7171", .driver_data = lt7171 }, + { .name = "ltc2972", .driver_data = ltc2972 }, + { .name = "ltc2974", .driver_data = ltc2974 }, + { .name = "ltc2975", .driver_data = ltc2975 }, + { .name = "ltc2977", .driver_data = ltc2977 }, + { .name = "ltc2978", .driver_data = ltc2978 }, + { .name = "ltc2979", .driver_data = ltc2979 }, + { .name = "ltc2980", .driver_data = ltc2980 }, + { .name = "ltc3880", .driver_data = ltc3880 }, + { .name = "ltc3882", .driver_data = ltc3882 }, + { .name = "ltc3883", .driver_data = ltc3883 }, + { .name = "ltc3884", .driver_data = ltc3884 }, + { .name = "ltc3886", .driver_data = ltc3886 }, + { .name = "ltc3887", .driver_data = ltc3887 }, + { .name = "ltc3889", .driver_data = ltc3889 }, + { .name = "ltc7132", .driver_data = ltc7132 }, + { .name = "ltc7841", .driver_data = ltc7841 }, + { .name = "ltc7880", .driver_data = ltc7880 }, + { .name = "ltm2987", .driver_data = ltm2987 }, + { .name = "ltm4664", .driver_data = ltm4664 }, + { .name = "ltm4673", .driver_data = ltm4673 }, + { .name = "ltm4675", .driver_data = ltm4675 }, + { .name = "ltm4676", .driver_data = ltm4676 }, + { .name = "ltm4677", .driver_data = ltm4677 }, + { .name = "ltm4678", .driver_data = ltm4678 }, + { .name = "ltm4680", .driver_data = ltm4680 }, + { .name = "ltm4686", .driver_data = ltm4686 }, + { .name = "ltm4700", .driver_data = ltm4700 }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index 824c16a75e2c..0219d03adb03 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -143,7 +143,7 @@ static int ltc3815_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc3815_id[] = { - {"ltc3815"}, + { .name = "ltc3815" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc3815_id); diff --git a/drivers/hwmon/pmbus/lx1308.c b/drivers/hwmon/pmbus/lx1308.c new file mode 100644 index 000000000000..0c453393796b --- /dev/null +++ b/drivers/hwmon/pmbus/lx1308.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "pmbus.h" + +#define LX1308_MFR_IOUT_OCP3_FAULT 0xBE +#define LX1308_MFR_IOUT_OCP3_WARN 0xBF + +/* + * Decode a Linear11-encoded word to an integer value. + * Linear11 format: bits[15:11] = signed 5-bit exponent, + * bits[10:0] = signed 11-bit mantissa. Result = mant * 2^exp. + */ +static inline int linear11_to_int(u16 word) +{ + s16 exp = ((s16)word) >> 11; + s16 mant = ((s16)((word & 0x7ff) << 5)) >> 5; + + return (exp >= 0) ? (int)((u32)mant << exp) : (mant >> -exp); +} + +static int lx1308_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + /* + * The LX1308 OCP3 registers (slow OCP, 60ms delay) use a + * manufacturer-specific U8.0 format. Read the byte value N and present + * it as a Linear11 word with exponent 0. + */ + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT); + if (ret < 0) + break; + ret &= 0x7FF; + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN); + if (ret < 0) + break; + ret &= 0x7FF; + break; + + /* + * The following registers are not implemented by the LX1308. Return + * -ENXIO to suppress the corresponding sysfs attributes. + */ + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_PIN_MAX: + case PMBUS_MFR_POUT_MAX: + case PMBUS_MFR_MAX_TEMP_1: + ret = -ENXIO; + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int lx1308_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_IOUT_OC_FAULT_LIMIT: + /* + * Decode Linear11 word from pmbus_core back to a plain integer + * and write as the U8.0 byte the device expects. + */ + ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT, + clamp_val(linear11_to_int(word), 0, 255)); + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN, + clamp_val(linear11_to_int(word), 0, 255)); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info lx1308_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_STATUS_INPUT, + + .read_word_data = lx1308_read_word_data, + .write_word_data = lx1308_write_word_data, +}; + +static const struct of_device_id lx1308_of_match[] = { + { .compatible = "luxshare,lx1308" }, + { } +}; + +MODULE_DEVICE_TABLE(of, lx1308_of_match); + +static const struct i2c_device_id lx1308_id[] = { + { "lx1308" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lx1308_id); + +static int lx1308_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + int ret; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read manufacturer id\n"); + buf[ret] = '\0'; + + if (ret != 12 || strncmp(buf, "LUXSHARE", 8)) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer ID '%s'\n", buf); + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Manufacturer Model\n"); + buf[ret] = '\0'; + + for (mid = lx1308_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, buf, strlen(mid->name))) + break; + } + if (!mid->name[0]) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer Model '%s'\n", buf); + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Manufacturer Revision\n"); + buf[ret] = '\0'; + + if (ret != 12 || buf[0] != 'V') + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer Revision '%s'\n", buf); + return pmbus_do_probe(client, &lx1308_info); +} + +static struct i2c_driver lx1308_driver = { + .driver = { + .name = "lx1308", + .of_match_table = lx1308_of_match, + }, + .probe = lx1308_probe, + .id_table = lx1308_id, +}; + +module_i2c_driver(lx1308_driver); + +MODULE_AUTHOR("Brian Chiang "); +MODULE_DESCRIPTION("PMBus driver for Luxshare LX1308"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index d5810b88ea8d..4c36f1ea27ee 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -23,10 +23,10 @@ #include "pmbus.h" static const struct i2c_device_id max15301_id[] = { - { "bmr461" }, - { "max15301" }, - { "max15303" }, - {} + { .name = "bmr461" }, + { .name = "max15301" }, + { .name = "max15303" }, + { } }; MODULE_DEVICE_TABLE(i2c, max15301_id); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index eb84915c2a83..351a1f535999 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -91,8 +91,8 @@ static int max16064_probe(struct i2c_client *client) } static const struct i2c_device_id max16064_id[] = { - {"max16064"}, - {} + { .name = "max16064" }, + { } }; MODULE_DEVICE_TABLE(i2c, max16064_id); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index 36dc13424d92..3dd1f6fd003b 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -263,11 +263,11 @@ static void max16601_remove(void *_data) } static const struct i2c_device_id max16601_id[] = { - {"max16508", max16508}, - {"max16600", max16600}, - {"max16601", max16601}, - {"max16602", max16602}, - {} + { .name = "max16508", .driver_data = max16508 }, + { .name = "max16600", .driver_data = max16600 }, + { .name = "max16601", .driver_data = max16601 }, + { .name = "max16602", .driver_data = max16602 }, + { } }; MODULE_DEVICE_TABLE(i2c, max16601_id); diff --git a/drivers/hwmon/pmbus/max17616.c b/drivers/hwmon/pmbus/max17616.c index 1d4a0ddb95bb..744fa5aefe93 100644 --- a/drivers/hwmon/pmbus/max17616.c +++ b/drivers/hwmon/pmbus/max17616.c @@ -46,7 +46,7 @@ static int max17616_probe(struct i2c_client *client) } static const struct i2c_device_id max17616_id[] = { - { "max17616" }, + { .name = "max17616" }, { } }; MODULE_DEVICE_TABLE(i2c, max17616_id); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index fe03164788df..4031f894e6ae 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -751,11 +751,11 @@ static int max20730_probe(struct i2c_client *client) } static const struct i2c_device_id max20730_id[] = { - { "max20710", max20710 }, - { "max20730", max20730 }, - { "max20734", max20734 }, - { "max20743", max20743 }, - { }, + { .name = "max20710", .driver_data = max20710 }, + { .name = "max20730", .driver_data = max20730 }, + { .name = "max20734", .driver_data = max20734 }, + { .name = "max20743", .driver_data = max20743 }, + { } }; MODULE_DEVICE_TABLE(i2c, max20730_id); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index ac8c43122133..e85676433dca 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -32,8 +32,8 @@ static int max20751_probe(struct i2c_client *client) } static const struct i2c_device_id max20751_id[] = { - {"max20751"}, - {} + { .name = "max20751" }, + { } }; MODULE_DEVICE_TABLE(i2c, max20751_id); diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c new file mode 100644 index 000000000000..cb2c23672166 --- /dev/null +++ b/drivers/hwmon/pmbus/max20830.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices MAX20830 + * + * Copyright (C) 2026 Analog Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +#define MAX20830_IC_DEVICE_ID_LENGTH 9 + +static struct pmbus_driver_info max20830_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, +}; + +static int max20830_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {}; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) && + !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + /* + * Use i2c_smbus_read_block_data() if supported, otherwise fall back + * to i2c_smbus_read_i2c_block_data() to support I2C controllers + * which do not support SMBus block reads. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + /* Reads 9 Data bytes from MAX20830 */ + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read IC_DEVICE_ID\n"); + } else { + /* Reads 1 length byte + 9 Data bytes from MAX20830 */ + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, + MAX20830_IC_DEVICE_ID_LENGTH + 1, + buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read IC_DEVICE_ID\n"); + /* + * Moves data forward, removing the length byte, this is to + * match the format of i2c_smbus_read_block_data(). + * Also adjust return value to reflect length byte removal. + */ + memmove(buf, buf + 1, MAX20830_IC_DEVICE_ID_LENGTH); + ret = ret - 1; + } + + /* + * MAX20830 IC_DEVICE_ID sends string data "MAX20830\0". + * Return value should at least be 9 bytes of data. + */ + if (ret < MAX20830_IC_DEVICE_ID_LENGTH) + return dev_err_probe(&client->dev, -ENODEV, + "IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n", + ret); + + /* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */ + buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0'; + if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1)) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported device: '%s'\n", buf); + + return pmbus_do_probe(client, &max20830_info); +} + +static const struct i2c_device_id max20830_id[] = { + {"max20830"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max20830_id); + +static const struct of_device_id max20830_of_match[] = { + { .compatible = "adi,max20830" }, + { } +}; +MODULE_DEVICE_TABLE(of, max20830_of_match); + +static struct i2c_driver max20830_driver = { + .driver = { + .name = "max20830", + .of_match_table = max20830_of_match, + }, + .probe = max20830_probe, + .id_table = max20830_id, +}; + +module_i2c_driver(max20830_driver); + +MODULE_AUTHOR("Alexis Czezar Torreno "); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20830"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max20860a.c b/drivers/hwmon/pmbus/max20860a.c new file mode 100644 index 000000000000..905f916f6c08 --- /dev/null +++ b/drivers/hwmon/pmbus/max20860a.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Analog Devices MAX20860A + * + * SPDX-FileCopyrightText: Copyright Hewlett Packard Enterprise Development LP + */ + +#include +#include +#include +#include "pmbus.h" + +#if IS_ENABLED(CONFIG_SENSORS_MAX20860A_REGULATOR) +static const struct regulator_desc max20860a_reg_desc[] = { + PMBUS_REGULATOR_ONE("vout"), +}; +#endif + +static struct pmbus_driver_info max20860a_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_INPUT, +#if IS_ENABLED(CONFIG_SENSORS_MAX20860A_REGULATOR) + .num_regulators = 1, + .reg_desc = max20860a_reg_desc, +#endif +}; + +static int max20860a_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &max20860a_info); +} + +static const struct i2c_device_id max20860a_id[] = { + {"max20860a"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max20860a_id); + +static const struct of_device_id max20860a_of_match[] = { + { .compatible = "adi,max20860a" }, + {} +}; +MODULE_DEVICE_TABLE(of, max20860a_of_match); + +static struct i2c_driver max20860a_driver = { + .driver = { + .name = "max20860a", + .of_match_table = max20860a_of_match, + }, + .probe = max20860a_probe, + .id_table = max20860a_id, +}; + +module_i2c_driver(max20860a_driver); + +MODULE_AUTHOR("Syed Arif "); +MODULE_AUTHOR("Sanman Pradhan "); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20860A"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 3caa76bcbeb5..bdcce810a479 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -447,10 +447,10 @@ static int max31785_probe(struct i2c_client *client) } static const struct i2c_device_id max31785_id[] = { - { "max31785" }, - { "max31785a" }, - { "max31785b" }, - { }, + { .name = "max31785" }, + { .name = "max31785a" }, + { .name = "max31785b" }, + { } }; MODULE_DEVICE_TABLE(i2c, max31785_id); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index cc96bb22f8f5..74876d2207fb 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -18,6 +18,7 @@ enum chips { adpm12160, adpm12200, + adpm12250, max34440, max34441, max34446, @@ -97,7 +98,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, break; case PMBUS_VIRT_READ_IOUT_AVG: if (data->id != max34446 && data->id != max34451 && - data->id != adpm12160 && data->id != adpm12200) + data->id != adpm12160 && data->id != adpm12200 && + data->id != adpm12250) return -ENXIO; ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); @@ -182,7 +184,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page, ret = pmbus_write_word_data(client, page, MAX34440_MFR_IOUT_PEAK, 0); if (!ret && (data->id == max34446 || data->id == max34451 || - data->id == adpm12160 || data->id == adpm12200)) + data->id == adpm12160 || data->id == adpm12200 || + data->id == adpm12250)) ret = pmbus_write_word_data(client, page, MAX34446_MFR_IOUT_AVG, 0); @@ -399,6 +402,40 @@ static struct pmbus_driver_info max34440_info[] = { .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, + [adpm12250] = { + .pages = 19, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 125, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 125, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_IN] = 250, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = -1, + .m[PSC_CURRENT_OUT] = 250, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -1, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + /* absent func below [18] are not for monitoring */ + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, + .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, + .func[14] = PMBUS_HAVE_IOUT, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, [max34440] = { .pages = 14, .format[PSC_VOLTAGE_IN] = direct, @@ -635,7 +672,8 @@ static int max34440_probe(struct i2c_client *client) rv = max34451_set_supported_funcs(client, data); if (rv) return rv; - } else if (data->id == adpm12160 || data->id == adpm12200) { + } else if (data->id == adpm12160 || data->id == adpm12200 || + data->id == adpm12250) { data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; } @@ -644,15 +682,16 @@ static int max34440_probe(struct i2c_client *client) } static const struct i2c_device_id max34440_id[] = { - {"adpm12160", adpm12160}, - {"adpm12200", adpm12200}, - {"max34440", max34440}, - {"max34441", max34441}, - {"max34446", max34446}, - {"max34451", max34451}, - {"max34460", max34460}, - {"max34461", max34461}, - {} + { .name = "adpm12160", .driver_data = adpm12160 }, + { .name = "adpm12200", .driver_data = adpm12200 }, + { .name = "adpm12250", .driver_data = adpm12250 }, + { .name = "max34440", .driver_data = max34440 }, + { .name = "max34441", .driver_data = max34441 }, + { .name = "max34446", .driver_data = max34446 }, + { .name = "max34451", .driver_data = max34451 }, + { .name = "max34460", .driver_data = max34460 }, + { .name = "max34461", .driver_data = max34461 }, + { } }; MODULE_DEVICE_TABLE(i2c, max34440_id); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index b3a2a7492bbf..212b28fa4bf2 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -171,7 +171,7 @@ static int max8688_probe(struct i2c_client *client) } static const struct i2c_device_id max8688_id[] = { - {"max8688"}, + { .name = "max8688" }, { } }; diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c index e83c70a3583f..3d6621e36fd3 100644 --- a/drivers/hwmon/pmbus/mp2856.c +++ b/drivers/hwmon/pmbus/mp2856.c @@ -54,9 +54,9 @@ static const int mp2856_max_phases[][MP2856_PAGE_NUM] = { }; static const struct i2c_device_id mp2856_id[] = { - {"mp2856", mp2856}, - {"mp2857", mp2857}, - {} + { .name = "mp2856", .driver_data = mp2856 }, + { .name = "mp2857", .driver_data = mp2857 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2856_id); diff --git a/drivers/hwmon/pmbus/mp2869.c b/drivers/hwmon/pmbus/mp2869.c index 4647892e5112..76c6017507b3 100644 --- a/drivers/hwmon/pmbus/mp2869.c +++ b/drivers/hwmon/pmbus/mp2869.c @@ -632,20 +632,20 @@ static int mp2869_probe(struct i2c_client *client) } static const struct i2c_device_id mp2869_id[] = { - {"mp2869", 0}, - {"mp29608", 1}, - {"mp29612", 2}, - {"mp29816", 3}, - {} + { .name = "mp2869" }, + { .name = "mp29608" }, + { .name = "mp29612" }, + { .name = "mp29816" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2869_id); -static const struct of_device_id __maybe_unused mp2869_of_match[] = { - {.compatible = "mps,mp2869", .data = (void *)0}, - {.compatible = "mps,mp29608", .data = (void *)1}, - {.compatible = "mps,mp29612", .data = (void *)2}, - {.compatible = "mps,mp29816", .data = (void *)3}, - {} +static const struct of_device_id mp2869_of_match[] = { + { .compatible = "mps,mp2869" }, + { .compatible = "mps,mp29608" }, + { .compatible = "mps,mp29612" }, + { .compatible = "mps,mp29816" }, + { } }; MODULE_DEVICE_TABLE(of, mp2869_of_match); diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c index 772a623ca7d0..c5f35daa3fe1 100644 --- a/drivers/hwmon/pmbus/mp2888.c +++ b/drivers/hwmon/pmbus/mp2888.c @@ -378,8 +378,8 @@ static int mp2888_probe(struct i2c_client *client) } static const struct i2c_device_id mp2888_id[] = { - {"mp2888"}, - {} + { .name = "mp2888" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2888_id); diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c index f8f4c91ec23c..316c1fc0d6cc 100644 --- a/drivers/hwmon/pmbus/mp2891.c +++ b/drivers/hwmon/pmbus/mp2891.c @@ -572,7 +572,7 @@ static int mp2891_probe(struct i2c_client *client) } static const struct i2c_device_id mp2891_id[] = { - { "mp2891" }, + { .name = "mp2891" }, { } }; MODULE_DEVICE_TABLE(i2c, mp2891_id); diff --git a/drivers/hwmon/pmbus/mp2925.c b/drivers/hwmon/pmbus/mp2925.c index ad094842cf2d..0a58b1ffd791 100644 --- a/drivers/hwmon/pmbus/mp2925.c +++ b/drivers/hwmon/pmbus/mp2925.c @@ -305,9 +305,9 @@ static int mp2925_probe(struct i2c_client *client) } static const struct i2c_device_id mp2925_id[] = { - {"mp2925"}, - {"mp2929"}, - {} + { .name = "mp2925" }, + { .name = "mp2929" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2925_id); diff --git a/drivers/hwmon/pmbus/mp29502.c b/drivers/hwmon/pmbus/mp29502.c index 7241373f1557..afc5e8c07e25 100644 --- a/drivers/hwmon/pmbus/mp29502.c +++ b/drivers/hwmon/pmbus/mp29502.c @@ -642,8 +642,8 @@ static int mp29502_probe(struct i2c_client *client) } static const struct i2c_device_id mp29502_id[] = { - {"mp29502", 0}, - {} + { .name = "mp29502", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp29502_id); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index d0bc47b12cb0..dca7e2fbcb44 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -1082,10 +1082,10 @@ static const struct of_device_id mp2975_of_match[] = { MODULE_DEVICE_TABLE(of, mp2975_of_match); static const struct i2c_device_id mp2975_id[] = { - {"mp2971", (kernel_ulong_t)&mp2975_ddinfo[mp2971]}, - {"mp2973", (kernel_ulong_t)&mp2975_ddinfo[mp2973]}, - {"mp2975", (kernel_ulong_t)&mp2975_ddinfo[mp2975]}, - {} + { .name = "mp2971", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2971] }, + { .name = "mp2973", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2973] }, + { .name = "mp2975", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2975] }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2975_id); diff --git a/drivers/hwmon/pmbus/mp2985.c b/drivers/hwmon/pmbus/mp2985.c new file mode 100644 index 000000000000..b0a4796ca8ce --- /dev/null +++ b/drivers/hwmon/pmbus/mp2985.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2985) + * + * Copyright (C) 2026 MPS + */ + +#include +#include +#include +#include +#include "pmbus.h" + +/* + * Vender specific register READ_PIN_EST(0x93), READ_IIN_EST(0x8E), + * MFR_VR_MULTI_CONFIG_R1(0x0D) and MFR_VR_MULTI_CONFIG_R2(0x1D). + * The READ_PIN_EST is used to read pin telemetry, the READ_IIN_EST + * is used to read iin telemetry and the MFR_VR_MULTI_CONFIG_R1, + * MFR_VR_MULTI_CONFIG_R2 are used to obtain vid scale. + */ +#define READ_PIN_EST 0x93 +#define READ_IIN_EST 0x8E +#define MFR_VR_MULTI_CONFIG_R1 0x0D +#define MFR_VR_MULTI_CONFIG_R2 0x1D + +#define MP2985_VOUT_DIV 64 +#define MP2985_VOUT_OVUV_UINT 125 +#define MP2985_VOUT_OVUV_DIV 64 + +#define MP2985_PAGE_NUM 2 + +#define MP2985_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +#define MP2985_RAIL2_FUNC (PMBUS_HAVE_PIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +struct mp2985_data { + struct pmbus_driver_info info; + int vout_scale[MP2985_PAGE_NUM]; + int vid_offset[MP2985_PAGE_NUM]; +}; + +#define to_mp2985_data(x) container_of(x, struct mp2985_data, info) + +static u16 mp2985_linear_exp_transfer(u16 word, u16 expect_exponent) +{ + s16 exponent, mantissa, target_exponent; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; + + /* + * The MP2985 does not support negtive limit value, if a negtive + * limit value is written, the limit value will become to 0. And + * the maximum positive limit value is limitted to 0x3FF. + */ + if (mantissa < 0) { + mantissa = 0; + } else { + if (exponent > target_exponent) { + mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ? + mantissa << (exponent - target_exponent) : + 0x3FF; + } else { + mantissa = clamp_val(mantissa >> (target_exponent - exponent), + 0, 0x3FF); + } + } + + return mantissa | ((expect_exponent << 11) & 0xf800); +} + +static int mp2985_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The MP2985 does not follow standard PMBus protocol completely, + * and the calculation of vout in this driver is based on direct + * format. As a result, the format of vout is enforced to direct. + */ + ret = PB_VOUT_MODE_DIRECT; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int mp2985_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 mp2985_data *data = to_mp2985_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + /* + * The MP2985 supports three vout mode, direct, linear11 and vid mode. + * In vid mode, the MP2985 vout telemetry has 49 vid step offset, but + * PMBUS_VOUT_OV_FAULT_LIMIT and PMBUS_VOUT_UV_FAULT_LIMIT do not take + * this into consideration, their resolution are 1.953125mV/LSB, as a + * result, format[PSC_VOLTAGE_OUT] can not be set to vid mode directly. + * Adding extra vid_offset variable for vout telemetry. + */ + ret = clamp_val(DIV_ROUND_CLOSEST(((ret & GENMASK(11, 0)) + + data->vid_offset[page]) * + data->vout_scale[page], MP2985_VOUT_DIV), + 0, 0x7FFF); + break; + case PMBUS_READ_IIN: + /* + * The MP2985 has standard PMBUS_READ_IIN register(0x89), but this is + * not used to read the input current of per rail. The input current + * is read through the vender redefined register READ_IIN_EST(0x8E). + */ + ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST); + break; + case PMBUS_READ_PIN: + /* + * The MP2985 has standard PMBUS_READ_PIN register(0x97), but this + * is not used to read the input power of per rail. The input power + * of per rail is read through the vender redefined register + * READ_PIN_EST(0x93). + */ + ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * MP2985_VOUT_OVUV_UINT, + MP2985_VOUT_OVUV_DIV); + break; + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * These register is not explicitly handled by the driver, + * as a result, return -ENODATA directly. + */ + ret = -ENODATA; + break; + default: + /* + * The MP2985 do not support other telemetry and limit value + * reading, so, return -EINVAL directly. + */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2985_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* + * The PMBUS_VIN_OV_FAULT_LIMIT, PMBUS_VIN_OV_WARN_LIMIT, + * PMBUS_VIN_UV_WARN_LIMIT and PMBUS_VIN_UV_FAULT_LIMIT + * of MP2985 is linear11 format, and the exponent is a + * constant value(5'b11101), so the exponent of word + * parameter should be converted to 5'b11101(0x1D). + */ + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, 0x1D)); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + /* + * The bit0-bit11 is the limit value, and bit12-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(11, 0)) | + clamp_val(DIV_ROUND_CLOSEST(word * MP2985_VOUT_OVUV_DIV, + MP2985_VOUT_OVUV_UINT), 0, 0xFFF)); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT of + * MP2985 is linear11 format, and the exponent is a + * constant value(5'b00000), so the exponent of word + * parameter should be converted to 5'b00000. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, 0x00)); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + /* + * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT + * of MP2985 is linear11 format, and the exponent can not be + * changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, + FIELD_GET(GENMASK(15, 11), + ret))); + break; + default: + /* + * The MP2985 do not support other limit value configuration, + * so, return -EINVAL directly. + */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mp2985_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2985_data *data = to_mp2985_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + /* + * The MP2985 supports three vout mode. If PMBUS_VOUT_MODE + * bit5 is 1, it is vid mode. If PMBUS PMBUS_VOUT_MODE bit4 + * is 1, it is linear11 mode, the vout scale is 1.953125mv/LSB. + * If PMBUS PMBUS_VOUT_MODE bit6 is 1, it is direct mode, the + * vout scale is 1mv/LSB. In vid mode, the MP2985 vout telemetry + * has 49 vid step offset. + */ + if (FIELD_GET(BIT(5), ret)) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, page == 0 ? + MFR_VR_MULTI_CONFIG_R1 : + MFR_VR_MULTI_CONFIG_R2); + if (ret < 0) + return ret; + + if (page == 0) { + if (FIELD_GET(BIT(4), ret)) + data->vout_scale[page] = 320; + else + data->vout_scale[page] = 640; + } else { + if (FIELD_GET(BIT(3), ret)) + data->vout_scale[page] = 320; + else + data->vout_scale[page] = 640; + } + + data->vid_offset[page] = 49; + + /* + * For vid mode, the MP2985 should be changed to page 2 + * to obtain vout scale value, this may confuse the PMBus + * core. To avoid this, switch back to the previous page + * again. + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + } else if (FIELD_GET(BIT(4), ret)) { + data->vout_scale[page] = 125; + data->vid_offset[page] = 0; + } else { + data->vout_scale[page] = 64; + data->vid_offset[page] = 0; + } + + return 0; +} + +static int mp2985_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + ret = mp2985_identify_vout_scale(client, info, 0); + if (ret < 0) + return ret; + + return mp2985_identify_vout_scale(client, info, 1); +} + +static struct pmbus_driver_info mp2985_info = { + .pages = MP2985_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP2985_RAIL1_FUNC, + .func[1] = MP2985_RAIL2_FUNC, + .read_word_data = mp2985_read_word_data, + .read_byte_data = mp2985_read_byte_data, + .write_word_data = mp2985_write_word_data, + .identify = mp2985_identify, +}; + +static int mp2985_probe(struct i2c_client *client) +{ + struct mp2985_data *data; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2985_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2985_info, sizeof(mp2985_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp2985_id[] = { + {"mp2985", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2985_id); + +static const struct of_device_id __maybe_unused mp2985_of_match[] = { + {.compatible = "mps,mp2985"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2985_of_match); + +static struct i2c_driver mp2985_driver = { + .driver = { + .name = "mp2985", + .of_match_table = mp2985_of_match, + }, + .probe = mp2985_probe, + .id_table = mp2985_id, +}; + +module_i2c_driver(mp2985_driver); + +MODULE_AUTHOR("Wensheng Wang "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2985 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c index 81c84fc8ed47..3a6a6c55a1f1 100644 --- a/drivers/hwmon/pmbus/mp2993.c +++ b/drivers/hwmon/pmbus/mp2993.c @@ -233,7 +233,7 @@ static int mp2993_probe(struct i2c_client *client) } static const struct i2c_device_id mp2993_id[] = { - { "mp2993" }, + { .name = "mp2993" }, { } }; MODULE_DEVICE_TABLE(i2c, mp2993_id); diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c index 319ae2721bcf..b803f3ddf8ea 100644 --- a/drivers/hwmon/pmbus/mp5920.c +++ b/drivers/hwmon/pmbus/mp5920.c @@ -66,7 +66,7 @@ static const struct of_device_id mp5920_of_match[] = { MODULE_DEVICE_TABLE(of, mp5920_of_match); static const struct i2c_device_id mp5920_id[] = { - { "mp5920" }, + { .name = "mp5920" }, { } }; diff --git a/drivers/hwmon/pmbus/mp5926.c b/drivers/hwmon/pmbus/mp5926.c index f0d1b30c7013..a40647472b16 100644 --- a/drivers/hwmon/pmbus/mp5926.c +++ b/drivers/hwmon/pmbus/mp5926.c @@ -157,8 +157,8 @@ static int mp5926_probe(struct i2c_client *client) } static const struct i2c_device_id mp5926_id[] = { - { "mp5926", 0 }, - {} + { .name = "mp5926", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp5926_id); diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c index 9a4ee79712cf..f8a5d1b42ff7 100644 --- a/drivers/hwmon/pmbus/mp5990.c +++ b/drivers/hwmon/pmbus/mp5990.c @@ -148,8 +148,8 @@ static struct pmbus_driver_info mp5998_info = { }; static const struct i2c_device_id mp5990_id[] = { - {"mp5990", mp5990}, - {"mp5998", mp5998}, + { .name = "mp5990", .driver_data = mp5990 }, + { .name = "mp5998", .driver_data = mp5998 }, { } }; MODULE_DEVICE_TABLE(i2c, mp5990_id); diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c index 42ca6748777a..d9049d326c80 100644 --- a/drivers/hwmon/pmbus/mp9941.c +++ b/drivers/hwmon/pmbus/mp9941.c @@ -291,7 +291,7 @@ static int mp9941_probe(struct i2c_client *client) } static const struct i2c_device_id mp9941_id[] = { - { "mp9941" }, + { .name = "mp9941" }, { } }; MODULE_DEVICE_TABLE(i2c, mp9941_id); diff --git a/drivers/hwmon/pmbus/mp9945.c b/drivers/hwmon/pmbus/mp9945.c index 34822e0de812..199480d85515 100644 --- a/drivers/hwmon/pmbus/mp9945.c +++ b/drivers/hwmon/pmbus/mp9945.c @@ -215,8 +215,8 @@ static int mp9945_probe(struct i2c_client *client) } static const struct i2c_device_id mp9945_id[] = { - {"mp9945"}, - {} + { .name = "mp9945" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp9945_id); diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 8f10e37a7a76..f49610adff89 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -145,9 +145,9 @@ static const struct of_device_id mpq7932_of_match[] = { MODULE_DEVICE_TABLE(of, mpq7932_of_match); static const struct i2c_device_id mpq7932_id[] = { - { "mpq2286", }, - { "mpq7932", }, - { }, + { .name = "mpq2286" }, + { .name = "mpq7932" }, + { } }; MODULE_DEVICE_TABLE(i2c, mpq7932_id); diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 87bd039c77b9..bbde55028290 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -110,11 +110,11 @@ static struct pmbus_driver_info mpq8785_info = { }; static const struct i2c_device_id mpq8785_id[] = { - { "mpm3695", mpm3695 }, - { "mpm3695-25", mpm3695_25 }, - { "mpm82504", mpm82504 }, - { "mpq8785", mpq8785 }, - { }, + { .name = "mpm3695", .driver_data = mpm3695 }, + { .name = "mpm3695-25", .driver_data = mpm3695_25 }, + { .name = "mpm82504", .driver_data = mpm82504 }, + { .name = "mpq8785", .driver_data = mpq8785 }, + { } }; MODULE_DEVICE_TABLE(i2c, mpq8785_id); diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c index aa98284bbdd8..9056dc387e87 100644 --- a/drivers/hwmon/pmbus/pim4328.c +++ b/drivers/hwmon/pmbus/pim4328.c @@ -39,15 +39,15 @@ struct pim4328_data { #define PIM4328_MFR_READ_STATUS 0xd0 static const struct i2c_device_id pim4328_id[] = { - {"bmr455", pim4328}, - {"pim4006", pim4006}, - {"pim4106", pim4006}, - {"pim4206", pim4006}, - {"pim4306", pim4006}, - {"pim4328", pim4328}, - {"pim4406", pim4006}, - {"pim4820", pim4820}, - {} + { .name = "bmr455", .driver_data = pim4328 }, + { .name = "pim4006", .driver_data = pim4006 }, + { .name = "pim4106", .driver_data = pim4006 }, + { .name = "pim4206", .driver_data = pim4006 }, + { .name = "pim4306", .driver_data = pim4006 }, + { .name = "pim4328", .driver_data = pim4328 }, + { .name = "pim4406", .driver_data = pim4006 }, + { .name = "pim4820", .driver_data = pim4820 }, + { } }; MODULE_DEVICE_TABLE(i2c, pim4328_id); diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c index 569b61dc1a32..d5ab085a2273 100644 --- a/drivers/hwmon/pmbus/pli1209bc.c +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -117,8 +117,8 @@ static int pli1209bc_probe(struct i2c_client *client) } static const struct i2c_device_id pli1209bc_id[] = { - {"pli1209bc"}, - {} + { .name = "pli1209bc" }, + { } }; MODULE_DEVICE_TABLE(i2c, pli1209bc_id); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c index c96c0aecb920..613654b15b4a 100644 --- a/drivers/hwmon/pmbus/pm6764tr.c +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -48,8 +48,8 @@ static int pm6764tr_probe(struct i2c_client *client) } static const struct i2c_device_id pm6764tr_id[] = { - {"pm6764tr"}, - {} + { .name = "pm6764tr" }, + { } }; MODULE_DEVICE_TABLE(i2c, pm6764tr_id); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index d1844c7a51ee..7a7eac90d62e 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -213,36 +213,40 @@ static const struct pmbus_device_info pmbus_info_one_status = { * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { - {"adp4000", (kernel_ulong_t)&pmbus_info_one}, - {"bmr310", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr453", (kernel_ulong_t)&pmbus_info_one}, - {"bmr454", (kernel_ulong_t)&pmbus_info_one}, - {"bmr456", (kernel_ulong_t)&pmbus_info_one}, - {"bmr457", (kernel_ulong_t)&pmbus_info_one}, - {"bmr458", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr480", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr490", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr491", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr492", (kernel_ulong_t)&pmbus_info_one}, - {"dps460", (kernel_ulong_t)&pmbus_info_one_skip}, - {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip}, - {"dps800", (kernel_ulong_t)&pmbus_info_one_skip}, - {"max20796", (kernel_ulong_t)&pmbus_info_one}, - {"mdt040", (kernel_ulong_t)&pmbus_info_one}, - {"ncp4200", (kernel_ulong_t)&pmbus_info_one}, - {"ncp4208", (kernel_ulong_t)&pmbus_info_one}, - {"pdt003", (kernel_ulong_t)&pmbus_info_one}, - {"pdt006", (kernel_ulong_t)&pmbus_info_one}, - {"pdt012", (kernel_ulong_t)&pmbus_info_one}, - {"pmbus", (kernel_ulong_t)&pmbus_info_zero}, - {"sgd009", (kernel_ulong_t)&pmbus_info_one_skip}, - {"tps40400", (kernel_ulong_t)&pmbus_info_one}, - {"tps544b20", (kernel_ulong_t)&pmbus_info_one}, - {"tps544b25", (kernel_ulong_t)&pmbus_info_one}, - {"tps544c20", (kernel_ulong_t)&pmbus_info_one}, - {"tps544c25", (kernel_ulong_t)&pmbus_info_one}, - {"udt020", (kernel_ulong_t)&pmbus_info_one}, - {} + { .name = "adp4000", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr310", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr316", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr321", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr350", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr351", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr453", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr454", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr456", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr457", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr458", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr480", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr490", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr491", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr492", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "dps460", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "dps650ab", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "dps800", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "max20796", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "mdt040", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "ncp4200", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "ncp4208", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt003", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt006", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt012", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pmbus", .driver_data = (kernel_ulong_t)&pmbus_info_zero }, + { .name = "sgd009", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "tps40400", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544b20", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544b25", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544c20", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544c25", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "udt020", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { } }; MODULE_DEVICE_TABLE(i2c, pmbus_id); diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 24c1f961c766..6ba2b3e0e565 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -130,10 +130,10 @@ static int pxe1610_probe(struct i2c_client *client) } static const struct i2c_device_id pxe1610_id[] = { - {"pxe1610"}, - {"pxe1110"}, - {"pxm1310"}, - {} + { .name = "pxe1610" }, + { .name = "pxe1110" }, + { .name = "pxm1310" }, + { } }; MODULE_DEVICE_TABLE(i2c, pxe1610_id); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c index a368cfa9d45a..ecac405d7536 100644 --- a/drivers/hwmon/pmbus/q54sj108a2.c +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -270,10 +270,10 @@ static const struct file_operations q54sj108a2_fops = { }; static const struct i2c_device_id q54sj108a2_id[] = { - { "q54sj108a2", q54sj108a2 }, - { "q54sn120a1", q54sj108a2 }, - { "q54sw120a7", q54sj108a2 }, - { }, + { .name = "q54sj108a2", .driver_data = q54sj108a2 }, + { .name = "q54sn120a1", .driver_data = q54sj108a2 }, + { .name = "q54sw120a7", .driver_data = q54sj108a2 }, + { } }; MODULE_DEVICE_TABLE(i2c, q54sj108a2_id); diff --git a/drivers/hwmon/pmbus/stef48h28.c b/drivers/hwmon/pmbus/stef48h28.c index 4bde2215697c..8e48dd3ba74b 100644 --- a/drivers/hwmon/pmbus/stef48h28.c +++ b/drivers/hwmon/pmbus/stef48h28.c @@ -48,8 +48,8 @@ static int stef48h28_probe(struct i2c_client *client) } static const struct i2c_device_id stef48h28_id[] = { - {"stef48h28"}, - {} + { .name = "stef48h28" }, + { } }; MODULE_DEVICE_TABLE(i2c, stef48h28_id); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c index 5cb905ed8ae5..b5ce7f975eea 100644 --- a/drivers/hwmon/pmbus/stpddc60.c +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -18,9 +18,9 @@ #define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6 static const struct i2c_device_id stpddc60_id[] = { - {"stpddc60"}, - {"bmr481"}, - {} + { .name = "stpddc60" }, + { .name = "bmr481" }, + { } }; MODULE_DEVICE_TABLE(i2c, stpddc60_id); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index d902d39f49f4..803926633370 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -195,8 +195,8 @@ static int tda38640_probe(struct i2c_client *client) } static const struct i2c_device_id tda38640_id[] = { - {"tda38640"}, - {} + { .name = "tda38640" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda38640_id); diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c index 05c6288ecafc..9d318e6509ab 100644 --- a/drivers/hwmon/pmbus/tps25990.c +++ b/drivers/hwmon/pmbus/tps25990.c @@ -387,8 +387,8 @@ static const struct pmbus_driver_info tps25990_base_info = { }; static const struct i2c_device_id tps25990_i2c_id[] = { - { "tps25990" }, - {} + { .name = "tps25990" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index 7c9fedaa068c..f7be075dc707 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -31,8 +31,8 @@ static int tps40422_probe(struct i2c_client *client) } static const struct i2c_device_id tps40422_id[] = { - {"tps40422"}, - {} + { .name = "tps40422" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps40422_id); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 94258e8cfd90..31e54608b3c9 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -291,15 +291,15 @@ static int tps53679_probe(struct i2c_client *client) } static const struct i2c_device_id tps53679_id[] = { - {"bmr474", tps53676}, - {"tps53647", tps53647}, - {"tps53667", tps53667}, - {"tps53676", tps53676}, - {"tps53679", tps53679}, - {"tps53681", tps53681}, - {"tps53685", tps53685}, - {"tps53688", tps53688}, - {} + { .name = "bmr474", .driver_data = tps53676 }, + { .name = "tps53647", .driver_data = tps53647 }, + { .name = "tps53667", .driver_data = tps53667 }, + { .name = "tps53676", .driver_data = tps53676 }, + { .name = "tps53679", .driver_data = tps53679 }, + { .name = "tps53681", .driver_data = tps53681 }, + { .name = "tps53685", .driver_data = tps53685 }, + { .name = "tps53688", .driver_data = tps53688 }, + { } }; MODULE_DEVICE_TABLE(i2c, tps53679_id); diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c index 44d7a6df1dbd..4222ff355e02 100644 --- a/drivers/hwmon/pmbus/tps546d24.c +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -42,8 +42,8 @@ static int tps546d24_probe(struct i2c_client *client) } static const struct i2c_device_id tps546d24_id[] = { - {"tps546d24"}, - {} + { .name = "tps546d24" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps546d24_id); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 9b5d34a110ba..f76c2913c9ca 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -143,14 +143,14 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) } static const struct i2c_device_id ucd9000_id[] = { - {"ucd9000", ucd9000}, - {"ucd90120", ucd90120}, - {"ucd90124", ucd90124}, - {"ucd90160", ucd90160}, - {"ucd90320", ucd90320}, - {"ucd9090", ucd9090}, - {"ucd90910", ucd90910}, - {} + { .name = "ucd9000", .driver_data = ucd9000 }, + { .name = "ucd90120", .driver_data = ucd90120 }, + { .name = "ucd90124", .driver_data = ucd90124 }, + { .name = "ucd90160", .driver_data = ucd90160 }, + { .name = "ucd90320", .driver_data = ucd90320 }, + { .name = "ucd9090", .driver_data = ucd9090 }, + { .name = "ucd90910", .driver_data = ucd90910 }, + { } }; MODULE_DEVICE_TABLE(i2c, ucd9000_id); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index f68adaf4a110..5e07bba111a1 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -22,15 +22,15 @@ enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246, ucd9248 }; static const struct i2c_device_id ucd9200_id[] = { - {"ucd9200", ucd9200}, - {"ucd9220", ucd9220}, - {"ucd9222", ucd9222}, - {"ucd9224", ucd9224}, - {"ucd9240", ucd9240}, - {"ucd9244", ucd9244}, - {"ucd9246", ucd9246}, - {"ucd9248", ucd9248}, - {} + { .name = "ucd9200", .driver_data = ucd9200 }, + { .name = "ucd9220", .driver_data = ucd9220 }, + { .name = "ucd9222", .driver_data = ucd9222 }, + { .name = "ucd9224", .driver_data = ucd9224 }, + { .name = "ucd9240", .driver_data = ucd9240 }, + { .name = "ucd9244", .driver_data = ucd9244 }, + { .name = "ucd9246", .driver_data = ucd9246 }, + { .name = "ucd9248", .driver_data = ucd9248 }, + { } }; MODULE_DEVICE_TABLE(i2c, ucd9200_id); diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c index 660bbfe16e1e..494dbe45ebc6 100644 --- a/drivers/hwmon/pmbus/xdp710.c +++ b/drivers/hwmon/pmbus/xdp710.c @@ -110,7 +110,7 @@ static const struct of_device_id xdp710_of_match[] = { }; static const struct i2c_device_id xdp710_id[] = { - {"xdp710"}, + { .name = "xdp710" }, { } }; MODULE_DEVICE_TABLE(i2c, xdp710_id); diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c index 8729a771f216..5a162084f338 100644 --- a/drivers/hwmon/pmbus/xdp720.c +++ b/drivers/hwmon/pmbus/xdp720.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Hardware monitoring driver for Infineon XDP720 Digital eFuse Controller + * Hardware monitoring driver for Infineon XDP720 / XDP730 Digital + * eFuse Controllers. + * + * Both parts share the same PMBus register map and direct-format + * coefficients; they differ in the GIMON gain step exposed via + * the TELEMETRY_AVG register and in the VDD_VIN pin number. * * Copyright (c) 2026 Infineon Technologies. All rights reserved. */ @@ -12,16 +17,30 @@ #include #include #include +#include +#include #include "pmbus.h" /* * The IMON resistor required to generate the system overcurrent protection. * Arbitrary default Rimon value: 2k Ohm */ -#define XDP720_DEFAULT_RIMON 2000000000 /* 2k ohm */ +#define XDP720_DEFAULT_RIMON 2000000000U /* 2k ohm */ #define XDP720_TELEMETRY_AVG 0xE9 +#define XDP720_TELEMETRY_AVG_GIMON BIT(10) /* high/low GIMON select */ -static struct pmbus_driver_info xdp720_info = { +/* Chip identifiers carried in OF match-data and i2c_device_id->driver_data. */ +enum xdp720_chip_id { + CHIP_XDP720 = 0, + CHIP_XDP730, +}; + +struct xdp720_data { + enum xdp720_chip_id id; + struct pmbus_driver_info info; +}; + +static const struct pmbus_driver_info xdp720_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = direct, .format[PSC_VOLTAGE_OUT] = direct, @@ -56,16 +75,18 @@ static struct pmbus_driver_info xdp720_info = { static int xdp720_probe(struct i2c_client *client) { - struct pmbus_driver_info *info; + struct xdp720_data *data; int ret; u32 rimon; int gimon; - info = devm_kmemdup(&client->dev, &xdp720_info, sizeof(*info), - GFP_KERNEL); - if (!info) + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; + data->id = (enum xdp720_chip_id)(uintptr_t)i2c_get_match_data(client); + data->info = xdp720_info; + ret = devm_regulator_get_enable(&client->dev, "vdd-vin"); if (ret) return dev_err_probe(&client->dev, ret, @@ -77,37 +98,48 @@ static int xdp720_probe(struct i2c_client *client) return ret; } - ret >>= 10; /* 10th bit of TELEMETRY_AVG REG for GIMON Value */ - ret &= GENMASK(0, 0); - if (ret == 1) - gimon = 18200; /* output gain 18.2 microA/A */ - else - gimon = 9100; /* output gain 9.1 microA/A */ + /* Bit 10 of TELEMETRY_AVG selects the GIMON gain step in microA/A */ + switch (data->id) { + case CHIP_XDP720: + gimon = (ret & XDP720_TELEMETRY_AVG_GIMON) ? 18200 : 9100; + dev_info(&client->dev, "Initialised XDP720 instance\n"); + break; + case CHIP_XDP730: + gimon = (ret & XDP720_TELEMETRY_AVG_GIMON) ? 20000 : 10000; + dev_info(&client->dev, "Initialised XDP730 instance\n"); + break; + default: + return -EINVAL; + } - if (of_property_read_u32(client->dev.of_node, - "infineon,rimon-micro-ohms", &rimon)) - rimon = XDP720_DEFAULT_RIMON; /* Default if not set via DT */ + if (device_property_read_u32(&client->dev, + "infineon,rimon-micro-ohms", &rimon)) + rimon = XDP720_DEFAULT_RIMON; /* Default if not in FW */ if (rimon == 0) return -EINVAL; /* Adapt the current and power scale for each instance */ - info->m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64) - info->m[PSC_CURRENT_OUT] * rimon * gimon, 1000000000000ULL); - info->m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64) - info->m[PSC_POWER] * rimon * gimon, 1000000000000000ULL); + data->info.m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64) + data->info.m[PSC_CURRENT_OUT] * rimon * gimon, + 1000000000000ULL); + data->info.m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64) + data->info.m[PSC_POWER] * rimon * gimon, + 1000000000000000ULL); - return pmbus_do_probe(client, info); + return pmbus_do_probe(client, &data->info); } static const struct of_device_id xdp720_of_match[] = { - { .compatible = "infineon,xdp720" }, + { .compatible = "infineon,xdp720", .data = (void *)CHIP_XDP720 }, + { .compatible = "infineon,xdp730", .data = (void *)CHIP_XDP730 }, {} }; MODULE_DEVICE_TABLE(of, xdp720_of_match); static const struct i2c_device_id xdp720_id[] = { - { "xdp720" }, - {} + { .name = "xdp720", .driver_data = CHIP_XDP720 }, + { .name = "xdp730", .driver_data = CHIP_XDP730 }, + { } }; MODULE_DEVICE_TABLE(i2c, xdp720_id); @@ -123,6 +155,6 @@ static struct i2c_driver xdp720_driver = { module_i2c_driver(xdp720_driver); MODULE_AUTHOR("Ashish Yadav "); -MODULE_DESCRIPTION("PMBus driver for Infineon XDP720 Digital eFuse Controller"); +MODULE_DESCRIPTION("PMBus driver for Infineon XDP720/XDP730 Digital eFuse Controllers"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index f3aa6339d60d..ed93e17ce8d5 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -164,10 +164,10 @@ static int xdpe122_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe122_id[] = { - {"xdpe11280"}, - {"xdpe12254"}, - {"xdpe12284"}, - {} + { .name = "xdpe11280" }, + { .name = "xdpe12254" }, + { .name = "xdpe12284" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe122_id); diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c index 67a3d5fe1daf..b557d2971d5f 100644 --- a/drivers/hwmon/pmbus/xdpe152c4.c +++ b/drivers/hwmon/pmbus/xdpe152c4.c @@ -44,9 +44,9 @@ static int xdpe152_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe152_id[] = { - {"xdpe152c4"}, - {"xdpe15284"}, - {} + { .name = "xdpe152c4" }, + { .name = "xdpe15284" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe152_id); diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c index 1755e3522ede..971e7b73752e 100644 --- a/drivers/hwmon/pmbus/xdpe1a2g7b.c +++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c @@ -87,9 +87,9 @@ static int xdpe1a2g7b_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe1a2g7b_id[] = { - { "xdpe1a2g5b" }, - { "xdpe1a2g7b" }, - {} + { .name = "xdpe1a2g5b" }, + { .name = "xdpe1a2g7b" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 97be69630cfb..5db2a7818d16 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -250,28 +250,28 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, } static const struct i2c_device_id zl6100_id[] = { - {"bmr450", zl2005}, - {"bmr451", zl2005}, - {"bmr462", zl2008}, - {"bmr463", zl2008}, - {"bmr464", zl2008}, - {"bmr465", zls4009}, - {"bmr466", zls1003}, - {"bmr467", zls4009}, - {"bmr469", zl8802}, - {"zl2004", zl2004}, - {"zl2005", zl2005}, - {"zl2006", zl2006}, - {"zl2008", zl2008}, - {"zl2105", zl2105}, - {"zl2106", zl2106}, - {"zl6100", zl6100}, - {"zl6105", zl6105}, - {"zl8802", zl8802}, - {"zl9101", zl9101}, - {"zl9117", zl9117}, - {"zls1003", zls1003}, - {"zls4009", zls4009}, + { .name = "bmr450", .driver_data = zl2005 }, + { .name = "bmr451", .driver_data = zl2005 }, + { .name = "bmr462", .driver_data = zl2008 }, + { .name = "bmr463", .driver_data = zl2008 }, + { .name = "bmr464", .driver_data = zl2008 }, + { .name = "bmr465", .driver_data = zls4009 }, + { .name = "bmr466", .driver_data = zls1003 }, + { .name = "bmr467", .driver_data = zls4009 }, + { .name = "bmr469", .driver_data = zl8802 }, + { .name = "zl2004", .driver_data = zl2004 }, + { .name = "zl2005", .driver_data = zl2005 }, + { .name = "zl2006", .driver_data = zl2006 }, + { .name = "zl2008", .driver_data = zl2008 }, + { .name = "zl2105", .driver_data = zl2105 }, + { .name = "zl2106", .driver_data = zl2106 }, + { .name = "zl6100", .driver_data = zl6100 }, + { .name = "zl6105", .driver_data = zl6105 }, + { .name = "zl8802", .driver_data = zl8802 }, + { .name = "zl9101", .driver_data = zl9101 }, + { .name = "zl9117", .driver_data = zl9117 }, + { .name = "zls1003", .driver_data = zls1003 }, + { .name = "zls4009", .driver_data = zls4009 }, { } }; MODULE_DEVICE_TABLE(i2c, zl6100_id); diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c index 06a2c56016d1..6e211601ea26 100644 --- a/drivers/hwmon/powr1220.c +++ b/drivers/hwmon/powr1220.c @@ -306,8 +306,8 @@ static int powr1220_probe(struct i2c_client *client) } static const struct i2c_device_id powr1220_ids[] = { - { "powr1014", powr1014, }, - { "powr1220", powr1220, }, + { .name = "powr1014", .driver_data = powr1014 }, + { .name = "powr1220", .driver_data = powr1220 }, { } }; diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c index 89d4da8aa4c0..2b408a69b085 100644 --- a/drivers/hwmon/pt5161l.c +++ b/drivers/hwmon/pt5161l.c @@ -617,8 +617,8 @@ static const struct acpi_device_id __maybe_unused pt5161l_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, pt5161l_acpi_match); static const struct i2c_device_id pt5161l_id[] = { - { "pt5161l" }, - {} + { .name = "pt5161l" }, + { } }; MODULE_DEVICE_TABLE(i2c, pt5161l_id); diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index a2938881ccd2..7b1d2829f012 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -5,9 +5,9 @@ * Based on firmware/raspberrypi.c by Noralf Trønnes * * Copyright (C) 2018 Stefan Wahren + * Copyright (C) 2026 Shubham Chakraborty */ #include -#include #include #include #include @@ -21,10 +21,18 @@ struct rpi_hwmon_data { struct device *hwmon_dev; struct rpi_firmware *fw; + u32 valid_inputs; u32 last_throttled; struct delayed_work get_values_poll_work; }; +static const char * const rpi_hwmon_labels[] = { + "core", + "sdram_c", + "sdram_i", + "sdram_p", +}; + static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) { u32 new_uv, old_uv, value; @@ -56,6 +64,22 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0); } +static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id, + long *val) +{ + struct rpi_firmware_get_voltage_request packet = + RPI_FIRMWARE_GET_VOLTAGE_REQUEST(id); + int ret; + + ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE, + &packet, sizeof(packet)); + if (ret) + return ret; + + *val = le32_to_cpu(packet.value) / 1000; + return 0; +} + static void get_values_poll(struct work_struct *work) { struct rpi_hwmon_data *data; @@ -72,24 +96,106 @@ static void get_values_poll(struct work_struct *work) schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); } +static void rpi_hwmon_cancel_poll_work(void *res) +{ + struct rpi_hwmon_data *data = res; + + disable_delayed_work_sync(&data->get_values_poll_work); +} + static int rpi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct rpi_hwmon_data *data = dev_get_drvdata(dev); - *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + switch (channel) { + case 0: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_CORE, + val); + case 1: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_C, + val); + case 2: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_I, + val); + case 3: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_P, + val); + default: + return -EOPNOTSUPP; + } + case hwmon_in_lcrit_alarm: + if (channel == 0) { + *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); + return 0; + } + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } + } + + return -EOPNOTSUPP; +} + +static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + if (type == hwmon_in && attr == hwmon_in_label) { + if (channel >= ARRAY_SIZE(rpi_hwmon_labels)) + return -EOPNOTSUPP; + + *str = rpi_hwmon_labels[channel]; + return 0; + } + + return -EOPNOTSUPP; +} + +static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct rpi_hwmon_data *data = _data; + + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + if (!(data->valid_inputs & BIT(channel))) + return 0; + return 0444; + case hwmon_in_lcrit_alarm: + if (channel == 0) + return 0444; + return 0; + default: + return 0; + } + } + return 0; } static const struct hwmon_channel_info * const rpi_info[] = { HWMON_CHANNEL_INFO(in, - HWMON_I_LCRIT_ALARM), + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), NULL }; static const struct hwmon_ops rpi_hwmon_ops = { - .visible = 0444, + .is_visible = rpi_is_visible, .read = rpi_read, + .read_string = rpi_read_string, }; static const struct hwmon_chip_info rpi_chip_info = { @@ -101,6 +207,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpi_hwmon_data *data; + long voltage; int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -110,6 +217,26 @@ static int rpi_hwmon_probe(struct platform_device *pdev) /* Parent driver assure that firmware is correct */ data->fw = dev_get_drvdata(dev->parent); + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_CORE, + &voltage); + if (!ret) + data->valid_inputs |= BIT(0); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_C, + &voltage); + if (!ret) + data->valid_inputs |= BIT(1); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_I, + &voltage); + if (!ret) + data->valid_inputs |= BIT(2); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_P, + &voltage); + if (!ret) + data->valid_inputs |= BIT(3); + data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt", data, &rpi_chip_info, @@ -117,8 +244,8 @@ static int rpi_hwmon_probe(struct platform_device *pdev) if (IS_ERR(data->hwmon_dev)) return PTR_ERR(data->hwmon_dev); - ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work, - get_values_poll); + INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll); + ret = devm_add_action_or_reset(dev, rpi_hwmon_cancel_poll_work, data); if (ret) return ret; platform_set_drvdata(pdev, data); @@ -159,6 +286,7 @@ static struct platform_driver rpi_hwmon_driver = { module_platform_driver(rpi_hwmon_driver); MODULE_AUTHOR("Stefan Wahren "); +MODULE_AUTHOR("Shubham Chakraborty "); MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:raspberrypi-hwmon"); diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index c5b2488c4c7f..c28f8625cd3a 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -221,8 +221,8 @@ static int sbtsi_probe(struct i2c_client *client) } static const struct i2c_device_id sbtsi_id[] = { - {"sbtsi"}, - {} + { .name = "sbtsi" }, + { } }; MODULE_DEVICE_TABLE(i2c, sbtsi_id); diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c index 105131c4acf7..591f5f572fe4 100644 --- a/drivers/hwmon/sg2042-mcu.c +++ b/drivers/hwmon/sg2042-mcu.c @@ -333,7 +333,7 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id sg2042_mcu_id[] = { - { "sg2042-hwmon-mcu" }, + { .name = "sg2042-hwmon-mcu" }, { } }; MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 3d55047e9baf..101cebbe68e4 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1036,11 +1036,11 @@ static void sht15_remove(struct platform_device *pdev) } static const struct platform_device_id sht15_device_ids[] = { - { "sht10", sht10 }, - { "sht11", sht11 }, - { "sht15", sht15 }, - { "sht71", sht71 }, - { "sht75", sht75 }, + { .name = "sht10", .driver_data = sht10 }, + { .name = "sht11", .driver_data = sht11 }, + { .name = "sht15", .driver_data = sht15 }, + { .name = "sht71", .driver_data = sht71 }, + { .name = "sht75", .driver_data = sht75 }, { } }; MODULE_DEVICE_TABLE(platform, sht15_device_ids); diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index 627d35070a42..085492669eeb 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -275,9 +275,9 @@ static int sht21_probe(struct i2c_client *client) /* Device ID table */ static const struct i2c_device_id sht21_id[] = { - { "sht20" }, - { "sht21" }, - { "sht25" }, + { .name = "sht20" }, + { .name = "sht21" }, + { .name = "sht25" }, { } }; MODULE_DEVICE_TABLE(i2c, sht21_id); diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 08306ccb6d0b..c2f6b73aa7f3 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -931,10 +931,10 @@ static int sht3x_probe(struct i2c_client *client) /* device ID table */ static const struct i2c_device_id sht3x_ids[] = { - {"sht3x", sht3x}, - {"sts3x", sts3x}, - {"sht85", sht3x}, - {} + { .name = "sht3x", .driver_data = sht3x }, + { .name = "sts3x", .driver_data = sts3x }, + { .name = "sht85", .driver_data = sht3x }, + { } }; MODULE_DEVICE_TABLE(i2c, sht3x_ids); diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index 5abe1227e109..9cace0e8acda 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -424,8 +424,8 @@ static int sht4x_probe(struct i2c_client *client) } static const struct i2c_device_id sht4x_id[] = { - { "sht4x" }, - { }, + { .name = "sht4x" }, + { } }; MODULE_DEVICE_TABLE(i2c, sht4x_id); diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index 2ac906e8e173..362a73cf661b 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -257,9 +257,9 @@ static int shtc1_probe(struct i2c_client *client) /* device ID table */ static const struct i2c_device_id shtc1_id[] = { - { "shtc1", shtc1 }, - { "shtw1", shtc1 }, - { "shtc3", shtc3 }, + { .name = "shtc1", .driver_data = shtc1 }, + { .name = "shtw1", .driver_data = shtc1 }, + { .name = "shtc3", .driver_data = shtc3 }, { } }; MODULE_DEVICE_TABLE(i2c, shtc1_id); diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 21103af4e139..1429b66e09b0 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -618,7 +618,7 @@ static int smsc47m192_probe(struct i2c_client *client) } static const struct i2c_device_id smsc47m192_id[] = { - { "smsc47m192" }, + { .name = "smsc47m192" }, { } }; MODULE_DEVICE_TABLE(i2c, smsc47m192_id); diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index 5da44571b6a0..cc40661cab21 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -746,7 +746,7 @@ static int spd5118_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id spd5118_i2c_id[] = { - { "spd5118" }, + { .name = "spd5118" }, { } }; MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id); diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index f9e8b2869164..ce23681f7981 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -72,7 +72,7 @@ static const int stts751_intervals[] = { }; static const struct i2c_device_id stts751_id[] = { - { "stts751" }, + { .name = "stts751" }, { } }; diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c index ba18b442b81e..4af9f0e2c0bc 100644 --- a/drivers/hwmon/tc654.c +++ b/drivers/hwmon/tc654.c @@ -551,9 +551,9 @@ static int tc654_probe(struct i2c_client *client) } static const struct i2c_device_id tc654_id[] = { - {"tc654"}, - {"tc655"}, - {} + { .name = "tc654" }, + { .name = "tc655" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc654_id); diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c index 7fb7b50ad1ad..e9113519be53 100644 --- a/drivers/hwmon/tc74.c +++ b/drivers/hwmon/tc74.c @@ -151,8 +151,8 @@ static int tc74_probe(struct i2c_client *client) } static const struct i2c_device_id tc74_id[] = { - { "tc74" }, - {} + { .name = "tc74" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc74_id); diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 0cbdb91698b1..b385ef64af75 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -407,8 +407,8 @@ static int thmc50_probe(struct i2c_client *client) } static const struct i2c_device_id thmc50_id[] = { - { "adm1022", adm1022 }, - { "thmc50", thmc50 }, + { .name = "adm1022", .driver_data = adm1022 }, + { .name = "thmc50", .driver_data = thmc50 }, { } }; MODULE_DEVICE_TABLE(i2c, thmc50_id); diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 3aa1a3fbeaa9..6bd1bed3cdb8 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -15,7 +15,8 @@ #include #include #include -#include +#include +#include #define DRIVER_NAME "tmp102" @@ -313,7 +314,7 @@ static int tmp102_probe(struct i2c_client *client) if (!tmp102) return -ENOMEM; - of_property_read_string(dev->of_node, "label", &tmp102->label); + device_property_read_string(dev, "label", &tmp102->label); i2c_set_clientdata(client, tmp102); @@ -395,12 +396,12 @@ static int tmp102_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); static const struct i2c_device_id tmp102_id[] = { - { "tmp102" }, + { .name = "tmp102" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp102_id); -static const struct of_device_id __maybe_unused tmp102_of_match[] = { +static const struct of_device_id tmp102_of_match[] = { { .compatible = "ti,tmp102" }, { }, }; @@ -408,7 +409,7 @@ MODULE_DEVICE_TABLE(of, tmp102_of_match); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, - .driver.of_match_table = of_match_ptr(tmp102_of_match), + .driver.of_match_table = tmp102_of_match, .driver.pm = pm_sleep_ptr(&tmp102_dev_pm_ops), .probe = tmp102_probe, .id_table = tmp102_id, diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c index 221bba8a215d..f13d24767575 100644 --- a/drivers/hwmon/tmp103.c +++ b/drivers/hwmon/tmp103.c @@ -194,7 +194,7 @@ static int tmp103_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); static const struct i2c_device_id tmp103_id[] = { - { "tmp103" }, + { .name = "tmp103" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp103_id); diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index 3ea5f6485744..1c4a58855e2d 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -537,10 +537,10 @@ static int tmp108_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { - { "p3t1035", (unsigned long)&p3t1035_data }, - { "p3t1085", (unsigned long)&tmp108_data }, - { "tmp108", (unsigned long)&tmp108_data }, - {} + { .name = "p3t1035", .driver_data = (unsigned long)&p3t1035_data }, + { .name = "p3t1085", .driver_data = (unsigned long)&tmp108_data }, + { .name = "tmp108", .driver_data = (unsigned long)&tmp108_data }, + { } }; MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 07f596581c6e..ca0ff525ea29 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -90,11 +90,11 @@ static const u8 TMP432_STATUS_REG[] = { */ static const struct i2c_device_id tmp401_id[] = { - { "tmp401", tmp401 }, - { "tmp411", tmp411 }, - { "tmp431", tmp431 }, - { "tmp432", tmp432 }, - { "tmp435", tmp435 }, + { .name = "tmp401", .driver_data = tmp401 }, + { .name = "tmp411", .driver_data = tmp411 }, + { .name = "tmp431", .driver_data = tmp431 }, + { .name = "tmp432", .driver_data = tmp432 }, + { .name = "tmp435", .driver_data = tmp435 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -560,7 +560,7 @@ static int tmp401_init_client(struct tmp401_data *data) return ret; } - ret = of_property_read_u32(data->client->dev.of_node, "ti,n-factor", &nfactor); + ret = of_property_read_s32(data->client->dev.of_node, "ti,n-factor", &nfactor); if (!ret) { if (data->kind == tmp401) { dev_err(&data->client->dev, "ti,tmp401 does not support n-factor correction\n"); diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 2ea9d3e9553d..ed00ccfdd7b3 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -56,11 +56,11 @@ static const u8 TMP421_TEMP_LSB[MAX_CHANNELS] = { 0x10, 0x11, 0x12, 0x13 }; #define TMP442_DEVICE_ID 0x42 static const struct i2c_device_id tmp421_id[] = { - { "tmp421", 2 }, - { "tmp422", 3 }, - { "tmp423", 4 }, - { "tmp441", 2 }, - { "tmp442", 3 }, + { .name = "tmp421", .driver_data = 2 }, + { .name = "tmp422", .driver_data = 3 }, + { .name = "tmp423", .driver_data = 4 }, + { .name = "tmp441", .driver_data = 2 }, + { .name = "tmp442", .driver_data = 3 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp421_id); diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index 98f2576d94c6..c3e031044d1e 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -65,8 +65,8 @@ static const u8 TMP464_THERM2_LIMIT[MAX_CHANNELS] = { #define TMP468_DEVICE_ID 0x0468 static const struct i2c_device_id tmp464_id[] = { - { "tmp464", TMP464_NUM_CHANNELS }, - { "tmp468", TMP468_NUM_CHANNELS }, + { .name = "tmp464", .driver_data = TMP464_NUM_CHANNELS }, + { .name = "tmp468", .driver_data = TMP468_NUM_CHANNELS }, { } }; MODULE_DEVICE_TABLE(i2c, tmp464_id); diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index 5acbfd7d088d..b160aa045f89 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -611,8 +611,8 @@ static int tmp51x_init(struct tmp51x_data *data) } static const struct i2c_device_id tmp51x_id[] = { - { "tmp512", TMP512_MAX_CHANNELS }, - { "tmp513", TMP513_MAX_CHANNELS }, + { .name = "tmp512", .driver_data = TMP512_MAX_CHANNELS }, + { .name = "tmp513", .driver_data = TMP513_MAX_CHANNELS }, { } }; MODULE_DEVICE_TABLE(i2c, tmp51x_id); diff --git a/drivers/hwmon/tsc1641.c b/drivers/hwmon/tsc1641.c index 2b5d34bab146..fc53cd5bb6e0 100644 --- a/drivers/hwmon/tsc1641.c +++ b/drivers/hwmon/tsc1641.c @@ -721,7 +721,7 @@ static int tsc1641_probe(struct i2c_client *client) } static const struct i2c_device_id tsc1641_id[] = { - { "tsc1641", 0 }, + { .name = "tsc1641" }, { } }; MODULE_DEVICE_TABLE(i2c, tsc1641_id); diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c index 401a28f55f93..54224dac10ae 100644 --- a/drivers/hwmon/w83773g.c +++ b/drivers/hwmon/w83773g.c @@ -34,7 +34,7 @@ static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 }; /* this is the number of sensors in the device */ static const struct i2c_device_id w83773_id[] = { - { "w83773g" }, + { .name = "w83773g" }, { } }; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index f664c2152a6d..c40f84e53469 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1559,10 +1559,10 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) } static const struct i2c_device_id w83781d_ids[] = { - { "w83781d", w83781d, }, - { "w83782d", w83782d, }, - { "w83783s", w83783s, }, - { "as99127f", as99127f }, + { .name = "w83781d", .driver_data = w83781d }, + { .name = "w83782d", .driver_data = w83782d }, + { .name = "w83783s", .driver_data = w83783s }, + { .name = "as99127f", .driver_data = as99127f }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, w83781d_ids); diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 996e36951f9d..4a777430af5c 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -333,7 +333,7 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev); static void w83791d_init_client(struct i2c_client *client); static const struct i2c_device_id w83791d_id[] = { - { "w83791d" }, + { .name = "w83791d" }, { } }; MODULE_DEVICE_TABLE(i2c, w83791d_id); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index b0b5f60eea53..f715c79389a5 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -296,7 +296,7 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev); static void w83792d_init_client(struct i2c_client *client); static const struct i2c_device_id w83792d_id[] = { - { "w83792d" }, + { .name = "w83792d" }, { } }; MODULE_DEVICE_TABLE(i2c, w83792d_id); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 24772cfbecb3..b1f906f06ab4 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -291,7 +291,7 @@ static void w83793_update_nonvolatile(struct device *dev); static struct w83793_data *w83793_update_device(struct device *dev); static const struct i2c_device_id w83793_id[] = { - { "w83793" }, + { .name = "w83793" }, { } }; MODULE_DEVICE_TABLE(i2c, w83793_id); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 5174db69db5e..c5ce0bf1b08e 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -2243,8 +2243,8 @@ static void w83795_remove(struct i2c_client *client) static const struct i2c_device_id w83795_id[] = { - { "w83795g", w83795g }, - { "w83795adg", w83795adg }, + { .name = "w83795g", .driver_data = w83795g }, + { .name = "w83795adg", .driver_data = w83795adg }, { } }; MODULE_DEVICE_TABLE(i2c, w83795_id); diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index df77b53a1b2f..e42506a3bcbe 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -74,7 +74,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); */ static const struct i2c_device_id w83l785ts_id[] = { - { "w83l785ts" }, + { .name = "w83l785ts" }, { } }; MODULE_DEVICE_TABLE(i2c, w83l785ts_id); diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 1d9109ca1585..a72397083cc4 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -751,7 +751,7 @@ w83l786ng_probe(struct i2c_client *client) } static const struct i2c_device_id w83l786ng_id[] = { - { "w83l786ng" }, + { .name = "w83l786ng" }, { } }; MODULE_DEVICE_TABLE(i2c, w83l786ng_id); diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index a578a10baff2..77a6f2bffcba 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -13,6 +13,7 @@ #define _HWMON_H_ #include +#include struct device; struct attribute_group; @@ -39,6 +40,7 @@ enum hwmon_chip_attributes { hwmon_chip_power_reset_history, hwmon_chip_register_tz, hwmon_chip_update_interval, + hwmon_chip_update_interval_us, hwmon_chip_alarms, hwmon_chip_samples, hwmon_chip_curr_samples, @@ -55,6 +57,7 @@ enum hwmon_chip_attributes { #define HWMON_C_POWER_RESET_HISTORY BIT(hwmon_chip_power_reset_history) #define HWMON_C_REGISTER_TZ BIT(hwmon_chip_register_tz) #define HWMON_C_UPDATE_INTERVAL BIT(hwmon_chip_update_interval) +#define HWMON_C_UPDATE_INTERVAL_US BIT(hwmon_chip_update_interval_us) #define HWMON_C_ALARMS BIT(hwmon_chip_alarms) #define HWMON_C_SAMPLES BIT(hwmon_chip_samples) #define HWMON_C_CURR_SAMPLES BIT(hwmon_chip_curr_samples) @@ -496,6 +499,8 @@ char *devm_hwmon_sanitize_name(struct device *dev, const char *name); void hwmon_lock(struct device *dev); void hwmon_unlock(struct device *dev); +DEFINE_GUARD(hwmon_lock, struct device *, hwmon_lock(_T), hwmon_unlock(_T)) + /** * hwmon_is_bad_char - Is the char invalid in a hwmon name * @ch: the char to be considered diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index e1f87fbfe554..975bef529854 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -156,6 +156,31 @@ enum rpi_firmware_clk_id { RPI_FIRMWARE_NUM_CLK_ID, }; +enum rpi_firmware_volt_id { + RPI_FIRMWARE_VOLT_ID_CORE = 1, + RPI_FIRMWARE_VOLT_ID_SDRAM_C = 2, + RPI_FIRMWARE_VOLT_ID_SDRAM_P = 3, + RPI_FIRMWARE_VOLT_ID_SDRAM_I = 4, + RPI_FIRMWARE_NUM_VOLT_ID, +}; + +/** + * struct rpi_firmware_get_voltage_request - Firmware request for a voltage + * @id: ID of the voltage being queried + * @value: Voltage in microvolts. Set by the firmware. + * + * Used by @RPI_FIRMWARE_GET_VOLTAGE. + */ +struct rpi_firmware_get_voltage_request { + __le32 id; + __le32 value; +} __packed; + +#define RPI_FIRMWARE_GET_VOLTAGE_REQUEST(_id) \ + { \ + .id = cpu_to_le32(_id), \ + } + /** * struct rpi_firmware_clk_rate_request - Firmware Request for a rate * @id: ID of the clock being queried