summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-16 00:37:32 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-16 00:37:32 +0300
commit46576fa32908043975471bd26fe833a7d8015b35 (patch)
tree8be9416e2c8445b7f9406725450f9121c9a5035e
parent405f6584d7d0fc46534fd370e374630283dffe60 (diff)
parentfb447217c59a13b2fff22d94de2498c185cd9032 (diff)
downloadlinux-46576fa32908043975471bd26fe833a7d8015b35.tar.xz
Merge tag 'hwmon-for-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New drivers: - Lenovo Yoga/Legion fan monitoring (yogafan) - LattePanda Sigma EC - Infineon XDP720 eFuse - Microchip MCP998X New device support: - TI INA234 - Infineon XDPE1A2G5B/7B - Renesas RAA228942 and RAA228943 (isl68137) - Delta Q54SN120A1 and Q54SW120A7 (pmbus) - TI TMP110 and TMP113 (tmp102) - Sony APS-379 (pmbus) - ITE IT8689E (it87) - ASUS ROG STRIX Z790-H, X470-F, and CROSSHAIR X670E (asus-ec-sensors) - GPD Win 5 (gpd-fan) Modernization and Cleanups: - Convert asus_atk0110 and acpi_power_meter ACPI drivers to platform drivers - Remove i2c_match_id() usage in many PMBus drivers - Use guard() for mutex protection in pmbus_core - Replace sprintf() with sysfs_emit() in ads7871, emc1403, max6650, ads7828, max31722, and tc74 - Various markup and documentation improvements for yogafan and ltc4282 Bug fixes: - Fix use-after-free and missing usb_kill_urb on disconnect in powerz driver - Avoid cacheline sharing for DMA buffer in powerz driver - Fix integer overflow in power calculation on 32-bit in isl28022 driver - Fix bugs in pt5161l_read_block_data() - Propagate SPI errors and fix incorrect error codes in ads7871 driver - Fix i2c_smbus_write_byte_data wrapper argument type in max31785 driver Device tree bindings: - Convert npcm750-pwm-fan to DT schema - Add bindings for Infineon XDP720, Microchip MCP998X, Sony APS-379, Renesas RAA228942/3, Delta Q54SN120A1/7, XDPE1A2G5B/7B, Aosong AHT10/20, DHT20, and TI INA234 - Adapt moortec,mr75203 bindings for T-Head TH1520" * tag 'hwmon-for-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (82 commits) hwmon: (ina233) Don't check for specific errors when parsing properties hwmon: (isl28022) Don't check for specific errors when parsing properties hwmon: (pmbus/tps25990) Don't check for specific errors when parsing properties hwmon: (nct6683) Add customer ID for ASRock B650I Lightning WiFi hwmon:(pmbus/xdp720) Add support for efuse xdp720 dt-bindings: hwmon/pmbus: Add Infineon XDP720 hwmon: add support for MCP998X dt-bindings: hwmon: add support for MCP998X hwmon: (powerz) Avoid cacheline sharing for DMA buffer hwmon: (isl28022) Fix integer overflow in power calculation on 32-bit hwmon: (pt5161l) Fix bugs in pt5161l_read_block_data() hwmon: (powerz) Fix missing usb_kill_urb() on signal interrupt hwmon: (powerz) Fix use-after-free on USB disconnect hwmon: pmbus: Add support for Sony APS-379 dt-bindings: trivial-devices: Add sony,aps-379 hwmon: (yogafan) various markup improvements hwmon: (sparx5) Make it selectable for ARCH_LAN969X hwmon: (tmp102) add support for update interval hwmon: (yogafan) fix markup warning hwmon: (yogafan) Add support for Lenovo Yoga/Legion fan monitoring ...
-rw-r--r--Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml105
-rw-r--r--Documentation/devicetree/bindings/hwmon/microchip,mcp9982.yaml237
-rw-r--r--Documentation/devicetree/bindings/hwmon/moortec,mr75203.yaml4
-rw-r--r--Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt88
-rw-r--r--Documentation/devicetree/bindings/hwmon/nuvoton,npcm750-pwm-fan.yaml139
-rw-r--r--Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml59
-rw-r--r--Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml93
-rw-r--r--Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml3
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml13
-rw-r--r--Documentation/hwmon/aps-379.rst57
-rw-r--r--Documentation/hwmon/asus_ec_sensors.rst3
-rw-r--r--Documentation/hwmon/bt1-pvt.rst117
-rw-r--r--Documentation/hwmon/ina2xx.rst25
-rw-r--r--Documentation/hwmon/index.rst5
-rw-r--r--Documentation/hwmon/isl68137.rst20
-rw-r--r--Documentation/hwmon/it87.rst26
-rw-r--r--Documentation/hwmon/lattepanda-sigma-ec.rst61
-rw-r--r--Documentation/hwmon/ltc4282.rst3
-rw-r--r--Documentation/hwmon/mcp9982.rst111
-rw-r--r--Documentation/hwmon/tmp102.rst40
-rw-r--r--Documentation/hwmon/yogafan.rst138
-rw-r--r--MAINTAINERS23
-rw-r--r--drivers/hwmon/Kconfig79
-rw-r--r--drivers/hwmon/Makefile4
-rw-r--r--drivers/hwmon/acpi_power_meter.c96
-rw-r--r--drivers/hwmon/ads7828.c4
-rw-r--r--drivers/hwmon/ads7871.c18
-rw-r--r--drivers/hwmon/aht10.c10
-rw-r--r--drivers/hwmon/aspeed-g6-pwm-tach.c8
-rw-r--r--drivers/hwmon/asus-ec-sensors.c29
-rw-r--r--drivers/hwmon/asus_atk0110.c92
-rw-r--r--drivers/hwmon/bt1-pvt.c1171
-rw-r--r--drivers/hwmon/bt1-pvt.h247
-rw-r--r--drivers/hwmon/emc1403.c2
-rw-r--r--drivers/hwmon/gpd-fan.c8
-rw-r--r--drivers/hwmon/hwmon.c1
-rw-r--r--drivers/hwmon/ina209.c11
-rw-r--r--drivers/hwmon/ina2xx.c76
-rw-r--r--drivers/hwmon/isl28022.c47
-rw-r--r--drivers/hwmon/it87.c61
-rw-r--r--drivers/hwmon/lattepanda-sigma-ec.c359
-rw-r--r--drivers/hwmon/lm75.c20
-rw-r--r--drivers/hwmon/ltc4282.c15
-rw-r--r--drivers/hwmon/max31722.c3
-rw-r--r--drivers/hwmon/max6650.c3
-rw-r--r--drivers/hwmon/mcp9982.c998
-rw-r--r--drivers/hwmon/nct6683.c3
-rw-r--r--drivers/hwmon/nct6775-platform.c23
-rw-r--r--drivers/hwmon/pmbus/Kconfig27
-rw-r--r--drivers/hwmon/pmbus/Makefile3
-rw-r--r--drivers/hwmon/pmbus/aps-379.c155
-rw-r--r--drivers/hwmon/pmbus/bel-pfe.c5
-rw-r--r--drivers/hwmon/pmbus/fsp-3y.c13
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c16
-rw-r--r--drivers/hwmon/pmbus/ina233.c28
-rw-r--r--drivers/hwmon/pmbus/isl68137.c52
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c2
-rw-r--r--drivers/hwmon/pmbus/max16601.c2
-rw-r--r--drivers/hwmon/pmbus/max20730.c5
-rw-r--r--drivers/hwmon/pmbus/max31785.c197
-rw-r--r--drivers/hwmon/pmbus/max34440.c4
-rw-r--r--drivers/hwmon/pmbus/pmbus.c4
-rw-r--r--drivers/hwmon/pmbus/pmbus.h13
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c298
-rw-r--r--drivers/hwmon/pmbus/q54sj108a2.c23
-rw-r--r--drivers/hwmon/pmbus/tps25990.c14
-rw-r--r--drivers/hwmon/pmbus/tps53679.c5
-rw-r--r--drivers/hwmon/pmbus/xdp720.c128
-rw-r--r--drivers/hwmon/pmbus/xdpe1a2g7b.c119
-rw-r--r--drivers/hwmon/powerz.c24
-rw-r--r--drivers/hwmon/pt5161l.c4
-rw-r--r--drivers/hwmon/tc74.c2
-rw-r--r--drivers/hwmon/tmp102.c128
-rw-r--r--drivers/hwmon/yogafan.c275
-rw-r--r--drivers/iio/adc/ina2xx-adc.c14
-rw-r--r--include/linux/platform_data/ina2xx.h16
76 files changed, 3856 insertions, 2478 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml b/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
deleted file mode 100644
index 5d3ce641fcde..000000000000
--- a/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
+++ /dev/null
@@ -1,105 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/hwmon/baikal,bt1-pvt.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Baikal-T1 PVT Sensor
-
-maintainers:
- - Serge Semin <fancer.lancer@gmail.com>
-
-description: |
- Baikal-T1 SoC provides an embedded process, voltage and temperature
- sensor to monitor an internal SoC environment (chip temperature, supply
- voltage and process monitor) and on time detect critical situations,
- which may cause the system instability and even damages. The IP-block
- is based on the Analog Bits PVT sensor, but is equipped with a dedicated
- control wrapper, which provides a MMIO registers-based access to the
- sensor core functionality (APB3-bus based) and exposes an additional
- functions like thresholds/data ready interrupts, its status and masks,
- measurements timeout. Its internal structure is depicted on the next
- diagram:
-
- Analog Bits core Bakal-T1 PVT control block
- +--------------------+ +------------------------+
- | Temperature sensor |-+ +------| Sensors control |
- |--------------------| |<---En---| |------------------------|
- | Voltage sensor |-|<--Mode--| +--->| Sampled data |
- |--------------------| |<--Trim--+ | |------------------------|
- | Low-Vt sensor |-| | +--| Thresholds comparator |
- |--------------------| |---Data----| | |------------------------|
- | High-Vt sensor |-| | +->| Interrupts status |
- |--------------------| |--Valid--+-+ | |------------------------|
- | Standard-Vt sensor |-+ +---+--| Interrupts mask |
- +--------------------+ |------------------------|
- ^ | Interrupts timeout |
- | +------------------------+
- | ^ ^
- Rclk-----+----------------------------------------+ |
- APB3-------------------------------------------------+
-
- This bindings describes the external Baikal-T1 PVT control interfaces
- like MMIO registers space, interrupt request number and clocks source.
- These are then used by the corresponding hwmon device driver to
- implement the sysfs files-based access to the sensors functionality.
-
-properties:
- compatible:
- const: baikal,bt1-pvt
-
- reg:
- maxItems: 1
-
- interrupts:
- maxItems: 1
-
- clocks:
- items:
- - description: PVT reference clock
- - description: APB3 interface clock
-
- clock-names:
- items:
- - const: ref
- - const: pclk
-
- "#thermal-sensor-cells":
- description: Baikal-T1 can be referenced as the CPU thermal-sensor
- const: 0
-
- baikal,pvt-temp-offset-millicelsius:
- description: |
- Temperature sensor trimming factor. It can be used to manually adjust the
- temperature measurements within 7.130 degrees Celsius.
- default: 0
- minimum: 0
- maximum: 7130
-
-additionalProperties: false
-
-required:
- - compatible
- - reg
- - interrupts
- - clocks
- - clock-names
-
-examples:
- - |
- #include <dt-bindings/interrupt-controller/mips-gic.h>
-
- pvt@1f200000 {
- compatible = "baikal,bt1-pvt";
- reg = <0x1f200000 0x1000>;
- #thermal-sensor-cells = <0>;
-
- interrupts = <GIC_SHARED 31 IRQ_TYPE_LEVEL_HIGH>;
-
- baikal,pvt-temp-offset-millicelsius = <1000>;
-
- clocks = <&ccu_sys>, <&ccu_sys>;
- clock-names = "ref", "pclk";
- };
-...
diff --git a/Documentation/devicetree/bindings/hwmon/microchip,mcp9982.yaml b/Documentation/devicetree/bindings/hwmon/microchip,mcp9982.yaml
new file mode 100644
index 000000000000..83dd2bf37e27
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/microchip,mcp9982.yaml
@@ -0,0 +1,237 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/microchip,mcp9982.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip MCP998X/33 and MCP998XD/33D Temperature Monitor
+
+maintainers:
+ - Victor Duicu <victor.duicu@microchip.com>
+
+description: |
+ The MCP998X/33 and MCP998XD/33D family is a high-accuracy 2-wire
+ multichannel automotive temperature monitor.
+ The datasheet can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP998X-Family-Data-Sheet-DS20006827.pdf
+
+properties:
+ compatible:
+ enum:
+ - microchip,mcp9933
+ - microchip,mcp9933d
+ - microchip,mcp9982
+ - microchip,mcp9982d
+ - microchip,mcp9983
+ - microchip,mcp9983d
+ - microchip,mcp9984
+ - microchip,mcp9984d
+ - microchip,mcp9985
+ - microchip,mcp9985d
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 1
+ maxItems: 2
+
+ interrupt-names:
+ description:
+ The chip family has three different interrupt pins divided among them.
+ The chips without "D" have alert-therm and therm-addr.
+ The chips with "D" have alert-therm and sys-shtdwn.
+ minItems: 1
+ items:
+ - enum: [alert-therm, therm-addr, sys-shtdwn]
+ - enum: [therm-addr, sys-shtdwn]
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ microchip,enable-anti-parallel:
+ description:
+ Enable anti-parallel diode mode operation.
+ MCP9984/84D/85/85D and MCP9933/33D support reading two external diodes
+ in anti-parallel connection on the same set of pins.
+ 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 anti-parallel diode connections, channels
+ 1 and 2 are affected together.
+ 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 anti-parallel diode connections, channels
+ 3 and 4 are affected together.
+ type: boolean
+
+ microchip,power-state:
+ description:
+ The chip can be set in Run state or Standby state. In Run state the ADC
+ is converting on all channels at the programmed conversion rate.
+ In Standby state the host must initiate a conversion cycle by writing
+ to the One-Shot register.
+ True value sets Run state.
+ Chips with "D" in the name can only be set in Run mode.
+ type: boolean
+
+ vdd-supply: true
+
+patternProperties:
+ "^channel@[1-4]$":
+ description:
+ Represents the external temperature channels to which
+ a remote diode is connected.
+ type: object
+
+ properties:
+ reg:
+ items:
+ maxItems: 1
+
+ label:
+ description: Unique name to identify which channel this is.
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp9982d
+ - microchip,mcp9983d
+ - microchip,mcp9984d
+ - microchip,mcp9985d
+ - microchip,mcp9933d
+ then:
+ properties:
+ interrupt-names:
+ items:
+ enum:
+ - alert-therm
+ - sys-shtdwn
+ required:
+ - microchip,power-state
+ - microchip,parasitic-res-on-channel1-2
+ else:
+ properties:
+ microchip,power-state: true
+ interrupt-names:
+ items:
+ enum:
+ - alert-therm
+ - therm-addr
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp9983d
+ - microchip,mcp9984d
+ - microchip,mcp9985d
+ then:
+ required:
+ - microchip,parasitic-res-on-channel3-4
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp9982
+ - microchip,mcp9982d
+ then:
+ properties:
+ microchip,enable-anti-parallel: false
+ patternProperties:
+ "^channel@[2-4]$": false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp9983
+ - microchip,mcp9983d
+ then:
+ properties:
+ microchip,enable-anti-parallel: false
+ patternProperties:
+ "^channel@[3-4]$": false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp9933
+ - microchip,mcp9933d
+ then:
+ patternProperties:
+ "^channel@[3-4]$": false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp9984
+ - microchip,mcp9984d
+ then:
+ properties:
+ channel@4: false
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ temperature-sensor@4c {
+ compatible = "microchip,mcp9985";
+ reg = <0x4c>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ microchip,enable-anti-parallel;
+ microchip,parasitic-res-on-channel1-2;
+ microchip,parasitic-res-on-channel3-4;
+ vdd-supply = <&vdd>;
+
+ channel@1 {
+ reg = <1>;
+ label = "Room Temperature";
+ };
+
+ channel@2 {
+ reg = <2>;
+ label = "GPU Temperature";
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/hwmon/moortec,mr75203.yaml b/Documentation/devicetree/bindings/hwmon/moortec,mr75203.yaml
index 56db2292f062..7d57c2934a8a 100644
--- a/Documentation/devicetree/bindings/hwmon/moortec,mr75203.yaml
+++ b/Documentation/devicetree/bindings/hwmon/moortec,mr75203.yaml
@@ -105,7 +105,7 @@ properties:
G coefficient for temperature equation.
Default for series 5 = 60000
Default for series 6 = 57400
- multipleOf: 100
+ multipleOf: 10
minimum: 1000
$ref: /schemas/types.yaml#/definitions/uint32
@@ -131,7 +131,7 @@ properties:
J coefficient for temperature equation.
Default for series 5 = -100
Default for series 6 = 0
- multipleOf: 100
+ multipleOf: 10
maximum: 0
$ref: /schemas/types.yaml#/definitions/int32
diff --git a/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
deleted file mode 100644
index 18095ba87a5a..000000000000
--- a/Documentation/devicetree/bindings/hwmon/npcm750-pwm-fan.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-Nuvoton NPCM PWM and Fan Tacho controller device
-
-The Nuvoton BMC NPCM7XX supports 8 Pulse-width modulation (PWM)
-controller outputs and 16 Fan tachometer controller inputs.
-
-The Nuvoton BMC NPCM8XX supports 12 Pulse-width modulation (PWM)
-controller outputs and 16 Fan tachometer controller inputs.
-
-Required properties for pwm-fan node
-- #address-cells : should be 1.
-- #size-cells : should be 0.
-- compatible : "nuvoton,npcm750-pwm-fan" for Poleg NPCM7XX.
- : "nuvoton,npcm845-pwm-fan" for Arbel NPCM8XX.
-- reg : specifies physical base address and size of the registers.
-- reg-names : must contain:
- * "pwm" for the PWM registers.
- * "fan" for the Fan registers.
-- clocks : phandle of reference clocks.
-- clock-names : must contain
- * "pwm" for PWM controller operating clock.
- * "fan" for Fan controller operating clock.
-- interrupts : contain the Fan interrupts with flags for falling edge.
-- pinctrl-names : a pinctrl state named "default" must be defined.
-- pinctrl-0 : phandle referencing pin configuration of the PWM and Fan
- controller ports.
-
-fan subnode format:
-===================
-Under fan subnode can be upto 8 child nodes, each child node representing a fan.
-Each fan subnode must have one PWM channel and at least one Fan tach channel.
-
-For PWM channel can be configured cooling-levels to create cooling device.
-Cooling device could be bound to a thermal zone for the thermal control.
-
-Required properties for each child node:
-- reg : specify the PWM output channel.
- integer value in the range 0 through 7, that represent
- the PWM channel number that used.
-
-- fan-tach-ch : specify the Fan tach input channel.
- integer value in the range 0 through 15, that represent
- the fan tach channel number that used.
-
- At least one Fan tach input channel is required
-
-Optional property for each child node:
-- cooling-levels: PWM duty cycle values in a range from 0 to 255
- which correspond to thermal cooling states.
-
-Examples:
-
-pwm_fan:pwm-fan-controller@103000 {
- #address-cells = <1>;
- #size-cells = <0>;
- compatible = "nuvoton,npcm750-pwm-fan";
- reg = <0x103000 0x2000>,
- <0x180000 0x8000>;
- reg-names = "pwm", "fan";
- clocks = <&clk NPCM7XX_CLK_APB3>,
- <&clk NPCM7XX_CLK_APB4>;
- clock-names = "pwm","fan";
- interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
- pinctrl-names = "default";
- pinctrl-0 = <&pwm0_pins &pwm1_pins &pwm2_pins
- &fanin0_pins &fanin1_pins &fanin2_pins
- &fanin3_pins &fanin4_pins>;
- fan@0 {
- reg = <0x00>;
- fan-tach-ch = /bits/ 8 <0x00 0x01>;
- cooling-levels = <127 255>;
- };
- fan@1 {
- reg = <0x01>;
- fan-tach-ch = /bits/ 8 <0x02 0x03>;
- };
- fan@2 {
- reg = <0x02>;
- fan-tach-ch = /bits/ 8 <0x04>;
- };
-
-};
diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,npcm750-pwm-fan.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,npcm750-pwm-fan.yaml
new file mode 100644
index 000000000000..73464af3078e
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/nuvoton,npcm750-pwm-fan.yaml
@@ -0,0 +1,139 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/nuvoton,npcm750-pwm-fan.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NPCM7xx/NPCM8xx PWM and Fan Tach Controller
+
+maintainers:
+ - Tomer Maimon <tmaimon77@gmail.com>
+
+description:
+ The NPCM7xx/NPCM8xx family includes a PWM and Fan Tachometer controller.
+ The controller provides up to 8 (NPCM7xx) or 12 (NPCM8xx) PWM channels and up
+ to 16 tachometer inputs. It is used for fan speed control and monitoring.
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,npcm750-pwm-fan
+ - nuvoton,npcm845-pwm-fan
+
+ reg:
+ maxItems: 2
+ description: Register addresses for PWM and Fan Tach units.
+
+ reg-names:
+ items:
+ - const: pwm
+ - const: fan
+
+ clocks:
+ maxItems: 2
+ description: Clocks for the PWM and Fan Tach modules.
+
+ clock-names:
+ items:
+ - const: pwm
+ - const: fan
+
+ interrupts:
+ description:
+ Contains the Fan interrupts with flags for falling edge.
+ For NPCM7XX, 8 interrupt lines are expected (one per PWM channel).
+ For NPCM8XX, 12 interrupt lines are expected (one per PWM channel).
+
+ minItems: 8
+ maxItems: 12
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^fan@[0-9a-f]+$":
+ type: object
+ $ref: fan-common.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description:
+ Specify the PWM output channel. Integer value in the range 0-7 for
+ NPCM7XX or 0-11 for NPCM8XX, representing the PWM channel number.
+
+ maximum: 11
+
+ fan-tach-ch:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ description:
+ The tach channel(s) used for the fan.
+ Integer values in the range 0-15.
+
+ items:
+ maximum: 15
+
+ cooling-levels:
+ description:
+ PWM duty cycle values in a range from 0 to 255 which
+ correspond to thermal cooling states. This property enables
+ thermal zone integration for automatic fan speed control
+ based on temperature.
+
+ items:
+ maximum: 255
+
+ required:
+ - reg
+ - fan-tach-ch
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ pwm_fan: pwm-fan@103000 {
+ compatible = "nuvoton,npcm750-pwm-fan";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg = <0x103000 0x2000>, <0x180000 0x8000>;
+ reg-names = "pwm", "fan";
+
+ clocks = <&clk NPCM7XX_CLK_APB3>, <&clk NPCM7XX_CLK_APB4>;
+ clock-names = "pwm", "fan";
+
+ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm0_pins &fanin0_pins>;
+
+ fan@0 {
+ reg = <0>;
+ fan-tach-ch = <0 1>;
+ cooling-levels = <64 128 192 255>;
+ };
+
+ fan@1 {
+ reg = <1>;
+ fan-tach-ch = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml
new file mode 100644
index 000000000000..72bc3a5e7139
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+
+$id: http://devicetree.org/schemas/hwmon/pmbus/infineon,xdp720.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Infineon XDP720 Digital eFuse Controller
+
+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.
+
+ Datasheet:
+ https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf
+
+properties:
+ compatible:
+ enum:
+ - infineon,xdp720
+
+ reg:
+ maxItems: 1
+
+ infineon,rimon-micro-ohms:
+ description:
+ The value of the RIMON resistor, in micro ohms, required to enable
+ the system overcurrent protection.
+
+ 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.
+
+required:
+ - compatible
+ - reg
+ - vdd-vin-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hwmon@11 {
+ compatible = "infineon,xdp720";
+ reg = <0x11>;
+ vdd-vin-supply = <&vdd_vin>;
+ infineon,rimon-micro-ohms = <1098000000>; /* 1.098k ohm */
+ };
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml
index ae23a05375cb..8216cdf758d8 100644
--- a/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml
@@ -16,49 +16,56 @@ description: |
properties:
compatible:
- enum:
- - isil,isl68137
- - renesas,isl68220
- - renesas,isl68221
- - renesas,isl68222
- - renesas,isl68223
- - renesas,isl68224
- - renesas,isl68225
- - renesas,isl68226
- - renesas,isl68227
- - renesas,isl68229
- - renesas,isl68233
- - renesas,isl68239
- - renesas,isl69222
- - renesas,isl69223
- - renesas,isl69224
- - renesas,isl69225
- - renesas,isl69227
- - renesas,isl69228
- - renesas,isl69234
- - renesas,isl69236
- - renesas,isl69239
- - renesas,isl69242
- - renesas,isl69243
- - renesas,isl69247
- - renesas,isl69248
- - renesas,isl69254
- - renesas,isl69255
- - renesas,isl69256
- - renesas,isl69259
- - isil,isl69260
- - renesas,isl69268
- - isil,isl69269
- - renesas,isl69298
- - renesas,raa228000
- - renesas,raa228004
- - renesas,raa228006
- - renesas,raa228228
- - renesas,raa228244
- - renesas,raa228246
- - renesas,raa229001
- - renesas,raa229004
- - renesas,raa229621
+ oneOf:
+ - enum:
+ - isil,isl68137
+ - renesas,isl68220
+ - renesas,isl68221
+ - renesas,isl68222
+ - renesas,isl68223
+ - renesas,isl68224
+ - renesas,isl68225
+ - renesas,isl68226
+ - renesas,isl68227
+ - renesas,isl68229
+ - renesas,isl68233
+ - renesas,isl68239
+ - renesas,isl69222
+ - renesas,isl69223
+ - renesas,isl69224
+ - renesas,isl69225
+ - renesas,isl69227
+ - renesas,isl69228
+ - renesas,isl69234
+ - renesas,isl69236
+ - renesas,isl69239
+ - renesas,isl69242
+ - renesas,isl69243
+ - renesas,isl69247
+ - renesas,isl69248
+ - renesas,isl69254
+ - renesas,isl69255
+ - renesas,isl69256
+ - renesas,isl69259
+ - isil,isl69260
+ - renesas,isl69268
+ - isil,isl69269
+ - renesas,isl69298
+ - renesas,raa228000
+ - renesas,raa228004
+ - renesas,raa228006
+ - renesas,raa228228
+ - renesas,raa228244
+ - renesas,raa228246
+ - renesas,raa229001
+ - renesas,raa229004
+ - renesas,raa229621
+
+ - items:
+ - enum:
+ - renesas,raa228942
+ - renesas,raa228943
+ - const: renesas,raa228244
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
index d3cde8936686..009d78b30859 100644
--- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
+++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
@@ -29,6 +29,7 @@ properties:
- ti,ina230
- ti,ina231
- ti,ina233
+ - ti,ina234
- ti,ina237
- ti,ina238
- ti,ina260
@@ -113,6 +114,7 @@ allOf:
- ti,ina228
- ti,ina230
- ti,ina231
+ - ti,ina234
- ti,ina237
- ti,ina238
- ti,ina260
@@ -134,6 +136,7 @@ allOf:
- ti,ina226
- ti,ina230
- ti,ina231
+ - ti,ina234
- ti,ina260
- ti,ina700
- ti,ina780
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index a482aeadcd44..23fd4513933a 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -59,6 +59,10 @@ properties:
- adi,lt7182s
# AMS iAQ-Core VOC Sensor
- ams,iaq-core
+ # Aosong temperature & humidity sensors with I2C interface
+ - aosong,aht10
+ - aosong,aht20
+ - aosong,dht20
# Arduino microcontroller interface over SPI on UnoQ board
- arduino,unoq-mcu
# Temperature monitoring of Astera Labs PT5161L PCIe retimer
@@ -97,6 +101,10 @@ properties:
- delta,dps920ab
# 1/4 Brick DC/DC Regulated Power Module
- delta,q54sj108a2
+ # 1300W 1/4 Brick DC/DC Regulated Power Module
+ - delta,q54sn120a1
+ # 2000W 1/4 Brick DC/DC Regulated Power Module
+ - delta,q54sw120a7
# Devantech SRF02 ultrasonic ranger in I2C mode
- devantech,srf02
# Devantech SRF08 ultrasonic ranger
@@ -157,6 +165,9 @@ properties:
- infineon,xdpe15284
# Infineon Multi-phase Digital VR Controller xdpe152c4
- infineon,xdpe152c4
+ # Infineon Multi-phase Digital VR Controller xdpe1a2g7b
+ - infineon,xdpe1a2g5b
+ - infineon,xdpe1a2g7b
# Injoinic IP5108 2.0A Power Bank IC with I2C
- injoinic,ip5108
# Injoinic IP5109 2.1A Power Bank IC with I2C
@@ -430,6 +441,8 @@ properties:
- smsc,emc6d103s
# Socionext Uniphier SMP control registers
- socionext,uniphier-smpctrl
+ # Sony APS-379 Power Supply
+ - sony,aps-379
# SparkFun Qwiic Joystick (COM-15168) with i2c interface
- sparkfun,qwiic-joystick
# STMicroelectronics Hot-swap controller stef48h28
diff --git a/Documentation/hwmon/aps-379.rst b/Documentation/hwmon/aps-379.rst
new file mode 100644
index 000000000000..6d4e63283e34
--- /dev/null
+++ b/Documentation/hwmon/aps-379.rst
@@ -0,0 +1,57 @@
+Kernel driver aps-379
+=====================
+
+Supported chips:
+
+ * Sony APS-379
+
+ Prefix: 'aps-379'
+
+ Addresses scanned: -
+
+ Authors:
+ - Chris Packham
+
+Description
+-----------
+
+This driver implements support for the PMBus monitor on the Sony APS-379
+modular power supply. The APS-379 deviates from the PMBus standard for the
+READ_VOUT command by using the linear11 format instead of linear16.
+
+The known supported PMBus commands are:
+
+=== ============================= ========= ======= =====
+Cmd Function Protocol Scaling Bytes
+=== ============================= ========= ======= =====
+01 On / Off Command (OPERATION) Byte R/W -- 1
+10 WRITE_PROTECT Byte R/W -- 1
+3B FAN_COMMAND_1 Word R/W -- 2
+46 Current Limit (in percent) Word R/W 2^0 2
+47 Current Limit Fault Response Byte R/W -- 1
+79 Alarm Data Bits (STATUS_WORD) Word Rd -- 2
+8B Output Voltage (READ_VOUT) Word Rd 2^-4 2
+8C Output Current (READ_IOUT) Word Rd 2^-2 2
+8D Power Supply Ambient Temp Word Rd 2^0 2
+90 READ_FAN_SPEED_1 Word Rd 2^6 2
+91 READ_FAN_SPEED_2 Word Rd 2^6 2
+96 Output Wattage (READ_POUT) Word Rd 2^1 2
+97 Input Wattage (READ_PIN) Word Rd 2^1 2
+9A Unit Model Number (MFR_MODEL) Block R/W -- 10
+9B Unit Revision Number Block R/W -- 10
+9E Unit Serial Number Block R/W -- 8
+99 Unit Manufacturer ID (MFR_ID) Block R/W -- 8
+D0 Unit Run Time Information Block Rd -- 4
+D5 Firmware Version Rd cust -- 8
+B0 User Data 1 (USER_DATA_00) Block R/W -- 4
+B1 User Data 2 (USER_DATA_01) Block R/W -- 4
+B2 User Data 3 (USER_DATA_02) Block R/W -- 4
+B3 User Data 4 (USER_DATA_03) Block R/W -- 4
+B4 User Data 5 (USER_DATA_04) Block R/W -- 4
+B5 User Data 6 (USER_DATA_05) Block R/W -- 4
+B6 User Data 7 (USER_DATA_06) Block R/W -- 4
+B7 User Data 8 (USER_DATA_07) Block R/W -- 4
+F0 Calibration command Byte R/W -- 1
+F1 Calibration data Word Wr 2^9 2
+F2 Unlock Calibration Byte Wr -- 1
+=== ============================= ========= ======= =====
diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst
index 58986546c723..9ad3f0a57f55 100644
--- a/Documentation/hwmon/asus_ec_sensors.rst
+++ b/Documentation/hwmon/asus_ec_sensors.rst
@@ -22,6 +22,7 @@ Supported boards:
* ROG CROSSHAIR VIII FORMULA
* ROG CROSSHAIR VIII HERO
* ROG CROSSHAIR VIII IMPACT
+ * ROG CROSSHAIR X670E EXTREME
* ROG CROSSHAIR X670E HERO
* ROG CROSSHAIR X670E GENE
* ROG MAXIMUS X HERO
@@ -32,6 +33,7 @@ Supported boards:
* ROG STRIX B550-I GAMING
* ROG STRIX B650E-I GAMING WIFI
* ROG STRIX B850-I GAMING WIFI
+ * ROG STRIX X470-F GAMING
* ROG STRIX X470-I GAMING
* ROG STRIX X570-E GAMING
* ROG STRIX X570-E GAMING WIFI II
@@ -48,6 +50,7 @@ Supported boards:
* ROG STRIX Z690-A GAMING WIFI D4
* ROG STRIX Z690-E GAMING WIFI
* ROG STRIX Z790-E GAMING WIFI II
+ * ROG STRIX Z790-H GAMING WIFI
* ROG STRIX Z790-I GAMING WIFI
* ROG ZENITH II EXTREME
* ROG ZENITH II EXTREME ALPHA
diff --git a/Documentation/hwmon/bt1-pvt.rst b/Documentation/hwmon/bt1-pvt.rst
deleted file mode 100644
index cbb0c0613132..000000000000
--- a/Documentation/hwmon/bt1-pvt.rst
+++ /dev/null
@@ -1,117 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0-only
-
-Kernel driver bt1-pvt
-=====================
-
-Supported chips:
-
- * Baikal-T1 PVT sensor (in SoC)
-
- Prefix: 'bt1-pvt'
-
- Addresses scanned: -
-
- Datasheet: Provided by BAIKAL ELECTRONICS upon request and under NDA
-
-Authors:
- Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
- Serge Semin <Sergey.Semin@baikalelectronics.ru>
-
-Description
------------
-
-This driver implements support for the hardware monitoring capabilities of the
-embedded into Baikal-T1 process, voltage and temperature sensors. PVT IP-core
-consists of one temperature and four voltage sensors, which can be used to
-monitor the chip internal environment like heating, supply voltage and
-transistors performance. The driver can optionally provide the hwmon alarms
-for each sensor the PVT controller supports. The alarms functionality is made
-compile-time configurable due to the hardware interface implementation
-peculiarity, which is connected with an ability to convert data from only one
-sensor at a time. Additional limitation is that the controller performs the
-thresholds checking synchronously with the data conversion procedure. Due to
-these in order to have the hwmon alarms automatically detected the driver code
-must switch from one sensor to another, read converted data and manually check
-the threshold status bits. Depending on the measurements timeout settings
-(update_interval sysfs node value) this design may cause additional burden on
-the system performance. So in case if alarms are unnecessary in your system
-design it's recommended to have them disabled to prevent the PVT IRQs being
-periodically raised to get the data cache/alarms status up to date. By default
-in alarm-less configuration the data conversion is performed by the driver
-on demand when read operation is requested via corresponding _input-file.
-
-Temperature Monitoring
-----------------------
-
-Temperature is measured with 10-bit resolution and reported in millidegree
-Celsius. The driver performs all the scaling by itself therefore reports true
-temperatures that don't need any user-space adjustments. While the data
-translation formulae isn't linear, which gives us non-linear discreteness,
-it's close to one, but giving a bit better accuracy for higher temperatures.
-The temperature input is mapped as follows (the last column indicates the input
-ranges)::
-
- temp1: CPU embedded diode -48.38C - +147.438C
-
-In case if the alarms kernel config is enabled in the driver the temperature input
-has associated min and max limits which trigger an alarm when crossed.
-
-Voltage Monitoring
-------------------
-
-The voltage inputs are also sampled with 10-bit resolution and reported in
-millivolts. But in this case the data translation formulae is linear, which
-provides a constant measurements discreteness. The data scaling is also
-performed by the driver, so returning true millivolts. The voltage inputs are
-mapped as follows (the last column indicates the input ranges)::
-
- in0: VDD (processor core) 0.62V - 1.168V
- in1: Low-Vt (low voltage threshold) 0.62V - 1.168V
- in2: High-Vt (high voltage threshold) 0.62V - 1.168V
- in3: Standard-Vt (standard voltage threshold) 0.62V - 1.168V
-
-In case if the alarms config is enabled in the driver the voltage inputs
-have associated min and max limits which trigger an alarm when crossed.
-
-Sysfs Attributes
-----------------
-
-Following is a list of all sysfs attributes that the driver provides, their
-permissions and a short description:
-
-=============================== ======= =======================================
-Name Perm Description
-=============================== ======= =======================================
-update_interval RW Measurements update interval per
- sensor.
-temp1_type RO Sensor type (always 1 as CPU embedded
- diode).
-temp1_label RO CPU Core Temperature sensor.
-temp1_input RO Measured temperature in millidegree
- Celsius.
-temp1_min RW Low limit for temp input.
-temp1_max RW High limit for temp input.
-temp1_min_alarm RO Temperature input alarm. Returns 1 if
- temperature input went below min limit,
- 0 otherwise.
-temp1_max_alarm RO Temperature input alarm. Returns 1 if
- temperature input went above max limit,
- 0 otherwise.
-temp1_offset RW Temperature offset in millidegree
- Celsius which is added to the
- temperature reading by the chip. It can
- be used to manually adjust the
- temperature measurements within 7.130
- degrees Celsius.
-in[0-3]_label RO CPU Voltage sensor (either core or
- low/high/standard thresholds).
-in[0-3]_input RO Measured voltage in millivolts.
-in[0-3]_min RW Low limit for voltage input.
-in[0-3]_max RW High limit for voltage input.
-in[0-3]_min_alarm RO Voltage input alarm. Returns 1 if
- voltage input went below min limit,
- 0 otherwise.
-in[0-3]_max_alarm RO Voltage input alarm. Returns 1 if
- voltage input went above max limit,
- 0 otherwise.
-=============================== ======= =======================================
diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
index a3860aae444c..d64e7af46a12 100644
--- a/Documentation/hwmon/ina2xx.rst
+++ b/Documentation/hwmon/ina2xx.rst
@@ -74,6 +74,16 @@ Supported chips:
https://us1.silergy.com/
+ * Texas Instruments INA234
+
+ Prefix: 'ina234'
+
+ Addresses: I2C 0x40 - 0x43
+
+ Datasheet: Publicly available at the Texas Instruments website
+
+ https://www.ti.com/
+
Author: Lothar Felten <lothar.felten@gmail.com>
Description
@@ -89,7 +99,7 @@ interface. The INA220 monitors both shunt drop and supply voltage.
The INA226 is a current shunt and power monitor with an I2C interface.
The INA226 monitors both a shunt voltage drop and bus supply voltage.
-INA230 and INA231 are high or low side current shunt and power monitors
+INA230, INA231, and INA234 are high or low side current shunt and power monitors
with an I2C interface. The chips monitor both a shunt voltage drop and
bus supply voltage.
@@ -124,8 +134,17 @@ power1_input Power(uW) measurement channel
shunt_resistor Shunt resistance(uOhm) channel (not for ina260)
======================= ===============================================
-Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655
-------------------------------------------------------------------------
+Additional sysfs entries
+------------------------
+
+Additional entries are available for the following chips:
+
+ * ina226
+ * ina230
+ * ina231
+ * ina234
+ * ina260
+ * sy24655
======================= ====================================================
curr1_lcrit Critical low current
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index b2ca8513cfcd..8b655e5d6b68 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -41,6 +41,7 @@ Hardware Monitoring Kernel Drivers
adt7475
aht10
amc6821
+ aps-379
aquacomputer_d5next
asb100
asc7621
@@ -52,7 +53,6 @@ Hardware Monitoring Kernel Drivers
bcm54140
bel-pfe
bpa-rs600
- bt1-pvt
cgbc-hwmon
chipcap2
coretemp
@@ -111,6 +111,7 @@ Hardware Monitoring Kernel Drivers
kbatt
kfan
lan966x
+ lattepanda-sigma-ec
lineage-pem
lm25066
lm63
@@ -174,6 +175,7 @@ Hardware Monitoring Kernel Drivers
mc33xs2410_hwmon
mc34vr500
mcp3021
+ mcp9982
menf21bmc
mlxreg-fan
mp2856
@@ -282,4 +284,5 @@ Hardware Monitoring Kernel Drivers
xdp710
xdpe12284
xdpe152c4
+ yogafan
zl6100
diff --git a/Documentation/hwmon/isl68137.rst b/Documentation/hwmon/isl68137.rst
index e77f582c2850..0ce20d09164f 100644
--- a/Documentation/hwmon/isl68137.rst
+++ b/Documentation/hwmon/isl68137.rst
@@ -394,6 +394,26 @@ Supported chips:
Provided by Renesas upon request and NDA
+ * Renesas RAA228942
+
+ Prefix: 'raa228942'
+
+ Addresses scanned: -
+
+ Datasheet:
+
+ Provided by Renesas upon request and NDA
+
+ * Renesas RAA228943
+
+ Prefix: 'raa228943'
+
+ Addresses scanned: -
+
+ Datasheet:
+
+ Provided by Renesas upon request and NDA
+
* Renesas RAA229001
Prefix: 'raa229001'
diff --git a/Documentation/hwmon/it87.rst b/Documentation/hwmon/it87.rst
index 5cef4f265000..fc1c90b023ae 100644
--- a/Documentation/hwmon/it87.rst
+++ b/Documentation/hwmon/it87.rst
@@ -25,6 +25,14 @@ Supported chips:
Datasheet: Not publicly available
+ * IT8689E
+
+ Prefix: 'it8689'
+
+ Addresses scanned: from Super I/O config space (8 I/O ports)
+
+ Datasheet: Not publicly available
+
* IT8705F
Prefix: 'it87'
@@ -228,9 +236,9 @@ Description
-----------
This driver implements support for the IT8603E, IT8620E, IT8623E, IT8628E,
-IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F,
-IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E,
-IT8792E/IT8795E, IT87952E and SiS950 chips.
+IT8689E, IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F,
+IT8728F, IT8732F, IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F,
+IT8786E, IT8790E, IT8792E/IT8795E, IT87952E and SiS950 chips.
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
joysticks and other miscellaneous stuff. For hardware monitoring, they
@@ -274,6 +282,9 @@ of the fan is not supported (value 0 of pwmX_enable).
The IT8620E and IT8628E are custom designs, hardware monitoring part is similar
to IT8728F. It only supports 16-bit fan mode. Both chips support up to 6 fans.
+The IT8689E supports newer autopwm, 12mV ADC, 16-bit fans, six fans, six PWM
+channels, PWM frequency 2, six temperature inputs, and AVCC3 (in9).
+
The IT8790E, IT8792E/IT8795E and IT87952E support up to 3 fans. 16-bit fan
mode is always enabled.
@@ -301,12 +312,15 @@ of 0.016 volt. IT8603E, IT8721F/IT8758E and IT8728F can measure between 0 and
2.8 volts with a resolution of 0.0109 volt. The battery voltage in8 does not
have limit registers.
-On the IT8603E, IT8620E, IT8628E, IT8721F/IT8758E, IT8732F, IT8781F, IT8782F,
-and IT8783E/F, some voltage inputs are internal and scaled inside the chip:
+On the IT8603E, IT8620E, IT8628E, IT8689E, IT8721F/IT8758E, IT8732F, IT8781F,
+IT8782F, and IT8783E/F, some voltage inputs are internal and scaled inside the
+chip:
+
* in3 (optional)
* in7 (optional for IT8781F, IT8782F, and IT8783E/F)
* in8 (always)
-* in9 (relevant for IT8603E only)
+* in9 (IT8603E, IT8622E, and IT8689E: always AVCC3; others: optional)
+
The driver handles this transparently so user-space doesn't have to care.
The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value:
diff --git a/Documentation/hwmon/lattepanda-sigma-ec.rst b/Documentation/hwmon/lattepanda-sigma-ec.rst
new file mode 100644
index 000000000000..8a521ee1fef1
--- /dev/null
+++ b/Documentation/hwmon/lattepanda-sigma-ec.rst
@@ -0,0 +1,61 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver lattepanda-sigma-ec
+=================================
+
+Supported systems:
+
+ * LattePanda Sigma (Intel 13th Gen i5-1340P)
+
+ DMI vendor: LattePanda
+
+ DMI product: LattePanda Sigma
+
+ BIOS version: 5.27 (verified)
+
+ Datasheet: Not available (EC registers discovered empirically)
+
+Author: Mariano Abad <weimaraner@gmail.com>
+
+Description
+-----------
+
+This driver provides hardware monitoring for the LattePanda Sigma
+single-board computer made by DFRobot. The board uses an ITE IT8613E
+Embedded Controller to manage a CPU cooling fan and thermal sensors.
+
+The BIOS declares the ACPI Embedded Controller (``PNP0C09``) with
+``_STA`` returning 0, preventing the kernel's ACPI EC subsystem from
+initializing. This driver reads the EC directly via the standard ACPI
+EC I/O ports (``0x62`` data, ``0x66`` command/status).
+
+Sysfs attributes
+----------------
+
+======================= ===============================================
+``fan1_input`` Fan speed in RPM (EC registers 0x2E:0x2F,
+ 16-bit big-endian)
+``fan1_label`` "CPU Fan"
+``temp1_input`` Board/ambient temperature in millidegrees
+ Celsius (EC register 0x60, unsigned)
+``temp1_label`` "Board Temp"
+``temp2_input`` CPU proximity temperature in millidegrees
+ Celsius (EC register 0x70, unsigned)
+``temp2_label`` "CPU Temp"
+======================= ===============================================
+
+Module parameters
+-----------------
+
+``force`` (bool, default false)
+ Force loading on BIOS versions other than 5.27. The driver still
+ requires DMI vendor and product name matching.
+
+Known limitations
+-----------------
+
+* Fan speed control is not supported. The fan is always under EC
+ automatic control.
+* The EC register map was verified only on BIOS version 5.27.
+ Other versions may use different register offsets; use the ``force``
+ parameter at your own risk.
diff --git a/Documentation/hwmon/ltc4282.rst b/Documentation/hwmon/ltc4282.rst
index a87ec3564998..dd730207b141 100644
--- a/Documentation/hwmon/ltc4282.rst
+++ b/Documentation/hwmon/ltc4282.rst
@@ -9,8 +9,7 @@ Supported chips:
Prefix: 'ltc4282'
- Addresses scanned: - I2C 0x40 - 0x5A (7-bit)
- Addresses scanned: - I2C 0x80 - 0xB4 with a step of 2 (8-bit)
+ Addresses scanned: -
Datasheet:
diff --git a/Documentation/hwmon/mcp9982.rst b/Documentation/hwmon/mcp9982.rst
new file mode 100644
index 000000000000..790ee1697b45
--- /dev/null
+++ b/Documentation/hwmon/mcp9982.rst
@@ -0,0 +1,111 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Kernel driver MCP998X
+=====================
+
+Supported chips:
+
+ * Microchip Technology MCP998X/MCP9933 and MCP998XD/MCP9933D
+
+ Prefix: 'mcp9982'
+
+ Datasheet:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP998X-Family-Data-Sheet-DS20006827.pdf
+
+Authors:
+
+ - Victor Duicu <victor.duicu@microchip.com>
+
+Description
+-----------
+
+This driver implements support for the MCP998X family containing: MCP9982,
+MCP9982D, MCP9983, MCP9983D, MCP9984, MCP9984D, MCP9985, MCP9985D,
+MCP9933 and MCP9933D.
+
+The MCP998X Family is a high accuracy 2-wire multichannel automotive
+temperature monitor.
+
+The chips in the family have different numbers of external channels,
+ranging from 1 (MCP9982) to 4 channels (MCP9985). Reading diodes in
+anti-parallel connection is supported by MCP9984/85/33 and
+MCP9984D/85D/33D. Dedicated hardware shutdown circuitry is present
+only in MCP998XD and MCP9933D.
+
+Temperatures are read in millidegrees Celsius, ranging from -64 to
+191.875 with 0.125 precision.
+
+Each channel has a minimum, maximum, and critical limit alongside associated alarms.
+The chips also implement a hysteresis mechanism which applies only to the maximum
+and critical limits. The relative difference between a limit and its hysteresis
+is the same for both and the value is kept in a single register.
+
+The chips measure temperatures with a variable conversion rate.
+Update_interval = Conversion/Second, so the available options are:
+- 16000 (ms) = 1 conv/16 sec
+- 8000 (ms) = 1 conv/8 sec
+- 4000 (ms) = 1 conv/4 sec
+- 2000 (ms) = 1 conv/2 sec
+- 1000 (ms) = 1 conv/sec
+- 500 (ms) = 2 conv/sec
+- 250 (ms) = 4 conv/sec
+- 125 (ms) = 8 conv/sec
+- 64 (ms) = 16 conv/sec
+- 32 (ms) = 32 conv/sec
+- 16 (ms) = 64 conv/sec
+
+Usage Notes
+-----------
+
+Parameters that can be configured in devicetree:
+- anti-parallel diode mode operation
+- resistance error correction on channels 1 and 2
+- resistance error correction on channels 3 and 4
+- power state
+
+Chips 82/83 and 82D/83D do not support anti-parallel diode mode.
+For chips with "D" in the name resistance error correction must be on.
+Please see Documentation/devicetree/bindings/hwmon/microchip,mcp9982.yaml
+for details.
+
+There are two power states:
+- Active state: in which the chip is converting on all channels at the
+programmed rate.
+
+- Standby state: in which the host must initiate a conversion cycle.
+
+Chips with "D" in the name work in Active state only and those without
+can work in either state.
+
+Chips with "D" in the name can't set update interval slower than 1 second.
+
+Among the hysteresis attributes, only the tempX_crit_hyst ones are writeable
+while the others are read only. Setting tempX_crit_hyst writes the difference
+between tempX_crit and tempX_crit_hyst in the hysteresis register. The new value
+applies automatically to the other limits. At power up the device starts with
+a 10 degree hysteresis.
+
+Sysfs entries
+-------------
+
+The following attributes are supported. The temperature limits and
+update_interval are read-write. The attribute tempX_crit_hyst is read-write,
+while tempX_max_hyst is read only. All other attributes are read only.
+
+======================= ==================================================
+temp[1-5]_label User name for channel.
+temp[1-5]_input Measured temperature for channel.
+
+temp[1-5]_crit Critical temperature limit.
+temp[1-5]_crit_alarm Critical temperature limit alarm.
+temp[1-5]_crit_hyst Critical temperature limit hysteresis.
+
+temp[1-5]_max High temperature limit.
+temp[1-5]_max_alarm High temperature limit alarm.
+temp[1-5]_max_hyst High temperature limit hysteresis.
+
+temp[1-5]_min Low temperature limit.
+temp[1-5]_min_alarm Low temperature limit alarm.
+
+update_interval The interval at which the chip will update readings.
+======================= ==================================================
diff --git a/Documentation/hwmon/tmp102.rst b/Documentation/hwmon/tmp102.rst
index b1f585531a88..425a09a3c9b3 100644
--- a/Documentation/hwmon/tmp102.rst
+++ b/Documentation/hwmon/tmp102.rst
@@ -11,6 +11,22 @@ Supported chips:
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
+ * Texas Instruments TMP110
+
+ Prefix: 'tmp110'
+
+ Addresses scanned: none
+
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp110.html
+
+ * Texas Instruments TMP113
+
+ Prefix: 'tmp113'
+
+ Addresses scanned: none
+
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp113.html
+
Author:
Steven King <sfking@fdwdc.com>
@@ -25,7 +41,25 @@ degree from -40 to +125 C. Resolution of the sensor is 0.0625 degree. The
operating temperature has a minimum of -55 C and a maximum of +150 C.
The TMP102 has a programmable update rate that can select between 8, 4, 1, and
-0.5 Hz. (Currently the driver only supports the default of 4 Hz).
+0.25 Hz.
+
+The TMP110 and TMP113 are software compatible with TMP102, but have different
+accuracy (maximum error) specifications. The TMP110 has an accuracy (maximum error)
+of 1.0 degree, TMP113 has an accuracy (maximum error) of 0.3 degree, while TMP102
+has an accuracy (maximum error) of 2.0 degree.
+
+sysfs-Interface
+---------------
+
+The following list includes the sysfs attributes that the driver provides, their
+permissions and a short description:
-The driver provides the common sysfs-interface for temperatures (see
-Documentation/hwmon/sysfs-interface.rst under Temperatures).
+=============================== ======= ===========================================
+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/yogafan.rst b/Documentation/hwmon/yogafan.rst
new file mode 100644
index 000000000000..c553a381f772
--- /dev/null
+++ b/Documentation/hwmon/yogafan.rst
@@ -0,0 +1,138 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+===============================================================================================
+Kernel driver yogafan
+===============================================================================================
+
+Supported chips:
+
+ * Lenovo Yoga, Legion, IdeaPad, Slim, Flex, and LOQ Embedded Controllers
+ * Prefix: 'yogafan'
+ * Addresses: ACPI handle (See Database Below)
+
+Author: Sergio Melas <sergiomelas@gmail.com>
+
+Description
+-----------
+
+This driver provides fan speed monitoring for modern Lenovo consumer laptops.
+Most Lenovo laptops do not provide fan tachometer data through standard
+ISA/LPC hardware monitoring chips. Instead, the data is stored in the
+Embedded Controller (EC) and exposed via ACPI.
+
+The driver implements a **Rate-Limited Lag (RLLag)** filter to handle
+the low-resolution and jittery sampling found in Lenovo EC firmware.
+
+Hardware Identification and Multiplier Logic
+--------------------------------------------
+
+The driver supports two distinct EC architectures. Differentiation is handled
+deterministically via a DMI Product Family quirk table during the probe phase,
+eliminating the need for runtime heuristics.
+
+1. 8-bit EC Architecture (Multiplier: 100)
+
+ - **Families:** Yoga, IdeaPad, Slim, Flex.
+ - **Technical Detail:** These models allocate a single 8-bit register for
+ tachometer data. Since 8-bit fields are limited to a value of 255, the
+ BIOS stores fan speed in units of 100 RPM (e.g., 42 = 4200 RPM).
+
+2. 16-bit EC Architecture (Multiplier: 1)
+
+ - **Families:** Legion, LOQ.
+ - **Technical Detail:** High-performance gaming models require greater
+ precision for fans exceeding 6000 RPM. These use a 16-bit word (2 bytes)
+ storing the raw RPM value directly.
+
+Filter Details
+--------------
+
+The RLLag filter is a passive discrete-time first-order lag model that ensures:
+ - **Smoothing:** Low-resolution step increments are smoothed into 1-RPM increments.
+ - **Slew-Rate Limiting:** Prevents unrealistic readings by capping the change
+ to 1500 RPM/s, matching physical fan inertia.
+ - **Polling Independence:** The filter math scales based on the time delta
+ between userspace reads, ensuring a consistent physical curve regardless
+ of polling frequency.
+
+Suspend and Resume
+------------------
+
+The driver utilizes the boottime clock (ktime_get_boottime()) to calculate the
+sampling delta. This ensures that time spent in system suspend is accounted
+for. If the delta exceeds 5 seconds (e.g., after waking the laptop), the
+filter automatically resets to the current hardware value to prevent
+reporting "ghost" RPM data from before the sleep state.
+
+Usage
+-----
+
+The driver exposes standard hwmon sysfs attributes:
+
+=============== ============================
+Attribute Description
+fanX_input Filtered fan speed in RPM.
+=============== ============================
+
+
+Note: If the hardware reports 0 RPM, the filter is bypassed and 0 is reported
+immediately to ensure the user knows the fan has stopped.
+
+
+====================================================================================================
+ LENOVO FAN CONTROLLER: MASTER REFERENCE DATABASE (2026)
+====================================================================================================
+
+::
+
+ MODEL (DMI PN) | FAMILY / SERIES | EC OFFSET | FULL ACPI OBJECT PATH | WIDTH | MULTiplier
+ ----------------------------------------------------------------------------------------------------
+ 82N7 | Yoga 14cACN | 0x06 | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
+ 80V2 / 81C3 | Yoga 710/720 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
+ 83E2 / 83DN | Yoga Pro 7/9 | 0xFE | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
+ 82A2 / 82A3 | Yoga Slim 7 | 0x06 | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
+ 81YM / 82FG | IdeaPad 5 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
+ 82JW / 82JU | Legion 5 (AMD) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS (Fan1) | 16-bit | 1
+ 82JW / 82JU | Legion 5 (AMD) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FA2S (Fan2) | 16-bit | 1
+ 82WQ | Legion 7i (Int) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS (Fan1) | 16-bit | 1
+ 82WQ | Legion 7i (Int) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FA2S (Fan2) | 16-bit | 1
+ 82XV / 83DV | LOQ 15/16 | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS /FA2S | 16-bit | 1
+ 83AK | ThinkBook G6 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
+ 81X1 | Flex 5 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
+ *Legacy* | Pre-2020 Models | 0x06 | \_SB.PCI0.LPC.EC.FAN0 | 8-bit | 100
+ ----------------------------------------------------------------------------------------------------
+
+METHODOLOGY & IDENTIFICATION:
+
+1. DSDT ANALYSIS (THE PATH):
+ BIOS ACPI tables were analyzed using 'iasl' and cross-referenced with
+ public dumps. Internal labels (FANS, FAN0, FA2S) are mapped to
+ EmbeddedControl OperationRegion offsets.
+
+2. EC MEMORY MAPPING (THE OFFSET):
+ Validated by matching NBFC (NoteBook FanControl) XML logic with DSDT Field
+ definitions found in BIOS firmware.
+
+3. DATA-WIDTH ANALYSIS (THE MULTIPLIER):
+ - 8-bit (Multiplier 100): Standard for Yoga/IdeaPad. Raw values (0-255).
+ - 16-bit (Multiplier 1): Standard for Legion/LOQ. Two registers (0xFE/0xFF).
+
+
+References
+----------
+
+1. **ACPI Specification (Field Objects):** Documentation on how 8-bit vs 16-bit
+ fields are accessed in OperationRegions.
+ https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#field-objects
+
+2. **NBFC Projects:** Community-driven reverse engineering
+ of Lenovo Legion/LOQ EC memory maps (16-bit raw registers).
+ https://github.com/hirschmann/nbfc/tree/master/Configs
+
+3. **Linux Kernel Timekeeping API:** Documentation for ktime_get_boottime() and
+ handling deltas across suspend states.
+ https://www.kernel.org/doc/html/latest/core-api/timekeeping.html
+
+4. **Lenovo IdeaPad Laptop Driver:** Reference for DMI-based hardware
+ feature gating in Lenovo laptops.
+ https://github.com/torvalds/linux/blob/master/drivers/platform/x86/ideapad-laptop.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 17b03d555a0d..ad5fba213a32 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14498,6 +14498,13 @@ F: drivers/net/wan/framer/
F: drivers/pinctrl/pinctrl-pef2256.c
F: include/linux/framer/
+LATTEPANDA SIGMA EC HARDWARE MONITOR DRIVER
+M: Mariano Abad <weimaraner@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/lattepanda-sigma-ec.rst
+F: drivers/hwmon/lattepanda-sigma-ec.c
+
LASI 53c700 driver for PARISC
M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
L: linux-scsi@vger.kernel.org
@@ -14947,6 +14954,14 @@ W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: drivers/media/usb/dvb-usb-v2/lmedm04*
+LENOVO YOGA FAN DRIVER
+M: Sergio Melas <sergiomelas@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+W: https://github.com/sergiomelas
+F: Documentation/hwmon/yogafan.rst
+F: drivers/hwmon/yogafan.c
+
LOADPIN SECURITY MODULE
M: Kees Cook <kees@kernel.org>
S: Supported
@@ -17426,6 +17441,14 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml
F: drivers/iio/adc/mcp3911.c
+MICROCHIP MCP9982 TEMPERATURE DRIVER
+M: Victor Duicu <victor.duicu@microchip.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/hwmon/microchip,mcp9982.yaml
+F: Documentation/hwmon/mcp9982.rst
+F: drivers/hwmon/mcp9982.c
+
MICROCHIP MMC/SD/SDIO MCI DRIVER
M: Aubin Constans <aubin.constans@microchip.com>
S: Maintained
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 328867242cb3..14e4cea48acc 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -457,32 +457,6 @@ config SENSORS_ATXP1
This driver can also be built as a module. If so, the module
will be called atxp1.
-config SENSORS_BT1_PVT
- tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
- depends on MIPS_BAIKAL_T1 || COMPILE_TEST
- select POLYNOMIAL
- help
- If you say yes here you get support for Baikal-T1 PVT sensor
- embedded into the SoC.
-
- This driver can also be built as a module. If so, the module will be
- called bt1-pvt.
-
-config SENSORS_BT1_PVT_ALARMS
- bool "Enable Baikal-T1 PVT sensor alarms"
- depends on SENSORS_BT1_PVT
- help
- Baikal-T1 PVT IP-block provides threshold registers for each
- supported sensor. But the corresponding interrupts might be
- generated by the thresholds comparator only in synchronization with
- a data conversion. Additionally there is only one sensor data can
- be converted at a time. All of these makes the interface impossible
- to be used for the hwmon alarms implementation without periodic
- switch between the PVT sensors. By default the data conversion is
- performed on demand from the user-space. If this config is enabled
- the data conversion will be periodically performed and the data will be
- saved in the internal driver cache.
-
config SENSORS_CGBC
tristate "Congatec Board Controller Sensors"
depends on MFD_CGBC
@@ -632,7 +606,7 @@ config SENSORS_I5K_AMB
config SENSORS_SPARX5
tristate "Sparx5 SoC temperature sensor"
- depends on ARCH_SPARX5 || COMPILE_TEST
+ depends on ARCH_SPARX5 || ARCH_LAN969X || COMPILE_TEST
help
If you say yes here you get support for temperature monitoring
with the Microchip Sparx5 SoC.
@@ -801,7 +775,6 @@ config SENSORS_G762
config SENSORS_GPIO_FAN
tristate "GPIO fan"
- depends on OF_GPIO
depends on GPIOLIB || COMPILE_TEST
depends on THERMAL || THERMAL=n
help
@@ -936,8 +909,8 @@ config SENSORS_IT87
If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F,
IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F, IT8758E,
IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E,
- IT8603E, IT8620E, IT8623E, and IT8628E sensor chips, and the SiS950
- clone.
+ IT8603E, IT8620E, IT8623E, IT8628E, and IT8689E sensor chips, and
+ the SiS950 clone.
This driver can also be built as a module. If so, the module
will be called it87.
@@ -990,6 +963,23 @@ config SENSORS_LAN966X
This driver can also be built as a module. If so, the module
will be called lan966x-hwmon.
+config SENSORS_LATTEPANDA_SIGMA_EC
+ tristate "LattePanda Sigma EC hardware monitoring"
+ depends on X86
+ depends on DMI
+ depends on HAS_IOPORT
+ help
+ If you say yes here you get support for the hardware monitoring
+ features of the Embedded Controller on LattePanda Sigma
+ single-board computers, including CPU fan speed (RPM) and
+ board and CPU temperatures.
+
+ The driver reads the EC directly via ACPI EC I/O ports and
+ uses DMI matching to ensure it only loads on supported hardware.
+
+ This driver can also be built as a module. If so, the module
+ will be called lattepanda-sigma-ec.
+
config SENSORS_LENOVO_EC
tristate "Sensor reader for Lenovo ThinkStations"
depends on X86
@@ -1388,6 +1378,17 @@ config SENSORS_MCP3021
This driver can also be built as a module. If so, the module
will be called mcp3021.
+config SENSORS_MCP9982
+ tristate "Microchip Technology MCP9982 driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to include support for Microchip Technology's MCP998X/33
+ and MCP998XD/33D Multichannel Automotive Temperature Monitor Family.
+
+ This driver can also be built as a module. If so, the module
+ will be called mcp9982.
+
config SENSORS_MLXREG_FAN
tristate "Mellanox FAN driver"
depends on MELLANOX_PLATFORM
@@ -2273,7 +2274,7 @@ config SENSORS_INA2XX
select REGMAP_I2C
help
If you say yes here you get support for INA219, INA220, INA226,
- INA230, INA231, INA260, and SY24655 power monitor chips.
+ INA230, INA231, INA234, INA260, and SY24655 power monitor chips.
The INA2xx driver is configured for the default configuration of
the part as described in the datasheet.
@@ -2362,8 +2363,8 @@ config SENSORS_TMP102
depends on I2C
select REGMAP_I2C
help
- If you say yes here you get support for Texas Instruments TMP102
- sensor chips.
+ If you say yes here you get support for Texas Instruments TMP102,
+ TMP110 and TMP113 sensor chips.
This driver can also be built as a module. If so, the module
will be called tmp102.
@@ -2661,6 +2662,18 @@ config SENSORS_XGENE
If you say yes here you get support for the temperature
and power sensors for APM X-Gene SoC.
+config SENSORS_YOGAFAN
+ tristate "Lenovo Yoga Fan Hardware Monitoring"
+ depends on ACPI && HWMON && DMI
+ help
+ If you say yes here you get support for fan speed monitoring
+ on Lenovo Yoga, Legion, IdeaPad, Slim and LOQ laptops.
+ The driver interfaces with the Embedded Controller via ACPI
+ and uses a Rate-Limited Lag filter to smooth RPM readings.
+
+ This driver can also be built as a module. If so, the module
+ will be called yogafan.
+
config SENSORS_INTEL_M10_BMC_HWMON
tristate "Intel MAX10 BMC Hardware Monitoring"
depends on MFD_INTEL_M10_BMC_CORE
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5833c807c688..4788996aa137 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -58,7 +58,6 @@ obj-$(CONFIG_SENSORS_ASPEED_G6) += aspeed-g6-pwm-tach.o
obj-$(CONFIG_SENSORS_ASUS_ROG_RYUJIN) += asus_rog_ryujin.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
-obj-$(CONFIG_SENSORS_BT1_PVT) += bt1-pvt.o
obj-$(CONFIG_SENSORS_CGBC) += cgbc-hwmon.o
obj-$(CONFIG_SENSORS_CHIPCAP2) += chipcap2.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
@@ -114,6 +113,7 @@ obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
obj-$(CONFIG_SENSORS_KBATT) += kbatt.o
obj-$(CONFIG_SENSORS_KFAN) += kfan.o
obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
+obj-$(CONFIG_SENSORS_LATTEPANDA_SIGMA_EC) += lattepanda-sigma-ec.o
obj-$(CONFIG_SENSORS_LENOVO_EC) += lenovo-ec-sensors.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
obj-$(CONFIG_SENSORS_LOCHNAGAR) += lochnagar-hwmon.o
@@ -170,6 +170,7 @@ obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o
obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
+obj-$(CONFIG_SENSORS_MCP9982) += mcp9982.o
obj-$(CONFIG_SENSORS_TC654) += tc654.o
obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o
obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
@@ -245,6 +246,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
+obj-$(CONFIG_SENSORS_YOGAFAN) += yogafan.o
obj-$(CONFIG_SENSORS_OCC) += occ/
obj-$(CONFIG_SENSORS_PECI) += peci/
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 1e3fab5f7946..be7f702dcde9 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -18,6 +18,7 @@
#include <linux/time.h>
#include <linux/err.h>
#include <linux/acpi.h>
+#include <linux/platform_device.h>
#define ACPI_POWER_METER_NAME "power_meter"
#define ACPI_POWER_METER_DEVICE_NAME "Power Meter"
@@ -814,16 +815,12 @@ end:
}
/* Handle ACPI event notifications */
-static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
+static void acpi_power_meter_notify(acpi_handle handle, u32 event, void *data)
{
- struct acpi_power_meter_resource *resource;
+ struct device *dev = data;
+ struct acpi_power_meter_resource *resource = dev_get_drvdata(dev);
int res;
- if (!device || !acpi_driver_data(device))
- return;
-
- resource = acpi_driver_data(device);
-
guard(mutex)(&acpi_notify_lock);
switch (event) {
@@ -837,43 +834,43 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
remove_domain_devices(resource);
res = read_capabilities(resource);
if (res)
- dev_err_once(&device->dev, "read capabilities failed.\n");
+ dev_err_once(dev, "read capabilities failed.\n");
res = read_domain_devices(resource);
if (res && res != -ENODEV)
- dev_err_once(&device->dev, "read domain devices failed.\n");
+ dev_err_once(dev, "read domain devices failed.\n");
mutex_unlock(&resource->lock);
resource->hwmon_dev =
- hwmon_device_register_with_info(&device->dev,
+ hwmon_device_register_with_info(dev,
ACPI_POWER_METER_NAME,
resource,
&power_meter_chip_info,
power_extra_groups);
if (IS_ERR(resource->hwmon_dev))
- dev_err_once(&device->dev, "register hwmon device failed.\n");
+ dev_err_once(dev, "register hwmon device failed.\n");
break;
case METER_NOTIFY_TRIP:
- sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME);
+ sysfs_notify(&dev->kobj, NULL, POWER_AVERAGE_NAME);
break;
case METER_NOTIFY_CAP:
mutex_lock(&resource->lock);
res = update_cap(resource);
if (res)
- dev_err_once(&device->dev, "update cap failed when capping value is changed.\n");
+ dev_err_once(dev, "update cap failed when capping value is changed.\n");
mutex_unlock(&resource->lock);
- sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME);
+ sysfs_notify(&dev->kobj, NULL, POWER_CAP_NAME);
break;
case METER_NOTIFY_INTERVAL:
- sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME);
+ sysfs_notify(&dev->kobj, NULL, POWER_AVG_INTERVAL_NAME);
break;
case METER_NOTIFY_CAPPING:
mutex_lock(&resource->lock);
resource->power_alarm = true;
mutex_unlock(&resource->lock);
- sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME);
- dev_info(&device->dev, "Capping in progress.\n");
+ sysfs_notify(&dev->kobj, NULL, POWER_ALARM_NAME);
+ dev_info(dev, "Capping in progress.\n");
break;
default:
WARN(1, "Unexpected event %d\n", event);
@@ -881,16 +878,15 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
}
acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS,
- dev_name(&device->dev), event, 0);
+ dev_name(&resource->acpi_dev->dev),
+ event, 0);
}
-static int acpi_power_meter_add(struct acpi_device *device)
+static int acpi_power_meter_probe(struct platform_device *pdev)
{
- int res;
+ struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
struct acpi_power_meter_resource *resource;
-
- if (!device)
- return -EINVAL;
+ int res;
resource = kzalloc_obj(*resource);
if (!resource)
@@ -901,7 +897,8 @@ static int acpi_power_meter_add(struct acpi_device *device)
mutex_init(&resource->lock);
strscpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME);
strscpy(acpi_device_class(device), ACPI_POWER_METER_CLASS);
- device->driver_data = resource;
+
+ platform_set_drvdata(pdev, resource);
#if IS_REACHABLE(CONFIG_ACPI_IPMI)
/*
@@ -914,7 +911,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
struct acpi_device *ipi_device = acpi_dev_get_first_match_dev("IPI0001", NULL, -1);
if (ipi_device && acpi_wait_for_acpi_ipmi())
- dev_warn(&device->dev, "Waiting for ACPI IPMI timeout");
+ dev_warn(&pdev->dev, "Waiting for ACPI IPMI timeout");
acpi_dev_put(ipi_device);
}
#endif
@@ -932,7 +929,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
goto exit_free_capability;
resource->hwmon_dev =
- hwmon_device_register_with_info(&device->dev,
+ hwmon_device_register_with_info(&pdev->dev,
ACPI_POWER_METER_NAME, resource,
&power_meter_chip_info,
power_extra_groups);
@@ -941,9 +938,16 @@ static int acpi_power_meter_add(struct acpi_device *device)
goto exit_remove;
}
+ res = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_power_meter_notify, &pdev->dev);
+ if (res)
+ goto exit_hwmon;
+
res = 0;
goto exit;
+exit_hwmon:
+ hwmon_device_unregister(resource->hwmon_dev);
exit_remove:
remove_domain_devices(resource);
exit_free_capability:
@@ -954,14 +958,13 @@ exit:
return res;
}
-static void acpi_power_meter_remove(struct acpi_device *device)
+static void acpi_power_meter_remove(struct platform_device *pdev)
{
- struct acpi_power_meter_resource *resource;
+ struct acpi_power_meter_resource *resource = platform_get_drvdata(pdev);
- if (!device || !acpi_driver_data(device))
- return;
+ acpi_dev_remove_notify_handler(resource->acpi_dev, ACPI_DEVICE_NOTIFY,
+ acpi_power_meter_notify);
- resource = acpi_driver_data(device);
if (!IS_ERR(resource->hwmon_dev))
hwmon_device_unregister(resource->hwmon_dev);
@@ -973,14 +976,7 @@ static void acpi_power_meter_remove(struct acpi_device *device)
static int acpi_power_meter_resume(struct device *dev)
{
- struct acpi_power_meter_resource *resource;
-
- if (!dev)
- return -EINVAL;
-
- resource = acpi_driver_data(to_acpi_device(dev));
- if (!resource)
- return -EINVAL;
+ struct acpi_power_meter_resource *resource = dev_get_drvdata(dev);
free_capabilities(resource);
read_capabilities(resource);
@@ -991,16 +987,14 @@ static int acpi_power_meter_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL,
acpi_power_meter_resume);
-static struct acpi_driver acpi_power_meter_driver = {
- .name = "power_meter",
- .class = ACPI_POWER_METER_CLASS,
- .ids = power_meter_ids,
- .ops = {
- .add = acpi_power_meter_add,
- .remove = acpi_power_meter_remove,
- .notify = acpi_power_meter_notify,
- },
- .drv.pm = pm_sleep_ptr(&acpi_power_meter_pm),
+static struct platform_driver acpi_power_meter_driver = {
+ .probe = acpi_power_meter_probe,
+ .remove = acpi_power_meter_remove,
+ .driver = {
+ .name = "acpi-power-meter",
+ .acpi_match_table = power_meter_ids,
+ .pm = &acpi_power_meter_pm,
+ },
};
/* Module init/exit routines */
@@ -1029,7 +1023,7 @@ static int __init acpi_power_meter_init(void)
dmi_check_system(pm_dmi_table);
- result = acpi_bus_register_driver(&acpi_power_meter_driver);
+ result = platform_driver_register(&acpi_power_meter_driver);
if (result < 0)
return result;
@@ -1038,7 +1032,7 @@ static int __init acpi_power_meter_init(void)
static void __exit acpi_power_meter_exit(void)
{
- acpi_bus_unregister_driver(&acpi_power_meter_driver);
+ platform_driver_unregister(&acpi_power_meter_driver);
}
MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 436637264056..7f43565ca284 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -62,8 +62,8 @@ static ssize_t ads7828_in_show(struct device *dev,
if (err < 0)
return err;
- return sprintf(buf, "%d\n",
- DIV_ROUND_CLOSEST(regval * data->lsb_resol, 1000));
+ return sysfs_emit(buf, "%d\n",
+ DIV_ROUND_CLOSEST(regval * data->lsb_resol, 1000));
}
static SENSOR_DEVICE_ATTR_RO(in0_input, ads7828_in, 0);
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index 5434c37969d7..9bfdf9e6bcd7 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -104,10 +104,14 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
*/
/*MUX_M3_BM forces single ended*/
/*This is also where the gain of the PGA would be set*/
- ads7871_write_reg8(spi, REG_GAIN_MUX,
- (MUX_CNV_BM | MUX_M3_BM | channel));
+ ret = ads7871_write_reg8(spi, REG_GAIN_MUX,
+ (MUX_CNV_BM | MUX_M3_BM | channel));
+ if (ret < 0)
+ return ret;
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
@@ -116,18 +120,22 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
while ((i < 2) && mux_cnv) {
i++;
ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
+ if (ret < 0)
+ return ret;
mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
msleep_interruptible(1);
}
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 sprintf(buf, "%d\n", val);
- } else {
- return -1;
+ return sysfs_emit(buf, "%d\n", val);
}
+
+ return -ETIMEDOUT;
}
static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0);
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c
index 4ce019d2cc80..66955395d058 100644
--- a/drivers/hwmon/aht10.c
+++ b/drivers/hwmon/aht10.c
@@ -62,6 +62,15 @@ static const struct i2c_device_id aht10_id[] = {
};
MODULE_DEVICE_TABLE(i2c, aht10_id);
+static const struct of_device_id aht10_of_match[] = {
+ { .compatible = "aosong,aht10", .data = (void *)aht10 },
+ { .compatible = "aosong,aht20", .data = (void *)aht20 },
+ { .compatible = "aosong,dht20", .data = (void *)dht20 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, aht10_of_match);
+
/**
* struct aht10_data - All the data required to operate an AHT10/AHT20 chip
* @client: the i2c client associated with the AHT10/AHT20
@@ -377,6 +386,7 @@ static int aht10_probe(struct i2c_client *client)
static struct i2c_driver aht10_driver = {
.driver = {
.name = "aht10",
+ .of_match_table = aht10_of_match,
},
.probe = aht10_probe,
.id_table = aht10_id,
diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c
index 44e1ecba205d..4f6e6d440dd4 100644
--- a/drivers/hwmon/aspeed-g6-pwm-tach.c
+++ b/drivers/hwmon/aspeed-g6-pwm-tach.c
@@ -517,13 +517,6 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev)
return 0;
}
-static void aspeed_pwm_tach_remove(struct platform_device *pdev)
-{
- struct aspeed_pwm_tach_data *priv = platform_get_drvdata(pdev);
-
- reset_control_assert(priv->reset);
-}
-
static const struct of_device_id aspeed_pwm_tach_match[] = {
{
.compatible = "aspeed,ast2600-pwm-tach",
@@ -537,7 +530,6 @@ MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match);
static struct platform_driver aspeed_pwm_tach_driver = {
.probe = aspeed_pwm_tach_probe,
- .remove = aspeed_pwm_tach_remove,
.driver = {
.name = "aspeed-g6-pwm-tach",
.of_match_table = aspeed_pwm_tach_match,
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index adedaf0db10e..b5d97a27f80d 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -456,6 +456,15 @@ static const struct ec_board_info board_info_crosshair_viii_impact = {
.family = family_amd_500_series,
};
+static const struct ec_board_info board_info_crosshair_x670e_extreme = {
+ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
+ SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_WATER_IN |
+ SENSOR_TEMP_WATER_OUT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
+ .family = family_amd_600_series,
+};
+
static const struct ec_board_info board_info_crosshair_x670e_gene = {
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
SENSOR_TEMP_T_SENSOR |
@@ -626,6 +635,14 @@ static const struct ec_board_info board_info_strix_b850_i_gaming_wifi = {
.family = family_amd_800_series,
};
+static const struct ec_board_info board_info_strix_x470_f_gaming = {
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_400_series,
+};
+
static const struct ec_board_info board_info_strix_x470_i_gaming = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
@@ -750,6 +767,12 @@ static const struct ec_board_info board_info_strix_z790_e_gaming_wifi_ii = {
.family = family_intel_700_series,
};
+static const struct ec_board_info board_info_strix_z790_h_gaming_wifi = {
+ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PC00_LPCB_SIO1_MUT0,
+ .family = family_intel_700_series,
+};
+
static const struct ec_board_info board_info_strix_z790_i_gaming_wifi = {
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_T_SENSOR_2 |
SENSOR_TEMP_VRM,
@@ -825,6 +848,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_crosshair_viii_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII IMPACT",
&board_info_crosshair_viii_impact),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E EXTREME",
+ &board_info_crosshair_x670e_extreme),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E GENE",
&board_info_crosshair_x670e_gene),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO",
@@ -845,6 +870,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_strix_b650e_i_gaming),
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",
+ &board_info_strix_x470_f_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING",
&board_info_strix_x470_i_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING",
@@ -877,6 +904,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_strix_z690_e_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z790-E GAMING WIFI II",
&board_info_strix_z790_e_gaming_wifi_ii),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z790-H GAMING WIFI",
+ &board_info_strix_z790_h_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z790-I GAMING WIFI",
&board_info_strix_z790_i_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME",
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index c80350e499e9..5688ff5f7c28 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -17,6 +17,7 @@
#include <linux/jiffies.h>
#include <linux/err.h>
#include <linux/acpi.h>
+#include <linux/platform_device.h>
#include <linux/string_choices.h>
#define ATK_HID "ATK0110"
@@ -107,7 +108,7 @@ enum atk_pack_member {
struct atk_data {
struct device *hwmon_dev;
acpi_handle atk_handle;
- struct acpi_device *acpi_dev;
+ struct device *dev;
bool old_interface;
@@ -187,18 +188,17 @@ struct atk_acpi_input_buf {
u32 param2;
};
-static int atk_add(struct acpi_device *device);
-static void atk_remove(struct acpi_device *device);
+static int atk_probe(struct platform_device *pdev);
+static void atk_remove(struct platform_device *pdev);
static void atk_print_sensor(struct atk_data *data, union acpi_object *obj);
static int atk_read_value(struct atk_sensor_data *sensor, u64 *value);
-static struct acpi_driver atk_driver = {
- .name = ATK_HID,
- .class = "hwmon",
- .ids = atk_ids,
- .ops = {
- .add = atk_add,
- .remove = atk_remove,
+static struct platform_driver atk_driver = {
+ .probe = atk_probe,
+ .remove = atk_remove,
+ .driver = {
+ .name = ATK_HID,
+ .acpi_match_table = atk_ids,
},
};
@@ -327,7 +327,7 @@ static union acpi_object *atk_get_pack_member(struct atk_data *data,
*/
static int validate_hwmon_pack(struct atk_data *data, union acpi_object *obj)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *tmp;
bool old_if = data->old_interface;
int const expected_size = old_if ? _HWMON_OLD_PACK_SIZE :
@@ -422,7 +422,7 @@ static char const *atk_sensor_type(union acpi_object *flags)
static void atk_print_sensor(struct atk_data *data, union acpi_object *obj)
{
#ifdef DEBUG
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *flags;
union acpi_object *name;
union acpi_object *limit1;
@@ -449,7 +449,7 @@ static void atk_print_sensor(struct atk_data *data, union acpi_object *obj)
static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value)
{
struct atk_data *data = sensor->data;
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
struct acpi_object_list params;
union acpi_object id;
acpi_status status;
@@ -487,7 +487,7 @@ static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value)
static union acpi_object *atk_ggrp(struct atk_data *data, u16 mux)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
struct acpi_buffer buf;
acpi_status ret;
struct acpi_object_list params;
@@ -523,7 +523,7 @@ static union acpi_object *atk_ggrp(struct atk_data *data, u16 mux)
static union acpi_object *atk_gitm(struct atk_data *data, u64 id)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
struct atk_acpi_input_buf buf;
union acpi_object tmp;
struct acpi_object_list params;
@@ -565,7 +565,7 @@ static union acpi_object *atk_gitm(struct atk_data *data, u64 id)
static union acpi_object *atk_sitm(struct atk_data *data,
struct atk_acpi_input_buf *buf)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
struct acpi_object_list params;
union acpi_object tmp;
struct acpi_buffer ret;
@@ -602,7 +602,7 @@ static union acpi_object *atk_sitm(struct atk_data *data,
static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value)
{
struct atk_data *data = sensor->data;
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *obj;
struct atk_acpi_ret_buffer *buf;
int err = 0;
@@ -819,7 +819,7 @@ static void atk_debugfs_cleanup(struct atk_data *data)
static int atk_add_sensor(struct atk_data *data, union acpi_object *obj)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *flags;
union acpi_object *name;
union acpi_object *limit1;
@@ -937,7 +937,7 @@ static int atk_add_sensor(struct atk_data *data, union acpi_object *obj)
static int atk_enumerate_old_hwmon(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
struct acpi_buffer buf;
union acpi_object *pack;
acpi_status status;
@@ -1012,7 +1012,7 @@ static int atk_enumerate_old_hwmon(struct atk_data *data)
static int atk_ec_present(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *pack;
union acpi_object *ec;
int ret;
@@ -1058,7 +1058,7 @@ static int atk_ec_present(struct atk_data *data)
static int atk_ec_enabled(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *obj;
struct atk_acpi_ret_buffer *buf;
int err;
@@ -1084,7 +1084,7 @@ static int atk_ec_enabled(struct atk_data *data)
static int atk_ec_ctl(struct atk_data *data, int enable)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *obj;
struct atk_acpi_input_buf sitm;
struct atk_acpi_ret_buffer *ec_ret;
@@ -1113,7 +1113,7 @@ static int atk_ec_ctl(struct atk_data *data, int enable)
static int atk_enumerate_new_hwmon(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
union acpi_object *pack;
int err;
int i;
@@ -1155,7 +1155,7 @@ static int atk_enumerate_new_hwmon(struct atk_data *data)
static int atk_init_attribute_groups(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
struct atk_sensor_data *s;
struct attribute **attrs;
int i = 0;
@@ -1181,7 +1181,7 @@ static int atk_init_attribute_groups(struct atk_data *data)
static int atk_register_hwmon(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
dev_dbg(dev, "registering hwmon device\n");
data->hwmon_dev = hwmon_device_register_with_groups(dev, "atk0110",
@@ -1193,7 +1193,7 @@ static int atk_register_hwmon(struct atk_data *data)
static int atk_probe_if(struct atk_data *data)
{
- struct device *dev = &data->acpi_dev->dev;
+ struct device *dev = data->dev;
acpi_handle ret;
acpi_status status;
int err = 0;
@@ -1266,7 +1266,7 @@ static int atk_probe_if(struct atk_data *data)
return err;
}
-static int atk_add(struct acpi_device *device)
+static int atk_probe(struct platform_device *pdev)
{
acpi_status ret;
int err;
@@ -1274,14 +1274,14 @@ static int atk_add(struct acpi_device *device)
union acpi_object *obj;
struct atk_data *data;
- dev_dbg(&device->dev, "adding...\n");
+ dev_dbg(&pdev->dev, "adding...\n");
- data = devm_kzalloc(&device->dev, sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->acpi_dev = device;
- data->atk_handle = device->handle;
+ data->dev = &pdev->dev;
+ data->atk_handle = ACPI_HANDLE(&pdev->dev);
INIT_LIST_HEAD(&data->sensor_list);
data->disable_ec = false;
@@ -1289,13 +1289,13 @@ static int atk_add(struct acpi_device *device)
ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL,
&buf, ACPI_TYPE_PACKAGE);
if (ret != AE_OK) {
- dev_dbg(&device->dev, "atk: method MBIF not found\n");
+ dev_dbg(&pdev->dev, "atk: method MBIF not found\n");
} else {
obj = buf.pointer;
if (obj->package.count >= 2) {
union acpi_object *id = &obj->package.elements[1];
if (id->type == ACPI_TYPE_STRING)
- dev_dbg(&device->dev, "board ID = %s\n",
+ dev_dbg(&pdev->dev, "board ID = %s\n",
id->string.pointer);
}
ACPI_FREE(buf.pointer);
@@ -1303,21 +1303,21 @@ static int atk_add(struct acpi_device *device)
err = atk_probe_if(data);
if (err) {
- dev_err(&device->dev, "No usable hwmon interface detected\n");
+ dev_err(&pdev->dev, "No usable hwmon interface detected\n");
goto out;
}
if (data->old_interface) {
- dev_dbg(&device->dev, "Using old hwmon interface\n");
+ dev_dbg(&pdev->dev, "Using old hwmon interface\n");
err = atk_enumerate_old_hwmon(data);
} else {
- dev_dbg(&device->dev, "Using new hwmon interface\n");
+ dev_dbg(&pdev->dev, "Using new hwmon interface\n");
err = atk_enumerate_new_hwmon(data);
}
if (err < 0)
goto out;
if (err == 0) {
- dev_info(&device->dev,
+ dev_info(&pdev->dev,
"No usable sensor detected, bailing out\n");
err = -ENODEV;
goto out;
@@ -1332,7 +1332,8 @@ static int atk_add(struct acpi_device *device)
atk_debugfs_init(data);
- device->driver_data = data;
+ platform_set_drvdata(pdev, data);
+
return 0;
out:
if (data->disable_ec)
@@ -1340,12 +1341,11 @@ out:
return err;
}
-static void atk_remove(struct acpi_device *device)
+static void atk_remove(struct platform_device *pdev)
{
- struct atk_data *data = device->driver_data;
- dev_dbg(&device->dev, "removing...\n");
+ struct atk_data *data = platform_get_drvdata(pdev);
- device->driver_data = NULL;
+ dev_dbg(&pdev->dev, "removing...\n");
atk_debugfs_cleanup(data);
@@ -1353,7 +1353,7 @@ static void atk_remove(struct acpi_device *device)
if (data->disable_ec) {
if (atk_ec_ctl(data, 0))
- dev_err(&device->dev, "Failed to disable EC\n");
+ dev_err(&pdev->dev, "Failed to disable EC\n");
}
}
@@ -1370,16 +1370,16 @@ static int __init atk0110_init(void)
if (dmi_check_system(atk_force_new_if))
new_if = true;
- ret = acpi_bus_register_driver(&atk_driver);
+ ret = platform_driver_register(&atk_driver);
if (ret)
- pr_info("acpi_bus_register_driver failed: %d\n", ret);
+ pr_info("platform_driver_register failed: %d\n", ret);
return ret;
}
static void __exit atk0110_exit(void)
{
- acpi_bus_unregister_driver(&atk_driver);
+ platform_driver_unregister(&atk_driver);
}
module_init(atk0110_init);
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
deleted file mode 100644
index b77ebac2e0ce..000000000000
--- a/drivers/hwmon/bt1-pvt.c
+++ /dev/null
@@ -1,1171 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
- *
- * Authors:
- * Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
- * Serge Semin <Sergey.Semin@baikalelectronics.ru>
- *
- * Baikal-T1 Process, Voltage, Temperature sensor driver
- */
-
-#include <linux/bitfield.h>
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/hwmon.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/ktime.h>
-#include <linux/limits.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/polynomial.h>
-#include <linux/seqlock.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-
-#include "bt1-pvt.h"
-
-/*
- * For the sake of the code simplification we created the sensors info table
- * with the sensor names, activation modes, threshold registers base address
- * and the thresholds bit fields.
- */
-static const struct pvt_sensor_info pvt_info[] = {
- PVT_SENSOR_INFO(0, "CPU Core Temperature", hwmon_temp, TEMP, TTHRES),
- PVT_SENSOR_INFO(0, "CPU Core Voltage", hwmon_in, VOLT, VTHRES),
- PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES),
- PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES),
- PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES),
-};
-
-/*
- * The original translation formulae of the temperature (in degrees of Celsius)
- * to PVT data and vice-versa are following:
- * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
- * 1.7204e2,
- * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
- * 3.1020e-1*(N^1) - 4.838e1,
- * where T = [-48.380, 147.438]C and N = [0, 1023].
- * They must be accordingly altered to be suitable for the integer arithmetics.
- * The technique is called 'factor redistribution', which just makes sure the
- * multiplications and divisions are made so to have a result of the operations
- * within the integer numbers limit. In addition we need to translate the
- * formulae to accept millidegrees of Celsius. Here what they look like after
- * the alterations:
- * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
- * 17204e2) / 1e4,
- * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
- * 48380,
- * where T = [-48380, 147438] mC and N = [0, 1023].
- */
-static const struct polynomial __maybe_unused poly_temp_to_N = {
- .total_divider = 10000,
- .terms = {
- {4, 18322, 10000, 10000},
- {3, 2343, 10000, 10},
- {2, 87018, 10000, 10},
- {1, 39269, 1000, 1},
- {0, 1720400, 1, 1}
- }
-};
-
-static const struct polynomial poly_N_to_temp = {
- .total_divider = 1,
- .terms = {
- {4, -16743, 1000, 1},
- {3, 81542, 1000, 1},
- {2, -182010, 1000, 1},
- {1, 310200, 1000, 1},
- {0, -48380, 1, 1}
- }
-};
-
-/*
- * Similar alterations are performed for the voltage conversion equations.
- * The original formulae are:
- * N = 1.8658e3*V - 1.1572e3,
- * V = (N + 1.1572e3) / 1.8658e3,
- * where V = [0.620, 1.168] V and N = [0, 1023].
- * After the optimization they looks as follows:
- * N = (18658e-3*V - 11572) / 10,
- * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
- */
-static const struct polynomial __maybe_unused poly_volt_to_N = {
- .total_divider = 10,
- .terms = {
- {1, 18658, 1000, 1},
- {0, -11572, 1, 1}
- }
-};
-
-static const struct polynomial poly_N_to_volt = {
- .total_divider = 10,
- .terms = {
- {1, 100000, 18658, 1},
- {0, 115720000, 1, 18658}
- }
-};
-
-static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
-{
- u32 old;
-
- old = readl_relaxed(reg);
- writel((old & ~mask) | (data & mask), reg);
-
- return old & mask;
-}
-
-/*
- * Baikal-T1 PVT mode can be updated only when the controller is disabled.
- * So first we disable it, then set the new mode together with the controller
- * getting back enabled. The same concerns the temperature trim and
- * measurements timeout. If it is necessary the interface mutex is supposed
- * to be locked at the time the operations are performed.
- */
-static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
-{
- u32 old;
-
- mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode);
-
- old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
- mode | old);
-}
-
-static inline u32 pvt_calc_trim(long temp)
-{
- temp = clamp_val(temp, 0, PVT_TRIM_TEMP);
-
- return DIV_ROUND_UP(temp, PVT_TRIM_STEP);
-}
-
-static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim)
-{
- u32 old;
-
- trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim);
-
- old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
- trim | old);
-}
-
-static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout)
-{
- u32 old;
-
- old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
- writel(tout, pvt->regs + PVT_TTIMEOUT);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old);
-}
-
-/*
- * This driver can optionally provide the hwmon alarms for each sensor the PVT
- * controller supports. The alarms functionality is made compile-time
- * configurable due to the hardware interface implementation peculiarity
- * described further in this comment. So in case if alarms are unnecessary in
- * your system design it's recommended to have them disabled to prevent the PVT
- * IRQs being periodically raised to get the data cache/alarms status up to
- * date.
- *
- * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor,
- * but is equipped with a dedicated control wrapper. It exposes the PVT
- * sub-block registers space via the APB3 bus. In addition the wrapper provides
- * a common interrupt vector of the sensors conversion completion events and
- * threshold value alarms. Alas the wrapper interface hasn't been fully thought
- * through. There is only one sensor can be activated at a time, for which the
- * thresholds comparator is enabled right after the data conversion is
- * completed. Due to this if alarms need to be implemented for all available
- * sensors we can't just set the thresholds and enable the interrupts. We need
- * to enable the sensors one after another and let the controller to detect
- * the alarms by itself at each conversion. This also makes pointless to handle
- * the alarms interrupts, since in occasion they happen synchronously with
- * data conversion completion. The best driver design would be to have the
- * completion interrupts enabled only and keep the converted value in the
- * driver data cache. This solution is implemented if hwmon alarms are enabled
- * in this driver. In case if the alarms are disabled, the conversion is
- * performed on demand at the time a sensors input file is read.
- */
-
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
-
-#define pvt_hard_isr NULL
-
-static irqreturn_t pvt_soft_isr(int irq, void *data)
-{
- const struct pvt_sensor_info *info;
- struct pvt_hwmon *pvt = data;
- struct pvt_cache *cache;
- u32 val, thres_sts, old;
-
- /*
- * DVALID bit will be cleared by reading the data. We need to save the
- * status before the next conversion happens. Threshold events will be
- * handled a bit later.
- */
- thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT);
-
- /*
- * Then lets recharge the PVT interface with the next sampling mode.
- * Lock the interface mutex to serialize trim, timeouts and alarm
- * thresholds settings.
- */
- cache = &pvt->cache[pvt->sensor];
- info = &pvt_info[pvt->sensor];
- pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ?
- PVT_SENSOR_FIRST : (pvt->sensor + 1);
-
- /*
- * For some reason we have to mask the interrupt before changing the
- * mode, otherwise sometimes the temperature mode doesn't get
- * activated even though the actual mode in the ctrl register
- * corresponds to one. Then we read the data. By doing so we also
- * recharge the data conversion. After this the mode corresponding
- * to the next sensor in the row is set. Finally we enable the
- * interrupts back.
- */
- mutex_lock(&pvt->iface_mtx);
-
- old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
- PVT_INTR_DVALID);
-
- val = readl(pvt->regs + PVT_DATA);
-
- pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
-
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old);
-
- mutex_unlock(&pvt->iface_mtx);
-
- /*
- * We can now update the data cache with data just retrieved from the
- * sensor. Lock write-seqlock to make sure the reader has a coherent
- * data.
- */
- write_seqlock(&cache->data_seqlock);
-
- cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val);
-
- write_sequnlock(&cache->data_seqlock);
-
- /*
- * While PVT core is doing the next mode data conversion, we'll check
- * whether the alarms were triggered for the current sensor. Note that
- * according to the documentation only one threshold IRQ status can be
- * set at a time, that's why if-else statement is utilized.
- */
- if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) {
- WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo);
- hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm,
- info->channel);
- } else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) {
- WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi);
- hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm,
- info->channel);
- }
-
- return IRQ_HANDLED;
-}
-
-static inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
-{
- return 0644;
-}
-
-static inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
-{
- return 0444;
-}
-
-static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- long *val)
-{
- struct pvt_cache *cache = &pvt->cache[type];
- unsigned int seq;
- u32 data;
-
- do {
- seq = read_seqbegin(&cache->data_seqlock);
- data = cache->data;
- } while (read_seqretry(&cache->data_seqlock, seq));
-
- if (type == PVT_TEMP)
- *val = polynomial_calc(&poly_N_to_temp, data);
- else
- *val = polynomial_calc(&poly_N_to_volt, data);
-
- return 0;
-}
-
-static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- bool is_low, long *val)
-{
- u32 data;
-
- /* No need in serialization, since it is just read from MMIO. */
- data = readl(pvt->regs + pvt_info[type].thres_base);
-
- if (is_low)
- data = FIELD_GET(PVT_THRES_LO_MASK, data);
- else
- data = FIELD_GET(PVT_THRES_HI_MASK, data);
-
- if (type == PVT_TEMP)
- *val = polynomial_calc(&poly_N_to_temp, data);
- else
- *val = polynomial_calc(&poly_N_to_volt, data);
-
- return 0;
-}
-
-static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- bool is_low, long val)
-{
- u32 data, limit, mask;
- int ret;
-
- if (type == PVT_TEMP) {
- val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
- data = polynomial_calc(&poly_temp_to_N, val);
- } else {
- val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
- data = polynomial_calc(&poly_volt_to_N, val);
- }
-
- /* Serialize limit update, since a part of the register is changed. */
- ret = mutex_lock_interruptible(&pvt->iface_mtx);
- if (ret)
- return ret;
-
- /* Make sure the upper and lower ranges don't intersect. */
- limit = readl(pvt->regs + pvt_info[type].thres_base);
- if (is_low) {
- limit = FIELD_GET(PVT_THRES_HI_MASK, limit);
- data = clamp_val(data, PVT_DATA_MIN, limit);
- data = FIELD_PREP(PVT_THRES_LO_MASK, data);
- mask = PVT_THRES_LO_MASK;
- } else {
- limit = FIELD_GET(PVT_THRES_LO_MASK, limit);
- data = clamp_val(data, limit, PVT_DATA_MAX);
- data = FIELD_PREP(PVT_THRES_HI_MASK, data);
- mask = PVT_THRES_HI_MASK;
- }
-
- pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data);
-
- mutex_unlock(&pvt->iface_mtx);
-
- return 0;
-}
-
-static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- bool is_low, long *val)
-{
- if (is_low)
- *val = !!READ_ONCE(pvt->cache[type].thres_sts_lo);
- else
- *val = !!READ_ONCE(pvt->cache[type].thres_sts_hi);
-
- return 0;
-}
-
-static const struct hwmon_channel_info * const pvt_channel_info[] = {
- HWMON_CHANNEL_INFO(chip,
- HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
- HWMON_CHANNEL_INFO(temp,
- HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
- HWMON_T_MIN | HWMON_T_MIN_ALARM |
- HWMON_T_MAX | HWMON_T_MAX_ALARM |
- HWMON_T_OFFSET),
- HWMON_CHANNEL_INFO(in,
- HWMON_I_INPUT | HWMON_I_LABEL |
- HWMON_I_MIN | HWMON_I_MIN_ALARM |
- HWMON_I_MAX | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LABEL |
- HWMON_I_MIN | HWMON_I_MIN_ALARM |
- HWMON_I_MAX | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LABEL |
- HWMON_I_MIN | HWMON_I_MIN_ALARM |
- HWMON_I_MAX | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LABEL |
- HWMON_I_MIN | HWMON_I_MIN_ALARM |
- HWMON_I_MAX | HWMON_I_MAX_ALARM),
- NULL
-};
-
-#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
-
-static irqreturn_t pvt_hard_isr(int irq, void *data)
-{
- struct pvt_hwmon *pvt = data;
- struct pvt_cache *cache;
- u32 val;
-
- /*
- * Mask the DVALID interrupt so after exiting from the handler a
- * repeated conversion wouldn't happen.
- */
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
- PVT_INTR_DVALID);
-
- /*
- * Nothing special for alarm-less driver. Just read the data, update
- * the cache and notify a waiter of this event.
- */
- val = readl(pvt->regs + PVT_DATA);
- if (!(val & PVT_DATA_VALID)) {
- dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
- return IRQ_HANDLED;
- }
-
- cache = &pvt->cache[pvt->sensor];
-
- WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val));
-
- complete(&cache->conversion);
-
- return IRQ_HANDLED;
-}
-
-#define pvt_soft_isr NULL
-
-static inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
-{
- return 0;
-}
-
-static inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
-{
- return 0;
-}
-
-static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- long *val)
-{
- struct pvt_cache *cache = &pvt->cache[type];
- unsigned long timeout;
- u32 data;
- int ret;
-
- /*
- * Lock PVT conversion interface until data cache is updated. The
- * data read procedure is following: set the requested PVT sensor
- * mode, enable IRQ and conversion, wait until conversion is finished,
- * then disable conversion and IRQ, and read the cached data.
- */
- ret = mutex_lock_interruptible(&pvt->iface_mtx);
- if (ret)
- return ret;
-
- pvt->sensor = type;
- pvt_set_mode(pvt, pvt_info[type].mode);
-
- /*
- * Unmask the DVALID interrupt and enable the sensors conversions.
- * Do the reverse procedure when conversion is done.
- */
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
-
- /*
- * Wait with timeout since in case if the sensor is suddenly powered
- * down the request won't be completed and the caller will hang up on
- * this procedure until the power is back up again. Multiply the
- * timeout by the factor of two to prevent a false timeout.
- */
- timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout));
- ret = wait_for_completion_timeout(&cache->conversion, timeout);
-
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
- PVT_INTR_DVALID);
-
- data = READ_ONCE(cache->data);
-
- mutex_unlock(&pvt->iface_mtx);
-
- if (!ret)
- return -ETIMEDOUT;
-
- if (type == PVT_TEMP)
- *val = polynomial_calc(&poly_N_to_temp, data);
- else
- *val = polynomial_calc(&poly_N_to_volt, data);
-
- return 0;
-}
-
-static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- bool is_low, long *val)
-{
- return -EOPNOTSUPP;
-}
-
-static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- bool is_low, long val)
-{
- return -EOPNOTSUPP;
-}
-
-static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
- bool is_low, long *val)
-{
- return -EOPNOTSUPP;
-}
-
-static const struct hwmon_channel_info * const pvt_channel_info[] = {
- HWMON_CHANNEL_INFO(chip,
- HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
- HWMON_CHANNEL_INFO(temp,
- HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
- HWMON_T_OFFSET),
- HWMON_CHANNEL_INFO(in,
- HWMON_I_INPUT | HWMON_I_LABEL,
- HWMON_I_INPUT | HWMON_I_LABEL,
- HWMON_I_INPUT | HWMON_I_LABEL,
- HWMON_I_INPUT | HWMON_I_LABEL),
- NULL
-};
-
-#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
-
-static inline bool pvt_hwmon_channel_is_valid(enum hwmon_sensor_types type,
- int ch)
-{
- switch (type) {
- case hwmon_temp:
- if (ch < 0 || ch >= PVT_TEMP_CHS)
- return false;
- break;
- case hwmon_in:
- if (ch < 0 || ch >= PVT_VOLT_CHS)
- return false;
- break;
- default:
- break;
- }
-
- /* The rest of the types are independent from the channel number. */
- return true;
-}
-
-static umode_t pvt_hwmon_is_visible(const void *data,
- enum hwmon_sensor_types type,
- u32 attr, int ch)
-{
- if (!pvt_hwmon_channel_is_valid(type, ch))
- return 0;
-
- switch (type) {
- case hwmon_chip:
- switch (attr) {
- case hwmon_chip_update_interval:
- return 0644;
- }
- break;
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_input:
- case hwmon_temp_type:
- case hwmon_temp_label:
- return 0444;
- case hwmon_temp_min:
- case hwmon_temp_max:
- return pvt_limit_is_visible(ch);
- case hwmon_temp_min_alarm:
- case hwmon_temp_max_alarm:
- return pvt_alarm_is_visible(ch);
- case hwmon_temp_offset:
- return 0644;
- }
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_input:
- case hwmon_in_label:
- return 0444;
- case hwmon_in_min:
- case hwmon_in_max:
- return pvt_limit_is_visible(PVT_VOLT + ch);
- case hwmon_in_min_alarm:
- case hwmon_in_max_alarm:
- return pvt_alarm_is_visible(PVT_VOLT + ch);
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int pvt_read_trim(struct pvt_hwmon *pvt, long *val)
-{
- u32 data;
-
- data = readl(pvt->regs + PVT_CTRL);
- *val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP;
-
- return 0;
-}
-
-static int pvt_write_trim(struct pvt_hwmon *pvt, long val)
-{
- u32 trim;
- int ret;
-
- /*
- * Serialize trim update, since a part of the register is changed and
- * the controller is supposed to be disabled during this operation.
- */
- ret = mutex_lock_interruptible(&pvt->iface_mtx);
- if (ret)
- return ret;
-
- trim = pvt_calc_trim(val);
- pvt_set_trim(pvt, trim);
-
- mutex_unlock(&pvt->iface_mtx);
-
- return 0;
-}
-
-static int pvt_read_timeout(struct pvt_hwmon *pvt, long *val)
-{
- int ret;
-
- ret = mutex_lock_interruptible(&pvt->iface_mtx);
- if (ret)
- return ret;
-
- /* Return the result in msec as hwmon sysfs interface requires. */
- *val = ktime_to_ms(pvt->timeout);
-
- mutex_unlock(&pvt->iface_mtx);
-
- return 0;
-}
-
-static int pvt_write_timeout(struct pvt_hwmon *pvt, long val)
-{
- unsigned long rate;
- ktime_t kt, cache;
- u32 data;
- int ret;
-
- rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
- if (!rate)
- return -ENODEV;
-
- /*
- * If alarms are enabled, the requested timeout must be divided
- * between all available sensors to have the requested delay
- * applicable to each individual sensor.
- */
- cache = kt = ms_to_ktime(val);
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
- kt = ktime_divns(kt, PVT_SENSORS_NUM);
-#endif
-
- /*
- * Subtract a constant lag, which always persists due to the limited
- * PVT sampling rate. Make sure the timeout is not negative.
- */
- kt = ktime_sub_ns(kt, PVT_TOUT_MIN);
- if (ktime_to_ns(kt) < 0)
- kt = ktime_set(0, 0);
-
- /*
- * Finally recalculate the timeout in terms of the reference clock
- * period.
- */
- data = ktime_divns(kt * rate, NSEC_PER_SEC);
-
- /*
- * Update the measurements delay, but lock the interface first, since
- * we have to disable PVT in order to have the new delay actually
- * updated.
- */
- ret = mutex_lock_interruptible(&pvt->iface_mtx);
- if (ret)
- return ret;
-
- pvt_set_tout(pvt, data);
- pvt->timeout = cache;
-
- mutex_unlock(&pvt->iface_mtx);
-
- return 0;
-}
-
-static int pvt_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int ch, long *val)
-{
- struct pvt_hwmon *pvt = dev_get_drvdata(dev);
-
- if (!pvt_hwmon_channel_is_valid(type, ch))
- return -EINVAL;
-
- switch (type) {
- case hwmon_chip:
- switch (attr) {
- case hwmon_chip_update_interval:
- return pvt_read_timeout(pvt, val);
- }
- break;
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_input:
- return pvt_read_data(pvt, ch, val);
- case hwmon_temp_type:
- *val = 1;
- return 0;
- case hwmon_temp_min:
- return pvt_read_limit(pvt, ch, true, val);
- case hwmon_temp_max:
- return pvt_read_limit(pvt, ch, false, val);
- case hwmon_temp_min_alarm:
- return pvt_read_alarm(pvt, ch, true, val);
- case hwmon_temp_max_alarm:
- return pvt_read_alarm(pvt, ch, false, val);
- case hwmon_temp_offset:
- return pvt_read_trim(pvt, val);
- }
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_input:
- return pvt_read_data(pvt, PVT_VOLT + ch, val);
- case hwmon_in_min:
- return pvt_read_limit(pvt, PVT_VOLT + ch, true, val);
- case hwmon_in_max:
- return pvt_read_limit(pvt, PVT_VOLT + ch, false, val);
- case hwmon_in_min_alarm:
- return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val);
- case hwmon_in_max_alarm:
- return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val);
- }
- break;
- default:
- break;
- }
-
- return -EOPNOTSUPP;
-}
-
-static int pvt_hwmon_read_string(struct device *dev,
- enum hwmon_sensor_types type,
- u32 attr, int ch, const char **str)
-{
- if (!pvt_hwmon_channel_is_valid(type, ch))
- return -EINVAL;
-
- switch (type) {
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_label:
- *str = pvt_info[ch].label;
- return 0;
- }
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_label:
- *str = pvt_info[PVT_VOLT + ch].label;
- return 0;
- }
- break;
- default:
- break;
- }
-
- return -EOPNOTSUPP;
-}
-
-static int pvt_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int ch, long val)
-{
- struct pvt_hwmon *pvt = dev_get_drvdata(dev);
-
- if (!pvt_hwmon_channel_is_valid(type, ch))
- return -EINVAL;
-
- switch (type) {
- case hwmon_chip:
- switch (attr) {
- case hwmon_chip_update_interval:
- return pvt_write_timeout(pvt, val);
- }
- break;
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_min:
- return pvt_write_limit(pvt, ch, true, val);
- case hwmon_temp_max:
- return pvt_write_limit(pvt, ch, false, val);
- case hwmon_temp_offset:
- return pvt_write_trim(pvt, val);
- }
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_min:
- return pvt_write_limit(pvt, PVT_VOLT + ch, true, val);
- case hwmon_in_max:
- return pvt_write_limit(pvt, PVT_VOLT + ch, false, val);
- }
- break;
- default:
- break;
- }
-
- return -EOPNOTSUPP;
-}
-
-static const struct hwmon_ops pvt_hwmon_ops = {
- .is_visible = pvt_hwmon_is_visible,
- .read = pvt_hwmon_read,
- .read_string = pvt_hwmon_read_string,
- .write = pvt_hwmon_write
-};
-
-static const struct hwmon_chip_info pvt_hwmon_info = {
- .ops = &pvt_hwmon_ops,
- .info = pvt_channel_info
-};
-
-static void pvt_clear_data(void *data)
-{
- struct pvt_hwmon *pvt = data;
-#if !defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
- int idx;
-
- for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
- complete_all(&pvt->cache[idx].conversion);
-#endif
-
- mutex_destroy(&pvt->iface_mtx);
-}
-
-static struct pvt_hwmon *pvt_create_data(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct pvt_hwmon *pvt;
- int ret, idx;
-
- pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL);
- if (!pvt)
- return ERR_PTR(-ENOMEM);
-
- ret = devm_add_action(dev, pvt_clear_data, pvt);
- if (ret) {
- dev_err(dev, "Can't add PVT data clear action\n");
- return ERR_PTR(ret);
- }
-
- pvt->dev = dev;
- pvt->sensor = PVT_SENSOR_FIRST;
- mutex_init(&pvt->iface_mtx);
-
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
- for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
- seqlock_init(&pvt->cache[idx].data_seqlock);
-#else
- for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
- init_completion(&pvt->cache[idx].conversion);
-#endif
-
- return pvt;
-}
-
-static int pvt_request_regs(struct pvt_hwmon *pvt)
-{
- struct platform_device *pdev = to_platform_device(pvt->dev);
-
- pvt->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(pvt->regs))
- return PTR_ERR(pvt->regs);
-
- return 0;
-}
-
-static void pvt_disable_clks(void *data)
-{
- struct pvt_hwmon *pvt = data;
-
- clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks);
-}
-
-static int pvt_request_clks(struct pvt_hwmon *pvt)
-{
- int ret;
-
- pvt->clks[PVT_CLOCK_APB].id = "pclk";
- pvt->clks[PVT_CLOCK_REF].id = "ref";
-
- ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
- if (ret) {
- dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n");
- return ret;
- }
-
- ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks);
- if (ret) {
- dev_err(pvt->dev, "Couldn't enable the PVT clocks\n");
- return ret;
- }
-
- ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt);
- if (ret) {
- dev_err(pvt->dev, "Can't add PVT clocks disable action\n");
- return ret;
- }
-
- return 0;
-}
-
-static int pvt_check_pwr(struct pvt_hwmon *pvt)
-{
- unsigned long tout;
- int ret = 0;
- u32 data;
-
- /*
- * Test out the sensor conversion functionality. If it is not done on
- * time then the domain must have been unpowered and we won't be able
- * to use the device later in this driver.
- * Note If the power source is lost during the normal driver work the
- * data read procedure will either return -ETIMEDOUT (for the
- * alarm-less driver configuration) or just stop the repeated
- * conversion. In the later case alas we won't be able to detect the
- * problem.
- */
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
- pvt_set_tout(pvt, 0);
- readl(pvt->regs + PVT_DATA);
-
- tout = PVT_TOUT_MIN / NSEC_PER_USEC;
- usleep_range(tout, 2 * tout);
-
- data = readl(pvt->regs + PVT_DATA);
- if (!(data & PVT_DATA_VALID)) {
- ret = -ENODEV;
- dev_err(pvt->dev, "Sensor is powered down\n");
- }
-
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-
- return ret;
-}
-
-static int pvt_init_iface(struct pvt_hwmon *pvt)
-{
- unsigned long rate;
- u32 trim, temp;
-
- rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
- if (!rate) {
- dev_err(pvt->dev, "Invalid reference clock rate\n");
- return -ENODEV;
- }
-
- /*
- * Make sure all interrupts and controller are disabled so not to
- * accidentally have ISR executed before the driver data is fully
- * initialized. Clear the IRQ status as well.
- */
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
- readl(pvt->regs + PVT_CLR_INTR);
- readl(pvt->regs + PVT_DATA);
-
- /* Setup default sensor mode, timeout and temperature trim. */
- pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
- pvt_set_tout(pvt, PVT_TOUT_DEF);
-
- /*
- * Preserve the current ref-clock based delay (Ttotal) between the
- * sensors data samples in the driver data so not to recalculate it
- * each time on the data requests and timeout reads. It consists of the
- * delay introduced by the internal ref-clock timer (N / Fclk) and the
- * constant timeout caused by each conversion latency (Tmin):
- * Ttotal = N / Fclk + Tmin
- * If alarms are enabled the sensors are polled one after another and
- * in order to get the next measurement of a particular sensor the
- * caller will have to wait for at most until all the others are
- * polled. In that case the formulae will look a bit different:
- * Ttotal = 5 * (N / Fclk + Tmin)
- */
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
- pvt->timeout = ktime_set(PVT_SENSORS_NUM * PVT_TOUT_DEF, 0);
- pvt->timeout = ktime_divns(pvt->timeout, rate);
- pvt->timeout = ktime_add_ns(pvt->timeout, PVT_SENSORS_NUM * PVT_TOUT_MIN);
-#else
- pvt->timeout = ktime_set(PVT_TOUT_DEF, 0);
- pvt->timeout = ktime_divns(pvt->timeout, rate);
- pvt->timeout = ktime_add_ns(pvt->timeout, PVT_TOUT_MIN);
-#endif
-
- trim = PVT_TRIM_DEF;
- if (!of_property_read_u32(pvt->dev->of_node,
- "baikal,pvt-temp-offset-millicelsius", &temp))
- trim = pvt_calc_trim(temp);
-
- pvt_set_trim(pvt, trim);
-
- return 0;
-}
-
-static int pvt_request_irq(struct pvt_hwmon *pvt)
-{
- struct platform_device *pdev = to_platform_device(pvt->dev);
- int ret;
-
- pvt->irq = platform_get_irq(pdev, 0);
- if (pvt->irq < 0)
- return pvt->irq;
-
- ret = devm_request_threaded_irq(pvt->dev, pvt->irq,
- pvt_hard_isr, pvt_soft_isr,
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
- IRQF_SHARED | IRQF_TRIGGER_HIGH |
- IRQF_ONESHOT,
-#else
- IRQF_SHARED | IRQF_TRIGGER_HIGH,
-#endif
- "pvt", pvt);
- if (ret) {
- dev_err(pvt->dev, "Couldn't request PVT IRQ\n");
- return ret;
- }
-
- return 0;
-}
-
-static int pvt_create_hwmon(struct pvt_hwmon *pvt)
-{
- pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt,
- &pvt_hwmon_info, NULL);
- if (IS_ERR(pvt->hwmon)) {
- dev_err(pvt->dev, "Couldn't create hwmon device\n");
- return PTR_ERR(pvt->hwmon);
- }
-
- return 0;
-}
-
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
-
-static void pvt_disable_iface(void *data)
-{
- struct pvt_hwmon *pvt = data;
-
- mutex_lock(&pvt->iface_mtx);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
- PVT_INTR_DVALID);
- mutex_unlock(&pvt->iface_mtx);
-}
-
-static int pvt_enable_iface(struct pvt_hwmon *pvt)
-{
- int ret;
-
- ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt);
- if (ret) {
- dev_err(pvt->dev, "Can't add PVT disable interface action\n");
- return ret;
- }
-
- /*
- * Enable sensors data conversion and IRQ. We need to lock the
- * interface mutex since hwmon has just been created and the
- * corresponding sysfs files are accessible from user-space,
- * which theoretically may cause races.
- */
- mutex_lock(&pvt->iface_mtx);
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
- mutex_unlock(&pvt->iface_mtx);
-
- return 0;
-}
-
-#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
-
-static int pvt_enable_iface(struct pvt_hwmon *pvt)
-{
- return 0;
-}
-
-#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
-
-static int pvt_probe(struct platform_device *pdev)
-{
- struct pvt_hwmon *pvt;
- int ret;
-
- pvt = pvt_create_data(pdev);
- if (IS_ERR(pvt))
- return PTR_ERR(pvt);
-
- ret = pvt_request_regs(pvt);
- if (ret)
- return ret;
-
- ret = pvt_request_clks(pvt);
- if (ret)
- return ret;
-
- ret = pvt_check_pwr(pvt);
- if (ret)
- return ret;
-
- ret = pvt_init_iface(pvt);
- if (ret)
- return ret;
-
- ret = pvt_request_irq(pvt);
- if (ret)
- return ret;
-
- ret = pvt_create_hwmon(pvt);
- if (ret)
- return ret;
-
- ret = pvt_enable_iface(pvt);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static const struct of_device_id pvt_of_match[] = {
- { .compatible = "baikal,bt1-pvt" },
- { }
-};
-MODULE_DEVICE_TABLE(of, pvt_of_match);
-
-static struct platform_driver pvt_driver = {
- .probe = pvt_probe,
- .driver = {
- .name = "bt1-pvt",
- .of_match_table = pvt_of_match
- }
-};
-module_platform_driver(pvt_driver);
-
-MODULE_AUTHOR("Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>");
-MODULE_DESCRIPTION("Baikal-T1 PVT driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h
deleted file mode 100644
index 93b8dd5e7c94..000000000000
--- a/drivers/hwmon/bt1-pvt.h
+++ /dev/null
@@ -1,247 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
- *
- * Baikal-T1 Process, Voltage, Temperature sensor driver
- */
-#ifndef __HWMON_BT1_PVT_H__
-#define __HWMON_BT1_PVT_H__
-
-#include <linux/completion.h>
-#include <linux/hwmon.h>
-#include <linux/kernel.h>
-#include <linux/ktime.h>
-#include <linux/mutex.h>
-#include <linux/seqlock.h>
-
-/* Baikal-T1 PVT registers and their bitfields */
-#define PVT_CTRL 0x00
-#define PVT_CTRL_EN BIT(0)
-#define PVT_CTRL_MODE_FLD 1
-#define PVT_CTRL_MODE_MASK GENMASK(3, PVT_CTRL_MODE_FLD)
-#define PVT_CTRL_MODE_TEMP 0x0
-#define PVT_CTRL_MODE_VOLT 0x1
-#define PVT_CTRL_MODE_LVT 0x2
-#define PVT_CTRL_MODE_HVT 0x4
-#define PVT_CTRL_MODE_SVT 0x6
-#define PVT_CTRL_TRIM_FLD 4
-#define PVT_CTRL_TRIM_MASK GENMASK(8, PVT_CTRL_TRIM_FLD)
-#define PVT_DATA 0x04
-#define PVT_DATA_VALID BIT(10)
-#define PVT_DATA_DATA_FLD 0
-#define PVT_DATA_DATA_MASK GENMASK(9, PVT_DATA_DATA_FLD)
-#define PVT_TTHRES 0x08
-#define PVT_VTHRES 0x0C
-#define PVT_LTHRES 0x10
-#define PVT_HTHRES 0x14
-#define PVT_STHRES 0x18
-#define PVT_THRES_LO_FLD 0
-#define PVT_THRES_LO_MASK GENMASK(9, PVT_THRES_LO_FLD)
-#define PVT_THRES_HI_FLD 10
-#define PVT_THRES_HI_MASK GENMASK(19, PVT_THRES_HI_FLD)
-#define PVT_TTIMEOUT 0x1C
-#define PVT_INTR_STAT 0x20
-#define PVT_INTR_MASK 0x24
-#define PVT_RAW_INTR_STAT 0x28
-#define PVT_INTR_DVALID BIT(0)
-#define PVT_INTR_TTHRES_LO BIT(1)
-#define PVT_INTR_TTHRES_HI BIT(2)
-#define PVT_INTR_VTHRES_LO BIT(3)
-#define PVT_INTR_VTHRES_HI BIT(4)
-#define PVT_INTR_LTHRES_LO BIT(5)
-#define PVT_INTR_LTHRES_HI BIT(6)
-#define PVT_INTR_HTHRES_LO BIT(7)
-#define PVT_INTR_HTHRES_HI BIT(8)
-#define PVT_INTR_STHRES_LO BIT(9)
-#define PVT_INTR_STHRES_HI BIT(10)
-#define PVT_INTR_ALL GENMASK(10, 0)
-#define PVT_CLR_INTR 0x2C
-
-/*
- * PVT sensors-related limits and default values
- * @PVT_TEMP_MIN: Minimal temperature in millidegrees of Celsius.
- * @PVT_TEMP_MAX: Maximal temperature in millidegrees of Celsius.
- * @PVT_TEMP_CHS: Number of temperature hwmon channels.
- * @PVT_VOLT_MIN: Minimal voltage in mV.
- * @PVT_VOLT_MAX: Maximal voltage in mV.
- * @PVT_VOLT_CHS: Number of voltage hwmon channels.
- * @PVT_DATA_MIN: Minimal PVT raw data value.
- * @PVT_DATA_MAX: Maximal PVT raw data value.
- * @PVT_TRIM_MIN: Minimal temperature sensor trim value.
- * @PVT_TRIM_MAX: Maximal temperature sensor trim value.
- * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value
- * when one is determined for Baikal-T1 SoC).
- * @PVT_TRIM_TEMP: Maximum temperature encoded by the trim factor.
- * @PVT_TRIM_STEP: Temperature stride corresponding to the trim value.
- * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds.
- * @PVT_TOUT_DEF: Default data measurements timeout. In case if alarms are
- * activated the PVT IRQ is enabled to be raised after each
- * conversion in order to have the thresholds checked and the
- * converted value cached. Too frequent conversions may cause
- * the system CPU overload. Lets set the 50ms delay between
- * them by default to prevent this.
- */
-#define PVT_TEMP_MIN -48380L
-#define PVT_TEMP_MAX 147438L
-#define PVT_TEMP_CHS 1
-#define PVT_VOLT_MIN 620L
-#define PVT_VOLT_MAX 1168L
-#define PVT_VOLT_CHS 4
-#define PVT_DATA_MIN 0
-#define PVT_DATA_MAX (PVT_DATA_DATA_MASK >> PVT_DATA_DATA_FLD)
-#define PVT_TRIM_MIN 0
-#define PVT_TRIM_MAX (PVT_CTRL_TRIM_MASK >> PVT_CTRL_TRIM_FLD)
-#define PVT_TRIM_TEMP 7130
-#define PVT_TRIM_STEP (PVT_TRIM_TEMP / PVT_TRIM_MAX)
-#define PVT_TRIM_DEF 0
-#define PVT_TOUT_MIN (NSEC_PER_SEC / 3000)
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
-# define PVT_TOUT_DEF 60000
-#else
-# define PVT_TOUT_DEF 0
-#endif
-
-/*
- * enum pvt_sensor_type - Baikal-T1 PVT sensor types (correspond to each PVT
- * sampling mode)
- * @PVT_SENSOR*: helpers to traverse the sensors in loops.
- * @PVT_TEMP: PVT Temperature sensor.
- * @PVT_VOLT: PVT Voltage sensor.
- * @PVT_LVT: PVT Low-Voltage threshold sensor.
- * @PVT_HVT: PVT High-Voltage threshold sensor.
- * @PVT_SVT: PVT Standard-Voltage threshold sensor.
- */
-enum pvt_sensor_type {
- PVT_SENSOR_FIRST,
- PVT_TEMP = PVT_SENSOR_FIRST,
- PVT_VOLT,
- PVT_LVT,
- PVT_HVT,
- PVT_SVT,
- PVT_SENSOR_LAST = PVT_SVT,
- PVT_SENSORS_NUM
-};
-
-/*
- * enum pvt_clock_type - Baikal-T1 PVT clocks.
- * @PVT_CLOCK_APB: APB clock.
- * @PVT_CLOCK_REF: PVT reference clock.
- */
-enum pvt_clock_type {
- PVT_CLOCK_APB,
- PVT_CLOCK_REF,
- PVT_CLOCK_NUM
-};
-
-/*
- * struct pvt_sensor_info - Baikal-T1 PVT sensor informational structure
- * @channel: Sensor channel ID.
- * @label: hwmon sensor label.
- * @mode: PVT mode corresponding to the channel.
- * @thres_base: upper and lower threshold values of the sensor.
- * @thres_sts_lo: low threshold status bitfield.
- * @thres_sts_hi: high threshold status bitfield.
- * @type: Sensor type.
- * @attr_min_alarm: Min alarm attribute ID.
- * @attr_min_alarm: Max alarm attribute ID.
- */
-struct pvt_sensor_info {
- int channel;
- const char *label;
- u32 mode;
- unsigned long thres_base;
- u32 thres_sts_lo;
- u32 thres_sts_hi;
- enum hwmon_sensor_types type;
- u32 attr_min_alarm;
- u32 attr_max_alarm;
-};
-
-#define PVT_SENSOR_INFO(_ch, _label, _type, _mode, _thres) \
- { \
- .channel = _ch, \
- .label = _label, \
- .mode = PVT_CTRL_MODE_ ##_mode, \
- .thres_base = PVT_ ##_thres, \
- .thres_sts_lo = PVT_INTR_ ##_thres## _LO, \
- .thres_sts_hi = PVT_INTR_ ##_thres## _HI, \
- .type = _type, \
- .attr_min_alarm = _type## _min, \
- .attr_max_alarm = _type## _max, \
- }
-
-/*
- * struct pvt_cache - PVT sensors data cache
- * @data: data cache in raw format.
- * @thres_sts_lo: low threshold status saved on the previous data conversion.
- * @thres_sts_hi: high threshold status saved on the previous data conversion.
- * @data_seqlock: cached data seq-lock.
- * @conversion: data conversion completion.
- */
-struct pvt_cache {
- u32 data;
-#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
- seqlock_t data_seqlock;
- u32 thres_sts_lo;
- u32 thres_sts_hi;
-#else
- struct completion conversion;
-#endif
-};
-
-/*
- * struct pvt_hwmon - Baikal-T1 PVT private data
- * @dev: device structure of the PVT platform device.
- * @hwmon: hwmon device structure.
- * @regs: pointer to the Baikal-T1 PVT registers region.
- * @irq: PVT events IRQ number.
- * @clks: Array of the PVT clocks descriptor (APB/ref clocks).
- * @ref_clk: Pointer to the reference clocks descriptor.
- * @iface_mtx: Generic interface mutex (used to lock the alarm registers
- * when the alarms enabled, or the data conversion interface
- * if alarms are disabled).
- * @sensor: current PVT sensor the data conversion is being performed for.
- * @cache: data cache descriptor.
- * @timeout: conversion timeout cache.
- */
-struct pvt_hwmon {
- struct device *dev;
- struct device *hwmon;
-
- void __iomem *regs;
- int irq;
-
- struct clk_bulk_data clks[PVT_CLOCK_NUM];
-
- struct mutex iface_mtx;
- enum pvt_sensor_type sensor;
- struct pvt_cache cache[PVT_SENSORS_NUM];
- ktime_t timeout;
-};
-
-/*
- * struct pvt_poly_term - a term descriptor of the PVT data translation
- * polynomial
- * @deg: degree of the term.
- * @coef: multiplication factor of the term.
- * @divider: distributed divider per each degree.
- * @divider_leftover: divider leftover, which couldn't be redistributed.
- */
-struct pvt_poly_term {
- unsigned int deg;
- long coef;
- long divider;
- long divider_leftover;
-};
-
-/*
- * struct pvt_poly - PVT data translation polynomial descriptor
- * @total_divider: total data divider.
- * @terms: polynomial terms up to a free one.
- */
-struct pvt_poly {
- long total_divider;
- struct pvt_poly_term terms[];
-};
-
-#endif /* __HWMON_BT1_PVT_H__ */
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index ccce948a4306..964a8cb278f1 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -40,7 +40,7 @@ static ssize_t power_state_show(struct device *dev, struct device_attribute *att
retval = regmap_read(data->regmap, 0x03, &val);
if (retval < 0)
return retval;
- return sprintf(buf, "%d\n", !!(val & BIT(6)));
+ return sysfs_emit(buf, "%d\n", !!(val & BIT(6)));
}
static ssize_t power_state_store(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c
index 1729729b135f..80de5f20781e 100644
--- a/drivers/hwmon/gpd-fan.c
+++ b/drivers/hwmon/gpd-fan.c
@@ -210,6 +210,14 @@ static const struct dmi_system_id dmi_table[] = {
.driver_data = &gpd_duo_drvdata,
},
{
+ // GPD Win 5 with AMD AI MAX 395
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1618-05"),
+ },
+ .driver_data = &gpd_duo_drvdata,
+ },
+ {
// GPD Pocket 4
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 9695dca62a7e..6812d1fd7c28 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -505,6 +505,7 @@ static bool is_string_attr(enum hwmon_sensor_types type, u32 attr)
(type == hwmon_curr && attr == hwmon_curr_label) ||
(type == hwmon_power && attr == hwmon_power_label) ||
(type == hwmon_energy && attr == hwmon_energy_label) ||
+ (type == hwmon_energy64 && attr == hwmon_energy_label) ||
(type == hwmon_humidity && attr == hwmon_humidity_label) ||
(type == hwmon_fan && attr == hwmon_fan_label);
}
diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c
index bd7b3380d847..a116f1600e81 100644
--- a/drivers/hwmon/ina209.c
+++ b/drivers/hwmon/ina209.c
@@ -27,8 +27,6 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
-#include <linux/platform_data/ina2xx.h>
-
/* register definitions */
#define INA209_CONFIGURATION 0x00
#define INA209_STATUS 0x01
@@ -487,7 +485,6 @@ static void ina209_restore_conf(struct i2c_client *client,
static int ina209_init_client(struct i2c_client *client,
struct ina209_data *data)
{
- struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);
u32 shunt;
int reg;
@@ -501,12 +498,8 @@ static int ina209_init_client(struct i2c_client *client,
return reg;
data->config_orig = reg;
- if (pdata) {
- if (pdata->shunt_uohms <= 0)
- return -EINVAL;
- shunt = pdata->shunt_uohms;
- } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor",
- &shunt)) {
+ if (!of_property_read_u32(client->dev.of_node, "shunt-resistor",
+ &shunt)) {
if (shunt == 0)
return -EINVAL;
} else {
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 69ac0468dee4..613ffb622b7c 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -1,22 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Driver for Texas Instruments INA219, INA226 power monitor chips
- *
- * INA219:
- * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface
- * Datasheet: https://www.ti.com/product/ina219
- *
- * INA220:
- * Bi-Directional Current/Power Monitor with I2C Interface
- * Datasheet: https://www.ti.com/product/ina220
- *
- * INA226:
- * Bi-Directional Current/Power Monitor with I2C Interface
- * Datasheet: https://www.ti.com/product/ina226
- *
- * INA230:
- * Bi-directional Current/Power Monitor with I2C Interface
- * Datasheet: https://www.ti.com/product/ina230
+ * Driver for Texas Instruments INA219, INA226 and register-layout compatible
+ * current/power monitor chips with I2C Interface
*
* Copyright (C) 2012 Lothar Felten <lothar.felten@gmail.com>
* Thanks to Jan Volkering
@@ -49,7 +34,6 @@
/* INA226 register definitions */
#define INA226_MASK_ENABLE 0x06
#define INA226_ALERT_LIMIT 0x07
-#define INA226_DIE_ID 0xFF
/* SY24655 register definitions */
#define SY24655_EIN 0x0A
@@ -135,18 +119,27 @@ static const struct regmap_config ina2xx_regmap_config = {
.writeable_reg = ina2xx_writeable_reg,
};
-enum ina2xx_ids { ina219, ina226, ina260, sy24655 };
+enum ina2xx_ids {
+ ina219,
+ ina226,
+ ina234,
+ ina260,
+ sy24655
+};
struct ina2xx_config {
u16 config_default;
bool has_alerts; /* chip supports alerts and limits */
bool has_ishunt; /* chip has internal shunt resistor */
- bool has_power_average; /* chip has internal shunt resistor */
+ bool has_power_average; /* chip supports average power */
+ bool has_update_interval;
int calibration_value;
int shunt_div;
+ int shunt_voltage_shift;
int bus_voltage_shift;
int bus_voltage_lsb; /* uV */
int power_lsb_factor;
+ int current_shift;
};
struct ina2xx_data {
@@ -165,44 +158,70 @@ static const struct ina2xx_config ina2xx_config[] = {
.config_default = INA219_CONFIG_DEFAULT,
.calibration_value = 4096,
.shunt_div = 100,
+ .shunt_voltage_shift = 0,
.bus_voltage_shift = 3,
.bus_voltage_lsb = 4000,
.power_lsb_factor = 20,
.has_alerts = false,
.has_ishunt = false,
.has_power_average = false,
+ .current_shift = 0,
+ .has_update_interval = false,
},
[ina226] = {
.config_default = INA226_CONFIG_DEFAULT,
.calibration_value = 2048,
.shunt_div = 400,
+ .shunt_voltage_shift = 0,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 25,
.has_alerts = true,
.has_ishunt = false,
.has_power_average = false,
+ .current_shift = 0,
+ .has_update_interval = true,
+ },
+ [ina234] = {
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_value = 2048,
+ .shunt_div = 25, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
+ .shunt_voltage_shift = 4,
+ .bus_voltage_shift = 4,
+ .bus_voltage_lsb = 25600,
+ .power_lsb_factor = 32,
+ .has_alerts = true,
+ .has_ishunt = false,
+ .has_power_average = false,
+ .current_shift = 4,
+ .has_update_interval = true,
},
[ina260] = {
.config_default = INA260_CONFIG_DEFAULT,
.shunt_div = 400,
+ .shunt_voltage_shift = 0,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 8,
.has_alerts = true,
.has_ishunt = true,
.has_power_average = false,
+ .current_shift = 0,
+ .has_update_interval = true,
},
[sy24655] = {
.config_default = SY24655_CONFIG_DEFAULT,
.calibration_value = 4096,
.shunt_div = 400,
+ .shunt_voltage_shift = 0,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 25,
.has_alerts = true,
.has_ishunt = false,
.has_power_average = true,
+ .current_shift = 0,
+ .has_update_interval = false,
},
};
@@ -255,7 +274,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
switch (reg) {
case INA2XX_SHUNT_VOLTAGE:
/* signed register */
- val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
+ val = (s16)regval >> data->config->shunt_voltage_shift;
+ val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
break;
case INA2XX_BUS_VOLTAGE:
val = (regval >> data->config->bus_voltage_shift) *
@@ -267,7 +287,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
break;
case INA2XX_CURRENT:
/* signed register, result in mA */
- val = (s16)regval * data->current_lsb_uA;
+ val = ((s16)regval >> data->config->current_shift) *
+ data->current_lsb_uA;
val = DIV_ROUND_CLOSEST(val, 1000);
break;
case INA2XX_CALIBRATION:
@@ -361,6 +382,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
case INA2XX_SHUNT_VOLTAGE:
val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
val *= data->config->shunt_div;
+ val <<= data->config->shunt_voltage_shift;
return clamp_val(val, 0, SHRT_MAX);
case INA2XX_BUS_VOLTAGE:
val = clamp_val(val, 0, 200000);
@@ -375,6 +397,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
/* signed register, result in mA */
val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
+ val <<= data->config->current_shift;
return clamp_val(val, SHRT_MIN, SHRT_MAX);
default:
/* programmer goofed */
@@ -706,7 +729,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
const struct ina2xx_data *data = _data;
bool has_alerts = data->config->has_alerts;
bool has_power_average = data->config->has_power_average;
- enum ina2xx_ids chip = data->chip;
+ bool has_update_interval = data->config->has_update_interval;
switch (type) {
case hwmon_in:
@@ -768,7 +791,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
case hwmon_chip:
switch (attr) {
case hwmon_chip_update_interval:
- if (chip == ina226 || chip == ina260)
+ if (has_update_interval)
return 0644;
break;
default:
@@ -982,6 +1005,7 @@ static const struct i2c_device_id ina2xx_id[] = {
{ "ina226", ina226 },
{ "ina230", ina226 },
{ "ina231", ina226 },
+ { "ina234", ina234 },
{ "ina260", ina260 },
{ "sy24655", sy24655 },
{ }
@@ -1014,6 +1038,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
.data = (void *)ina226
},
{
+ .compatible = "ti,ina234",
+ .data = (void *)ina234
+ },
+ {
.compatible = "ti,ina260",
.data = (void *)ina260
},
diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c
index c2e559dde63f..96fcfbfff48f 100644
--- a/drivers/hwmon/isl28022.c
+++ b/drivers/hwmon/isl28022.c
@@ -9,6 +9,7 @@
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -185,8 +186,8 @@ static int isl28022_read_power(struct device *dev, u32 attr, long *val)
ISL28022_REG_POWER, &regval);
if (err < 0)
return err;
- *val = ((51200000L * ((long)data->gain)) /
- (long)data->shunt) * (long)regval;
+ *val = min(div_u64(51200000ULL * data->gain * regval,
+ data->shunt), LONG_MAX);
break;
default:
return -EOPNOTSUPP;
@@ -337,21 +338,28 @@ DEFINE_SHOW_ATTRIBUTE(shunt_voltage);
*/
static int isl28022_read_properties(struct device *dev, struct isl28022_data *data)
{
+ const char *propname;
u32 val;
int err;
- err = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val);
- if (err == -EINVAL)
+ propname = "shunt-resistor-micro-ohms";
+ if (device_property_present(dev, propname)) {
+ err = device_property_read_u32(dev, propname, &val);
+ if (err)
+ return err;
+ } else {
val = 10000;
- else if (err < 0)
- return err;
+ }
data->shunt = val;
- err = device_property_read_u32(dev, "renesas,shunt-range-microvolt", &val);
- if (err == -EINVAL)
+ propname = "renesas,shunt-range-microvolt";
+ if (device_property_present(dev, propname)) {
+ err = device_property_read_u32(dev, propname, &val);
+ if (err)
+ return err;
+ } else {
val = 320000;
- else if (err < 0)
- return err;
+ }
switch (val) {
case 40000:
@@ -375,20 +383,19 @@ static int isl28022_read_properties(struct device *dev, struct isl28022_data *da
goto shunt_invalid;
break;
default:
- return dev_err_probe(dev, -EINVAL,
- "renesas,shunt-range-microvolt invalid value %d\n",
- val);
+ return dev_err_probe(dev, -EINVAL, "%s invalid value %u\n", propname, val);
}
- err = device_property_read_u32(dev, "renesas,average-samples", &val);
- if (err == -EINVAL)
+ propname = "renesas,average-samples";
+ if (device_property_present(dev, propname)) {
+ err = device_property_read_u32(dev, propname, &val);
+ if (err)
+ return err;
+ } else {
val = 1;
- else if (err < 0)
- return err;
+ }
if (val > 128 || hweight32(val) != 1)
- return dev_err_probe(dev, -EINVAL,
- "renesas,average-samples invalid value %d\n",
- val);
+ return dev_err_probe(dev, -EINVAL, "%s invalid value %u\n", propname, val);
data->average = val;
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 5cfb98a0512f..5fd310662ee4 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -16,6 +16,7 @@
* IT8622E Super I/O chip w/LPC interface
* IT8623E Super I/O chip w/LPC interface
* IT8628E Super I/O chip w/LPC interface
+ * IT8689E Super I/O chip w/LPC interface
* IT8705F Super I/O chip w/LPC interface
* IT8712F Super I/O chip w/LPC interface
* IT8716F Super I/O chip w/LPC interface
@@ -64,7 +65,7 @@
enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732,
it8771, it8772, it8781, it8782, it8783, it8786, it8790,
- it8792, it8603, it8620, it8622, it8628, it87952 };
+ it8792, it8603, it8620, it8622, it8628, it8689, it87952 };
static struct platform_device *it87_pdev[2];
@@ -162,6 +163,7 @@ static inline void superio_exit(int ioreg, bool noexit)
#define IT8622E_DEVID 0x8622
#define IT8623E_DEVID 0x8623
#define IT8628E_DEVID 0x8628
+#define IT8689E_DEVID 0x8689
#define IT87952E_DEVID 0x8695
/* Logical device 4 (Environmental Monitor) registers */
@@ -502,6 +504,15 @@ static const struct it87_devices it87_devices[] = {
| FEAT_SIX_TEMP | FEAT_VIN3_5V | FEAT_FANCTL_ONOFF,
.peci_mask = 0x07,
},
+ [it8689] = {
+ .name = "it8689",
+ .model = "IT8689E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_SIX_FANS | FEAT_IN7_INTERNAL
+ | FEAT_SIX_PWM | FEAT_PWM_FREQ2 | FEAT_SIX_TEMP | FEAT_AVCC3
+ | FEAT_FANCTL_ONOFF,
+ .smbus_bitmap = BIT(1) | BIT(2),
+ },
[it87952] = {
.name = "it87952",
.model = "IT87952E",
@@ -2785,6 +2796,9 @@ static int __init it87_find(int sioaddr, unsigned short *address,
case IT8628E_DEVID:
sio_data->type = it8628;
break;
+ case IT8689E_DEVID:
+ sio_data->type = it8689;
+ break;
case IT87952E_DEVID:
sio_data->type = it87952;
break;
@@ -3002,6 +3016,51 @@ static int __init it87_find(int sioaddr, unsigned short *address,
sio_data->beep_pin = superio_inb(sioaddr,
IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8689) {
+ int reg;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for pwm5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO1_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(4);
+
+ /* Check for fan4, fan5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG);
+ if (!(reg & BIT(5)))
+ sio_data->skip_fan |= BIT(3);
+ if (!(reg & BIT(4)))
+ sio_data->skip_fan |= BIT(4);
+
+ /* Check for pwm3, fan3 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+
+ /* Check for pwm4 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO4_REG);
+ if (reg & BIT(2))
+ sio_data->skip_pwm |= BIT(3);
+
+ /* Check for pwm2, fan2 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+ /* Check for pwm6, fan6 */
+ if (!(reg & BIT(7))) {
+ sio_data->skip_pwm |= BIT(5);
+ sio_data->skip_fan |= BIT(5);
+ }
+
+ /* in9 (AVCC3) is always internal, no PINX2 check needed */
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
} else if (sio_data->type == it8622) {
int reg;
diff --git a/drivers/hwmon/lattepanda-sigma-ec.c b/drivers/hwmon/lattepanda-sigma-ec.c
new file mode 100644
index 000000000000..f06097422201
--- /dev/null
+++ b/drivers/hwmon/lattepanda-sigma-ec.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for LattePanda Sigma EC.
+ *
+ * The LattePanda Sigma is an x86 SBC made by DFRobot with an ITE IT8613E
+ * Embedded Controller that manages a CPU fan and thermal sensors.
+ *
+ * The BIOS declares the ACPI Embedded Controller (PNP0C09) with _STA
+ * returning 0 and provides only stub ECRD/ECWT methods that return Zero
+ * for all registers. Since the kernel's ACPI EC subsystem never initializes,
+ * ec_read() is not available and direct port I/O to the standard ACPI EC
+ * ports (0x62/0x66) is used instead.
+ *
+ * Because ACPI never initializes the EC, there is no concurrent firmware
+ * access to these ports, and no ACPI Global Lock or namespace mutex is
+ * required. The hwmon with_info API serializes all sysfs callbacks,
+ * so no additional driver-level locking is needed.
+ *
+ * The EC register map was discovered by dumping all 256 registers,
+ * identifying those that change in real-time, and validating by physically
+ * stopping the fan and observing the RPM register drop to zero. The map
+ * has been verified on BIOS version 5.27; other versions may differ.
+ *
+ * Copyright (c) 2026 Mariano Abad <weimaraner@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/hwmon.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "lattepanda_sigma_ec"
+
+/* EC I/O ports (standard ACPI EC interface) */
+#define EC_DATA_PORT 0x62
+#define EC_CMD_PORT 0x66 /* also status port */
+
+/* EC commands */
+#define EC_CMD_READ 0x80
+
+/* EC status register bits */
+#define EC_STATUS_OBF 0x01 /* Output Buffer Full */
+#define EC_STATUS_IBF 0x02 /* Input Buffer Full */
+
+/* EC register offsets for LattePanda Sigma (BIOS 5.27) */
+#define EC_REG_FAN_RPM_HI 0x2E
+#define EC_REG_FAN_RPM_LO 0x2F
+#define EC_REG_TEMP_BOARD 0x60
+#define EC_REG_TEMP_CPU 0x70
+#define EC_REG_FAN_DUTY 0x93
+
+/*
+ * EC polling uses udelay() because the EC typically responds within a
+ * few microseconds. The kernel's own ACPI EC driver (drivers/acpi/ec.c)
+ * likewise uses udelay() for busy-polling with a per-poll delay of 550us.
+ *
+ * usleep_range() was tested but caused EC protocol failures: the EC
+ * clears its status flags within microseconds, and sleeping for 50-100us
+ * between polls allowed the flags to transition past the expected state.
+ *
+ * The worst-case total busy-wait of 25ms covers EC recovery after errors.
+ * In practice the EC responds within 10us so the loop exits immediately.
+ */
+#define EC_TIMEOUT_US 25000
+#define EC_POLL_US 1
+
+static bool force;
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force,
+ "Force loading on untested BIOS versions (default: false)");
+
+static struct platform_device *lps_ec_pdev;
+
+static int ec_wait_ibf_clear(void)
+{
+ int i;
+
+ for (i = 0; i < EC_TIMEOUT_US; i++) {
+ if (!(inb(EC_CMD_PORT) & EC_STATUS_IBF))
+ return 0;
+ udelay(EC_POLL_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int ec_wait_obf_set(void)
+{
+ int i;
+
+ for (i = 0; i < EC_TIMEOUT_US; i++) {
+ if (inb(EC_CMD_PORT) & EC_STATUS_OBF)
+ return 0;
+ udelay(EC_POLL_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int ec_read_reg(u8 reg, u8 *val)
+{
+ int ret;
+
+ ret = ec_wait_ibf_clear();
+ if (ret)
+ return ret;
+
+ outb(EC_CMD_READ, EC_CMD_PORT);
+
+ ret = ec_wait_ibf_clear();
+ if (ret)
+ return ret;
+
+ outb(reg, EC_DATA_PORT);
+
+ ret = ec_wait_obf_set();
+ if (ret)
+ return ret;
+
+ *val = inb(EC_DATA_PORT);
+ return 0;
+}
+
+/*
+ * Read a 16-bit big-endian value from two consecutive EC registers.
+ *
+ * The EC may update the register pair between reading the high and low
+ * bytes, which could produce a corrupted value if the high byte rolls
+ * over (e.g., 0x0100 -> 0x00FF read as 0x01FF). Guard against this by
+ * re-reading the high byte after reading the low byte. If the high byte
+ * changed, re-read the low byte to get a consistent pair.
+ * See also lm90_read16() which uses the same approach.
+ */
+static int ec_read_reg16(u8 reg_hi, u8 reg_lo, u16 *val)
+{
+ int ret;
+ u8 oldh, newh, lo;
+
+ ret = ec_read_reg(reg_hi, &oldh);
+ if (ret)
+ return ret;
+
+ ret = ec_read_reg(reg_lo, &lo);
+ if (ret)
+ return ret;
+
+ ret = ec_read_reg(reg_hi, &newh);
+ if (ret)
+ return ret;
+
+ if (oldh != newh) {
+ ret = ec_read_reg(reg_lo, &lo);
+ if (ret)
+ return ret;
+ }
+
+ *val = ((u16)newh << 8) | lo;
+ return 0;
+}
+
+static int
+lps_ec_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel,
+ const char **str)
+{
+ switch (type) {
+ case hwmon_fan:
+ *str = "CPU Fan";
+ return 0;
+ case hwmon_temp:
+ *str = channel == 0 ? "Board Temp" : "CPU Temp";
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t
+lps_ec_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ if (attr == hwmon_fan_input || attr == hwmon_fan_label)
+ return 0444;
+ break;
+ case hwmon_temp:
+ if (attr == hwmon_temp_input || attr == hwmon_temp_label)
+ return 0444;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int
+lps_ec_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ u16 rpm;
+ u8 v;
+ int ret;
+
+ switch (type) {
+ case hwmon_fan:
+ if (attr != hwmon_fan_input)
+ return -EOPNOTSUPP;
+ ret = ec_read_reg16(EC_REG_FAN_RPM_HI,
+ EC_REG_FAN_RPM_LO, &rpm);
+ if (ret)
+ return ret;
+ *val = rpm;
+ return 0;
+
+ case hwmon_temp:
+ if (attr != hwmon_temp_input)
+ return -EOPNOTSUPP;
+ ret = ec_read_reg(channel == 0 ? EC_REG_TEMP_BOARD
+ : EC_REG_TEMP_CPU,
+ &v);
+ if (ret)
+ return ret;
+ /* EC reports unsigned 8-bit temperature in degrees Celsius */
+ *val = (unsigned long)v * 1000;
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_channel_info * const lps_ec_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL),
+ NULL
+};
+
+static const struct hwmon_ops lps_ec_ops = {
+ .is_visible = lps_ec_is_visible,
+ .read = lps_ec_read,
+ .read_string = lps_ec_read_string,
+};
+
+static const struct hwmon_chip_info lps_ec_chip_info = {
+ .ops = &lps_ec_ops,
+ .info = lps_ec_info,
+};
+
+static int lps_ec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hwmon;
+ u8 test;
+ int ret;
+
+ if (!devm_request_region(dev, EC_DATA_PORT, 1, DRIVER_NAME))
+ return dev_err_probe(dev, -EBUSY,
+ "Failed to request EC data port 0x%x\n",
+ EC_DATA_PORT);
+
+ if (!devm_request_region(dev, EC_CMD_PORT, 1, DRIVER_NAME))
+ return dev_err_probe(dev, -EBUSY,
+ "Failed to request EC cmd port 0x%x\n",
+ EC_CMD_PORT);
+
+ /* Sanity check: verify EC is responsive */
+ ret = ec_read_reg(EC_REG_FAN_DUTY, &test);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "EC not responding on ports 0x%x/0x%x\n",
+ EC_DATA_PORT, EC_CMD_PORT);
+
+ hwmon = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, NULL,
+ &lps_ec_chip_info, NULL);
+ if (IS_ERR(hwmon))
+ return dev_err_probe(dev, PTR_ERR(hwmon),
+ "Failed to register hwmon device\n");
+
+ dev_info(dev, "EC hwmon registered (fan duty: %u%%)\n", test);
+ return 0;
+}
+
+/* DMI table with strict BIOS version match (override with force=1) */
+static const struct dmi_system_id lps_ec_dmi_table[] = {
+ {
+ .ident = "LattePanda Sigma",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LattePanda"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LattePanda Sigma"),
+ DMI_MATCH(DMI_BIOS_VERSION, "5.27"),
+ },
+ },
+ { } /* terminator */
+};
+MODULE_DEVICE_TABLE(dmi, lps_ec_dmi_table);
+
+/* Loose table (vendor + product only) for use with force=1 */
+static const struct dmi_system_id lps_ec_dmi_table_force[] = {
+ {
+ .ident = "LattePanda Sigma",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LattePanda"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LattePanda Sigma"),
+ },
+ },
+ { } /* terminator */
+};
+
+static struct platform_driver lps_ec_driver = {
+ .probe = lps_ec_probe,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init lps_ec_init(void)
+{
+ int ret;
+
+ if (!dmi_check_system(lps_ec_dmi_table)) {
+ if (!force || !dmi_check_system(lps_ec_dmi_table_force))
+ return -ENODEV;
+ pr_warn("%s: BIOS version not verified, loading due to force=1\n",
+ DRIVER_NAME);
+ }
+
+ ret = platform_driver_register(&lps_ec_driver);
+ if (ret)
+ return ret;
+
+ lps_ec_pdev = platform_device_register_simple(DRIVER_NAME, -1,
+ NULL, 0);
+ if (IS_ERR(lps_ec_pdev)) {
+ platform_driver_unregister(&lps_ec_driver);
+ return PTR_ERR(lps_ec_pdev);
+ }
+
+ return 0;
+}
+
+static void __exit lps_ec_exit(void)
+{
+ platform_device_unregister(lps_ec_pdev);
+ platform_driver_unregister(&lps_ec_driver);
+}
+
+module_init(lps_ec_init);
+module_exit(lps_ec_exit);
+
+MODULE_AUTHOR("Mariano Abad <weimaraner@gmail.com>");
+MODULE_DESCRIPTION("Hardware monitoring driver for LattePanda Sigma EC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index eda93a8c23c9..f1a1e5b888f6 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -108,6 +108,7 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
#define PCT2075_REG_IDLE 0x04
struct lm75_data {
+ const char *label;
struct regmap *regmap;
u16 orig_conf;
u8 resolution; /* In bits, 9 to 16 */
@@ -363,6 +364,16 @@ static irqreturn_t lm75_alarm_handler(int irq, void *private)
return IRQ_HANDLED;
}
+static int lm75_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct lm75_data *data = dev_get_drvdata(dev);
+
+ *str = data->label;
+
+ return 0;
+}
+
static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@@ -534,6 +545,9 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_temp_input:
return 0444;
+ case hwmon_temp_label:
+ /* Hide label node if label is not provided */
+ return config_data->label ? 0444 : 0;
case hwmon_temp_max:
case hwmon_temp_max_hyst:
return 0644;
@@ -553,13 +567,14 @@ static const struct hwmon_channel_info * const lm75_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp,
- HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+ HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | HWMON_T_MAX_HYST |
HWMON_T_ALARM),
NULL
};
static const struct hwmon_ops lm75_hwmon_ops = {
.is_visible = lm75_is_visible,
+ .read_string = lm75_read_string,
.read = lm75_read,
.write = lm75_write,
};
@@ -721,6 +736,9 @@ static int lm75_generic_probe(struct device *dev, const char *name,
/* needed by custom regmap callbacks */
dev_set_drvdata(dev, data);
+ /* Save the connected input label if available */
+ device_property_read_string(dev, "label", &data->label);
+
data->kind = kind;
data->regmap = regmap;
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index db6534e67991..b9084424160d 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -1328,15 +1328,16 @@ static int ltc4282_setup(struct ltc4282_state *st, struct device *dev)
if (ret)
return ret;
+ /* default to 1 milli-ohm so we can probe without FW properties */
+ st->rsense = 1 * (NANO / MILLI);
ret = device_property_read_u32(dev, "adi,rsense-nano-ohms",
&st->rsense);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to read adi,rsense-nano-ohms\n");
- if (st->rsense < CENTI)
- return dev_err_probe(dev, -EINVAL,
- "adi,rsense-nano-ohms too small (< %lu)\n",
- CENTI);
+ if (!ret) {
+ if (st->rsense < CENTI)
+ return dev_err_probe(dev, -EINVAL,
+ "adi,rsense-nano-ohms too small (< %lu)\n",
+ CENTI);
+ }
/*
* The resolution for rsense is tenths of micro (eg: 62.5 uOhm) which
diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c
index 9a31ef388396..6c5c86c75c36 100644
--- a/drivers/hwmon/max31722.c
+++ b/drivers/hwmon/max31722.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
#define MAX31722_REG_CFG 0x00
#define MAX31722_REG_TEMP_LSB 0x01
@@ -56,7 +57,7 @@ static ssize_t max31722_temp_show(struct device *dev,
if (ret < 0)
return ret;
/* Keep 12 bits and multiply by the scale of 62.5 millidegrees/bit. */
- return sprintf(buf, "%d\n", (s16)le16_to_cpu(ret) * 125 / 32);
+ return sysfs_emit(buf, "%d\n", (s16)le16_to_cpu(ret) * 125 / 32);
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, max31722_temp, 0);
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 9649c6611d5f..56b8157885bb 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -27,6 +27,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/sysfs.h>
#include <linux/thermal.h>
/*
@@ -312,7 +313,7 @@ static ssize_t alarm_show(struct device *dev,
mutex_unlock(&data->update_lock);
}
- return sprintf(buf, "%d\n", alarm);
+ return sysfs_emit(buf, "%d\n", alarm);
}
static SENSOR_DEVICE_ATTR_RO(gpio1_alarm, alarm, MAX6650_ALRM_GPIO1);
diff --git a/drivers/hwmon/mcp9982.c b/drivers/hwmon/mcp9982.c
new file mode 100644
index 000000000000..26c69e3388ab
--- /dev/null
+++ b/drivers/hwmon/mcp9982.c
@@ -0,0 +1,998 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HWMON driver for MCP998X/33 and MCP998XD/33D Multichannel Automotive
+ * Temperature Monitor Family
+ *
+ * Copyright (C) 2026 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Victor Duicu <victor.duicu@microchip.com>
+ *
+ * Datasheet can be found here:
+ * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP998X-Family-Data-Sheet-DS20006827.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+#include <linux/util_macros.h>
+
+/* MCP9982 Registers */
+#define MCP9982_HIGH_BYTE_ADDR(index) (2 * (index))
+#define MCP9982_ONE_SHOT_ADDR 0x0A
+#define MCP9982_INTERNAL_HIGH_LIMIT_ADDR 0x0B
+#define MCP9982_INTERNAL_LOW_LIMIT_ADDR 0x0C
+#define MCP9982_EXT_HIGH_LIMIT_ADDR(index) (4 * ((index) - 1) + 0x0D)
+#define MCP9982_EXT_LOW_LIMIT_ADDR(index) (4 * ((index) - 1) + 0x0F)
+#define MCP9982_THERM_LIMIT_ADDR(index) ((index) + 0x1D)
+#define MCP9982_CFG_ADDR 0x22
+#define MCP9982_CONV_ADDR 0x24
+#define MCP9982_HYS_ADDR 0x25
+#define MCP9982_CONSEC_ALRT_ADDR 0x26
+#define MCP9982_ALRT_CFG_ADDR 0x27
+#define MCP9982_RUNNING_AVG_ADDR 0x28
+#define MCP9982_HOTTEST_CFG_ADDR 0x29
+#define MCP9982_STATUS_ADDR 0x2A
+#define MCP9982_EXT_FAULT_STATUS_ADDR 0x2B
+#define MCP9982_HIGH_LIMIT_STATUS_ADDR 0x2C
+#define MCP9982_LOW_LIMIT_STATUS_ADDR 0x2D
+#define MCP9982_THERM_LIMIT_STATUS_ADDR 0x2E
+#define MCP9982_HOTTEST_HIGH_BYTE_ADDR 0x2F
+#define MCP9982_HOTTEST_LOW_BYTE_ADDR 0x30
+#define MCP9982_HOTTEST_STATUS_ADDR 0x31
+#define MCP9982_THERM_SHTDWN_CFG_ADDR 0x32
+#define MCP9982_HRDW_THERM_SHTDWN_LIMIT_ADDR 0x33
+#define MCP9982_EXT_BETA_CFG_ADDR(index) ((index) + 0x33)
+#define MCP9982_EXT_IDEAL_ADDR(index) ((index) + 0x35)
+
+/* MCP9982 Bits */
+#define MCP9982_CFG_MSKAL BIT(7)
+#define MCP9982_CFG_RS BIT(6)
+#define MCP9982_CFG_ATTHM BIT(5)
+#define MCP9982_CFG_RECD12 BIT(4)
+#define MCP9982_CFG_RECD34 BIT(3)
+#define MCP9982_CFG_RANGE BIT(2)
+#define MCP9982_CFG_DA_ENA BIT(1)
+#define MCP9982_CFG_APDD BIT(0)
+
+#define MCP9982_STATUS_BUSY BIT(5)
+
+/* Constants and default values */
+#define MCP9982_MAX_NUM_CHANNELS 5
+#define MCP9982_BETA_AUTODETECT 16
+#define MCP9982_IDEALITY_DEFAULT 18
+#define MCP9982_OFFSET 64
+#define MCP9982_DEFAULT_CONSEC_ALRT_VAL 112
+#define MCP9982_DEFAULT_HYS_VAL 10
+#define MCP9982_DEFAULT_CONV_VAL 6
+#define MCP9982_WAKE_UP_TIME_US 125000
+#define MCP9982_WAKE_UP_TIME_MAX_US 130000
+#define MCP9982_HIGH_LIMIT_DEFAULT 85000
+#define MCP9982_LOW_LIMIT_DEFAULT 0
+
+static const struct hwmon_channel_info * const mcp9985_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ 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_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_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_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_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_CHANNEL_INFO(chip,
+ HWMON_C_UPDATE_INTERVAL),
+ NULL
+};
+
+/**
+ * struct mcp9982_features - features of a mcp9982 instance
+ * @name: chip's name
+ * @phys_channels: number of physical channels supported by the chip
+ * @hw_thermal_shutdown: presence of hardware thermal shutdown circuitry
+ * @allow_apdd: whether the chip supports enabling APDD
+ * @has_recd34: whether the chip has the channels that are affected by recd34
+ */
+struct mcp9982_features {
+ const char *name;
+ u8 phys_channels;
+ bool hw_thermal_shutdown;
+ bool allow_apdd;
+ bool has_recd34;
+};
+
+static const struct mcp9982_features mcp9933_chip_config = {
+ .name = "mcp9933",
+ .phys_channels = 3,
+ .hw_thermal_shutdown = false,
+ .allow_apdd = true,
+ .has_recd34 = false,
+};
+
+static const struct mcp9982_features mcp9933d_chip_config = {
+ .name = "mcp9933d",
+ .phys_channels = 3,
+ .hw_thermal_shutdown = true,
+ .allow_apdd = true,
+ .has_recd34 = false,
+};
+
+static const struct mcp9982_features mcp9982_chip_config = {
+ .name = "mcp9982",
+ .phys_channels = 2,
+ .hw_thermal_shutdown = false,
+ .allow_apdd = false,
+ .has_recd34 = false,
+};
+
+static const struct mcp9982_features mcp9982d_chip_config = {
+ .name = "mcp9982d",
+ .phys_channels = 2,
+ .hw_thermal_shutdown = true,
+ .allow_apdd = false,
+ .has_recd34 = false,
+};
+
+static const struct mcp9982_features mcp9983_chip_config = {
+ .name = "mcp9983",
+ .phys_channels = 3,
+ .hw_thermal_shutdown = false,
+ .allow_apdd = false,
+ .has_recd34 = true,
+};
+
+static const struct mcp9982_features mcp9983d_chip_config = {
+ .name = "mcp9983d",
+ .phys_channels = 3,
+ .hw_thermal_shutdown = true,
+ .allow_apdd = false,
+ .has_recd34 = true,
+};
+
+static const struct mcp9982_features mcp9984_chip_config = {
+ .name = "mcp9984",
+ .phys_channels = 4,
+ .hw_thermal_shutdown = false,
+ .allow_apdd = true,
+ .has_recd34 = true,
+};
+
+static const struct mcp9982_features mcp9984d_chip_config = {
+ .name = "mcp9984d",
+ .phys_channels = 4,
+ .hw_thermal_shutdown = true,
+ .allow_apdd = true,
+ .has_recd34 = true,
+};
+
+static const struct mcp9982_features mcp9985_chip_config = {
+ .name = "mcp9985",
+ .phys_channels = 5,
+ .hw_thermal_shutdown = false,
+ .allow_apdd = true,
+ .has_recd34 = true,
+};
+
+static const struct mcp9982_features mcp9985d_chip_config = {
+ .name = "mcp9985d",
+ .phys_channels = 5,
+ .hw_thermal_shutdown = true,
+ .allow_apdd = true,
+ .has_recd34 = true,
+};
+
+static const unsigned int mcp9982_update_interval[11] = {
+ 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 64, 32, 16
+};
+
+/* MCP9982 regmap configuration */
+static const struct regmap_range mcp9982_regmap_wr_ranges[] = {
+ regmap_reg_range(MCP9982_ONE_SHOT_ADDR, MCP9982_CFG_ADDR),
+ regmap_reg_range(MCP9982_CONV_ADDR, MCP9982_HOTTEST_CFG_ADDR),
+ regmap_reg_range(MCP9982_THERM_SHTDWN_CFG_ADDR, MCP9982_THERM_SHTDWN_CFG_ADDR),
+ regmap_reg_range(MCP9982_EXT_BETA_CFG_ADDR(1), MCP9982_EXT_IDEAL_ADDR(4)),
+};
+
+static const struct regmap_access_table mcp9982_regmap_wr_table = {
+ .yes_ranges = mcp9982_regmap_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp9982_regmap_wr_ranges),
+};
+
+static const struct regmap_range mcp9982_regmap_rd_ranges[] = {
+ regmap_reg_range(MCP9982_HIGH_BYTE_ADDR(0), MCP9982_CFG_ADDR),
+ regmap_reg_range(MCP9982_CONV_ADDR, MCP9982_EXT_IDEAL_ADDR(4)),
+};
+
+static const struct regmap_access_table mcp9982_regmap_rd_table = {
+ .yes_ranges = mcp9982_regmap_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp9982_regmap_rd_ranges),
+};
+
+static bool mcp9982_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCP9982_ONE_SHOT_ADDR:
+ case MCP9982_INTERNAL_HIGH_LIMIT_ADDR:
+ case MCP9982_INTERNAL_LOW_LIMIT_ADDR:
+ case MCP9982_EXT_LOW_LIMIT_ADDR(1):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(1) + 1:
+ case MCP9982_EXT_LOW_LIMIT_ADDR(2):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(2) + 1:
+ case MCP9982_EXT_LOW_LIMIT_ADDR(3):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(3) + 1:
+ case MCP9982_EXT_LOW_LIMIT_ADDR(4):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(4) + 1:
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(1):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(1) + 1:
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(2):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(2) + 1:
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(3):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(3) + 1:
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(4):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(4) + 1:
+ case MCP9982_THERM_LIMIT_ADDR(0):
+ case MCP9982_THERM_LIMIT_ADDR(1):
+ case MCP9982_THERM_LIMIT_ADDR(2):
+ case MCP9982_THERM_LIMIT_ADDR(3):
+ case MCP9982_THERM_LIMIT_ADDR(4):
+ case MCP9982_CFG_ADDR:
+ case MCP9982_CONV_ADDR:
+ case MCP9982_HYS_ADDR:
+ case MCP9982_CONSEC_ALRT_ADDR:
+ case MCP9982_ALRT_CFG_ADDR:
+ case MCP9982_RUNNING_AVG_ADDR:
+ case MCP9982_HOTTEST_CFG_ADDR:
+ case MCP9982_THERM_SHTDWN_CFG_ADDR:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config mcp9982_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &mcp9982_regmap_rd_table,
+ .wr_table = &mcp9982_regmap_wr_table,
+ .volatile_reg = mcp9982_is_volatile_reg,
+ .max_register = MCP9982_EXT_IDEAL_ADDR(4),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+/**
+ * struct mcp9982_priv - information about chip parameters
+ * @regmap: device register map
+ * @chip: pointer to structure holding chip features
+ * @labels: labels of the channels
+ * @interval_idx: index representing the current update interval
+ * @enabled_channel_mask: mask containing which channels should be enabled
+ * @num_channels: number of active physical channels
+ * @recd34_enable: state of Resistance Error Correction(REC) on channels 3 and 4
+ * @recd12_enable: state of Resistance Error Correction(REC) on channels 1 and 2
+ * @apdd_enable: state of anti-parallel diode mode
+ * @run_state: chip is in Run state, otherwise is in Standby state
+ */
+struct mcp9982_priv {
+ struct regmap *regmap;
+ const struct mcp9982_features *chip;
+ const char *labels[MCP9982_MAX_NUM_CHANNELS];
+ unsigned int interval_idx;
+ unsigned long enabled_channel_mask;
+ u8 num_channels;
+ bool recd34_enable;
+ bool recd12_enable;
+ bool apdd_enable;
+ bool run_state;
+};
+
+static int mcp9982_read_limit(struct mcp9982_priv *priv, u8 address, long *val)
+{
+ unsigned int limit, reg_high, reg_low;
+ int ret;
+
+ switch (address) {
+ case MCP9982_INTERNAL_HIGH_LIMIT_ADDR:
+ case MCP9982_INTERNAL_LOW_LIMIT_ADDR:
+ case MCP9982_THERM_LIMIT_ADDR(0):
+ case MCP9982_THERM_LIMIT_ADDR(1):
+ case MCP9982_THERM_LIMIT_ADDR(2):
+ case MCP9982_THERM_LIMIT_ADDR(3):
+ case MCP9982_THERM_LIMIT_ADDR(4):
+ ret = regmap_read(priv->regmap, address, &limit);
+ if (ret)
+ return ret;
+
+ *val = ((int)limit - MCP9982_OFFSET) * 1000;
+
+ return 0;
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(1):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(2):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(3):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(4):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(1):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(2):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(3):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(4):
+ /*
+ * In order to keep consistency with reading temperature memory region we will use
+ * single byte I2C read.
+ */
+ ret = regmap_read(priv->regmap, address, &reg_high);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, address + 1, &reg_low);
+ if (ret)
+ return ret;
+
+ *val = ((reg_high << 8) + reg_low) >> 5;
+ *val = (*val - (MCP9982_OFFSET << 3)) * 125;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long *val)
+{
+ struct mcp9982_priv *priv = dev_get_drvdata(dev);
+ unsigned int reg_high, reg_low, hyst, reg_status;
+ int ret;
+ u8 addr;
+
+ /*
+ * In Standby State the conversion cycle must be initated manually in
+ * order to read fresh temperature values and the status of the alarms.
+ */
+ if (!priv->run_state) {
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_crit_alarm:
+ ret = regmap_write(priv->regmap, MCP9982_ONE_SHOT_ADDR, 1);
+ if (ret)
+ return ret;
+ /*
+ * When the device is in Standby mode, 125 ms need
+ * to pass from writing in One Shot register before
+ * the conversion cycle begins.
+ */
+ usleep_range(MCP9982_WAKE_UP_TIME_US, MCP9982_WAKE_UP_TIME_MAX_US);
+ ret = regmap_read_poll_timeout
+ (priv->regmap, MCP9982_STATUS_ADDR,
+ reg_status, !(reg_status & MCP9982_STATUS_BUSY),
+ MCP9982_WAKE_UP_TIME_US,
+ MCP9982_WAKE_UP_TIME_US * 10);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ /*
+ * The only areas of memory that support SMBus block read are 80h->89h
+ * (temperature memory block) and 90h->97h(status memory block).
+ * In this context the read operation uses SMBus protocol and the first
+ * value returned will be the number of addresses that can be read.
+ * Temperature memory block is 10 bytes long and status memory block is 8
+ * bytes long.
+ *
+ * Depending on the read instruction used, the chip behaves differently:
+ * - regmap_bulk_read() when applied to the temperature memory block
+ * (80h->89h), the chip replies with SMBus block read, including count,
+ * additionally to the high and the low bytes. This function cannot be
+ * applied on the memory region 00h->09h(memory area which does not support
+ * block reads, returns wrong data) unless use_single_read is set in
+ * regmap_config.
+ *
+ * - regmap_multi_reg_read() when applied to the 00h->09h area uses I2C
+ * and returns only the high and low temperature bytes. When applied to
+ * the temperature memory block (80h->89h) returns the count till the end of
+ * the temperature memory block(aka SMBus count).
+ *
+ * - i2c_smbus_read_block_data() is not supported by all drivers.
+ *
+ * In order to keep consistency with reading limit memory region we will
+ * use single byte I2C read.
+ *
+ * Low register is latched when high temperature register is read.
+ */
+ ret = regmap_read(priv->regmap, MCP9982_HIGH_BYTE_ADDR(channel), &reg_high);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, MCP9982_HIGH_BYTE_ADDR(channel) + 1,
+ &reg_low);
+ if (ret)
+ return ret;
+
+ *val = ((reg_high << 8) + reg_low) >> 5;
+ *val = (*val - (MCP9982_OFFSET << 3)) * 125;
+
+ return 0;
+ case hwmon_temp_max:
+ if (channel)
+ addr = MCP9982_EXT_HIGH_LIMIT_ADDR(channel);
+ else
+ addr = MCP9982_INTERNAL_HIGH_LIMIT_ADDR;
+
+ return mcp9982_read_limit(priv, addr, val);
+ case hwmon_temp_max_alarm:
+ *val = regmap_test_bits(priv->regmap, MCP9982_HIGH_LIMIT_STATUS_ADDR,
+ BIT(channel));
+ if (*val < 0)
+ return *val;
+
+ return 0;
+ case hwmon_temp_max_hyst:
+ if (channel)
+ addr = MCP9982_EXT_HIGH_LIMIT_ADDR(channel);
+ else
+ addr = MCP9982_INTERNAL_HIGH_LIMIT_ADDR;
+ ret = mcp9982_read_limit(priv, addr, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, MCP9982_HYS_ADDR, &hyst);
+ if (ret)
+ return ret;
+
+ *val -= hyst * 1000;
+
+ return 0;
+ case hwmon_temp_min:
+ if (channel)
+ addr = MCP9982_EXT_LOW_LIMIT_ADDR(channel);
+ else
+ addr = MCP9982_INTERNAL_LOW_LIMIT_ADDR;
+
+ return mcp9982_read_limit(priv, addr, val);
+ case hwmon_temp_min_alarm:
+ *val = regmap_test_bits(priv->regmap, MCP9982_LOW_LIMIT_STATUS_ADDR,
+ BIT(channel));
+ if (*val < 0)
+ return *val;
+
+ return 0;
+ case hwmon_temp_crit:
+ return mcp9982_read_limit(priv, MCP9982_THERM_LIMIT_ADDR(channel), val);
+ case hwmon_temp_crit_alarm:
+ *val = regmap_test_bits(priv->regmap, MCP9982_THERM_LIMIT_STATUS_ADDR,
+ BIT(channel));
+ if (*val < 0)
+ return *val;
+
+ return 0;
+ case hwmon_temp_crit_hyst:
+ ret = mcp9982_read_limit(priv, MCP9982_THERM_LIMIT_ADDR(channel), val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, MCP9982_HYS_ADDR, &hyst);
+ if (ret)
+ return ret;
+
+ *val -= hyst * 1000;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = mcp9982_update_interval[priv->interval_idx];
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp9982_read_label(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ struct mcp9982_priv *priv = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_label:
+ *str = priv->labels[channel];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mcp9982_write_limit(struct mcp9982_priv *priv, u8 address, long val)
+{
+ int ret;
+ unsigned int regh, regl;
+
+ switch (address) {
+ case MCP9982_INTERNAL_HIGH_LIMIT_ADDR:
+ case MCP9982_INTERNAL_LOW_LIMIT_ADDR:
+ case MCP9982_THERM_LIMIT_ADDR(0):
+ case MCP9982_THERM_LIMIT_ADDR(1):
+ case MCP9982_THERM_LIMIT_ADDR(2):
+ case MCP9982_THERM_LIMIT_ADDR(3):
+ case MCP9982_THERM_LIMIT_ADDR(4):
+ regh = DIV_ROUND_CLOSEST(val, 1000);
+ regh = clamp_val(regh, 0, 255);
+
+ return regmap_write(priv->regmap, address, regh);
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(1):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(2):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(3):
+ case MCP9982_EXT_HIGH_LIMIT_ADDR(4):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(1):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(2):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(3):
+ case MCP9982_EXT_LOW_LIMIT_ADDR(4):
+ val = DIV_ROUND_CLOSEST(val, 125);
+ regh = (val >> 3) & 0xff;
+ regl = (val & 0x07) << 5;
+ /* Block writing is not supported by the chip. */
+ ret = regmap_write(priv->regmap, address, regh);
+ if (ret)
+ return ret;
+
+ return regmap_write(priv->regmap, address + 1, regl);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp9982_write_hyst(struct mcp9982_priv *priv, int channel, long val)
+{
+ int hyst, ret;
+ int limit;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ val = clamp_val(val, 0, 255);
+
+ /* Therm register is 8 bits and so it keeps only the integer part of the temperature. */
+ ret = regmap_read(priv->regmap, MCP9982_THERM_LIMIT_ADDR(channel), &limit);
+ if (ret)
+ return ret;
+
+ hyst = clamp_val(limit - val, 0, 255);
+
+ return regmap_write(priv->regmap, MCP9982_HYS_ADDR, hyst);
+}
+
+static int mcp9982_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long val)
+{
+ struct mcp9982_priv *priv = dev_get_drvdata(dev);
+ unsigned int idx;
+ u8 addr;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+
+ /*
+ * For MCP998XD and MCP9933D update interval
+ * can't be longer than 1 second.
+ */
+ if (priv->chip->hw_thermal_shutdown)
+ val = clamp_val(val, 0, 1000);
+
+ idx = find_closest_descending(val, mcp9982_update_interval,
+ ARRAY_SIZE(mcp9982_update_interval));
+ priv->interval_idx = idx;
+
+ return regmap_write(priv->regmap, MCP9982_CONV_ADDR, idx);
+ default:
+ return -EINVAL;
+ }
+ case hwmon_temp:
+ val = clamp_val(val, -64000, 191875);
+ val = val + (MCP9982_OFFSET * 1000);
+ switch (attr) {
+ case hwmon_temp_max:
+ if (channel)
+ addr = MCP9982_EXT_HIGH_LIMIT_ADDR(channel);
+ else
+ addr = MCP9982_INTERNAL_HIGH_LIMIT_ADDR;
+
+ return mcp9982_write_limit(priv, addr, val);
+ case hwmon_temp_min:
+ if (channel)
+ addr = MCP9982_EXT_LOW_LIMIT_ADDR(channel);
+ else
+ addr = MCP9982_INTERNAL_LOW_LIMIT_ADDR;
+
+ return mcp9982_write_limit(priv, addr, val);
+ case hwmon_temp_crit:
+ return mcp9982_write_limit(priv, MCP9982_THERM_LIMIT_ADDR(channel), val);
+ case hwmon_temp_crit_hyst:
+ return mcp9982_write_hyst(priv, channel, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static umode_t mcp9982_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ const struct mcp9982_priv *priv = _data;
+
+ if (!test_bit(channel, &priv->enabled_channel_mask))
+ return 0;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_label:
+ if (priv->labels[channel])
+ return 0444;
+ else
+ return 0;
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_crit_alarm:
+ return 0444;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_crit_hyst:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return 0644;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_ops mcp9982_hwmon_ops = {
+ .is_visible = mcp9982_is_visible,
+ .read = mcp9982_read,
+ .read_string = mcp9982_read_label,
+ .write = mcp9982_write,
+};
+
+static int mcp9982_init(struct device *dev, struct mcp9982_priv *priv)
+{
+ long high_limit, low_limit;
+ unsigned int i;
+ int ret;
+ u8 val;
+
+ /* Chips 82/83 and 82D/83D do not support anti-parallel diode mode. */
+ if (!priv->chip->allow_apdd && priv->apdd_enable == 1)
+ return dev_err_probe(dev, -EINVAL, "Incorrect setting of APDD.\n");
+
+ /* Chips with "D" work only in Run state. */
+ if (priv->chip->hw_thermal_shutdown && !priv->run_state)
+ return dev_err_probe(dev, -EINVAL, "Incorrect setting of Power State.\n");
+
+ /* All chips with "D" in the name must have RECD12 enabled. */
+ if (priv->chip->hw_thermal_shutdown && !priv->recd12_enable)
+ return dev_err_probe(dev, -EINVAL, "Incorrect setting of RECD12.\n");
+ /* Chips 83D/84D/85D must have RECD34 enabled. */
+ if (priv->chip->hw_thermal_shutdown)
+ if ((priv->chip->has_recd34 && !priv->recd34_enable))
+ return dev_err_probe(dev, -EINVAL, "Incorrect setting of RECD34.\n");
+
+ /*
+ * Set default values in registers.
+ * APDD, RECD12 and RECD34 are active on 0.
+ */
+ val = FIELD_PREP(MCP9982_CFG_MSKAL, 1) |
+ FIELD_PREP(MCP9982_CFG_RS, !priv->run_state) |
+ FIELD_PREP(MCP9982_CFG_ATTHM, 1) |
+ FIELD_PREP(MCP9982_CFG_RECD12, !priv->recd12_enable) |
+ FIELD_PREP(MCP9982_CFG_RECD34, !priv->recd34_enable) |
+ FIELD_PREP(MCP9982_CFG_RANGE, 1) | FIELD_PREP(MCP9982_CFG_DA_ENA, 0) |
+ FIELD_PREP(MCP9982_CFG_APDD, !priv->apdd_enable);
+
+ ret = regmap_write(priv->regmap, MCP9982_CFG_ADDR, val);
+ if (ret)
+ return ret;
+
+ /*
+ * Read initial value from register.
+ * The convert register utilises only 4 out of 8 bits.
+ * Numerical values 0->10 set their respective update intervals,
+ * while numerical values 11->15 default to 1 second.
+ */
+ ret = regmap_read(priv->regmap, MCP9982_CONV_ADDR, &priv->interval_idx);
+ if (ret)
+ return ret;
+ if (priv->interval_idx >= 11)
+ priv->interval_idx = 4;
+
+ ret = regmap_write(priv->regmap, MCP9982_HYS_ADDR, MCP9982_DEFAULT_HYS_VAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, MCP9982_CONSEC_ALRT_ADDR, MCP9982_DEFAULT_CONSEC_ALRT_VAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, MCP9982_ALRT_CFG_ADDR, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, MCP9982_RUNNING_AVG_ADDR, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, MCP9982_HOTTEST_CFG_ADDR, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Only external channels 1 and 2 support beta compensation.
+ * Set beta auto-detection.
+ */
+ for (i = 1; i < 3; i++)
+ if (test_bit(i, &priv->enabled_channel_mask)) {
+ ret = regmap_write(priv->regmap, MCP9982_EXT_BETA_CFG_ADDR(i),
+ MCP9982_BETA_AUTODETECT);
+ if (ret)
+ return ret;
+ }
+
+ high_limit = MCP9982_HIGH_LIMIT_DEFAULT + (MCP9982_OFFSET * 1000);
+ low_limit = MCP9982_LOW_LIMIT_DEFAULT + (MCP9982_OFFSET * 1000);
+
+ /* Set default values for internal channel limits. */
+ if (test_bit(0, &priv->enabled_channel_mask)) {
+ ret = mcp9982_write_limit(priv, MCP9982_INTERNAL_HIGH_LIMIT_ADDR, high_limit);
+ if (ret)
+ return ret;
+
+ ret = mcp9982_write_limit(priv, MCP9982_INTERNAL_LOW_LIMIT_ADDR, low_limit);
+ if (ret)
+ return ret;
+
+ ret = mcp9982_write_limit(priv, MCP9982_THERM_LIMIT_ADDR(0), high_limit);
+ if (ret)
+ return ret;
+ }
+
+ /* Set ideality factor and limits to default for external channels. */
+ for (i = 1; i < MCP9982_MAX_NUM_CHANNELS; i++)
+ if (test_bit(i, &priv->enabled_channel_mask)) {
+ ret = regmap_write(priv->regmap, MCP9982_EXT_IDEAL_ADDR(i),
+ MCP9982_IDEALITY_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = mcp9982_write_limit(priv, MCP9982_EXT_HIGH_LIMIT_ADDR(i), high_limit);
+ if (ret)
+ return ret;
+
+ ret = mcp9982_write_limit(priv, MCP9982_EXT_LOW_LIMIT_ADDR(i), low_limit);
+ if (ret)
+ return ret;
+
+ ret = mcp9982_write_limit(priv, MCP9982_THERM_LIMIT_ADDR(i), high_limit);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp9982_parse_fw_config(struct device *dev, int device_nr_channels)
+{
+ struct mcp9982_priv *priv = dev_get_drvdata(dev);
+ unsigned int reg_nr;
+ int ret;
+
+ /* Initialise internal channel( which is always present ). */
+ priv->labels[0] = "internal diode";
+ priv->enabled_channel_mask = 1;
+
+ /* Default values to work on systems without devicetree or firmware nodes. */
+ if (!dev_fwnode(dev)) {
+ priv->num_channels = device_nr_channels;
+ priv->enabled_channel_mask = BIT(priv->num_channels) - 1;
+ priv->apdd_enable = false;
+ priv->recd12_enable = true;
+ priv->recd34_enable = true;
+ priv->run_state = true;
+ return 0;
+ }
+
+ priv->apdd_enable =
+ device_property_read_bool(dev, "microchip,enable-anti-parallel");
+
+ priv->recd12_enable =
+ device_property_read_bool(dev, "microchip,parasitic-res-on-channel1-2");
+
+ priv->recd34_enable =
+ device_property_read_bool(dev, "microchip,parasitic-res-on-channel3-4");
+
+ priv->run_state =
+ device_property_read_bool(dev, "microchip,power-state");
+
+ priv->num_channels = device_get_child_node_count(dev) + 1;
+
+ if (priv->num_channels > device_nr_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "More channels than the chip supports.\n");
+
+ /* Read information about the external channels. */
+ device_for_each_named_child_node_scoped(dev, child, "channel") {
+ reg_nr = 0;
+ ret = fwnode_property_read_u32(child, "reg", &reg_nr);
+ if (ret || !reg_nr || reg_nr >= device_nr_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "Channel reg is incorrectly set.\n");
+
+ fwnode_property_read_string(child, "label", &priv->labels[reg_nr]);
+ set_bit(reg_nr, &priv->enabled_channel_mask);
+ }
+
+ return 0;
+}
+
+static const struct hwmon_chip_info mcp998x_chip_info = {
+ .ops = &mcp9982_hwmon_ops,
+ .info = mcp9985_info,
+};
+
+static int mcp9982_probe(struct i2c_client *client)
+{
+ const struct mcp9982_features *chip;
+ struct device *dev = &client->dev;
+ struct mcp9982_priv *priv;
+ struct device *hwmon_dev;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct mcp9982_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(client, &mcp9982_regmap_config);
+
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "Cannot initialize register map.\n");
+
+ dev_set_drvdata(dev, priv);
+
+ chip = i2c_get_match_data(client);
+ if (!chip)
+ return -EINVAL;
+ priv->chip = chip;
+
+ ret = mcp9982_parse_fw_config(dev, chip->phys_channels);
+ if (ret)
+ return ret;
+
+ ret = mcp9982_init(dev, priv);
+ if (ret)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, chip->name, priv,
+ &mcp998x_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id mcp9982_id[] = {
+ { .name = "mcp9933", .driver_data = (kernel_ulong_t)&mcp9933_chip_config },
+ { .name = "mcp9933d", .driver_data = (kernel_ulong_t)&mcp9933d_chip_config },
+ { .name = "mcp9982", .driver_data = (kernel_ulong_t)&mcp9982_chip_config },
+ { .name = "mcp9982d", .driver_data = (kernel_ulong_t)&mcp9982d_chip_config },
+ { .name = "mcp9983", .driver_data = (kernel_ulong_t)&mcp9983_chip_config },
+ { .name = "mcp9983d", .driver_data = (kernel_ulong_t)&mcp9983d_chip_config },
+ { .name = "mcp9984", .driver_data = (kernel_ulong_t)&mcp9984_chip_config },
+ { .name = "mcp9984d", .driver_data = (kernel_ulong_t)&mcp9984d_chip_config },
+ { .name = "mcp9985", .driver_data = (kernel_ulong_t)&mcp9985_chip_config },
+ { .name = "mcp9985d", .driver_data = (kernel_ulong_t)&mcp9985d_chip_config },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp9982_id);
+
+static const struct of_device_id mcp9982_of_match[] = {
+ {
+ .compatible = "microchip,mcp9933",
+ .data = &mcp9933_chip_config,
+ }, {
+ .compatible = "microchip,mcp9933d",
+ .data = &mcp9933d_chip_config,
+ }, {
+ .compatible = "microchip,mcp9982",
+ .data = &mcp9982_chip_config,
+ }, {
+ .compatible = "microchip,mcp9982d",
+ .data = &mcp9982d_chip_config,
+ }, {
+ .compatible = "microchip,mcp9983",
+ .data = &mcp9983_chip_config,
+ }, {
+ .compatible = "microchip,mcp9983d",
+ .data = &mcp9983d_chip_config,
+ }, {
+ .compatible = "microchip,mcp9984",
+ .data = &mcp9984_chip_config,
+ }, {
+ .compatible = "microchip,mcp9984d",
+ .data = &mcp9984d_chip_config,
+ }, {
+ .compatible = "microchip,mcp9985",
+ .data = &mcp9985_chip_config,
+ }, {
+ .compatible = "microchip,mcp9985d",
+ .data = &mcp9985d_chip_config,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp9982_of_match);
+
+static struct i2c_driver mcp9982_driver = {
+ .driver = {
+ .name = "mcp9982",
+ .of_match_table = mcp9982_of_match,
+ },
+ .probe = mcp9982_probe,
+ .id_table = mcp9982_id,
+};
+module_i2c_driver(mcp9982_driver);
+
+MODULE_AUTHOR("Victor Duicu <victor.duicu@microchip.com>");
+MODULE_DESCRIPTION("MCP998X/33 and MCP998XD/33D Multichannel Automotive Temperature Monitor Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 4a8380414038..0581770380cc 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -182,6 +182,7 @@ superio_exit(int ioreg)
#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_REG_BUILD_YEAR 0x604
#define NCT6683_REG_BUILD_MONTH 0x605
@@ -1245,6 +1246,8 @@ static int nct6683_probe(struct platform_device *pdev)
break;
case NCT6683_CUSTOMER_ID_ASROCK5:
break;
+ case NCT6683_CUSTOMER_ID_ASROCK6:
+ break;
default:
if (!force)
return -ENODEV;
diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
index 555029dfe713..1975399ac440 100644
--- a/drivers/hwmon/nct6775-platform.c
+++ b/drivers/hwmon/nct6775-platform.c
@@ -1159,6 +1159,7 @@ static const char * const asus_wmi_boards[] = {
"Pro A520M-C",
"Pro A520M-C II",
"Pro B550M-C",
+ "Pro WS W480-ACE",
"Pro WS X570-ACE",
"ProArt B550-CREATOR",
"ProArt X570-CREATOR WIFI",
@@ -1258,6 +1259,7 @@ static const char * const asus_wmi_boards[] = {
"TUF Z390-PRO GAMING",
"TUF Z390M-PRO GAMING",
"TUF Z390M-PRO GAMING (WI-FI)",
+ "W480/SYS",
"WS Z390 PRO",
"Z490-GUNDAM (WI-FI)",
};
@@ -1270,6 +1272,7 @@ static const char * const asus_msi_boards[] = {
"EX-B760M-V5 D4",
"EX-H510M-V3",
"EX-H610M-V3 D4",
+ "G15CE",
"G15CF",
"PRIME A620M-A",
"PRIME B560-PLUS",
@@ -1320,6 +1323,8 @@ static const char * const asus_msi_boards[] = {
"PRIME X670-P",
"PRIME X670-P WIFI",
"PRIME X670E-PRO WIFI",
+ "PRIME X870-P",
+ "PRIME X870-P WIFI",
"PRIME Z590-A",
"PRIME Z590-P",
"PRIME Z590-P WIFI",
@@ -1362,11 +1367,18 @@ static const char * const asus_msi_boards[] = {
"ProArt B660-CREATOR D4",
"ProArt B760-CREATOR D4",
"ProArt X670E-CREATOR WIFI",
+ "ProArt X870E-CREATOR WIFI",
"ProArt Z690-CREATOR WIFI",
"ProArt Z790-CREATOR WIFI",
"ROG CROSSHAIR X670E EXTREME",
"ROG CROSSHAIR X670E GENE",
"ROG CROSSHAIR X670E HERO",
+ "ROG CROSSHAIR X870E APEX",
+ "ROG CROSSHAIR X870E DARK HERO",
+ "ROG CROSSHAIR X870E EXTREME",
+ "ROG CROSSHAIR X870E GLACIAL",
+ "ROG CROSSHAIR X870E HERO",
+ "ROG CROSSHAIR X870E HERO BTF",
"ROG MAXIMUS XIII APEX",
"ROG MAXIMUS XIII EXTREME",
"ROG MAXIMUS XIII EXTREME GLACIAL",
@@ -1404,6 +1416,11 @@ static const char * const asus_msi_boards[] = {
"ROG STRIX X670E-E GAMING WIFI",
"ROG STRIX X670E-F GAMING WIFI",
"ROG STRIX X670E-I GAMING WIFI",
+ "ROG STRIX X870-A GAMING WIFI",
+ "ROG STRIX X870-F GAMING WIFI",
+ "ROG STRIX X870-I GAMING WIFI",
+ "ROG STRIX X870E-E GAMING WIFI",
+ "ROG STRIX X870E-E GAMING WIFI7 R2",
"ROG STRIX X870E-H GAMING WIFI7",
"ROG STRIX Z590-A GAMING WIFI",
"ROG STRIX Z590-A GAMING WIFI II",
@@ -1451,6 +1468,9 @@ static const char * const asus_msi_boards[] = {
"TUF GAMING H770-PRO WIFI",
"TUF GAMING X670E-PLUS",
"TUF GAMING X670E-PLUS WIFI",
+ "TUF GAMING X870-PLUS WIFI",
+ "TUF GAMING X870-PRO WIFI7 W NEO",
+ "TUF GAMING X870E-PLUS WIFI7",
"TUF GAMING Z590-PLUS",
"TUF GAMING Z590-PLUS WIFI",
"TUF GAMING Z690-PLUS",
@@ -1460,6 +1480,9 @@ static const char * const asus_msi_boards[] = {
"TUF GAMING Z790-PLUS D4",
"TUF GAMING Z790-PLUS WIFI",
"TUF GAMING Z790-PLUS WIFI D4",
+ "X870 AYW GAMING WIFI W",
+ "X870 MAX GAMING WIFI7",
+ "X870 MAX GAMING WIFI7 W",
"Z590 WIFI GUNDAM EDITION",
};
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index fc1273abe357..8f4bff375ecb 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -77,6 +77,15 @@ config SENSORS_ADP1050_REGULATOR
µModule regulators that can provide microprocessor power from 54V
power distribution architecture.
+config SENSORS_APS_379
+ tristate "Sony APS-379 Power Supplies"
+ help
+ If you say yes here you get hardware monitoring support for Sony
+ APS-379 Power Supplies.
+
+ This driver can also be built as a module. If so, the module will
+ be called aps-379.
+
config SENSORS_BEL_PFE
tristate "Bel PFE Compatible Power Supplies"
help
@@ -702,6 +711,15 @@ config SENSORS_XDP710
This driver can also be built as a module. If so, the module will
be called xdp710.
+config SENSORS_XDP720
+ tristate "Infineon XDP720 family"
+ help
+ If you say yes here you get hardware monitoring support for Infineon
+ XDP720.
+
+ This driver can also be built as a module. If so, the module will
+ be called xdp720.
+
config SENSORS_XDPE152
tristate "Infineon XDPE152 family"
help
@@ -711,6 +729,15 @@ config SENSORS_XDPE152
This driver can also be built as a module. If so, the module will
be called xdpe152c4.
+config SENSORS_XDPE1A2G7B
+ tristate "Infineon XDPE1A2G7B"
+ help
+ If you say yes here you get hardware monitoring support for Infineon
+ XDPE1A2G5B and XDPE1A2G7B.
+
+ This driver can also be built as a module. If so, the module will
+ be called xdpe1a2g7b.
+
config SENSORS_XDPE122
tristate "Infineon XDPE122 family"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index d6c86924f887..7129b62bc00f 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SENSORS_ACBEL_FSG032) += acbel-fsg032.o
obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_ADP1050) += adp1050.o
+obj-$(CONFIG_SENSORS_APS_379) += aps-379.o
obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o
obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o
obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
@@ -68,8 +69,10 @@ obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
obj-$(CONFIG_SENSORS_XDP710) += xdp710.o
+obj-$(CONFIG_SENSORS_XDP720) += xdp720.o
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o
+obj-$(CONFIG_SENSORS_XDPE1A2G7B) += xdpe1a2g7b.o
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o
obj-$(CONFIG_SENSORS_CRPS) += crps.o
diff --git a/drivers/hwmon/pmbus/aps-379.c b/drivers/hwmon/pmbus/aps-379.c
new file mode 100644
index 000000000000..7d46cd647e20
--- /dev/null
+++ b/drivers/hwmon/pmbus/aps-379.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Sony APS-379 Power Supplies
+ *
+ * Copyright 2026 Allied Telesis Labs
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+/*
+ * The VOUT format used by the chip is linear11, not linear16. Provide a hard
+ * coded VOUT_MODE that says VOUT is in linear mode with a fixed exponent of
+ * 2^-4.
+ */
+#define APS_379_VOUT_MODE ((u8)(-4 & 0x1f))
+
+static int aps_379_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ return APS_379_VOUT_MODE;
+ default:
+ return -ENODATA;
+ }
+}
+
+/*
+ * The APS-379 uses linear11 format instead of linear16. We've reported the exponent
+ * via the PMBUS_VOUT_MODE so we just return the mantissa here.
+ */
+static int aps_379_read_vout(struct i2c_client *client)
+{
+ int ret;
+
+ ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_READ_VOUT);
+ if (ret < 0)
+ return ret;
+
+ return clamp_val(sign_extend32(ret & 0x7ff, 10), 0, 0x3ff);
+}
+
+static int aps_379_read_word_data(struct i2c_client *client, int page, int phase, int reg)
+{
+ switch (reg) {
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ case PMBUS_VOUT_OV_WARN_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ case PMBUS_IOUT_UC_FAULT_LIMIT:
+ case PMBUS_UT_WARN_LIMIT:
+ case PMBUS_UT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ case PMBUS_POUT_OP_WARN_LIMIT:
+ case PMBUS_MFR_IIN_MAX:
+ case PMBUS_MFR_PIN_MAX:
+ case PMBUS_MFR_VOUT_MIN:
+ case PMBUS_MFR_VOUT_MAX:
+ case PMBUS_MFR_IOUT_MAX:
+ case PMBUS_MFR_POUT_MAX:
+ case PMBUS_MFR_MAX_TEMP_1:
+ /* These commands return data but it is invalid/un-documented */
+ return -ENXIO;
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ /*
+ * The standard requires this to be a value in Amps but it's
+ * actually a percentage of the rated output (123A for
+ * 110-240Vac, 110A for 90-100Vac) which we don't know. Ignore
+ * it rather than guessing.
+ */
+ return -ENXIO;
+ case PMBUS_READ_VOUT:
+ return aps_379_read_vout(client);
+ default:
+ return -ENODATA;
+ }
+}
+
+static struct pmbus_driver_info aps_379_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_FAN] = linear,
+ .func[0] = PMBUS_HAVE_VOUT |
+ PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_POUT |
+ PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_FAN12,
+ .read_byte_data = aps_379_read_byte_data,
+ .read_word_data = aps_379_read_word_data,
+};
+
+static const struct i2c_device_id aps_379_id[] = {
+ { "aps-379", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, aps_379_id);
+
+static int aps_379_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = { 0 };
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_WORD_DATA
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read Manufacturer Model\n");
+ return ret;
+ }
+
+ if (strncasecmp(buf, aps_379_id[0].name, strlen(aps_379_id[0].name)) != 0) {
+ buf[ret] = '\0';
+ dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
+ return -ENODEV;
+ }
+
+ return pmbus_do_probe(client, &aps_379_info);
+}
+
+static const struct of_device_id __maybe_unused aps_379_of_match[] = {
+ { .compatible = "sony,aps-379" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aps_379_of_match);
+
+static struct i2c_driver aps_379_driver = {
+ .driver = {
+ .name = "aps-379",
+ .of_match_table = of_match_ptr(aps_379_of_match),
+ },
+ .probe = aps_379_probe,
+ .id_table = aps_379_id,
+};
+
+module_i2c_driver(aps_379_driver);
+
+MODULE_AUTHOR("Chris Packham");
+MODULE_DESCRIPTION("PMBus driver for Sony APS-379");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c
index ddf9d9a2958c..6499556f735b 100644
--- a/drivers/hwmon/pmbus/bel-pfe.c
+++ b/drivers/hwmon/pmbus/bel-pfe.c
@@ -88,13 +88,10 @@ static struct pmbus_driver_info pfe_driver_info[] = {
},
};
-static const struct i2c_device_id pfe_device_id[];
-
static int pfe_pmbus_probe(struct i2c_client *client)
{
- int model;
+ int model = (uintptr_t)i2c_get_match_data(client);
- model = (int)i2c_match_id(pfe_device_id, client)->driver_data;
client->dev.platform_data = &pfe_plat_data;
/*
diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c
index a4dc09e2ef75..cad4d2330003 100644
--- a/drivers/hwmon/pmbus/fsp-3y.c
+++ b/drivers/hwmon/pmbus/fsp-3y.c
@@ -222,12 +222,6 @@ static int fsp3y_detect(struct i2c_client *client)
return -ENODEV;
}
-static const struct i2c_device_id fsp3y_id[] = {
- {"ym2151e", ym2151e},
- {"yh5151e", yh5151e},
- { }
-};
-
static int fsp3y_probe(struct i2c_client *client)
{
struct fsp3y_data *data;
@@ -242,7 +236,7 @@ static int fsp3y_probe(struct i2c_client *client)
if (data->chip < 0)
return data->chip;
- id = i2c_match_id(fsp3y_id, client);
+ id = i2c_client_get_device_id(client);
if (data->chip != id->driver_data)
dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n",
id->name, (int)id->driver_data, data->chip);
@@ -276,6 +270,11 @@ static int fsp3y_probe(struct i2c_client *client)
return pmbus_do_probe(client, &data->info);
}
+static const struct i2c_device_id fsp3y_id[] = {
+ {"ym2151e", ym2151e},
+ {"yh5151e", yh5151e},
+ { }
+};
MODULE_DEVICE_TABLE(i2c, fsp3y_id);
static struct i2c_driver fsp3y_driver = {
diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index d05ef7a968a9..6c7256d997f4 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -58,7 +58,7 @@ enum {
CFFPS_DEBUGFS_NUM_ENTRIES
};
-enum versions { cffps1, cffps2, cffps_unknown };
+enum versions { cffps_unknown, cffps1, cffps2 };
struct ibm_cffps {
enum versions version;
@@ -482,19 +482,9 @@ MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
static int ibm_cffps_probe(struct i2c_client *client)
{
int i, rc;
- enum versions vs = cffps_unknown;
+ enum versions vs = (uintptr_t)i2c_get_match_data(client);
struct dentry *debugfs;
struct ibm_cffps *psu;
- const void *md = of_device_get_match_data(&client->dev);
- const struct i2c_device_id *id;
-
- if (md) {
- vs = (uintptr_t)md;
- } else {
- id = i2c_match_id(ibm_cffps_id, client);
- if (id)
- vs = (enum versions)id->driver_data;
- }
if (vs == cffps_unknown) {
u16 ccin_revision = 0;
@@ -534,7 +524,7 @@ static int ibm_cffps_probe(struct i2c_client *client)
}
/* Set the client name to include the version number. */
- snprintf(client->name, I2C_NAME_SIZE, "cffps%d", vs + 1);
+ snprintf(client->name, I2C_NAME_SIZE, "cffps%d", vs);
}
client->dev.platform_data = &ibm_cffps_pdata;
diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c
index 7aebd854763a..652087589c55 100644
--- a/drivers/hwmon/pmbus/ina233.c
+++ b/drivers/hwmon/pmbus/ina233.c
@@ -85,6 +85,7 @@ static int ina233_read_word_data(struct i2c_client *client, int page,
static int ina233_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
+ const char *propname;
int ret, m, R;
u32 rshunt;
u32 max_current;
@@ -114,27 +115,28 @@ static int ina233_probe(struct i2c_client *client)
/* If INA233 skips current/power, shunt-resistor and current-lsb aren't needed. */
/* read rshunt value (uOhm) */
- ret = device_property_read_u32(dev, "shunt-resistor", &rshunt);
- if (ret) {
- if (ret != -EINVAL)
- return dev_err_probe(dev, ret, "Shunt resistor property read fail.\n");
+ propname = "shunt-resistor";
+ if (device_property_present(dev, propname)) {
+ ret = device_property_read_u32(dev, propname, &rshunt);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s property read fail.\n", propname);
+ } else {
rshunt = INA233_RSHUNT_DEFAULT;
}
if (!rshunt)
- return dev_err_probe(dev, -EINVAL,
- "Shunt resistor cannot be zero.\n");
+ return dev_err_probe(dev, -EINVAL, "%s cannot be zero.\n", propname);
/* read Maximum expected current value (uA) */
- ret = device_property_read_u32(dev, "ti,maximum-expected-current-microamp", &max_current);
- if (ret) {
- if (ret != -EINVAL)
- return dev_err_probe(dev, ret,
- "Maximum expected current property read fail.\n");
+ propname = "ti,maximum-expected-current-microamp";
+ if (device_property_present(dev, propname)) {
+ ret = device_property_read_u32(dev, propname, &max_current);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s property read fail.\n", propname);
+ } else {
max_current = INA233_MAX_CURRENT_DEFAULT;
}
if (max_current < 32768)
- return dev_err_probe(dev, -EINVAL,
- "Maximum expected current cannot less then 32768.\n");
+ return dev_err_probe(dev, -EINVAL, "%s cannot be less than 32768.\n", propname);
/* Calculate Current_LSB according to the spec formula */
current_lsb = max_current / 32768;
diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c
index 3e3a887aad05..21d047b577a4 100644
--- a/drivers/hwmon/pmbus/isl68137.c
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -23,52 +23,6 @@
#define RAA_DMPVR2_READ_VMON 0xc8
#define MAX_CHANNELS 4
-enum chips {
- isl68137,
- isl68220,
- isl68221,
- isl68222,
- isl68223,
- isl68224,
- isl68225,
- isl68226,
- isl68227,
- isl68229,
- isl68233,
- isl68239,
- isl69222,
- isl69223,
- isl69224,
- isl69225,
- isl69227,
- isl69228,
- isl69234,
- isl69236,
- isl69239,
- isl69242,
- isl69243,
- isl69247,
- isl69248,
- isl69254,
- isl69255,
- isl69256,
- isl69259,
- isl69260,
- isl69268,
- isl69269,
- isl69298,
- raa228000,
- raa228004,
- raa228006,
- raa228228,
- raa228244,
- raa228246,
- raa229001,
- raa229004,
- raa229141,
- raa229621,
-};
-
enum variants {
raa_dmpvr1_2rail,
raa_dmpvr2_1rail,
@@ -90,8 +44,6 @@ struct isl68137_data {
#define to_isl68137_data(x) container_of(x, struct isl68137_data, info)
-static const struct i2c_device_id raa_dmpvr_id[];
-
static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
int page,
char *buf)
@@ -393,7 +345,7 @@ static int isl68137_probe(struct i2c_client *client)
memcpy(&data->info, &raa_dmpvr_info, sizeof(data->info));
info = &data->info;
- switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) {
+ switch ((uintptr_t)i2c_get_match_data(client)) {
case raa_dmpvr1_2rail:
info->pages = 2;
info->R[PSC_VOLTAGE_IN] = 3;
@@ -498,6 +450,8 @@ static const struct i2c_device_id raa_dmpvr_id[] = {
{"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},
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 8f5be520a15d..d69a5e675e80 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -733,7 +733,7 @@ static int ltc2978_probe(struct i2c_client *client)
return chip_id;
data->id = chip_id;
- id = i2c_match_id(ltc2978_id, client);
+ id = i2c_client_get_device_id(client);
if (data->id != id->driver_data)
dev_warn(&client->dev,
"Device mismatch: Configured %s (%d), detected %d\n",
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
index d696e506aafb..36dc13424d92 100644
--- a/drivers/hwmon/pmbus/max16601.c
+++ b/drivers/hwmon/pmbus/max16601.c
@@ -318,7 +318,7 @@ static int max16601_probe(struct i2c_client *client)
if (chip_id < 0)
return chip_id;
- id = i2c_match_id(max16601_id, client);
+ id = i2c_client_get_device_id(client);
if (chip_id != id->driver_data)
dev_warn(&client->dev,
"Device mismatch: Configured %s (%d), detected %d\n",
diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c
index 95869d198ecf..fe03164788df 100644
--- a/drivers/hwmon/pmbus/max20730.c
+++ b/drivers/hwmon/pmbus/max20730.c
@@ -715,10 +715,7 @@ static int max20730_probe(struct i2c_client *client)
return -ENODEV;
}
- if (client->dev.of_node)
- chip_id = (uintptr_t)of_device_get_match_data(dev);
- else
- chip_id = i2c_match_id(max20730_id, client)->driver_data;
+ chip_id = (uintptr_t)i2c_get_match_data(client);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index 1f94d38a1637..3caa76bcbeb5 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -31,8 +31,6 @@ struct max31785_data {
struct pmbus_driver_info info;
};
-#define to_max31785_data(x) container_of(x, struct max31785_data, info)
-
/*
* MAX31785 Driver Workaround
*
@@ -40,9 +38,8 @@ struct max31785_data {
* These issues are not indicated by the device itself, except for occasional
* NACK responses during master transactions. No error bits are set in STATUS_BYTE.
*
- * To address this, we introduce a delay of 250us between consecutive accesses
- * to the fan controller. This delay helps mitigate communication problems by
- * allowing sufficient time between accesses.
+ * Keep minimal local delay handling for raw pre-probe SMBus accesses.
+ * Normal PMBus-mediated accesses use pmbus_driver_info.access_delay instead.
*/
static inline void max31785_wait(const struct max31785_data *data)
{
@@ -54,89 +51,40 @@ static inline void max31785_wait(const struct max31785_data *data)
}
static int max31785_i2c_write_byte_data(struct i2c_client *client,
- struct max31785_data *driver_data,
- int command, u16 data)
+ struct max31785_data *data,
+ int command, u8 value)
{
int rc;
- max31785_wait(driver_data);
- rc = i2c_smbus_write_byte_data(client, command, data);
- driver_data->access = ktime_get();
+ max31785_wait(data);
+ rc = i2c_smbus_write_byte_data(client, command, value);
+ data->access = ktime_get();
return rc;
}
static int max31785_i2c_read_word_data(struct i2c_client *client,
- struct max31785_data *driver_data,
+ struct max31785_data *data,
int command)
{
int rc;
- max31785_wait(driver_data);
+ max31785_wait(data);
rc = i2c_smbus_read_word_data(client, command);
- driver_data->access = ktime_get();
- return rc;
-}
-
-static int _max31785_read_byte_data(struct i2c_client *client,
- struct max31785_data *driver_data,
- int page, int command)
-{
- int rc;
-
- max31785_wait(driver_data);
- rc = pmbus_read_byte_data(client, page, command);
- driver_data->access = ktime_get();
- return rc;
-}
-
-static int _max31785_write_byte_data(struct i2c_client *client,
- struct max31785_data *driver_data,
- int page, int command, u16 data)
-{
- int rc;
-
- max31785_wait(driver_data);
- rc = pmbus_write_byte_data(client, page, command, data);
- driver_data->access = ktime_get();
- return rc;
-}
-
-static int _max31785_read_word_data(struct i2c_client *client,
- struct max31785_data *driver_data,
- int page, int phase, int command)
-{
- int rc;
-
- max31785_wait(driver_data);
- rc = pmbus_read_word_data(client, page, phase, command);
- driver_data->access = ktime_get();
- return rc;
-}
-
-static int _max31785_write_word_data(struct i2c_client *client,
- struct max31785_data *driver_data,
- int page, int command, u16 data)
-{
- int rc;
-
- max31785_wait(driver_data);
- rc = pmbus_write_word_data(client, page, command, data);
- driver_data->access = ktime_get();
+ data->access = ktime_get();
return rc;
}
static int max31785_read_byte_data(struct i2c_client *client, int page, int reg)
{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max31785_data *driver_data = to_max31785_data(info);
-
switch (reg) {
case PMBUS_VOUT_MODE:
return -ENOTSUPP;
case PMBUS_FAN_CONFIG_12:
- return _max31785_read_byte_data(client, driver_data,
- page - MAX31785_NR_PAGES,
- reg);
+ if (page < MAX31785_NR_PAGES)
+ return -ENODATA;
+ return pmbus_read_byte_data(client,
+ page - MAX31785_NR_PAGES,
+ reg);
}
return -ENODATA;
@@ -178,14 +126,28 @@ static int max31785_read_long_data(struct i2c_client *client, int page,
if (rc < 0)
return rc;
+ /*
+ * Ensure the raw transfer is properly spaced from the
+ * preceding PMBus transaction.
+ */
+ pmbus_wait(client);
+
rc = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
- if (rc < 0)
- return rc;
+
+ /*
+ * Update PMBus core timing state for the raw transfer, even on error.
+ * Pass 0 as the operation mask since this is a raw read, intentionally
+ * neither PMBUS_OP_WRITE nor PMBUS_OP_PAGE_CHANGE.
+ */
+ pmbus_update_ts(client, 0);
+
+ if (rc != ARRAY_SIZE(msg))
+ return rc < 0 ? rc : -EIO;
*data = (rspbuf[0] << (0 * 8)) | (rspbuf[1] << (1 * 8)) |
(rspbuf[2] << (2 * 8)) | (rspbuf[3] << (3 * 8));
- return rc;
+ return 0;
}
static int max31785_get_pwm(struct i2c_client *client, int page)
@@ -203,19 +165,16 @@ static int max31785_get_pwm(struct i2c_client *client, int page)
return rv;
}
-static int max31785_get_pwm_mode(struct i2c_client *client,
- struct max31785_data *driver_data, int page)
+static int max31785_get_pwm_mode(struct i2c_client *client, int page)
{
int config;
int command;
- config = _max31785_read_byte_data(client, driver_data, page,
- PMBUS_FAN_CONFIG_12);
+ config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
if (config < 0)
return config;
- command = _max31785_read_word_data(client, driver_data, page, 0xff,
- PMBUS_FAN_COMMAND_1);
+ command = pmbus_read_word_data(client, page, 0xff, PMBUS_FAN_COMMAND_1);
if (command < 0)
return command;
@@ -233,8 +192,6 @@ static int max31785_get_pwm_mode(struct i2c_client *client,
static int max31785_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max31785_data *driver_data = to_max31785_data(info);
u32 val;
int rv;
@@ -263,7 +220,7 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
rv = max31785_get_pwm(client, page);
break;
case PMBUS_VIRT_PWM_ENABLE_1:
- rv = max31785_get_pwm_mode(client, driver_data, page);
+ rv = max31785_get_pwm_mode(client, page);
break;
default:
rv = -ENODATA;
@@ -294,35 +251,7 @@ static inline u32 max31785_scale_pwm(u32 sensor_val)
return (sensor_val * 100) / 255;
}
-static int max31785_update_fan(struct i2c_client *client,
- struct max31785_data *driver_data, int page,
- u8 config, u8 mask, u16 command)
-{
- int from, rv;
- u8 to;
-
- from = _max31785_read_byte_data(client, driver_data, page,
- PMBUS_FAN_CONFIG_12);
- if (from < 0)
- return from;
-
- to = (from & ~mask) | (config & mask);
-
- if (to != from) {
- rv = _max31785_write_byte_data(client, driver_data, page,
- PMBUS_FAN_CONFIG_12, to);
- if (rv < 0)
- return rv;
- }
-
- rv = _max31785_write_word_data(client, driver_data, page,
- PMBUS_FAN_COMMAND_1, command);
-
- return rv;
-}
-
-static int max31785_pwm_enable(struct i2c_client *client,
- struct max31785_data *driver_data, int page,
+static int max31785_pwm_enable(struct i2c_client *client, int page,
u16 word)
{
int config = 0;
@@ -351,23 +280,20 @@ static int max31785_pwm_enable(struct i2c_client *client,
return -EINVAL;
}
- return max31785_update_fan(client, driver_data, page, config,
- PB_FAN_1_RPM, rate);
+ return pmbus_update_fan(client, page, 0, config,
+ PB_FAN_1_RPM, rate);
}
static int max31785_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max31785_data *driver_data = to_max31785_data(info);
-
switch (reg) {
case PMBUS_VIRT_PWM_1:
- return max31785_update_fan(client, driver_data, page, 0,
- PB_FAN_1_RPM,
- max31785_scale_pwm(word));
+ return pmbus_update_fan(client, page, 0, 0,
+ PB_FAN_1_RPM,
+ max31785_scale_pwm(word));
case PMBUS_VIRT_PWM_ENABLE_1:
- return max31785_pwm_enable(client, driver_data, page, word);
+ return max31785_pwm_enable(client, page, word);
default:
break;
}
@@ -391,6 +317,7 @@ static const struct pmbus_driver_info max31785_info = {
.read_byte_data = max31785_read_byte_data,
.read_word_data = max31785_read_word_data,
.write_byte = max31785_write_byte,
+ .access_delay = MAX31785_WAIT_DELAY_US,
/* RPM */
.format[PSC_FAN] = direct,
@@ -438,29 +365,29 @@ static const struct pmbus_driver_info max31785_info = {
};
static int max31785_configure_dual_tach(struct i2c_client *client,
- struct pmbus_driver_info *info)
+ struct max31785_data *data)
{
+ struct pmbus_driver_info *info = &data->info;
int ret;
int i;
- struct max31785_data *driver_data = to_max31785_data(info);
for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
- ret = max31785_i2c_write_byte_data(client, driver_data,
+ ret = max31785_i2c_write_byte_data(client, data,
PMBUS_PAGE, i);
if (ret < 0)
return ret;
- ret = max31785_i2c_read_word_data(client, driver_data,
+ ret = max31785_i2c_read_word_data(client, data,
MFR_FAN_CONFIG);
if (ret < 0)
return ret;
if (ret & MFR_FAN_CONFIG_DUAL_TACH) {
- int virtual = MAX31785_NR_PAGES + i;
+ int vpage = MAX31785_NR_PAGES + i;
- info->pages = virtual + 1;
- info->func[virtual] |= PMBUS_HAVE_FAN12;
- info->func[virtual] |= PMBUS_PAGE_VIRTUAL;
+ info->pages = vpage + 1;
+ info->func[vpage] |= PMBUS_HAVE_FAN12;
+ info->func[vpage] |= PMBUS_PAGE_VIRTUAL;
}
}
@@ -471,7 +398,7 @@ static int max31785_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pmbus_driver_info *info;
- struct max31785_data *driver_data;
+ struct max31785_data *data;
bool dual_tach = false;
int ret;
@@ -480,20 +407,20 @@ static int max31785_probe(struct i2c_client *client)
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- driver_data = devm_kzalloc(dev, sizeof(struct max31785_data), GFP_KERNEL);
- if (!driver_data)
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- info = &driver_data->info;
- driver_data->access = ktime_get();
+ data->access = ktime_get();
+ info = &data->info;
*info = max31785_info;
- ret = max31785_i2c_write_byte_data(client, driver_data,
- PMBUS_PAGE, 255);
+ ret = max31785_i2c_write_byte_data(client, data,
+ PMBUS_PAGE, 0xff);
if (ret < 0)
return ret;
- ret = i2c_smbus_read_word_data(client, MFR_REVISION);
+ ret = max31785_i2c_read_word_data(client, data, MFR_REVISION);
if (ret < 0)
return ret;
@@ -509,11 +436,13 @@ static int max31785_probe(struct i2c_client *client)
}
if (dual_tach) {
- ret = max31785_configure_dual_tach(client, info);
+ ret = max31785_configure_dual_tach(client, data);
if (ret < 0)
return ret;
}
+ max31785_wait(data);
+
return pmbus_do_probe(client, info);
}
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index 8ea4e68d4e9d..cc96bb22f8f5 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -71,8 +71,6 @@ struct max34440_data {
#define to_max34440_data(x) container_of(x, struct max34440_data, info)
-static const struct i2c_device_id max34440_id[];
-
static int max34440_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
@@ -628,7 +626,7 @@ static int max34440_probe(struct i2c_client *client)
GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->id = i2c_match_id(max34440_id, client)->driver_data;
+ data->id = (uintptr_t)i2c_get_match_data(client);
data->info = max34440_info[data->id];
data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT;
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index 920cd5408141..d1844c7a51ee 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -20,8 +20,6 @@ struct pmbus_device_info {
u32 flags;
};
-static const struct i2c_device_id pmbus_id[];
-
/*
* Find sensor groups and status registers on each page.
*/
@@ -174,7 +172,7 @@ static int pmbus_probe(struct i2c_client *client)
if (!info)
return -ENOMEM;
- device_info = (struct pmbus_device_info *)i2c_match_id(pmbus_id, client)->driver_data;
+ device_info = (struct pmbus_device_info *)i2c_get_match_data(client);
if (device_info->flags) {
pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
GFP_KERNEL);
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index d2e9bfb5320f..23e3eda58870 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -10,6 +10,7 @@
#define PMBUS_H
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/regulator/driver.h>
/*
@@ -416,7 +417,7 @@ enum pmbus_sensor_classes {
#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */
enum pmbus_data_format { linear = 0, ieee754, direct, vid };
-enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
+enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv, nvidia195mv };
/* PMBus revision identifiers */
#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */
@@ -424,6 +425,10 @@ enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */
#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */
+/* Operation type flags for pmbus_update_ts */
+#define PMBUS_OP_WRITE BIT(0)
+#define PMBUS_OP_PAGE_CHANGE BIT(1)
+
struct pmbus_driver_info {
int pages; /* Total number of pages */
u8 phases[PMBUS_PAGES]; /* Number of phases per page */
@@ -541,6 +546,8 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev,
void pmbus_clear_cache(struct i2c_client *client);
void pmbus_set_update(struct i2c_client *client, u8 reg, bool update);
+void pmbus_wait(struct i2c_client *client);
+void pmbus_update_ts(struct i2c_client *client, int op);
int pmbus_set_page(struct i2c_client *client, int page, int phase);
int pmbus_read_word_data(struct i2c_client *client, int page, int phase,
u8 reg);
@@ -563,7 +570,11 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode);
int pmbus_lock_interruptible(struct i2c_client *client);
+void pmbus_lock(struct i2c_client *client);
void pmbus_unlock(struct i2c_client *client);
+
+DEFINE_GUARD(pmbus_lock, struct i2c_client *, pmbus_lock(_T), pmbus_unlock(_T))
+
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command);
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client);
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 572be3ebc03d..e8fdd799c71c 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -37,8 +37,7 @@
* The type of operation used for picking the delay between
* successive pmbus operations.
*/
-#define PMBUS_OP_WRITE BIT(0)
-#define PMBUS_OP_PAGE_CHANGE BIT(1)
+/* PMBUS_OP_WRITE and PMBUS_OP_PAGE_CHANGE are defined in pmbus.h */
static int wp = -1;
module_param(wp, int, 0444);
@@ -179,7 +178,7 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS");
/* Some chips need a delay between accesses. */
-static void pmbus_wait(struct i2c_client *client)
+void pmbus_wait(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get());
@@ -187,9 +186,10 @@ static void pmbus_wait(struct i2c_client *client)
if (delay > 0)
fsleep(delay);
}
+EXPORT_SYMBOL_NS_GPL(pmbus_wait, "PMBUS");
/* Sets the last operation timestamp for pmbus_wait */
-static void pmbus_update_ts(struct i2c_client *client, int op)
+void pmbus_update_ts(struct i2c_client *client, int op)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
@@ -203,6 +203,7 @@ static void pmbus_update_ts(struct i2c_client *client, int op)
if (delay > 0)
data->next_access_backoff = ktime_add_us(ktime_get(), delay);
}
+EXPORT_SYMBOL_NS_GPL(pmbus_update_ts, "PMBUS");
int pmbus_set_page(struct i2c_client *client, int page, int phase)
{
@@ -891,6 +892,10 @@ static s64 pmbus_reg2data_vid(struct pmbus_data *data,
if (val >= 0x0 && val <= 0xd8)
rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100);
break;
+ case nvidia195mv:
+ if (val >= 0x01)
+ rv = 195 + (val - 1) * 5; /* VID step is 5mv */
+ break;
}
return rv;
}
@@ -1156,12 +1161,11 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
int ret, status;
u16 regval;
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
+
status = pmbus_get_status(client, page, reg);
- if (status < 0) {
- ret = status;
- goto unlock;
- }
+ if (status < 0)
+ return status;
if (s1)
pmbus_update_sensor_data(client, s1);
@@ -1173,7 +1177,7 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
if (data->revision >= PMBUS_REV_12) {
ret = _pmbus_write_byte_data(client, page, reg, regval);
if (ret)
- goto unlock;
+ return ret;
} else {
pmbus_clear_fault_page(client, page);
}
@@ -1181,14 +1185,10 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
if (s1 && s2) {
s64 v1, v2;
- if (s1->data < 0) {
- ret = s1->data;
- goto unlock;
- }
- if (s2->data < 0) {
- ret = s2->data;
- goto unlock;
- }
+ if (s1->data < 0)
+ return s1->data;
+ if (s2->data < 0)
+ return s2->data;
v1 = pmbus_reg2data(data, s1);
v2 = pmbus_reg2data(data, s2);
@@ -1196,8 +1196,6 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
} else {
ret = !!regval;
}
-unlock:
- mutex_unlock(&data->update_lock);
return ret;
}
@@ -1227,16 +1225,16 @@ static ssize_t pmbus_show_sensor(struct device *dev,
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
struct pmbus_data *data = i2c_get_clientdata(client);
- ssize_t ret;
+ s64 val;
- mutex_lock(&data->update_lock);
- pmbus_update_sensor_data(client, sensor);
- if (sensor->data < 0)
- ret = sensor->data;
- else
- ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor));
- mutex_unlock(&data->update_lock);
- return ret;
+ scoped_guard(pmbus_lock, client) {
+ pmbus_update_sensor_data(client, sensor);
+ if (sensor->data < 0)
+ return sensor->data;
+ val = pmbus_reg2data(data, sensor);
+ }
+
+ return sysfs_emit(buf, "%lld\n", val);
}
static ssize_t pmbus_set_sensor(struct device *dev,
@@ -1246,7 +1244,6 @@ static ssize_t pmbus_set_sensor(struct device *dev,
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
- ssize_t rv = count;
s64 val;
int ret;
u16 regval;
@@ -1254,15 +1251,15 @@ static ssize_t pmbus_set_sensor(struct device *dev,
if (kstrtos64(buf, 10, &val) < 0)
return -EINVAL;
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
+
regval = pmbus_data2reg(data, sensor, val);
ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
if (ret < 0)
- rv = ret;
- else
- sensor->data = -ENODATA;
- mutex_unlock(&data->update_lock);
- return rv;
+ return ret;
+
+ sensor->data = -ENODATA;
+ return count;
}
static ssize_t pmbus_show_label(struct device *dev,
@@ -1364,7 +1361,7 @@ static int pmbus_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
struct pmbus_data *pmbus_data = tdata->pmbus_data;
struct i2c_client *client = to_i2c_client(pmbus_data->dev);
struct device *dev = pmbus_data->hwmon_dev;
- int ret = 0;
+ int _temp;
if (!dev) {
/* May not even get to hwmon yet */
@@ -1372,15 +1369,15 @@ static int pmbus_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
return 0;
}
- mutex_lock(&pmbus_data->update_lock);
- pmbus_update_sensor_data(client, sensor);
- if (sensor->data < 0)
- ret = sensor->data;
- else
- *temp = (int)pmbus_reg2data(pmbus_data, sensor);
- mutex_unlock(&pmbus_data->update_lock);
+ scoped_guard(pmbus_lock, client) {
+ pmbus_update_sensor_data(client, sensor);
+ if (sensor->data < 0)
+ return sensor->data;
+ _temp = (int)pmbus_reg2data(pmbus_data, sensor);
+ }
- return ret;
+ *temp = _temp;
+ return 0;
}
static const struct thermal_zone_device_ops pmbus_thermal_ops = {
@@ -2412,13 +2409,12 @@ static ssize_t pmbus_show_samples(struct device *dev,
int val;
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_samples_reg *reg = to_samples_reg(devattr);
- struct pmbus_data *data = i2c_get_clientdata(client);
- mutex_lock(&data->update_lock);
- val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg);
- mutex_unlock(&data->update_lock);
- if (val < 0)
- return val;
+ scoped_guard(pmbus_lock, client) {
+ val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg);
+ if (val < 0)
+ return val;
+ }
return sysfs_emit(buf, "%d\n", val);
}
@@ -2431,14 +2427,13 @@ static ssize_t pmbus_set_samples(struct device *dev,
long val;
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_samples_reg *reg = to_samples_reg(devattr);
- struct pmbus_data *data = i2c_get_clientdata(client);
if (kstrtol(buf, 0, &val) < 0)
return -EINVAL;
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
+
ret = _pmbus_write_word_data(client, reg->page, reg->attr->reg, val);
- mutex_unlock(&data->update_lock);
return ret ? : count;
}
@@ -2950,14 +2945,9 @@ static int _pmbus_is_enabled(struct i2c_client *client, u8 page)
static int __maybe_unused pmbus_is_enabled(struct i2c_client *client, u8 page)
{
- struct pmbus_data *data = i2c_get_clientdata(client);
- int ret;
-
- mutex_lock(&data->update_lock);
- ret = _pmbus_is_enabled(client, page);
- mutex_unlock(&data->update_lock);
+ guard(pmbus_lock)(client);
- return ret;
+ return _pmbus_is_enabled(client, page);
}
#define to_dev_attr(_dev_attr) \
@@ -2987,14 +2977,13 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
}
}
-static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
+static int _pmbus_get_flags(struct i2c_client *client, u8 page, unsigned int *flags,
unsigned int *event, bool notify)
{
+ struct pmbus_data *data = i2c_get_clientdata(client);
int i, status;
const struct pmbus_status_category *cat;
const struct pmbus_status_assoc *bit;
- struct device *dev = data->dev;
- struct i2c_client *client = to_i2c_client(dev);
int func = data->info->func[page];
*flags = 0;
@@ -3070,16 +3059,12 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag
return 0;
}
-static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
+static int __maybe_unused pmbus_get_flags(struct i2c_client *client, u8 page, unsigned int *flags,
unsigned int *event, bool notify)
{
- int ret;
+ guard(pmbus_lock)(client);
- mutex_lock(&data->update_lock);
- ret = _pmbus_get_flags(data, page, flags, event, notify);
- mutex_unlock(&data->update_lock);
-
- return ret;
+ return _pmbus_get_flags(client, page, flags, event, notify);
}
#if IS_ENABLED(CONFIG_REGULATOR)
@@ -3095,17 +3080,13 @@ static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_data *data = i2c_get_clientdata(client);
u8 page = rdev_get_id(rdev);
- int ret;
- mutex_lock(&data->update_lock);
- ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
- PB_OPERATION_CONTROL_ON,
- enable ? PB_OPERATION_CONTROL_ON : 0);
- mutex_unlock(&data->update_lock);
+ guard(pmbus_lock)(client);
- return ret;
+ return pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+ PB_OPERATION_CONTROL_ON,
+ enable ? PB_OPERATION_CONTROL_ON : 0);
}
static int pmbus_regulator_enable(struct regulator_dev *rdev)
@@ -3122,54 +3103,41 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_data *data = i2c_get_clientdata(client);
int event;
- return pmbus_get_flags(data, rdev_get_id(rdev), flags, &event, false);
+ return pmbus_get_flags(client, rdev_get_id(rdev), flags, &event, false);
}
static int pmbus_regulator_get_status(struct regulator_dev *rdev)
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_data *data = i2c_get_clientdata(client);
u8 page = rdev_get_id(rdev);
int status, ret;
int event;
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
+
status = pmbus_get_status(client, page, PMBUS_STATUS_WORD);
- if (status < 0) {
- ret = status;
- goto unlock;
- }
+ if (status < 0)
+ return status;
- if (status & PB_STATUS_OFF) {
- ret = REGULATOR_STATUS_OFF;
- goto unlock;
- }
+ if (status & PB_STATUS_OFF)
+ return REGULATOR_STATUS_OFF;
/* If regulator is ON & reports power good then return ON */
- if (!(status & PB_STATUS_POWER_GOOD_N)) {
- ret = REGULATOR_STATUS_ON;
- goto unlock;
- }
+ if (!(status & PB_STATUS_POWER_GOOD_N))
+ return REGULATOR_STATUS_ON;
- ret = _pmbus_get_flags(data, rdev_get_id(rdev), &status, &event, false);
+ ret = _pmbus_get_flags(client, rdev_get_id(rdev), &status, &event, false);
if (ret)
- goto unlock;
+ return ret;
if (status & (REGULATOR_ERROR_UNDER_VOLTAGE | REGULATOR_ERROR_OVER_CURRENT |
- REGULATOR_ERROR_REGULATION_OUT | REGULATOR_ERROR_FAIL | REGULATOR_ERROR_OVER_TEMP)) {
- ret = REGULATOR_STATUS_ERROR;
- goto unlock;
- }
+ REGULATOR_ERROR_REGULATION_OUT | REGULATOR_ERROR_FAIL | REGULATOR_ERROR_OVER_TEMP))
+ return REGULATOR_STATUS_ERROR;
- ret = REGULATOR_STATUS_UNDEFINED;
-
-unlock:
- mutex_unlock(&data->update_lock);
- return ret;
+ return REGULATOR_STATUS_UNDEFINED;
}
static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page)
@@ -3234,19 +3202,16 @@ static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
.class = PSC_VOLTAGE_OUT,
.convert = true,
};
- int ret;
+ int voltage;
- mutex_lock(&data->update_lock);
- s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
- if (s.data < 0) {
- ret = s.data;
- goto unlock;
+ scoped_guard(pmbus_lock, client) {
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
+ if (s.data < 0)
+ return s.data;
+ voltage = (int)pmbus_reg2data(data, &s);
}
- ret = (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
-unlock:
- mutex_unlock(&data->update_lock);
- return ret;
+ return voltage * 1000; /* unit is uV */
}
static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
@@ -3263,22 +3228,18 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
};
int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
int low, high;
- int ret;
*selector = 0;
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
+
low = pmbus_regulator_get_low_margin(client, s.page);
- if (low < 0) {
- ret = low;
- goto unlock;
- }
+ if (low < 0)
+ return low;
high = pmbus_regulator_get_high_margin(client, s.page);
- if (high < 0) {
- ret = high;
- goto unlock;
- }
+ if (high < 0)
+ return high;
/* Make sure we are within margins */
if (low > val)
@@ -3288,10 +3249,7 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
val = pmbus_data2reg(data, &s, val);
- ret = _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
-unlock:
- mutex_unlock(&data->update_lock);
- return ret;
+ return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
}
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
@@ -3301,7 +3259,6 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
int val, low, high;
- int ret;
if (data->flags & PMBUS_VOUT_PROTECTED)
return 0;
@@ -3314,29 +3271,20 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
(rdev->desc->uV_step * selector), 1000); /* convert to mV */
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev));
- if (low < 0) {
- ret = low;
- goto unlock;
- }
+ if (low < 0)
+ return low;
high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev));
- if (high < 0) {
- ret = high;
- goto unlock;
- }
+ if (high < 0)
+ return high;
- if (val >= low && val <= high) {
- ret = val * 1000; /* unit is uV */
- goto unlock;
- }
+ if (val >= low && val <= high)
+ return val * 1000; /* unit is uV */
- ret = 0;
-unlock:
- mutex_unlock(&data->update_lock);
- return ret;
+ return 0;
}
const struct regulator_ops pmbus_regulator_ops = {
@@ -3472,16 +3420,16 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
struct i2c_client *client = to_i2c_client(data->dev);
int i, status, event;
- mutex_lock(&data->update_lock);
+ guard(pmbus_lock)(client);
+
for (i = 0; i < data->info->pages; i++) {
- _pmbus_get_flags(data, i, &status, &event, true);
+ _pmbus_get_flags(client, i, &status, &event, true);
if (event)
pmbus_regulator_notify(data, i, event);
}
pmbus_clear_faults(client);
- mutex_unlock(&data->update_lock);
return IRQ_HANDLED;
}
@@ -3537,15 +3485,13 @@ static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
static int pmbus_debugfs_get(void *data, u64 *val)
{
- int rc;
struct pmbus_debugfs_entry *entry = data;
- struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
+ struct i2c_client *client = entry->client;
+ int rc;
- rc = mutex_lock_interruptible(&pdata->update_lock);
- if (rc)
- return rc;
- rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg);
- mutex_unlock(&pdata->update_lock);
+ guard(pmbus_lock)(client);
+
+ rc = _pmbus_read_byte_data(client, entry->page, entry->reg);
if (rc < 0)
return rc;
@@ -3558,15 +3504,14 @@ DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL,
static int pmbus_debugfs_get_status(void *data, u64 *val)
{
- int rc;
struct pmbus_debugfs_entry *entry = data;
- struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
+ struct i2c_client *client = entry->client;
+ struct pmbus_data *pdata = i2c_get_clientdata(client);
+ int rc;
- rc = mutex_lock_interruptible(&pdata->update_lock);
- if (rc)
- return rc;
- rc = pdata->read_status(entry->client, entry->page);
- mutex_unlock(&pdata->update_lock);
+ guard(pmbus_lock)(client);
+
+ rc = pdata->read_status(client, entry->page);
if (rc < 0)
return rc;
@@ -3582,17 +3527,14 @@ static ssize_t pmbus_debugfs_block_read(struct file *file, char __user *buf,
{
int rc;
struct pmbus_debugfs_entry *entry = file->private_data;
- struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
+ struct i2c_client *client = entry->client;
char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
- rc = mutex_lock_interruptible(&pdata->update_lock);
- if (rc)
- return rc;
- rc = pmbus_read_block_data(entry->client, entry->page, entry->reg,
- data);
- mutex_unlock(&pdata->update_lock);
- if (rc < 0)
- return rc;
+ scoped_guard(pmbus_lock, client) {
+ rc = pmbus_read_block_data(client, entry->page, entry->reg, data);
+ if (rc < 0)
+ return rc;
+ }
/* Add newline at the end of a read data */
data[rc] = '\n';
@@ -3871,6 +3813,14 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
}
EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS");
+void pmbus_lock(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_lock, "PMBUS");
+
int pmbus_lock_interruptible(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
index d5d60a9af8c5..a368cfa9d45a 100644
--- a/drivers/hwmon/pmbus/q54sj108a2.c
+++ b/drivers/hwmon/pmbus/q54sj108a2.c
@@ -271,6 +271,8 @@ static const struct file_operations q54sj108a2_fops = {
static const struct i2c_device_id q54sj108a2_id[] = {
{ "q54sj108a2", q54sj108a2 },
+ { "q54sn120a1", q54sj108a2 },
+ { "q54sw120a7", q54sj108a2 },
{ },
};
@@ -280,6 +282,7 @@ static int q54sj108a2_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+ const struct i2c_device_id *mid;
enum chips chip_id;
int ret, i;
struct dentry *debugfs;
@@ -292,10 +295,7 @@ static int q54sj108a2_probe(struct i2c_client *client)
I2C_FUNC_SMBUS_BLOCK_DATA))
return -ENODEV;
- if (client->dev.of_node)
- chip_id = (enum chips)(unsigned long)of_device_get_match_data(dev);
- else
- chip_id = i2c_match_id(q54sj108a2_id, client)->driver_data;
+ chip_id = (enum chips)(uintptr_t)i2c_get_match_data(client);
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0) {
@@ -316,8 +316,12 @@ static int q54sj108a2_probe(struct i2c_client *client)
dev_err(dev, "Failed to read Manufacturer Model\n");
return ret;
}
- if (ret != 14 || strncmp(buf, "Q54SJ108A2", 10)) {
- buf[ret] = '\0';
+ buf[ret] = '\0';
+ for (mid = q54sj108a2_id; mid->name[0]; mid++) {
+ if (!strncasecmp(mid->name, buf, strlen(mid->name)))
+ break;
+ }
+ if (!mid->name[0]) {
dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
return -ENODEV;
}
@@ -327,7 +331,10 @@ static int q54sj108a2_probe(struct i2c_client *client)
dev_err(dev, "Failed to read Manufacturer Revision\n");
return ret;
}
- if (ret != 4 || buf[0] != 'S') {
+ /*
+ * accept manufacturer revision with optional NUL byte
+ */
+ if (!(ret == 4 || ret == 5) || buf[0] != 'S') {
buf[ret] = '\0';
dev_err(dev, "Unsupported Manufacturer Revision '%s'\n", buf);
return -ENODEV;
@@ -404,6 +411,8 @@ static int q54sj108a2_probe(struct i2c_client *client)
static const struct of_device_id q54sj108a2_of_match[] = {
{ .compatible = "delta,q54sj108a2", .data = (void *)q54sj108a2 },
+ { .compatible = "delta,q54sn120a1", .data = (void *)q54sj108a2 },
+ { .compatible = "delta,q54sw120a7", .data = (void *)q54sj108a2 },
{ },
};
diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c
index c13edd7e1abf..05c6288ecafc 100644
--- a/drivers/hwmon/pmbus/tps25990.c
+++ b/drivers/hwmon/pmbus/tps25990.c
@@ -402,12 +402,18 @@ static int tps25990_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pmbus_driver_info *info;
- u32 rimon = TPS25990_DEFAULT_RIMON;
+ const char *propname;
+ u32 rimon;
int ret;
- ret = device_property_read_u32(dev, "ti,rimon-micro-ohms", &rimon);
- if (ret < 0 && ret != -EINVAL)
- return dev_err_probe(dev, ret, "failed to get rimon\n");
+ propname = "ti,rimon-micro-ohms";
+ if (device_property_present(dev, propname)) {
+ ret = device_property_read_u32(dev, propname, &rimon);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get %s\n", propname);
+ } else {
+ rimon = TPS25990_DEFAULT_RIMON;
+ }
info = devm_kmemdup(dev, &tps25990_base_info, sizeof(*info), GFP_KERNEL);
if (!info)
diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
index 249974c13aa3..94258e8cfd90 100644
--- a/drivers/hwmon/pmbus/tps53679.c
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -253,10 +253,7 @@ static int tps53679_probe(struct i2c_client *client)
struct pmbus_driver_info *info;
enum chips chip_id;
- if (dev->of_node)
- chip_id = (uintptr_t)of_device_get_match_data(dev);
- else
- chip_id = i2c_match_id(tps53679_id, client)->driver_data;
+ chip_id = (uintptr_t)i2c_get_match_data(client);
info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL);
if (!info)
diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c
new file mode 100644
index 000000000000..8729a771f216
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdp720.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon XDP720 Digital eFuse Controller
+ *
+ * Copyright (c) 2026 Infineon Technologies. All rights reserved.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/bitops.h>
+#include <linux/math64.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_TELEMETRY_AVG 0xE9
+
+static struct pmbus_driver_info xdp720_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_POWER] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+
+ .m[PSC_VOLTAGE_IN] = 4653,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -2,
+ .m[PSC_VOLTAGE_OUT] = 4653,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = -2,
+ /*
+ * Current and Power measurement depends on the RIMON (kOhm) and
+ * GIMON(microA/A) values.
+ */
+ .m[PSC_CURRENT_OUT] = 24668,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = -4,
+ .m[PSC_POWER] = 4486,
+ .b[PSC_POWER] = 0,
+ .R[PSC_POWER] = -1,
+ .m[PSC_TEMPERATURE] = 54,
+ .b[PSC_TEMPERATURE] = 22521,
+ .R[PSC_TEMPERATURE] = -1,
+
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int xdp720_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ int ret;
+ u32 rimon;
+ int gimon;
+
+ info = devm_kmemdup(&client->dev, &xdp720_info, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ret = devm_regulator_get_enable(&client->dev, "vdd-vin");
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to enable vdd-vin supply\n");
+
+ ret = i2c_smbus_read_word_data(client, XDP720_TELEMETRY_AVG);
+ if (ret < 0) {
+ dev_err(&client->dev, "Can't get TELEMETRY_AVG\n");
+ 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 */
+
+ 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 (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);
+}
+
+static const struct of_device_id xdp720_of_match[] = {
+ { .compatible = "infineon,xdp720" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, xdp720_of_match);
+
+static const struct i2c_device_id xdp720_id[] = {
+ { "xdp720" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, xdp720_id);
+
+static struct i2c_driver xdp720_driver = {
+ .driver = {
+ .name = "xdp720",
+ .of_match_table = xdp720_of_match,
+ },
+ .probe = xdp720_probe,
+ .id_table = xdp720_id,
+};
+
+module_i2c_driver(xdp720_driver);
+
+MODULE_AUTHOR("Ashish Yadav <ashish.yadav@infineon.com>");
+MODULE_DESCRIPTION("PMBus driver for Infineon XDP720 Digital eFuse Controller");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c
new file mode 100644
index 000000000000..1755e3522ede
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon Multi-phase Digital XDPE1A2G5B
+ * and XDPE1A2G7B Controllers
+ *
+ * Copyright (c) 2026 Infineon Technologies. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define XDPE1A2G7B_PAGE_NUM 2
+#define XDPE1A2G7B_NVIDIA_195MV 0x1E /* NVIDIA mode 1.95mV, VID step is 5mV */
+
+static int xdpe1a2g7b_identify(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ u8 vout_params;
+ int vout_mode;
+
+ /*
+ * XDPE1A2G5B and XDPE1A2G7B support both Linear and NVIDIA PWM VID data
+ * formats via VOUT_MODE. Note that the device pages/loops are not fully
+ * independent: configuration is shared, so programming each page/loop
+ * separately is not supported.
+ */
+ vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+ if (vout_mode < 0)
+ return vout_mode;
+
+ switch (vout_mode >> 5) {
+ case 0:
+ info->format[PSC_VOLTAGE_OUT] = linear;
+ return 0;
+ case 1:
+ info->format[PSC_VOLTAGE_OUT] = vid;
+ vout_params = vout_mode & GENMASK(4, 0);
+ /* Check for VID Code Type */
+ switch (vout_params) {
+ case XDPE1A2G7B_NVIDIA_195MV:
+ /* VID vrm_version for PAGE0 and PAGE1 */
+ info->vrm_version[0] = nvidia195mv;
+ info->vrm_version[1] = nvidia195mv;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct pmbus_driver_info xdpe1a2g7b_info = {
+ .pages = XDPE1A2G7B_PAGE_NUM,
+ .identify = xdpe1a2g7b_identify,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = 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_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[1] = 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_STATUS_INPUT,
+};
+
+static int xdpe1a2g7b_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+
+ info = devm_kmemdup(&client->dev, &xdpe1a2g7b_info, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id xdpe1a2g7b_id[] = {
+ { "xdpe1a2g5b" },
+ { "xdpe1a2g7b" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id);
+
+static const struct of_device_id __maybe_unused xdpe1a2g7b_of_match[] = {
+ { .compatible = "infineon,xdpe1a2g5b" },
+ { .compatible = "infineon,xdpe1a2g7b" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, xdpe1a2g7b_of_match);
+
+static struct i2c_driver xdpe1a2g7b_driver = {
+ .driver = {
+ .name = "xdpe1a2g7b",
+ .of_match_table = of_match_ptr(xdpe1a2g7b_of_match),
+ },
+ .probe = xdpe1a2g7b_probe,
+ .id_table = xdpe1a2g7b_id,
+};
+
+module_i2c_driver(xdpe1a2g7b_driver);
+
+MODULE_AUTHOR("Ashish Yadav <ashish.yadav@infineon.com>");
+MODULE_DESCRIPTION("PMBus driver for Infineon XDPE1A2G5B/7B");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/powerz.c b/drivers/hwmon/powerz.c
index 4e663d5b4e33..6e1359144cab 100644
--- a/drivers/hwmon/powerz.c
+++ b/drivers/hwmon/powerz.c
@@ -6,6 +6,7 @@
#include <linux/completion.h>
#include <linux/device.h>
+#include <linux/dma-mapping.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -33,7 +34,9 @@ struct powerz_sensor_data {
} __packed;
struct powerz_priv {
- char transfer_buffer[64]; /* first member to satisfy DMA alignment */
+ __dma_from_device_group_begin();
+ char transfer_buffer[64];
+ __dma_from_device_group_end();
struct mutex mutex;
struct completion completion;
struct urb *urb;
@@ -106,8 +109,12 @@ static void powerz_usb_cmd_complete(struct urb *urb)
static int powerz_read_data(struct usb_device *udev, struct powerz_priv *priv)
{
+ long rc;
int ret;
+ if (!priv->urb)
+ return -ENODEV;
+
priv->status = -ETIMEDOUT;
reinit_completion(&priv->completion);
@@ -124,8 +131,14 @@ static int powerz_read_data(struct usb_device *udev, struct powerz_priv *priv)
if (ret)
return ret;
- if (!wait_for_completion_interruptible_timeout
- (&priv->completion, msecs_to_jiffies(5))) {
+ rc = wait_for_completion_interruptible_timeout(&priv->completion,
+ msecs_to_jiffies(5));
+ if (rc < 0) {
+ usb_kill_urb(priv->urb);
+ return rc;
+ }
+
+ if (rc == 0) {
usb_kill_urb(priv->urb);
return -EIO;
}
@@ -224,6 +237,8 @@ static int powerz_probe(struct usb_interface *intf,
mutex_init(&priv->mutex);
init_completion(&priv->completion);
+ usb_set_intfdata(intf, priv);
+
hwmon_dev =
devm_hwmon_device_register_with_info(parent, DRIVER_NAME, priv,
&powerz_chip_info, NULL);
@@ -232,8 +247,6 @@ static int powerz_probe(struct usb_interface *intf,
return PTR_ERR(hwmon_dev);
}
- usb_set_intfdata(intf, priv);
-
return 0;
}
@@ -244,6 +257,7 @@ static void powerz_disconnect(struct usb_interface *intf)
mutex_lock(&priv->mutex);
usb_kill_urb(priv->urb);
usb_free_urb(priv->urb);
+ priv->urb = NULL;
mutex_unlock(&priv->mutex);
}
diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c
index 20e3cfa625f1..89d4da8aa4c0 100644
--- a/drivers/hwmon/pt5161l.c
+++ b/drivers/hwmon/pt5161l.c
@@ -121,7 +121,7 @@ static int pt5161l_read_block_data(struct pt5161l_data *data, u32 address,
int ret, tries;
u8 remain_len = len;
u8 curr_len;
- u8 wbuf[16], rbuf[24];
+ u8 wbuf[16], rbuf[I2C_SMBUS_BLOCK_MAX];
u8 cmd = 0x08; /* [7]:pec_en, [4:2]:func, [1]:start, [0]:end */
u8 config = 0x00; /* [6]:cfg_type, [4:1]:burst_len, [0]:address bit16 */
@@ -151,7 +151,7 @@ static int pt5161l_read_block_data(struct pt5161l_data *data, u32 address,
break;
}
if (tries >= 3)
- return ret;
+ return ret < 0 ? ret : -EIO;
memcpy(val, rbuf, curr_len);
val += curr_len;
diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c
index 9984373a25fb..7fb7b50ad1ad 100644
--- a/drivers/hwmon/tc74.c
+++ b/drivers/hwmon/tc74.c
@@ -92,7 +92,7 @@ static ssize_t temp_input_show(struct device *dev,
if (ret)
return ret;
- return sprintf(buf, "%d\n", data->temp_input * 1000);
+ return sysfs_emit(buf, "%d\n", data->temp_input * 1000);
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 5b10c395a84d..3aa1a3fbeaa9 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -50,11 +50,16 @@
#define CONVERSION_TIME_MS 35 /* in milli-seconds */
+#define NUM_SAMPLE_TIMES 4
+#define DEFAULT_SAMPLE_TIME_MS 250
+static const unsigned int *sample_times = (const unsigned int []){ 125, 250, 1000, 4000 };
+
struct tmp102 {
const char *label;
struct regmap *regmap;
u16 config_orig;
unsigned long ready_time;
+ u16 sample_time;
};
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
@@ -79,8 +84,20 @@ static int tmp102_read_string(struct device *dev, enum hwmon_sensor_types type,
return 0;
}
-static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *temp)
+static int tmp102_read_chip(struct device *dev, u32 attr, long *val)
+{
+ struct tmp102 *tmp102 = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = tmp102->sample_time;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tmp102_read_temp(struct device *dev, u32 attr, long *val)
{
struct tmp102 *tmp102 = dev_get_drvdata(dev);
unsigned int regval;
@@ -108,13 +125,54 @@ static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
err = regmap_read(tmp102->regmap, reg, &regval);
if (err < 0)
return err;
- *temp = tmp102_reg_to_mC(regval);
+
+ *val = tmp102_reg_to_mC(regval);
return 0;
}
-static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long temp)
+static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return tmp102_read_chip(dev, attr, val);
+ case hwmon_temp:
+ return tmp102_read_temp(dev, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tmp102_update_interval(struct device *dev, long val)
+{
+ struct tmp102 *tmp102 = dev_get_drvdata(dev);
+ u8 index;
+ s32 err;
+
+ index = find_closest(val, sample_times, NUM_SAMPLE_TIMES);
+
+ err = regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
+ (TMP102_CONF_CR1 | TMP102_CONF_CR0), (3 - index) << 6);
+ if (err < 0)
+ return err;
+ tmp102->sample_time = sample_times[index];
+
+ return 0;
+}
+
+static int tmp102_write_chip(struct device *dev, u32 attr, long val)
+{
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return tmp102_update_interval(dev, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int tmp102_write_temp(struct device *dev, u32 attr, long val)
{
struct tmp102 *tmp102 = dev_get_drvdata(dev);
int reg;
@@ -130,8 +188,22 @@ static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
return -EOPNOTSUPP;
}
- temp = clamp_val(temp, -256000, 255000);
- return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(temp));
+ val = clamp_val(val, -256000, 255000);
+ return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(val));
+}
+
+static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return tmp102_write_chip(dev, attr, val);
+ case hwmon_temp:
+ return tmp102_write_temp(dev, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
}
static umode_t tmp102_is_visible(const void *data, enum hwmon_sensor_types type,
@@ -139,27 +211,39 @@ static umode_t tmp102_is_visible(const void *data, enum hwmon_sensor_types type,
{
const struct tmp102 *tmp102 = data;
- if (type != hwmon_temp)
- return 0;
-
- switch (attr) {
- case hwmon_temp_input:
- return 0444;
- case hwmon_temp_label:
- if (tmp102->label)
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
return 0444;
- return 0;
- case hwmon_temp_max_hyst:
- case hwmon_temp_max:
- return 0644;
+ case hwmon_temp_label:
+ if (tmp102->label)
+ return 0444;
+ return 0;
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_max:
+ return 0644;
+ default:
+ break;
+ }
+ break;
default:
- return 0;
+ break;
}
+ return 0;
}
static const struct hwmon_channel_info * const tmp102_info[] = {
HWMON_CHANNEL_INFO(chip,
- HWMON_C_REGISTER_TZ),
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | HWMON_T_MAX_HYST),
NULL
@@ -237,6 +321,8 @@ static int tmp102_probe(struct i2c_client *client)
if (IS_ERR(tmp102->regmap))
return PTR_ERR(tmp102->regmap);
+ tmp102->sample_time = DEFAULT_SAMPLE_TIME_MS;
+
err = regmap_read(tmp102->regmap, TMP102_CONF_REG, &regval);
if (err < 0) {
dev_err(dev, "error reading config register\n");
diff --git a/drivers/hwmon/yogafan.c b/drivers/hwmon/yogafan.c
new file mode 100644
index 000000000000..605cc928f21f
--- /dev/null
+++ b/drivers/hwmon/yogafan.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/**
+ * yoga_fan.c - Lenovo Yoga/Legion Fan Hardware Monitoring Driver
+ *
+ * Provides fan speed monitoring for Lenovo Yoga, Legion, and IdeaPad
+ * laptops by interfacing with the Embedded Controller (EC) via ACPI.
+ *
+ * The driver implements a passive discrete-time first-order lag filter
+ * with slew-rate limiting (RLLag). This addresses low-resolution
+ * tachometer sampling in the EC by smoothing RPM readings based on
+ * the time delta (dt) between userspace requests, ensuring physical
+ * consistency without background task overhead or race conditions.
+ * The filter implements multirate filtering with autoreset in case
+ * of large sampling time.
+ *
+ * Copyright (C) 2021-2026 Sergio Melas <sergiomelas@gmail.com>
+ */
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/math64.h>
+
+/* Driver Configuration Constants */
+#define DRVNAME "yogafan"
+#define MAX_FANS 8
+
+/* Filter Configuration Constants */
+#define TAU_MS 1000 /* Time constant for the first-order lag (ms) */
+#define MAX_SLEW_RPM_S 1500 /* Maximum allowed change in RPM per second */
+#define MAX_SAMPLING 5000 /* Maximum allowed Ts for reset (ms) */
+#define MIN_SAMPLING 100 /* Minimum interval between filter updates (ms) */
+
+/* RPM Sanitation Constants */
+#define RPM_FLOOR_LIMIT 50 /* Snap filtered value to 0 if raw is 0 */
+
+struct yogafan_config {
+ int multiplier;
+ int fan_count;
+ const char *paths[2];
+};
+
+struct yoga_fan_data {
+ acpi_handle active_handles[MAX_FANS];
+ long filtered_val[MAX_FANS];
+ ktime_t last_sample[MAX_FANS];
+ int multiplier;
+ int fan_count;
+};
+
+/* Specific configurations mapped via DMI */
+static const struct yogafan_config yoga_8bit_fans_cfg = {
+ .multiplier = 100,
+ .fan_count = 1,
+ .paths = { "\\_SB.PCI0.LPC0.EC0.FANS", NULL }
+};
+
+static const struct yogafan_config ideapad_8bit_fan0_cfg = {
+ .multiplier = 100,
+ .fan_count = 1,
+ .paths = { "\\_SB.PCI0.LPC0.EC0.FAN0", NULL }
+};
+
+static const struct yogafan_config legion_16bit_dual_cfg = {
+ .multiplier = 1,
+ .fan_count = 2,
+ .paths = { "\\_SB.PCI0.LPC0.EC0.FANS", "\\_SB.PCI0.LPC0.EC0.FA2S" }
+};
+
+static void apply_rllag_filter(struct yoga_fan_data *data, int idx, long raw_rpm)
+{
+ ktime_t now = ktime_get_boottime();
+ s64 dt_ms = ktime_to_ms(ktime_sub(now, data->last_sample[idx]));
+ long delta, step, limit, alpha;
+ s64 temp_num;
+
+ if (raw_rpm < RPM_FLOOR_LIMIT) {
+ data->filtered_val[idx] = 0;
+ data->last_sample[idx] = now;
+ return;
+ }
+
+ if (data->last_sample[idx] == 0 || dt_ms > MAX_SAMPLING) {
+ data->filtered_val[idx] = raw_rpm;
+ data->last_sample[idx] = now;
+ return;
+ }
+
+ if (dt_ms < MIN_SAMPLING)
+ return;
+
+ delta = raw_rpm - data->filtered_val[idx];
+ if (delta == 0) {
+ data->last_sample[idx] = now;
+ return;
+ }
+
+ temp_num = dt_ms << 12;
+ alpha = (long)div64_s64(temp_num, (s64)(TAU_MS + dt_ms));
+ step = (delta * alpha) >> 12;
+
+ if (step == 0 && delta != 0)
+ step = (delta > 0) ? 1 : -1;
+
+ limit = (MAX_SLEW_RPM_S * (long)dt_ms) / 1000;
+ if (limit < 1)
+ limit = 1;
+
+ if (step > limit)
+ step = limit;
+ else if (step < -limit)
+ step = -limit;
+
+ data->filtered_val[idx] += step;
+ data->last_sample[idx] = now;
+}
+
+static int yoga_fan_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct yoga_fan_data *data = dev_get_drvdata(dev);
+ unsigned long long raw_acpi;
+ acpi_status status;
+
+ if (type != hwmon_fan || attr != hwmon_fan_input)
+ return -EOPNOTSUPP;
+
+ status = acpi_evaluate_integer(data->active_handles[channel], NULL, NULL, &raw_acpi);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ apply_rllag_filter(data, channel, (long)raw_acpi * data->multiplier);
+ *val = data->filtered_val[channel];
+
+ return 0;
+}
+
+static umode_t yoga_fan_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct yoga_fan_data *fan_data = data;
+
+ if (type == hwmon_fan && channel < fan_data->fan_count)
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops yoga_fan_hwmon_ops = {
+ .is_visible = yoga_fan_is_visible,
+ .read = yoga_fan_read,
+};
+
+static const struct hwmon_channel_info *yoga_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),
+ NULL
+};
+
+static const struct hwmon_chip_info yoga_fan_chip_info = {
+ .ops = &yoga_fan_hwmon_ops,
+ .info = yoga_fan_info,
+};
+
+static const struct dmi_system_id yogafan_quirks[] = {
+ {
+ .ident = "Lenovo Yoga",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Yoga"),
+ },
+ .driver_data = (void *)&yoga_8bit_fans_cfg,
+ },
+ {
+ .ident = "Lenovo Legion",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Legion"),
+ },
+ .driver_data = (void *)&legion_16bit_dual_cfg,
+ },
+ {
+ .ident = "Lenovo IdeaPad",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "IdeaPad"),
+ },
+ .driver_data = (void *)&ideapad_8bit_fan0_cfg,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(dmi, yogafan_quirks);
+
+static int yoga_fan_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ const struct yogafan_config *cfg;
+ struct yoga_fan_data *data;
+ struct device *hwmon_dev;
+ int i;
+
+ dmi_id = dmi_first_match(yogafan_quirks);
+ if (!dmi_id)
+ return -ENODEV;
+
+ cfg = dmi_id->driver_data;
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->multiplier = cfg->multiplier;
+
+ for (i = 0; i < cfg->fan_count; i++) {
+ acpi_status status;
+
+ status = acpi_get_handle(NULL, (char *)cfg->paths[i],
+ &data->active_handles[data->fan_count]);
+ if (ACPI_SUCCESS(status))
+ data->fan_count++;
+ }
+
+ if (data->fan_count == 0)
+ return -ENODEV;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, DRVNAME,
+ data, &yoga_fan_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver yoga_fan_driver = {
+ .driver = { .name = DRVNAME },
+ .probe = yoga_fan_probe,
+};
+
+static struct platform_device *yoga_fan_device;
+
+static int __init yoga_fan_init(void)
+{
+ int ret;
+
+ if (!dmi_check_system(yogafan_quirks))
+ return -ENODEV;
+
+ ret = platform_driver_register(&yoga_fan_driver);
+ if (ret)
+ return ret;
+
+ yoga_fan_device = platform_device_register_simple(DRVNAME, -1, NULL, 0);
+ if (IS_ERR(yoga_fan_device)) {
+ platform_driver_unregister(&yoga_fan_driver);
+ return PTR_ERR(yoga_fan_device);
+ }
+ return 0;
+}
+
+static void __exit yoga_fan_exit(void)
+{
+ platform_device_unregister(yoga_fan_device);
+ platform_driver_unregister(&yoga_fan_driver);
+}
+
+module_init(yoga_fan_init);
+module_exit(yoga_fan_exit);
+
+MODULE_AUTHOR("Sergio Melas <sergiomelas@gmail.com>");
+MODULE_DESCRIPTION("Lenovo Yoga/Legion Fan Monitor Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index 857e1b69d6cd..c6cded508738 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -33,8 +33,6 @@
#include <linux/sched/task.h>
#include <linux/util_macros.h>
-#include <linux/platform_data/ina2xx.h>
-
/* INA2XX registers definition */
#define INA2XX_CONFIG 0x00
#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
@@ -980,16 +978,8 @@ static int ina2xx_probe(struct i2c_client *client)
mutex_init(&chip->state_lock);
- if (of_property_read_u32(client->dev.of_node,
- "shunt-resistor", &val) < 0) {
- struct ina2xx_platform_data *pdata =
- dev_get_platdata(&client->dev);
-
- if (pdata)
- val = pdata->shunt_uohms;
- else
- val = INA2XX_RSHUNT_DEFAULT;
- }
+ if (of_property_read_u32(client->dev.of_node, "shunt-resistor", &val) < 0)
+ val = INA2XX_RSHUNT_DEFAULT;
ret = set_shunt_resistor(chip, val);
if (ret)
diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h
deleted file mode 100644
index 2aa5ee9a9050..000000000000
--- a/include/linux/platform_data/ina2xx.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Driver for Texas Instruments INA219, INA226 power monitor chips
- *
- * Copyright (C) 2012 Lothar Felten <lothar.felten@gmail.com>
- *
- * For further information, see the Documentation/hwmon/ina2xx.rst file.
- */
-
-/**
- * struct ina2xx_platform_data - ina2xx info
- * @shunt_uohms shunt resistance in microohms
- */
-struct ina2xx_platform_data {
- long shunt_uohms;
-};