diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-16 05:40:21 +0300 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-16 05:40:21 +0300 |
| commit | fc2ce3ee106f2d53eb344f5c4963c897bbb21634 (patch) | |
| tree | 9477c5967a84f334d7c8a5d7d53ffd8b0679fdac | |
| parent | 7e54cdd0564f5393eb773b88cb49f55859e90474 (diff) | |
| parent | 9955c92abe72564a49c293b1c15cd3b4f02ea6a0 (diff) | |
| download | linux-fc2ce3ee106f2d53eb344f5c4963c897bbb21634.tar.xz | |
Merge tag 'hwmon-for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"New drivers for the following chips:
- Analog Devices LTC4283 Swap Controller
- Analog Devices MAX20830
- Analog Devices MAX20860A
- ARCTIC Fan Controller
- Delta E50SN12051
- Luxshare LX1308
- Microchip EMC1812/13/14/15/33
- Monolithic MP2985
- Murata D1U74T PSU
New chip support added to existing drivers:
- asus-ec-sensors: Support for ROG MAXIMUS Z790 EXTREME, ROG STRIX
B850-E GAMING WIFI, and ROG STRIX B650E-E GAMING WIFI
- dell-smm: Add Dell Latitude 7530 to fan control whitelist
- nct6683: Support for ASRock Z890 Pro-A
- pmbus: Support for Flex BMR316, BMR321, BMR350 and BMR351
- pmbus/max34440: Support for ADPM12250
- pmbus/xdp720: Support for Infineon xdp730, and fix driver issues
reported by Sashiko
New functionality:
- Add support for update_interval_us chip attribute, and support it
in ina238 driver
- Add support for guard() and scoped_guard() for subsystem locks, and
use it in adt7411, ina2xx, and lm90 drivers
- emc2305: Support configurable fan PWM at shutdown
- lm63: Expose PWM frequency and LUT hysteresis as writable
- lm75: Support active-high alert polarity
- nct7802: Add time step attributes for tweaking responsiveness
- pmbus/adm1266: Add rtc debugfs entries for rtc, powerup_counter,
clear_blackbox, and firmware_revision
- raspberrypi: Fix delayed-work teardown race, add voltage input
support as well as voltage domain IDs
- mcp9982: Add support for reporting external diode faults
Miscellaneous bug fixes, changes and improvements:
- Use named initializers for platform_device_id arrays and
i2c_device_data, and remove unused driver data
- Various drivers: Move MODULE_DEVICE_TABLE next to the table itself
- ads7871: Convert to hwmon_device_register_with_info(), and use
DMA-safe buffer for SPI writes
- adt7411: document supported sysfs attributes
- adt7462: Add of_match_table to support devicetree
- adt7475: Add explicit header include
- coretemp; Fix outdated documentation, coding style issues, and
replace hardcoded core count with dynamic value
- cros_ec: Drop unused assignment of platform_device_id driver data
- emc2305: Fix fan channel index handling
- gpd-fan: Reject EC PWM value 0 as invalid, fix race condition
between device removal and sysfs access, upgrade log level from
warn to err for platform device creation failure, initialize EC
before registering hwmon device, drop global driver data and use
per-device allocation
- htu31: document debugfs serial_number
- ina238: Add support for samples and update_interval
- it87: Clamp negative values to zero in set_fan()
- lm75: Add explicit header include, Add explicit default cases in
lm75_is_visible(), and add section for sysfs interface to
documentation
- pmbus/lm25066: Fix PMBus coefficients for LM5064/5066/5066i
- tmp102: Use device_property_read_string API
- tmp401: Read "ti,n-factor" as signed
- Convert zyxel,nsa320-mcu to DT schema"
* tag 'hwmon-for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (81 commits)
hwmon: tmp401: Read "ti,n-factor" as signed
hwmon: (pmbus/lm25066) Fix PMBus coefficients for LM5064/5066/5066i
hwmon: (gpd-fan) Reject EC PWM value 0 as invalid
hwmon: (dell-smm) Add Dell Latitude 7530 to fan control whitelist
hwmon: temperature: add support for EMC1812
dt-bindings: hwmon: temperature: add support for EMC1812
hwmon: (gpd-fan): fix race condition between device removal and sysfs access
hwmon: (gpd-fan): upgrade log level from warn to err for platform device creation failure
hwmon: (gpd-fan): Initialize EC before registering hwmon device
hwmon: (gpd-fan): drop global driver data and use per-device allocation
hwmon: (pmbus/max34440): add support adpm12250
hwmon: (ina238) Add update_interval_us attribute
hwmon: Add update_interval_us chip attribute
hwmon: (ina238) Add support for samples and update_interval
gpio: gpio-ltc4283: Add support for the LTC4283 Swap Controller
hwmon: ltc4283: Add support for the LTC4283 Swap Controller
dt-bindings: hwmon: Document the LTC4283 Swap Controller
hwmon: (pmbus/xdp720) Fix driver issues xdp720/730
hwmon: (pmbus/xdp720) Add support for efuse xdp730
dt-bindings: hwmon/pmbus: Add Infineon xdp730
...
260 files changed, 8132 insertions, 1093 deletions
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Ă¡ <nuno.sa@analog.com> + +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 <marius.cristea@microchip.com> + +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 <alexisczezar.torreno@analog.com> + +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 <psanman@juniper.net> + +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 <ashish.yadav@infineon.com> 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 <linux@baker-net.org.uk> + - Guenter Roeck <linux@roeck-us.net> + +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 <dt-bindings/gpio/gpio.h> + + 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 <aureo.serrano@arctic.de> + +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 <abdurrahman@nexthop.ai> + + +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 <marius.cristea@microchip.com> + + +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Ă¡ <nuno.sa@analog.com> + +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 <chiang.brian@inventec.com> + + +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 <alexisczezar.torreno@analog.com> + + +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 <arif.syed@hpe.com> + - Sanman Pradhan <psanman@juniper.net> + + +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 <wenswang@yeah.net> + +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 <aureo.serrano@arctic.de> +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 <arnd@arndb.de> M: Krzysztof Kozlowski <krzk@kernel.org> @@ -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Ă¡ <nuno.sa@analog.com> +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 <Delphine_CC_Chiu@Wiwynn.com> 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 <alexisczezar.torreno@analog.com> +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 <psanman@juniper.net> +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 <rashanmu@gmail.com> 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 <marius.cristea@microchip.com> +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 <codrin.ciubotariu@microchip.com> 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 <wenswang@yeah.net> +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 <noahwang.wang@outlook.com> 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 <abdurrahman@nexthop.ai> +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 <b-liu@ti.com> 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 <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/gpio/driver.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#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Ă¡ <nuno.sa@analog.com>"); +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 <linux/init.h> #include <linux/spi/spi.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/delay.h> @@ -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 <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> +#include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/log2.h> #include <linux/slab.h> @@ -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 <linux/err.h> #include <linux/jiffies.h> #include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/util_macros.h> #include <dt-bindings/pwm/pwm.h> @@ -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 <linux/completion.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/unaligned.h> + +#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 <aureo.serrano@arctic.de>"); +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 <linux/moduleparam.h> #include <linux/pci.h> #include <asm/msr.h> -#include <asm/processor.h> +#include <linux/processor.h> #include <asm/cpu_device_id.h> #include <linux/sched/isolation.h> @@ -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 <linux@weissschuh.net"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 47b373ea6db4..6bb8b8d759c7 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1575,6 +1575,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], }, { + .ident = "Dell Latitude 7530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7530"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], + }, + { .ident = "Dell Latitude E6440", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 3d4057309950..7e839308e58f 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2515,8 +2515,8 @@ static void dme1737_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id dme1737_id[] = { - { "dme1737", dme1737 }, - { "sch5027", sch5027 }, + { .name = "dme1737", .driver_data = dme1737 }, + { .name = "sch5027", .driver_data = sch5027 }, { } }; MODULE_DEVICE_TABLE(i2c, dme1737_id); diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 42ec34cb8a5f..0618f6de9679 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -367,11 +367,11 @@ static int ds1621_probe(struct i2c_client *client) } static const struct i2c_device_id ds1621_id[] = { - { "ds1621", ds1621 }, - { "ds1625", ds1625 }, - { "ds1631", ds1631 }, - { "ds1721", ds1721 }, - { "ds1731", ds1731 }, + { .name = "ds1621", .driver_data = ds1621 }, + { .name = "ds1625", .driver_data = ds1625 }, + { .name = "ds1631", .driver_data = ds1631 }, + { .name = "ds1721", .driver_data = ds1721 }, + { .name = "ds1731", .driver_data = ds1731 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1621_id); diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index ce397042d90b..25287f0fa4c2 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -233,8 +233,8 @@ static int ds620_probe(struct i2c_client *client) } static const struct i2c_device_id ds620_id[] = { - {"ds620"}, - {} + { .name = "ds620" }, + { } }; MODULE_DEVICE_TABLE(i2c, ds620_id); diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index 964a8cb278f1..cd753b38709f 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -639,18 +639,18 @@ static const struct hwmon_chip_info emc1403_chip_info = { /* Last digit of chip name indicates number of channels */ static const struct i2c_device_id emc1403_idtable[] = { - { "emc1402", emc1402 }, - { "emc1403", emc1403 }, - { "emc1404", emc1404 }, - { "emc1412", emc1402 }, - { "emc1413", emc1403 }, - { "emc1414", emc1404 }, - { "emc1422", emc1402 }, - { "emc1423", emc1403 }, - { "emc1424", emc1404 }, - { "emc1428", emc1428 }, - { "emc1438", emc1428 }, - { "emc1442", emc1402 }, + { .name = "emc1402", .driver_data = emc1402 }, + { .name = "emc1403", .driver_data = emc1403 }, + { .name = "emc1404", .driver_data = emc1404 }, + { .name = "emc1412", .driver_data = emc1402 }, + { .name = "emc1413", .driver_data = emc1403 }, + { .name = "emc1414", .driver_data = emc1404 }, + { .name = "emc1422", .driver_data = emc1402 }, + { .name = "emc1423", .driver_data = emc1403 }, + { .name = "emc1424", .driver_data = emc1404 }, + { .name = "emc1428", .driver_data = emc1428 }, + { .name = "emc1438", .driver_data = emc1428 }, + { .name = "emc1442", .driver_data = emc1402 }, { } }; MODULE_DEVICE_TABLE(i2c, emc1403_idtable); diff --git a/drivers/hwmon/emc1812.c b/drivers/hwmon/emc1812.c new file mode 100644 index 000000000000..68575c27d090 --- /dev/null +++ b/drivers/hwmon/emc1812.c @@ -0,0 +1,965 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HWMON driver for Microchip EMC1812/13/14/15/33 Multichannel high-accuracy + * 2-wire low-voltage remote diode temperature monitor family. + * + * Copyright (C) 2026 Microchip Technology Inc. and its subsidiaries + * + * Author: Marius Cristea <marius.cristea@microchip.com> + * + * 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 <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/string.h> +#include <linux/units.h> +#include <linux/util_macros.h> + +/* 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 <marius.cristea@microchip.com>"); +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 <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> +#include <linux/util_macros.h> /* 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, @@ -630,6 +718,47 @@ static ssize_t show_lut_temp_hyst(struct device *dev, } /* + * 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 <linux/i3c/device.h> #include <linux/hwmon.h> #include <linux/err.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/util_macros.h> #include <linux/regulator/consumer.h> @@ -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 <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/bits.h> + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/device/devres.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/math.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/module.h> + +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#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Ă¡ <nuno.sa@analog.com>"); +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: @@ -511,6 +518,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 <linux@schildmann.info>"); MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>"); 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 <linux/slab.h> #include <linux/timekeeping.h> +#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 <linux/i2c.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include <linux/string.h> + +#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 <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#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 <kevin.chang2@amd.com>"); +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}, - - {"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}, - - {"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 = "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 }, + + { .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 }, + + { .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, + .m = 5456, + .b = 2118, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 5456, - .b = 2118, + .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, + .m = 5405, + .b = -600, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 5405, - .b = -600, + .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, + .m = 7645, + .b = 100, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 7645, - .b = 100, + .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 <linux/err.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#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 <chiang.brian@inventec.com>"); +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 <linux/errno.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/string.h> +#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 <alexisczezar.torreno@analog.com>"); +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 <linux/i2c.h> +#include <linux/module.h> +#include <linux/regulator/driver.h> +#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 <arif.syed@hpe.com>"); +MODULE_AUTHOR("Sanman Pradhan <psanman@juniper.net>"); +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 <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#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 <wenswang@yeah.net>"); +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 <linux/of_device.h> #include <linux/bitops.h> #include <linux/math64.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> #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 */ + +/* 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 struct pmbus_driver_info xdp720_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); - - return pmbus_do_probe(client, info); + 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, &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 <ashish.yadav@infineon.com>"); -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 <stefan.wahren@i2se.com> + * Copyright (C) 2026 Shubham Chakraborty <chakrabortyshubham66@gmail.com> */ #include <linux/device.h> -#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/module.h> @@ -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 <wahrenst@gmx.net>"); +MODULE_AUTHOR("Shubham Chakraborty <chakrabortyshubham66@gmail.com>"); 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 <linux/jiffies.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #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 <linux/bitops.h> +#include <linux/cleanup.h> 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 |
