summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-27 14:04:53 +0100
committerMark Brown <broonie@kernel.org>2026-07-27 14:04:53 +0100
commit9e6b894306716aa3dc33de7280347c8de2bfb277 (patch)
tree95f05ce906666b632d3bdde8238b45d250030d53
parent7a1a6a733a1a3e7862e6f2735ad67191cc81320f (diff)
parent0109905cfdf4b64da014596f31c2ee5029ef0870 (diff)
downloadlinux-next-9e6b894306716aa3dc33de7280347c8de2bfb277.tar.gz
linux-next-9e6b894306716aa3dc33de7280347c8de2bfb277.zip
Merge branch 'hwmon-next' of https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
-rw-r--r--Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml43
-rw-r--r--Documentation/devicetree/bindings/hwmon/amphenol,chipcap2.yaml6
-rw-r--r--Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml72
-rw-r--r--Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml16
-rw-r--r--Documentation/devicetree/bindings/hwmon/pmbus/silergy,sq24860.yaml74
-rw-r--r--Documentation/devicetree/bindings/hwmon/pmbus/ti,lm25066.yaml20
-rw-r--r--Documentation/devicetree/bindings/hwmon/sensirion,sht30.yaml71
-rw-r--r--Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml3
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml2
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml2
-rw-r--r--Documentation/hwmon/adm1275.rst24
-rw-r--r--Documentation/hwmon/asus_ec_sensors.rst4
-rw-r--r--Documentation/hwmon/chipcap2.rst2
-rw-r--r--Documentation/hwmon/coretemp.rst4
-rw-r--r--Documentation/hwmon/index.rst3
-rw-r--r--Documentation/hwmon/max34440.rst26
-rw-r--r--Documentation/hwmon/mpq82d00.rst73
-rw-r--r--Documentation/hwmon/sht3x.rst17
-rw-r--r--Documentation/hwmon/sq24860.rst96
-rw-r--r--Documentation/hwmon/tvs-mpfs.rst53
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/hwmon/Kconfig31
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/acpi_power_meter.c6
-rw-r--r--drivers/hwmon/applesmc.c490
-rw-r--r--drivers/hwmon/asus-ec-sensors.c84
-rw-r--r--drivers/hwmon/axi-fan-control.c3
-rw-r--r--drivers/hwmon/chipcap2.c25
-rw-r--r--drivers/hwmon/coretemp.c2
-rw-r--r--drivers/hwmon/cros_ec_hwmon.c29
-rw-r--r--drivers/hwmon/da9055-hwmon.c5
-rw-r--r--drivers/hwmon/eic7700-pvt.c509
-rw-r--r--drivers/hwmon/eic7700-pvt.h99
-rw-r--r--drivers/hwmon/gxp-fan-ctrl.c106
-rw-r--r--drivers/hwmon/ina2xx.c20
-rw-r--r--drivers/hwmon/lm90.c4
-rw-r--r--drivers/hwmon/ltc4283.c1
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c4
-rw-r--r--drivers/hwmon/peci/common.h4
-rw-r--r--drivers/hwmon/pmbus/Kconfig47
-rw-r--r--drivers/hwmon/pmbus/Makefile2
-rw-r--r--drivers/hwmon/pmbus/acbel-fsg032.c1
-rw-r--r--drivers/hwmon/pmbus/adm1275.c142
-rw-r--r--drivers/hwmon/pmbus/lm25066.c37
-rw-r--r--drivers/hwmon/pmbus/max34440.c96
-rw-r--r--drivers/hwmon/pmbus/mp2975.c5
-rw-r--r--drivers/hwmon/pmbus/mpq82d00.c249
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c4
-rw-r--r--drivers/hwmon/pmbus/sq24860.c430
-rw-r--r--drivers/hwmon/pmbus/xdpe1a2g7b.c11
-rw-r--r--drivers/hwmon/pwm-fan.c6
-rw-r--r--drivers/hwmon/sht15.c4
-rw-r--r--drivers/hwmon/sht3x.c15
-rw-r--r--drivers/hwmon/tmp401.c2
-rw-r--r--drivers/hwmon/tvs-mpfs.c390
-rw-r--r--drivers/hwmon/xgene-hwmon.c4
-rw-r--r--drivers/hwmon/yogafan.c14
57 files changed, 3117 insertions, 385 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
index d6a7517f2a50..283cd1662689 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
@@ -25,19 +25,38 @@ description: |
https://www.silergy.com/
download/downloadFile?id=5669&type=product&ftype=note
+ The BD12780 and BD12780A are hot-swap controllers from ROHM. They are
+ functionally compatible with the ADM1278. The main difference between
+ the BD12780A and the BD12780 is amount of configurable I2C addresses.
+
+ Datasheets:
+ https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
+ https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+
+ The BD12790 is a ROHM hot-swap controller, functionally similar to the
+ ADM1272.
+
properties:
compatible:
- enum:
- - adi,adm1075
- - adi,adm1272
- - adi,adm1273
- - adi,adm1275
- - adi,adm1276
- - adi,adm1278
- - adi,adm1281
- - adi,adm1293
- - adi,adm1294
- - silergy,mc09c
+ oneOf:
+ - enum:
+ - adi,adm1075
+ - adi,adm1272
+ - adi,adm1273
+ - adi,adm1275
+ - adi,adm1276
+ - adi,adm1278
+ - adi,adm1281
+ - adi,adm1293
+ - adi,adm1294
+ - rohm,bd12780
+ - rohm,bd12790
+ - silergy,mc09c
+
+ # Require BD12780 as a fall-back for BD12780A.
+ - items:
+ - const: rohm,bd12780a
+ - const: rohm,bd12780
reg:
maxItems: 1
@@ -88,6 +107,7 @@ allOf:
enum:
- adi,adm1272
- adi,adm1273
+ - rohm,bd12790
then:
properties:
adi,volt-curr-sample-average:
@@ -104,6 +124,7 @@ allOf:
- adi,adm1281
- adi,adm1293
- adi,adm1294
+ - rohm,bd12780
- silergy,mc09c
then:
properties:
diff --git a/Documentation/devicetree/bindings/hwmon/amphenol,chipcap2.yaml b/Documentation/devicetree/bindings/hwmon/amphenol,chipcap2.yaml
index 17351fdbefce..56b0cecfca5f 100644
--- a/Documentation/devicetree/bindings/hwmon/amphenol,chipcap2.yaml
+++ b/Documentation/devicetree/bindings/hwmon/amphenol,chipcap2.yaml
@@ -45,6 +45,8 @@ properties:
- const: low
- const: high
+ label: true
+
vdd-supply:
description:
Dedicated, controllable supply-regulator to reset the device and
@@ -55,6 +57,9 @@ required:
- reg
- vdd-supply
+allOf:
+ - $ref: hwmon-common.yaml#
+
additionalProperties: false
examples:
@@ -72,6 +77,7 @@ examples:
<5 IRQ_TYPE_EDGE_RISING>,
<6 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "ready", "low", "high";
+ label = "Room";
vdd-supply = <&reg_vdd>;
};
};
diff --git a/Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml b/Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml
new file mode 100644
index 000000000000..58ec8635dce3
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/eswin,eic7700-pvt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ESWIN EIC7700 PVT Sensor
+
+maintainers:
+ - Yulin Lu <luyulin@eswincomputing.com>
+ - Huan He <hehuan1@eswincomputing.com>
+
+description:
+ ESWIN EIC7700 SoC integrates embedded voltage and temperature sensors to
+ monitor the internal SoC environment. The system includes two PVT sensor
+ instances. The PVT0 monitors the main SoC power domain. The PVT1 sensor
+ monitors the DDR core power domain.
+
+allOf:
+ - $ref: /schemas/hwmon/hwmon-common.yaml#
+
+properties:
+ compatible:
+ const: eswin,eic7700-pvt
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: PVT enable clock
+ - description: APB bus clock
+
+ clock-names:
+ items:
+ - const: enable
+ - const: apb
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ '#thermal-sensor-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+ - resets
+ - '#thermal-sensor-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ sensor@50b00000 {
+ compatible = "eswin,eic7700-pvt";
+ reg = <0x50b00000 0x10000>;
+ clocks = <&clocks 244>, <&clocks 234>;
+ clock-names = "enable", "apb";
+ interrupts = <349>;
+ interrupt-parent = <&plic>;
+ label = "pvt0";
+ resets = <&reset 111>;
+ #thermal-sensor-cells = <0>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml b/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml
index 4a52aac6be72..963aa640dc05 100644
--- a/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml
+++ b/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml
@@ -18,21 +18,12 @@ properties:
const: hpe,gxp-fan-ctrl
reg:
- items:
- - description: Fan controller PWM
- - description: Programmable logic
- - description: Function 2
-
- reg-names:
- items:
- - const: base
- - const: pl
- - const: fn2
+ description: Fan controller PWM
+ maxItems: 1
required:
- compatible
- reg
- - reg-names
additionalProperties: false
@@ -40,6 +31,5 @@ examples:
- |
fan-controller@1000c00 {
compatible = "hpe,gxp-fan-ctrl";
- reg = <0x1000c00 0x200>, <0xd1000000 0xff>, <0x80200000 0x100000>;
- reg-names = "base", "pl", "fn2";
+ reg = <0x1000c00 0x200>;
};
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/silergy,sq24860.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/silergy,sq24860.yaml
new file mode 100644
index 000000000000..03ef82c11e1a
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/silergy,sq24860.yaml
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+
+$id: http://devicetree.org/schemas/hwmon/pmbus/silergy,sq24860.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Silergy SQ24860 eFuse
+
+maintainers:
+ - Ziming Zhu <ziming.zhu@silergycorp.com>
+
+description:
+ The Silergy SQ24860 is an integrated, high-current circuit protection and
+ power management device with PMBus interface.
+
+properties:
+ compatible:
+ const: silergy,sq24860
+
+ reg:
+ maxItems: 1
+
+ silergy,rimon-micro-ohms:
+ description:
+ Micro-ohms value of the resistance installed between the IMON pin and
+ the ground reference.
+
+ interrupts:
+ description: PMBus SMBAlert interrupt.
+ maxItems: 1
+
+ regulators:
+ type: object
+ description:
+ List of regulators provided by this controller.
+
+ properties:
+ vout:
+ $ref: /schemas/regulator/regulator.yaml#
+ type: object
+ unevaluatedProperties: false
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - silergy,rimon-micro-ohms
+
+additionalProperties: false
+
+examples:
+ - |
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hw-monitor@40 {
+ compatible = "silergy,sq24860";
+ reg = <0x40>;
+
+ interrupt-parent = <&gpio>;
+ interrupts = <42 8>;
+ silergy,rimon-micro-ohms = <1600000000>;
+
+ regulators {
+ cpu0_vout: vout {
+ regulator-name = "main_cpu0";
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/ti,lm25066.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/ti,lm25066.yaml
index a20f140dc79a..fe42daabaaa8 100644
--- a/Documentation/devicetree/bindings/hwmon/pmbus/ti,lm25066.yaml
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/ti,lm25066.yaml
@@ -46,12 +46,32 @@ properties:
additionalProperties: false
+ ti,current-range:
+ description: |
+ Configure the current limit setting. When present, this property
+ overrides the hardware setting of the physical CL pin by configuring
+ the DEVICE_SETUP register.
+ - "low": maps to 25 mV (LM25066) or 26 mV (LM5064, LM5066, LM5066i)
+ - "high": maps to 46 mV (LM25066) or 50 mV (LM5064, LM5066, LM5066i)
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - low
+ - high
+
required:
- compatible
- reg
allOf:
- $ref: /schemas/hwmon/hwmon-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,lm25056
+ then:
+ properties:
+ ti,current-range: false
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/hwmon/sensirion,sht30.yaml b/Documentation/devicetree/bindings/hwmon/sensirion,sht30.yaml
new file mode 100644
index 000000000000..dee0252941c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/sensirion,sht30.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/sensirion,sht30.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sensirion SHT3x and GXCAS GXHT30 humidity and temperature sensors
+
+maintainers:
+ - Zaixiang Xu <zaixiang.xu.dev@gmail.com>
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - sensirion,sht30
+ - sensirion,sts30
+ - items:
+ - enum:
+ - gxcas,gxht30
+ - sensirion,sht31
+ - sensirion,sht35
+ - sensirion,sht85
+ - const: sensirion,sht30
+ - items:
+ - enum:
+ - sensirion,sts31
+ - sensirion,sts32
+ - sensirion,sts35
+ - const: sensirion,sts30
+
+ reg:
+ maxItems: 1
+
+ vdd-supply:
+ description: Regulator that provides power to the VDD pin.
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the nRESET pin. Active low.
+
+ interrupts:
+ maxItems: 1
+ description:
+ Interrupt connected to the ALERT pin.
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ humidity-sensor@44 {
+ compatible = "gxcas,gxht30", "sensirion,sht30";
+ reg = <0x44>;
+ vdd-supply = <&vcc_3v3_reg>;
+ reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
index 009d78b30859..a79ade3142fa 100644
--- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
+++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
@@ -28,6 +28,7 @@ properties:
- ti,ina228
- ti,ina230
- ti,ina231
+ - ti,ina232
- ti,ina233
- ti,ina234
- ti,ina237
@@ -114,6 +115,7 @@ allOf:
- ti,ina228
- ti,ina230
- ti,ina231
+ - ti,ina232
- ti,ina234
- ti,ina237
- ti,ina238
@@ -136,6 +138,7 @@ allOf:
- ti,ina226
- ti,ina230
- ti,ina231
+ - ti,ina232
- ti,ina234
- ti,ina260
- ti,ina700
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 435c4baab436..8116f21bf8be 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -362,6 +362,8 @@ properties:
- mps,mp9941
# Monolithic Power Systems Inc. digital step-down converter mp9945
- mps,mp9945
+ # Monolithic Power Systems Inc. digital step-down converter mpq82d00
+ - mps,mpq82d00
# Murata D1U74T-W power supply unit
- murata,d1u74t
# Temperature sensor with integrated fan control
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 12cfe65c1e0c..442c0009e310 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -698,6 +698,8 @@ patternProperties:
description: Gateworks Corporation
use "gateworks" vendor prefix
deprecated: true
+ "^gxcas,.*":
+ description: Beijing Galaxy-CAS Technology Co., Ltd.
"^hannstar,.*":
description: HannStar Display Corporation
"^haochuangyi,.*":
diff --git a/Documentation/hwmon/adm1275.rst b/Documentation/hwmon/adm1275.rst
index cf923f20fa52..d8495be313b8 100644
--- a/Documentation/hwmon/adm1275.rst
+++ b/Documentation/hwmon/adm1275.rst
@@ -67,6 +67,30 @@ Supported chips:
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1293_1294.pdf
+ * ROHM Semiconductor BD12780
+
+ Prefix: 'bd12780'
+
+ Addresses scanned: -
+
+ Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
+
+ * ROHM Semiconductor BD12780A
+
+ Prefix: 'bd12780'
+
+ Addresses scanned: -
+
+ Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+
+ * ROHM Semiconductor BD12790
+
+ Prefix: 'bd12790'
+
+ Addresses scanned: -
+
+ Datasheet: -
+
* Silergy SQ24905C
Prefix: 'mc09c'
diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst
index 77a709517437..a5db48d5a72e 100644
--- a/Documentation/hwmon/asus_ec_sensors.rst
+++ b/Documentation/hwmon/asus_ec_sensors.rst
@@ -17,6 +17,7 @@ Supported boards:
* ProArt X670E-CREATOR WIFI
* ProArt X870E-CREATOR WIFI
* ProArt B550-CREATOR
+ * ProArt Z690-CREATOR WIFI
* ROG CROSSHAIR VIII DARK HERO
* ROG CROSSHAIR VIII HERO (WI-FI)
* ROG CROSSHAIR VIII FORMULA
@@ -25,11 +26,13 @@ Supported boards:
* ROG CROSSHAIR X670E EXTREME
* ROG CROSSHAIR X670E HERO
* ROG CROSSHAIR X670E GENE
+ * ROG CROSSHAIR X870E HERO
* ROG MAXIMUS X HERO
* ROG MAXIMUS XI HERO
* ROG MAXIMUS XI HERO (WI-FI)
* ROG MAXIMUS Z690 FORMULA
* ROG MAXIMUS Z790 EXTREME
+ * ROG MAXIMUS Z790 HERO
* ROG STRIX B550-E GAMING
* ROG STRIX B550-I GAMING
* ROG STRIX B650E-E GAMING WIFI
@@ -47,6 +50,7 @@ Supported boards:
* 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 Z390-F GAMING
* ROG STRIX Z490-F GAMING
diff --git a/Documentation/hwmon/chipcap2.rst b/Documentation/hwmon/chipcap2.rst
index dc165becc64c..c38d87b91b69 100644
--- a/Documentation/hwmon/chipcap2.rst
+++ b/Documentation/hwmon/chipcap2.rst
@@ -70,4 +70,6 @@ humidity1_min_hyst: RW humidity low hystersis
humidity1_max_hyst: RW humidity high hystersis
humidity1_min_alarm: RO humidity low alarm indicator
humidity1_max_alarm: RO humidity high alarm indicator
+humidity1_label: RO descriptive name for the sensor
+temp1_label: RO descriptive name for the sensor
=============================== ======= ========================================
diff --git a/Documentation/hwmon/coretemp.rst b/Documentation/hwmon/coretemp.rst
index f63b21f24d42..349301683381 100644
--- a/Documentation/hwmon/coretemp.rst
+++ b/Documentation/hwmon/coretemp.rst
@@ -44,9 +44,9 @@ Temperature known as TjMax is the maximum junction temperature of processor,
which depends on the CPU model. See table below. At this temperature, protection
mechanism will perform actions to forcibly cool down the processor. Alarm
may be raised, if the temperature grows enough (more than TjMax) to trigger
-the Out-Of-Spec bit. Following table summarizes the exported sysfs files:
+the Out-Of-Spec bit. The following table summarizes the exported sysfs files:
-All Sysfs entries are named with their core_id (represented here by 'X').
+All sysfs entries are named with their core_id (represented here by 'X').
================= ========================================================
tempX_input Core temperature (in millidegrees Celsius).
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 29130df44d12..956f3da0b722 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -201,6 +201,7 @@ Hardware Monitoring Kernel Drivers
mp5990
mp9941
mp9945
+ mpq82d00
mpq8785
nct6683
nct6775
@@ -253,6 +254,7 @@ Hardware Monitoring Kernel Drivers
smsc47m1
sparx5-temp
spd5118
+ sq24860
stpddc60
surface_fan
sy7636a-hwmon
@@ -272,6 +274,7 @@ Hardware Monitoring Kernel Drivers
tps53679
tps546d24
tsc1641
+ tvs-mpfs
twl4030-madc-hwmon
ucd9000
ucd9200
diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst
index e7421f4dbf38..866f22b88e9b 100644
--- a/Documentation/hwmon/max34440.rst
+++ b/Documentation/hwmon/max34440.rst
@@ -65,6 +65,16 @@ Supported chips:
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34451.pdf
+ * Maxim MAX34452
+
+ PMBus 16-Channel V/I Monitor and 12-Channel Sequencer/Marginer
+
+ Prefixes: 'max34452'
+
+ Addresses scanned: -
+
+ Datasheet: -
+
* Maxim MAX34460
PMBus 12-Channel Voltage Monitor & Sequencer
@@ -94,11 +104,11 @@ Description
This driver supports multiple devices: hardware monitoring for Maxim MAX34440
PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply
Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data
-Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and
-MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. The
-MAX34451 supports monitoring voltage or current of 12 channels based on GIN
-pins. The MAX34460 supports 12 voltage channels, and the MAX34461 supports 16
-voltage channels. The ADPM12160, ADPM12200, and ADPM12250 also monitors both
+Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34452, MAX34460,
+and MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. The
+MAX34451 and MAX34452 support monitoring voltage or current of 16 channels based
+on GIN pins. The MAX34460 supports 12 voltage channels, and the MAX34461 supports
+16 voltage channels. The ADPM12160, ADPM12200, and ADPM12250 also monitor both
input and output of voltage and current.
The driver is a client driver to the core PMBus driver. Please see
@@ -171,7 +181,7 @@ curr[1-6]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT
register.
curr[1-6]_max_alarm Current high alarm. From IOUT_OC_WARNING status.
curr[1-6]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status.
-curr[1-4]_average Historical average current (MAX34446/34451 only).
+curr[1-4]_average Historical average current (MAX34446/34451/34452 only).
curr[1-6]_highest Historical maximum current.
curr[1-6]_reset_history Write any value to reset history.
======================= ========================================================
@@ -223,7 +233,7 @@ temp[1-8]_reset_history Write any value to reset history.
.. note::
- - MAX34451 supports attribute groups in[1-16] (or curr[1-16] based on
- input pins) and temp[1-5].
+ - MAX34451 and MAX34452 support attribute groups in[1-16] (or curr[1-16]
+ based on input pins) and temp[1-5].
- MAX34460 supports attribute groups in[1-12] and temp[1-5].
- MAX34461 supports attribute groups in[1-16] and temp[1-5].
diff --git a/Documentation/hwmon/mpq82d00.rst b/Documentation/hwmon/mpq82d00.rst
new file mode 100644
index 000000000000..0749c130bd11
--- /dev/null
+++ b/Documentation/hwmon/mpq82d00.rst
@@ -0,0 +1,73 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver mpq82d00
+======================
+
+Supported chips:
+
+ * MPS mpq82d00
+
+ Prefix: 'mpq82d00'
+
+Author:
+
+ Wensheng Wang <wenswang@yeah.net>
+
+Description
+-----------
+
+This driver implements support for Monolithic Power Systems, Inc. (MPS)
+MPQ82D00 Step-Down Controller.
+
+Device compliant with:
+
+- PMBus rev 1.3 interface.
+
+The driver exports the following attributes via the 'sysfs' files
+for input voltage:
+
+**in1_input**
+
+**in1_label**
+
+**in1_alarm**
+
+The driver provides the following attributes for output voltage:
+
+**in2_input**
+
+**in2_label**
+
+**in2_alarm**
+
+The driver provides the following attributes for output current:
+
+**curr1_input**
+
+**curr1_label**
+
+**curr1_crit**
+
+**curr1_crit_alarm**
+
+The driver provides the following attributes for input power:
+
+**power1_input**
+
+**power1_label**
+
+**power1_alarm**
+
+The driver provides the following attributes for output power:
+
+**power2_input**
+
+**power2_label**
+
+The driver provides the following attributes for temperature:
+
+**temp1_input**
+
+**temp1_crit**
+
+**temp1_crit_alarm**
diff --git a/Documentation/hwmon/sht3x.rst b/Documentation/hwmon/sht3x.rst
index ea1642920295..4f67671eb0cd 100644
--- a/Documentation/hwmon/sht3x.rst
+++ b/Documentation/hwmon/sht3x.rst
@@ -31,6 +31,14 @@ Supported chips:
Datasheet: https://sensirion.com/media/documents/4B40CEF3/640B2346/Sensirion_Humidity_Sensors_SHT85_Datasheet.pdf
+ * GXCAS GXHT30
+
+ Prefix: 'sht3x'
+
+ Addresses scanned: none
+
+ Datasheet: https://www.galaxy-cas.com/uploads/files/202509/GXHT30_datasheet_V3.9_20250919170350.pdf
+
Author:
- David Frey <david.frey@sensirion.com>
@@ -40,9 +48,12 @@ Description
-----------
This driver implements support for the Sensirion SHT3x-DIS, STS3x-DIS and SHT85
-series of humidity and temperature sensors. Temperature is measured in degrees
-celsius, relative humidity is expressed as a percentage. In the sysfs interface,
-all values are scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
+series of humidity and temperature sensors, as well as the compatible GXCAS
+GXHT30. The GXHT30 is a drop-in replacement for the SHT30: instantiate it from
+devicetree with the "gxcas,gxht30", "sensirion,sht30" compatibles, or manually
+using the sht3x device name. Temperature is measured in degrees celsius,
+relative humidity is expressed as a percentage. In the sysfs interface, all
+values are scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
The device communicates with the I2C protocol. SHT3x sensors can have the I2C
addresses 0x44 or 0x45 (0x4a or 0x4b for sts3x), depending on the wiring. SHT85
diff --git a/Documentation/hwmon/sq24860.rst b/Documentation/hwmon/sq24860.rst
new file mode 100644
index 000000000000..f0182b955d8a
--- /dev/null
+++ b/Documentation/hwmon/sq24860.rst
@@ -0,0 +1,96 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver sq24860
+=====================
+
+Supported chips:
+
+ * Silergy SQ24860
+
+ Prefix: 'sq24860'
+
+Author:
+
+ Ziming Zhu <ziming.zhu@silergycorp.com>
+
+Description
+------------
+
+This driver implements support for the Silergy SQ24860 eFuse. The device is an
+integrated circuit protection and power management device with a PMBus
+interface.
+
+The device supports direct format for reading input voltage, output voltage,
+auxiliary voltage, input current, input power, and temperature.
+
+The current and power measurement scale depends on the resistor connected
+between the IMON pin and ground. The resistor value can be configured with the
+``silergy,rimon-micro-ohms`` device tree property. See
+``Documentation/devicetree/bindings/hwmon/pmbus/silergy,sq24860.yaml`` for details.
+
+Due to the specificities of the chip, all history reset attributes are tied
+together. Resetting the history of one sensor resets the history of all sensors.
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+======================= ======================================================
+in1_label "vin"
+in1_input Measured input voltage.
+in1_average Average measured input voltage.
+in1_min Minimum input voltage limit.
+in1_lcrit Critical low input voltage limit.
+in1_max Maximum input voltage limit.
+in1_crit Critical high input voltage limit.
+in1_min_alarm Input voltage low warning alarm.
+in1_lcrit_alarm Input voltage low fault alarm.
+in1_max_alarm Input voltage high warning alarm.
+in1_crit_alarm Input voltage high fault alarm.
+in1_highest Historical maximum input voltage.
+in1_lowest Historical minimum input voltage.
+in1_reset_history Write any value to reset history.
+
+in2_label "vmon"
+in2_input Measured auxiliary input voltage.
+
+in3_label "vout1"
+in3_input Measured output voltage.
+in3_average Average measured output voltage.
+in3_min Minimum output voltage limit.
+in3_min_alarm Output voltage low alarm.
+in3_lowest Historical minimum output voltage.
+in3_reset_history Write any value to reset history.
+
+curr1_label "iin"
+curr1_input Measured input current.
+curr1_average Average measured input current.
+curr1_max Maximum input current warning limit.
+curr1_crit Critical input over-current fault limit.
+curr1_max_alarm Input current warning alarm.
+curr1_crit_alarm Input over-current fault alarm.
+curr1_highest Historical maximum input current.
+curr1_reset_history Write any value to reset history.
+
+power1_label "pin"
+power1_input Measured input power.
+power1_average Average measured input power.
+power1_max Maximum input power warning limit.
+power1_alarm Input power warning alarm.
+power1_input_highest Historical maximum input power.
+power1_reset_history Write any value to reset history.
+
+temp1_input Measured temperature.
+temp1_average Average measured temperature.
+temp1_max Maximum temperature warning limit.
+temp1_crit Critical temperature fault limit.
+temp1_max_alarm Temperature warning alarm.
+temp1_crit_alarm Temperature fault alarm.
+temp1_highest Historical maximum temperature.
+temp1_reset_history Write any value to reset history.
+
+samples Number of samples used for average values.
+======================= ======================================================
+
diff --git a/Documentation/hwmon/tvs-mpfs.rst b/Documentation/hwmon/tvs-mpfs.rst
new file mode 100644
index 000000000000..1035812f363a
--- /dev/null
+++ b/Documentation/hwmon/tvs-mpfs.rst
@@ -0,0 +1,53 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver tvs-mpfs
+======================
+
+Supported chips:
+
+ * PolarFire SoC
+
+Authors:
+
+ - Conor Dooley <conor.dooley@microchip.com>
+ - Lars Randers <lranders@mail.dk>
+
+Description
+-----------
+
+This driver implements support for the temperature and voltage sensors on
+PolarFire SoC. The temperature reports how hot the die is, and the voltages are
+the SoC's 1.05, 1.8 and 2.5 volt rails respectively.
+
+
+Usage Notes
+-----------
+
+update_interval has a permitted range of 0 to 8 milliseconds.
+
+Temperatures are read in millidegrees Celsius, but the hardware measures in
+degrees Kelvin, storing the result as 11.4 fixed point data, for a maximum
+value of 2047.9375 degrees Kelvin.
+
+Voltages are read in millivolts. The hardware measures in millivolts, storing
+the value as 12.3 fixed point data, for a maximum of 4095.875 millivolts.
+The minimum value reportable by the driver is 0 volts, although the hardware
+is capable of measuring negative values.
+
+Sysfs entries
+-------------
+
+The following attributes are supported. update_interval is read-write, as are
+the enables. All other attributes are read only.
+
+======================= ====================================================
+temp1_label Fixed name for channel.
+temp1_input Measured temperature for channel.
+temp1_enable Enable/disable for channel.
+
+in[0-2]_label Fixed name for channel.
+in[0-2]_input Measured voltage for channel.
+in[0-2]_enable Enable/disable for channel.
+
+update_interval The interval at which the chip will update readings.
+======================= ====================================================
diff --git a/MAINTAINERS b/MAINTAINERS
index c508c6d99908..f8643b4e0981 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18343,6 +18343,13 @@ S: Maintained
F: Documentation/hwmon/mp9945.rst
F: drivers/hwmon/pmbus/mp9945.c
+MPS MPQ82D00 DRIVER
+M: Wensheng Wang <wenswang@yeah.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/mpq82d00.rst
+F: drivers/hwmon/pmbus/mpq82d00.c
+
MR800 AVERMEDIA USB FM RADIO DRIVER
M: Alexey Klimov <alexey.klimov@linaro.org>
L: linux-media@vger.kernel.org
@@ -23287,6 +23294,7 @@ F: drivers/char/hw_random/mpfs-rng.c
F: drivers/clk/microchip/clk-mpfs*.c
F: drivers/firmware/microchip/mpfs-auto-update.c
F: drivers/gpio/gpio-mpfs.c
+F: drivers/hwmon/tvs-mpfs.c
F: drivers/i2c/busses/i2c-microchip-corei2c.c
F: drivers/mailbox/mailbox-mpfs.c
F: drivers/pci/controller/plda/pcie-microchip-host.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2bfbcc033d59..741704203e52 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -802,7 +802,7 @@ config SENSORS_GXP_FAN_CTRL
If you say yes here you get support for GXP fan control functionality.
The GXP controls fan function via the CPLD through the use of PWM
- registers. This driver reports status and pwm setting of the fans.
+ registers. This driver enables pwm setting of the fans.
config SENSORS_HIH6130
tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor"
@@ -942,6 +942,19 @@ config SENSORS_JC42
This driver can also be built as a module. If so, the module
will be called jc42.
+config SENSORS_POLARFIRE_SOC_TVS
+ tristate "PolarFire SoC (MPFS) temperature and voltage sensor"
+ depends on POLARFIRE_SOC_SYSCONS || COMPILE_TEST
+ depends on MFD_SYSCON
+ help
+ This driver adds support for the PolarFire SoC (MPFS) Temperature and
+ Voltage Sensor.
+
+ To compile this driver as a module, choose M here. the
+ module will be called tvs-mpfs.
+
+ If unsure, say N.
+
config SENSORS_POWERZ
tristate "ChargerLAB POWER-Z USB-C tester"
depends on USB
@@ -2010,8 +2023,9 @@ config SENSORS_SHT3x
depends on I2C
select CRC8
help
- If you say yes here you get support for the Sensiron SHT30, SHT31 and
- SHT85 humidity and temperature sensors.
+ If you say yes here you get support for the Sensiron SHT30, SHT31,
+ SHT85 and the compatible GXCAS GXHT30 humidity and temperature
+ sensors.
This driver can also be built as a module. If so, the module
will be called sht3x.
@@ -2070,6 +2084,17 @@ config SENSORS_DME1737
This driver can also be built as a module. If so, the module
will be called dme1737.
+config SENSORS_EIC7700_PVT
+ tristate "Eswin EIC7700 Voltage, Temperature sensor driver"
+ depends on ARCH_ESWIN || COMPILE_TEST
+ select POLYNOMIAL
+ help
+ If you say yes here you get support for Eswin EIC7700 PVT sensor
+ embedded into the SoC.
+
+ This driver can also be built as a module. If so, the module will be
+ called eic7700-pvt.
+
config SENSORS_EMC1403
tristate "SMSC EMC1403/23 thermal sensor"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 63effc0ab8d1..63809eeec2f4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o
obj-$(CONFIG_SENSORS_DS620) += ds620.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
+obj-$(CONFIG_SENSORS_EIC7700_PVT) += eic7700-pvt.o
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
@@ -197,6 +198,7 @@ obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+obj-$(CONFIG_SENSORS_POLARFIRE_SOC_TVS) += tvs-mpfs.o
obj-$(CONFIG_SENSORS_POWERZ) += powerz.o
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
obj-$(CONFIG_SENSORS_PROM21_XHCI) += prom21-xhci.o
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 0c9b9f4180fb..8a539e8d1334 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -21,8 +21,6 @@
#include <linux/platform_device.h>
#define ACPI_POWER_METER_NAME "power_meter"
-#define ACPI_POWER_METER_DEVICE_NAME "Power Meter"
-#define ACPI_POWER_METER_CLASS "pwr_meter_resource"
#define NUM_SENSORS 17
@@ -877,7 +875,7 @@ static void acpi_power_meter_notify(acpi_handle handle, u32 event, void *data)
break;
}
- acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS,
+ acpi_bus_generate_netlink_event("pwr_meter_resource",
dev_name(&resource->acpi_dev->dev),
event, 0);
}
@@ -899,8 +897,6 @@ static int acpi_power_meter_probe(struct platform_device *pdev)
resource->sensors_valid = 0;
resource->acpi_dev = device;
mutex_init(&resource->lock);
- strscpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME);
- strscpy(acpi_device_class(device), ACPI_POWER_METER_CLASS);
platform_set_drvdata(pdev, resource);
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 90a14a7f2c4c..ca56bd8b170e 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -33,6 +33,7 @@
#include <linux/workqueue.h>
#include <linux/err.h>
#include <linux/bits.h>
+#include <asm/barrier.h>
/* data port used by Apple SMC */
#define APPLESMC_DATA_PORT 0x300
@@ -133,6 +134,7 @@ static struct applesmc_registers {
bool init_complete; /* true when fully initialized */
struct applesmc_entry *cache; /* cached key entries */
const char **index; /* temperature key index */
+ char fan_positions[10][17]; /* cached fan position labels */
} smcreg = {
.mutex = __MUTEX_INITIALIZER(smcreg.mutex),
};
@@ -143,8 +145,8 @@ static s16 rest_x;
static s16 rest_y;
static u8 backlight_state[2];
-static struct device *hwmon_dev;
static struct input_dev *applesmc_idev;
+static struct device *hwmon_dev;
/*
* Last index written to key_at_index sysfs file, and value to use for all other
@@ -372,7 +374,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
__be32 be;
int ret = 0;
- if (cache->valid)
+ /* Pairs with smp_store_release() to ensure cache contents are visible */
+ if (smp_load_acquire(&cache->valid))
return cache;
mutex_lock(&smcreg.mutex);
@@ -391,7 +394,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
cache->len = info[0];
memcpy(cache->type, &info[1], 4);
cache->flags = info[5];
- cache->valid = true;
+ /* Pairs with smp_load_acquire() to commit cache contents before setting valid */
+ smp_store_release(&cache->valid, true);
out:
mutex_unlock(&smcreg.mutex);
@@ -566,7 +570,7 @@ static int applesmc_init_smcreg_try(void)
{
struct applesmc_registers *s = &smcreg;
bool left_light_sensor = false, right_light_sensor = false;
- unsigned int count;
+ unsigned int count, i;
u8 tmp[1];
int ret;
@@ -597,6 +601,16 @@ static int applesmc_init_smcreg_try(void)
if (s->fan_count > 10)
s->fan_count = 10;
+ for (i = 0; i < s->fan_count; i++) {
+ char newkey[5];
+
+ scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, i);
+ ret = applesmc_read_key(newkey, s->fan_positions[i], 16);
+ s->fan_positions[i][16] = 0;
+ if (ret)
+ scnprintf(s->fan_positions[i], 17, " Fan %d", i);
+ }
+
ret = applesmc_get_lower_bound(&s->temp_begin, "T");
if (ret)
return ret;
@@ -808,33 +822,6 @@ out:
return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right);
}
-/* Displays sensor key as label */
-static ssize_t applesmc_show_sensor_label(struct device *dev,
- struct device_attribute *devattr, char *sysfsbuf)
-{
- const char *key = smcreg.index[to_index(devattr)];
-
- return sysfs_emit(sysfsbuf, "%s\n", key);
-}
-
-/* Displays degree Celsius * 1000 */
-static ssize_t applesmc_show_temperature(struct device *dev,
- struct device_attribute *devattr, char *sysfsbuf)
-{
- const char *key = smcreg.index[to_index(devattr)];
- int ret;
- s16 value;
- int temp;
-
- ret = applesmc_read_s16(key, &value);
- if (ret)
- return ret;
-
- temp = 250 * (value >> 6);
-
- return sysfs_emit(sysfsbuf, "%d\n", temp);
-}
-
static ssize_t applesmc_show_fan_speed(struct device *dev,
struct device_attribute *attr, char *sysfsbuf)
{
@@ -854,99 +841,6 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
return sysfs_emit(sysfsbuf, "%u\n", speed);
}
-static ssize_t applesmc_store_fan_speed(struct device *dev,
- struct device_attribute *attr,
- const char *sysfsbuf, size_t count)
-{
- int ret;
- unsigned long speed;
- char newkey[5];
- u8 buffer[2];
-
- if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000)
- return -EINVAL; /* Bigger than a 14-bit value */
-
- scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
- to_index(attr));
-
- buffer[0] = (speed >> 6) & 0xff;
- buffer[1] = (speed << 2) & 0xff;
- ret = applesmc_write_key(newkey, buffer, 2);
-
- if (ret)
- return ret;
- else
- return count;
-}
-
-static ssize_t applesmc_show_fan_manual(struct device *dev,
- struct device_attribute *attr, char *sysfsbuf)
-{
- int ret;
- u16 manual = 0;
- u8 buffer[2];
-
- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
- if (ret)
- return ret;
-
- manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
- return sysfs_emit(sysfsbuf, "%d\n", manual);
-}
-
-static ssize_t applesmc_store_fan_manual(struct device *dev,
- struct device_attribute *attr,
- const char *sysfsbuf, size_t count)
-{
- int ret;
- u8 buffer[2];
- unsigned long input;
- u16 val;
-
- if (kstrtoul(sysfsbuf, 10, &input) < 0)
- return -EINVAL;
-
- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
- if (ret)
- goto out;
-
- val = (buffer[0] << 8 | buffer[1]);
-
- if (input)
- val = val | (0x01 << to_index(attr));
- else
- val = val & ~(0x01 << to_index(attr));
-
- buffer[0] = (val >> 8) & 0xFF;
- buffer[1] = val & 0xFF;
-
- ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
-
-out:
- if (ret)
- return ret;
- else
- return count;
-}
-
-static ssize_t applesmc_show_fan_position(struct device *dev,
- struct device_attribute *attr, char *sysfsbuf)
-{
- int ret;
- char newkey[5];
- u8 buffer[17];
-
- scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr));
-
- ret = applesmc_read_key(newkey, buffer, 16);
- buffer[16] = 0;
-
- if (ret)
- return ret;
-
- return sysfs_emit(sysfsbuf, "%s\n", buffer + 4);
-}
-
static ssize_t applesmc_calibrate_show(struct device *dev,
struct device_attribute *attr, char *sysfsbuf)
{
@@ -1094,22 +988,7 @@ static struct applesmc_node_group light_sensor_group[] = {
{ }
};
-static struct applesmc_node_group fan_group[] = {
- { "fan%d_label", applesmc_show_fan_position },
- { "fan%d_input", applesmc_show_fan_speed, NULL, 0 },
- { "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 },
- { "fan%d_max", applesmc_show_fan_speed, NULL, 2 },
- { "fan%d_safe", applesmc_show_fan_speed, NULL, 3 },
- { "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 },
- { "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual },
- { }
-};
-static struct applesmc_node_group temp_group[] = {
- { "temp%d_label", applesmc_show_sensor_label },
- { "temp%d_input", applesmc_show_temperature },
- { }
-};
/* Module stuff */
@@ -1307,8 +1186,238 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = {
};
MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);
+static struct applesmc_dev_attr *fan_safe_attrs;
+static struct attribute **fan_safe_attr_list;
+static struct attribute_group fan_safe_group;
+static const struct attribute_group *applesmc_extra_groups[2];
+
+static u32 *applesmc_temp_config;
+static u32 *applesmc_fan_config;
+static u32 *applesmc_pwm_config;
+static struct hwmon_channel_info *applesmc_info_temp;
+static struct hwmon_channel_info *applesmc_info_fan;
+static struct hwmon_channel_info *applesmc_info_pwm;
+static const struct hwmon_channel_info **applesmc_info_arr;
+static struct hwmon_chip_info *applesmc_chip;
+
+static void applesmc_free_hwmon(void)
+{
+ kfree(applesmc_temp_config);
+ kfree(applesmc_fan_config);
+ kfree(applesmc_pwm_config);
+ kfree(applesmc_info_temp);
+ kfree(applesmc_info_fan);
+ kfree(applesmc_info_pwm);
+ kfree(applesmc_info_arr);
+ kfree(applesmc_chip);
+ kfree(fan_safe_attrs);
+ kfree(fan_safe_attr_list);
+}
+
+static umode_t applesmc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ if (attr == hwmon_temp_input || attr == hwmon_temp_label)
+ return 0444;
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_label:
+ case hwmon_fan_max:
+ return 0444;
+ case hwmon_fan_min:
+ case hwmon_fan_target:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ if (attr == hwmon_pwm_enable)
+ return 0644;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int applesmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ int ret;
+
+ switch (type) {
+ case hwmon_temp:
+ if (attr == hwmon_temp_input) {
+ const char *key = smcreg.index[channel];
+ s16 value;
+
+ ret = applesmc_read_s16(key, &value);
+ if (ret)
+ return ret;
+ *val = 250 * (value >> 6);
+ return 0;
+ }
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input: {
+ char key[5];
+ u8 buffer[2];
+
+ scnprintf(key, sizeof(key), "F%dAc", channel);
+ ret = applesmc_read_key(key, buffer, 2);
+ if (ret)
+ return ret;
+ *val = ((buffer[0] << 8 | buffer[1]) >> 2);
+ return 0;
+ }
+ case hwmon_fan_min: {
+ char key[5];
+ u8 buffer[2];
+
+ scnprintf(key, sizeof(key), "F%dMn", channel);
+ ret = applesmc_read_key(key, buffer, 2);
+ if (ret)
+ return ret;
+ *val = ((buffer[0] << 8 | buffer[1]) >> 2);
+ return 0;
+ }
+ case hwmon_fan_max: {
+ char key[5];
+ u8 buffer[2];
+
+ scnprintf(key, sizeof(key), "F%dMx", channel);
+ ret = applesmc_read_key(key, buffer, 2);
+ if (ret)
+ return ret;
+ *val = ((buffer[0] << 8 | buffer[1]) >> 2);
+ return 0;
+ }
+ case hwmon_fan_target: {
+ char key[5];
+ u8 buffer[2];
+
+ scnprintf(key, sizeof(key), "F%dTg", channel);
+ ret = applesmc_read_key(key, buffer, 2);
+ if (ret)
+ return ret;
+ *val = ((buffer[0] << 8 | buffer[1]) >> 2);
+ return 0;
+ }
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ if (attr == hwmon_pwm_enable) {
+ u8 buffer[2];
+
+ ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+ if (ret)
+ return ret;
+ *val = (((buffer[0] << 8 | buffer[1]) >> channel) & 0x01) ? 1 : 2;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+
+static int applesmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ int ret;
+
+ switch (type) {
+ case hwmon_fan:
+ if (attr == hwmon_fan_min || attr == hwmon_fan_target) {
+ char key[5];
+ u8 buffer[2];
+ const char *fmt = (attr == hwmon_fan_min) ? "F%dMn" : "F%dTg";
+
+ if (val < 0 || val >= 0x4000)
+ return -EINVAL;
+ scnprintf(key, sizeof(key), fmt, channel);
+ buffer[0] = (val >> 6) & 0xff;
+ buffer[1] = (val << 2) & 0xff;
+ return applesmc_write_key(key, buffer, 2);
+ }
+ break;
+ case hwmon_pwm:
+ if (attr == hwmon_pwm_enable) {
+ u8 buffer[2];
+ u16 manual_val;
+ const struct applesmc_entry *entry;
+
+ if (val != 1 && val != 2)
+ return -EINVAL;
+
+ entry = applesmc_get_entry_by_key(FANS_MANUAL);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ mutex_lock(&smcreg.mutex);
+ ret = read_smc(APPLESMC_READ_CMD, entry->key, buffer, 2);
+ if (ret)
+ goto out_unlock;
+ manual_val = (buffer[0] << 8 | buffer[1]);
+ if (val == 1)
+ manual_val |= (0x01 << channel);
+ else
+ manual_val &= ~(0x01 << channel);
+ buffer[0] = (manual_val >> 8) & 0xff;
+ buffer[1] = manual_val & 0xff;
+ ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buffer, 2);
+out_unlock:
+ mutex_unlock(&smcreg.mutex);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+
+static int applesmc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ switch (type) {
+ case hwmon_temp:
+ if (attr == hwmon_temp_label) {
+ *str = smcreg.index[channel];
+ return 0;
+ }
+ break;
+ case hwmon_fan:
+ if (attr == hwmon_fan_label) {
+ *str = smcreg.fan_positions[channel] + 4;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops applesmc_hwmon_ops = {
+ .is_visible = applesmc_hwmon_is_visible,
+ .read = applesmc_hwmon_read,
+ .write = applesmc_hwmon_write,
+ .read_string = applesmc_hwmon_read_string,
+};
+
static int __init applesmc_init(void)
{
+ int i;
int ret;
if (!dmi_check_system(applesmc_whitelist)) {
@@ -1343,17 +1452,97 @@ static int __init applesmc_init(void)
if (ret)
goto out_smcreg;
- ret = applesmc_create_nodes(fan_group, smcreg.fan_count);
- if (ret)
+ /* allocate hwmon channel configs */
+ applesmc_temp_config = kcalloc(smcreg.index_count + 1,
+ sizeof(*applesmc_temp_config), GFP_KERNEL);
+ applesmc_fan_config = kcalloc(smcreg.fan_count + 1,
+ sizeof(*applesmc_fan_config), GFP_KERNEL);
+ applesmc_pwm_config = kcalloc(smcreg.fan_count + 1,
+ sizeof(*applesmc_pwm_config), GFP_KERNEL);
+ if (!applesmc_temp_config || !applesmc_fan_config || !applesmc_pwm_config) {
+ ret = -ENOMEM;
goto out_info;
+ }
- ret = applesmc_create_nodes(temp_group, smcreg.index_count);
- if (ret)
- goto out_fans;
+ for (i = 0; i < smcreg.index_count; i++)
+ applesmc_temp_config[i] = HWMON_T_INPUT | HWMON_T_LABEL;
+ applesmc_temp_config[smcreg.index_count] = 0;
+
+ for (i = 0; i < smcreg.fan_count; i++) {
+ applesmc_fan_config[i] = HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN |
+ HWMON_F_MAX | HWMON_F_TARGET;
+ applesmc_pwm_config[i] = HWMON_PWM_ENABLE;
+ }
+ applesmc_fan_config[smcreg.fan_count] = 0;
+ applesmc_pwm_config[smcreg.fan_count] = 0;
+
+ applesmc_info_temp = kzalloc_obj(*applesmc_info_temp, GFP_KERNEL);
+ applesmc_info_fan = kzalloc_obj(*applesmc_info_fan, GFP_KERNEL);
+ applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm, GFP_KERNEL);
+ if (!applesmc_info_temp || !applesmc_info_fan || !applesmc_info_pwm) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+
+ applesmc_info_temp->type = hwmon_temp;
+ applesmc_info_temp->config = applesmc_temp_config;
+
+ applesmc_info_fan->type = hwmon_fan;
+ applesmc_info_fan->config = applesmc_fan_config;
+
+ applesmc_info_pwm->type = hwmon_pwm;
+ applesmc_info_pwm->config = applesmc_pwm_config;
+
+ applesmc_info_arr = kcalloc(4, sizeof(*applesmc_info_arr), GFP_KERNEL);
+ if (!applesmc_info_arr) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+
+ applesmc_info_arr[0] = applesmc_info_temp;
+ applesmc_info_arr[1] = applesmc_info_fan;
+ applesmc_info_arr[2] = applesmc_info_pwm;
+ applesmc_info_arr[3] = NULL;
+
+ applesmc_chip = kzalloc_obj(*applesmc_chip, GFP_KERNEL);
+ if (!applesmc_chip) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+
+ applesmc_chip->ops = &applesmc_hwmon_ops;
+ applesmc_chip->info = applesmc_info_arr;
+
+ /* Create non-standard fanX_safe attributes group */
+ fan_safe_attrs = kcalloc(smcreg.fan_count,
+ sizeof(*fan_safe_attrs), GFP_KERNEL);
+ fan_safe_attr_list = kcalloc(smcreg.fan_count + 1,
+ sizeof(*fan_safe_attr_list), GFP_KERNEL);
+ if (!fan_safe_attrs || !fan_safe_attr_list) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+
+ for (i = 0; i < smcreg.fan_count; i++) {
+ struct applesmc_dev_attr *node = &fan_safe_attrs[i];
+
+ scnprintf(node->name, sizeof(node->name), "fan%d_safe", i + 1);
+ node->sda.index = (3 << 16) | (i & 0xffff); /* Option 3 (safe speed) */
+ node->sda.dev_attr.show = applesmc_show_fan_speed;
+ node->sda.dev_attr.store = NULL;
+ sysfs_attr_init(&node->sda.dev_attr.attr);
+ node->sda.dev_attr.attr.name = node->name;
+ node->sda.dev_attr.attr.mode = 0444;
+ fan_safe_attr_list[i] = &node->sda.dev_attr.attr;
+ }
+ fan_safe_attr_list[smcreg.fan_count] = NULL;
+ fan_safe_group.attrs = fan_safe_attr_list;
+ applesmc_extra_groups[0] = &fan_safe_group;
+ applesmc_extra_groups[1] = NULL;
ret = applesmc_create_accelerometer();
if (ret)
- goto out_temperature;
+ goto out_info;
ret = applesmc_create_light_sensor();
if (ret)
@@ -1363,7 +1552,8 @@ static int __init applesmc_init(void)
if (ret)
goto out_light_sysfs;
- hwmon_dev = hwmon_device_register(&pdev->dev);
+ hwmon_dev = hwmon_device_register_with_info(&pdev->dev, "applesmc", NULL,
+ applesmc_chip, applesmc_extra_groups);
if (IS_ERR(hwmon_dev)) {
ret = PTR_ERR(hwmon_dev);
goto out_light_ledclass;
@@ -1377,11 +1567,8 @@ out_light_sysfs:
applesmc_release_light_sensor();
out_accelerometer:
applesmc_release_accelerometer();
-out_temperature:
- applesmc_destroy_nodes(temp_group);
-out_fans:
- applesmc_destroy_nodes(fan_group);
out_info:
+ applesmc_free_hwmon();
applesmc_destroy_nodes(info_group);
out_smcreg:
applesmc_destroy_smcreg();
@@ -1402,13 +1589,12 @@ static void __exit applesmc_exit(void)
applesmc_release_key_backlight();
applesmc_release_light_sensor();
applesmc_release_accelerometer();
- applesmc_destroy_nodes(temp_group);
- applesmc_destroy_nodes(fan_group);
applesmc_destroy_nodes(info_group);
applesmc_destroy_smcreg();
platform_device_unregister(pdev);
platform_driver_unregister(&applesmc_driver);
release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+ applesmc_free_hwmon();
}
module_init(applesmc_init);
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index 01a022b3ee82..d386f056dc55 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -185,6 +185,20 @@ enum ec_sensors {
#define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2)
#define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3)
+/*
+ * The values for temperature sensor readings without physical sensors connected.
+ * The value varies across generations and is seemingly defined by the EC chip
+ * used in the given board.
+ */
+static const s32 temperature_blank_values[] = {-62, -60, -40};
+
+static const s32 environment_temp_sensors =
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_T_SENSOR_ALT1 |
+ SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT |
+ SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT |
+ SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 |
+ SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3;
+
enum board_family {
family_unknown,
family_amd_400_series,
@@ -386,6 +400,8 @@ static const struct ec_sensor_info sensors_family_intel_600[] = {
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
+ [ec_sensor_fan_cpu_opt] =
+ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
[ec_sensor_fan_water_flow] =
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbe),
[ec_sensor_temp_water_in] =
@@ -487,6 +503,14 @@ static const struct ec_board_info board_info_crosshair_x670e_hero = {
.family = family_amd_600_series,
};
+static const struct ec_board_info board_info_crosshair_x870e_hero = {
+ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
+ SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
+ .family = family_amd_800_series,
+};
+
static const struct ec_board_info board_info_maximus_vi_hero = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR |
@@ -527,6 +551,14 @@ static const struct ec_board_info board_info_maximus_z790_extreme = {
.family = family_intel_700_series,
};
+static const struct ec_board_info board_info_maximus_z790_hero = {
+ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW |
+ SENSOR_FAN_CPU_OPT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
+ .family = family_intel_700_series,
+};
+
static const struct ec_board_info board_info_prime_x470_pro = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
@@ -590,6 +622,13 @@ static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = {
.family = family_amd_800_series,
};
+static const struct ec_board_info board_info_pro_art_z690_creator_wifi = {
+ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CPU_OPT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PC00_LPCB_SIO1_MUT0,
+ .family = family_intel_600_series,
+};
+
static const struct ec_board_info board_info_pro_ws_trx50_sage_wifi = {
/* Board also has a nct6798 */
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_VRME |
@@ -743,7 +782,7 @@ static const struct ec_board_info board_info_strix_x870_i_gaming_wifi = {
static const struct ec_board_info board_info_strix_x870e_e_gaming_wifi = {
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
- SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR |
SENSOR_FAN_CPU_OPT,
.mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
.family = family_amd_800_series,
@@ -859,6 +898,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_pro_art_x670E_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X870E-CREATOR WIFI",
&board_info_pro_art_x870E_creator_wifi),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt Z690-CREATOR WIFI",
+ &board_info_pro_art_z690_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI",
&board_info_pro_ws_trx50_sage_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI A",
@@ -883,6 +924,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_crosshair_x670e_gene),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO",
&board_info_crosshair_x670e_hero),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X870E HERO",
+ &board_info_crosshair_x870e_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO",
&board_info_maximus_xi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)",
@@ -893,6 +936,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_maximus_z690_formula),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME",
&board_info_maximus_z790_extreme),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 HERO",
+ &board_info_maximus_z790_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING",
&board_info_strix_b550_e_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING",
@@ -927,6 +972,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_strix_x870_i_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI",
&board_info_strix_x870e_e_gaming_wifi),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI7 R2",
+ &board_info_strix_x870e_e_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-H GAMING WIFI7",
&board_info_strix_x870e_h_gaming_wifi7),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING",
@@ -955,6 +1002,7 @@ static const struct dmi_system_id dmi_table[] = {
};
struct ec_sensor {
+ /* this is ec_sensors enum value */
unsigned int info_index;
s32 cached_value;
};
@@ -1047,6 +1095,12 @@ get_sensor_info(const struct ec_sensors_data *state, int index)
return state->sensors_info + state->sensors[index].info_index;
}
+static enum ec_sensors
+get_ec_sensor_type(const struct ec_sensors_data *state, int index)
+{
+ return state->sensors[index].info_index;
+}
+
static int find_ec_sensor_index(const struct ec_sensors_data *ec,
enum hwmon_sensor_types type, int channel)
{
@@ -1290,6 +1344,17 @@ static int get_cached_value_or_update(const struct device *dev,
return 0;
}
+static bool is_blank_temperature_value(s32 value)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(temperature_blank_values); ++i) {
+ if (value == temperature_blank_values[i])
+ return true;
+ }
+ return false;
+}
+
/*
* Now follow the functions that implement the hwmon interface
*/
@@ -1297,6 +1362,8 @@ static int get_cached_value_or_update(const struct device *dev,
static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
+ const struct ec_sensor_info *sensor_info;
+ enum ec_sensors ec_sensor;
int ret;
s32 value = 0;
@@ -1308,12 +1375,19 @@ static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
}
ret = get_cached_value_or_update(dev, sidx, state, &value);
- if (!ret) {
- *val = scale_sensor_value(value,
- get_sensor_info(state, sidx)->type);
+ if (ret)
+ return ret;
+
+ sensor_info = get_sensor_info(state, sidx);
+ if (sensor_info->type == hwmon_temp) {
+ ec_sensor = get_ec_sensor_type(state, sidx);
+ if ((environment_temp_sensors & BIT(ec_sensor)) &&
+ is_blank_temperature_value(value))
+ return -ENODATA;
}
+ *val = scale_sensor_value(value, sensor_info->type);
- return ret;
+ return 0;
}
static int asus_ec_hwmon_read_string(struct device *dev,
diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c
index 1cb481a1ad26..04a767d762b3 100644
--- a/drivers/hwmon/axi-fan-control.c
+++ b/drivers/hwmon/axi-fan-control.c
@@ -508,8 +508,7 @@ static int axi_fan_control_probe(struct platform_device *pdev)
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
NULL, ctl);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "failed to request an irq\n");
+ return ret;
return 0;
}
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 4aecf463180f..086571d556b7 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -22,6 +22,8 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#define CC2_START_CM 0xA0
#define CC2_START_NOM 0x80
@@ -83,6 +85,7 @@ struct cc2_data {
struct i2c_client *client;
struct regulator *regulator;
const char *name;
+ const char *label;
int irq_ready;
int irq_low;
int irq_high;
@@ -449,6 +452,8 @@ static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_humidity_input:
return 0444;
+ case hwmon_humidity_label:
+ return cc2->label ? 0444 : 0;
case hwmon_humidity_min_alarm:
return cc2->rh_alarm.low_alarm_visible ? 0444 : 0;
case hwmon_humidity_max_alarm:
@@ -466,6 +471,8 @@ static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_temp_input:
return 0444;
+ case hwmon_temp_label:
+ return cc2->label ? 0444 : 0;
default:
return 0;
}
@@ -552,6 +559,16 @@ static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val)
return 0;
}
+static int cc2_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct cc2_data *data = dev_get_drvdata(dev);
+
+ *str = data->label;
+
+ return 0;
+}
+
static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
@@ -670,8 +687,9 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
}
static const struct hwmon_channel_info *cc2_info[] = {
- HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
- HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_MIN | HWMON_H_MAX |
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_LABEL |
+ HWMON_H_MIN | HWMON_H_MAX |
HWMON_H_MIN_HYST | HWMON_H_MAX_HYST |
HWMON_H_MIN_ALARM | HWMON_H_MAX_ALARM),
NULL
@@ -680,6 +698,7 @@ static const struct hwmon_channel_info *cc2_info[] = {
static const struct hwmon_ops cc2_hwmon_ops = {
.is_visible = cc2_is_visible,
.read = cc2_read,
+ .read_string = cc2_read_string,
.write = cc2_write,
};
@@ -710,6 +729,8 @@ static int cc2_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(data->regulator),
"Failed to get regulator\n");
+ device_property_read_string(dev, "label", &data->label);
+
ret = cc2_request_ready_irq(data, dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to request ready irq\n");
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 6215ea49faaa..d47ba64788e1 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -68,7 +68,7 @@ enum coretemp_attr_index {
* This value is passed as "id" field to rdmsr/wrmsr functions.
* @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
* from where the temperature values should be read.
- * @attr_size: Total number of pre-core attrs displayed in the sysfs.
+ * @attr_size: Number of sysfs attributes in this temperature data group.
*/
struct temp_data {
int temp;
diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c
index 03bfcc40bb7c..1337b646e022 100644
--- a/drivers/hwmon/cros_ec_hwmon.c
+++ b/drivers/hwmon/cros_ec_hwmon.c
@@ -5,6 +5,7 @@
* Copyright (C) 2024 Thomas Weißschuh <linux@weissschuh.net>
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/hwmon.h>
#include <linux/math.h>
@@ -24,6 +25,7 @@
struct cros_ec_hwmon_priv {
struct cros_ec_device *cros_ec;
+ struct device *hwmon_dev;
const char *temp_sensor_names[EC_TEMP_SENSOR_ENTRIES + EC_TEMP_SENSOR_B_ENTRIES];
u8 usable_fans;
bool fan_control_supported;
@@ -146,9 +148,17 @@ static bool cros_ec_hwmon_is_error_temp(u8 temp)
temp == EC_TEMP_SENSOR_NOT_CALIBRATED;
}
+/* This differs slightly from the variant in units.h to avoid rounding inconsistencies. */
+#define CROS_EC_HWMON_ABSOLUTE_ZERO_MILLICELSIUS (-273000)
+
+static long cros_ec_hwmon_kelvin_to_millicelsius(long t)
+{
+ return t * MILLIDEGREE_PER_DEGREE + CROS_EC_HWMON_ABSOLUTE_ZERO_MILLICELSIUS;
+}
+
static long cros_ec_hwmon_temp_to_millicelsius(u8 temp)
{
- return kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET));
+ return cros_ec_hwmon_kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET));
}
static bool cros_ec_hwmon_attr_is_temp_threshold(u32 attr)
@@ -227,7 +237,7 @@ static int cros_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
cros_ec_hwmon_attr_to_thres(attr),
&threshold);
if (ret == 0)
- *val = kelvin_to_millicelsius(threshold);
+ *val = cros_ec_hwmon_kelvin_to_millicelsius(threshold);
}
}
@@ -401,6 +411,8 @@ static int cros_ec_hwmon_cooling_get_cur_state(struct thermal_cooling_device *cd
u8 read_val;
int ret;
+ guard(hwmon_lock)(priv->hwmon_priv->hwmon_dev);
+
ret = cros_ec_hwmon_read_pwm_value(priv->hwmon_priv->cros_ec, priv->index, &read_val);
if (ret)
return ret;
@@ -414,6 +426,8 @@ static int cros_ec_hwmon_cooling_set_cur_state(struct thermal_cooling_device *cd
{
const struct cros_ec_hwmon_cooling_priv *priv = cdev->devdata;
+ guard(hwmon_lock)(priv->hwmon_priv->hwmon_dev);
+
return cros_ec_hwmon_write_pwm_input(priv->hwmon_priv->cros_ec, priv->index, val);
}
@@ -547,7 +561,6 @@ static int cros_ec_hwmon_probe(struct platform_device *pdev)
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
struct cros_ec_hwmon_priv *priv;
- struct device *hwmon_dev;
u8 thermal_version;
int ret;
@@ -570,13 +583,17 @@ static int cros_ec_hwmon_probe(struct platform_device *pdev)
priv->fan_control_supported = cros_ec_hwmon_probe_fan_control_supported(priv->cros_ec);
priv->temp_threshold_supported = is_cros_ec_cmd_available(priv->cros_ec,
EC_CMD_THERMAL_GET_THRESHOLD, 1);
+
+ priv->hwmon_dev = devm_hwmon_device_register_with_info(dev, "cros_ec", priv,
+ &cros_ec_hwmon_chip_info, NULL);
+ if (IS_ERR(priv->hwmon_dev))
+ return PTR_ERR(priv->hwmon_dev);
+
cros_ec_hwmon_register_fan_cooling_devices(dev, priv);
- hwmon_dev = devm_hwmon_device_register_with_info(dev, "cros_ec", priv,
- &cros_ec_hwmon_chip_info, NULL);
platform_set_drvdata(pdev, priv);
- return PTR_ERR_OR_ZERO(hwmon_dev);
+ return 0;
}
static int cros_ec_hwmon_suspend(struct platform_device *pdev, pm_message_t state)
diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c
index 7652d553b8ea..7d330baa0f05 100644
--- a/drivers/hwmon/da9055-hwmon.c
+++ b/drivers/hwmon/da9055-hwmon.c
@@ -256,11 +256,8 @@ static int da9055_hwmon_probe(struct platform_device *pdev)
NULL, da9055_auxadc_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"adc-irq", hwmon);
- if (ret != 0) {
- dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n",
- ret);
+ if (ret != 0)
return ret;
- }
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9055",
hwmon,
diff --git a/drivers/hwmon/eic7700-pvt.c b/drivers/hwmon/eic7700-pvt.c
new file mode 100644
index 000000000000..d7403ab33f4e
--- /dev/null
+++ b/drivers/hwmon/eic7700-pvt.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN EIC7700 Voltage, Temperature sensor driver
+ *
+ * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.
+ *
+ * Authors:
+ * Yulin Lu <luyulin@eswincomputing.com>
+ * Huan He <hehuan1@eswincomputing.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/polynomial.h>
+#include <linux/reset.h>
+#include "eic7700-pvt.h"
+
+static const struct pvt_sensor_info pvt_info[] = {
+ PVT_SENSOR_INFO(0, "Temperature", hwmon_temp, TEMP),
+ PVT_SENSOR_INFO(0, "Voltage", hwmon_in, VOLT),
+};
+
+static const char * const pvt_clk_names[PVT_CLK_NUM] = {"enable", "apb"};
+
+/*
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * to PVT data and vice-versa are following:
+ * N = 6.0818e-8*(T^4) +1.2873e-5*(T^3) + 7.2244e-3*(T^2) + 3.6484*(T^1) +
+ * 1.6198e2,
+ * T = -1.8439e-11*(N^4) + 8.0705e-8*(N^3) + -1.8501e-4*(N^2) +
+ * 3.2843e-1*(N^1) - 4.8690e1,
+ * where T = [-40, 125]C and N = [27, 771].
+ * 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 = (60818e-20*(T^4) + 12873e-14*(T^3) + 72244e-9*(T^2) + 36484e-3*T +
+ * 16198e2) / 1e4,
+ * T = -18439e-12*(N^4) + 80705e-9*(N^3) - 185010e-6*(N^2) + 328430e-3*N -
+ * 48690,
+ * where T = [-40000, 125000] mC and N = [27, 771].
+ */
+static const struct polynomial poly_N_to_temp = {
+ .total_divider = 1,
+ .terms = {
+ {4, -18439, 1000, 1},
+ {3, 80705, 1000, 1},
+ {2, -185010, 1000, 1},
+ {1, 328430, 1000, 1},
+ {0, -48690, 1, 1}
+ }
+};
+
+/*
+ * Similar alterations are performed for the voltage conversion equations.
+ * The original formulae are:
+ * N = 1.3905e3*V - 5.7685e2,
+ * V = (N + 5.7685e2) / 1.3905e3,
+ * where V = [0.72, 0.88] V and N = [424, 646].
+ * After the optimization they looks as follows:
+ * N = (13905e-3*V - 5768.5) / 10,
+ * V = (N * 10^5 / 13905 + 57685 * 10^3 / 13905) / 10.
+ * where V = [720, 880] mV and N = [424, 646].
+ */
+static const struct polynomial poly_N_to_volt = {
+ .total_divider = 10,
+ .terms = {
+ {1, 100000, 13905, 1},
+ {0, 57685000, 1, 13905}
+ }
+};
+
+static inline u32 eic7700_pvt_update(void __iomem *reg, u32 mask, u32 data)
+{
+ u32 old;
+
+ old = readl_relaxed(reg);
+ writel((old & ~mask) | (data & mask), reg);
+
+ return old & mask;
+}
+
+static inline void eic7700_pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
+{
+ u32 old;
+
+ mode = FIELD_PREP(PVT_MODE_MASK, mode);
+
+ old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
+ eic7700_pvt_update(pvt->regs + PVT_MODE, PVT_MODE_MASK, mode);
+ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old);
+}
+
+static inline void eic7700_pvt_set_trim(struct pvt_hwmon *pvt, u32 val)
+{
+ u32 old;
+
+ old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
+ writel(val, pvt->regs + PVT_TRIM);
+ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old);
+}
+
+static irqreturn_t eic7700_pvt_hard_isr(int irq, void *data)
+{
+ struct pvt_hwmon *pvt = data;
+ u32 stat, val;
+ int active;
+
+ if (IS_ENABLED(CONFIG_PM)) {
+ active = pm_runtime_get_if_active(pvt->dev);
+ if (active <= 0)
+ return IRQ_NONE;
+ }
+
+ stat = readl(pvt->regs + PVT_INT);
+ if (!(stat & PVT_INT_STAT)) {
+ if (IS_ENABLED(CONFIG_PM))
+ pm_runtime_put(pvt->dev);
+ return IRQ_NONE;
+ }
+
+ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
+ /*
+ * Read the data, update the cache and notify a waiter of this event.
+ */
+ val = readl(pvt->regs + PVT_DATA);
+ WRITE_ONCE(pvt->data_cache, FIELD_GET(PVT_DATA_OUT, val));
+ complete(&pvt->conversion);
+
+ if (IS_ENABLED(CONFIG_PM))
+ pm_runtime_put(pvt->dev);
+
+ return IRQ_HANDLED;
+}
+
+static int eic7700_pvt_read_data(struct pvt_hwmon *pvt,
+ enum pvt_sensor_type type, long *val)
+{
+ unsigned long timeout;
+ u32 data;
+ int ret;
+
+ /*
+ * Wait for PVT conversion to complete and update the data cache. The
+ * data read procedure is following: set the requested PVT sensor mode,
+ * enable conversion, wait until conversion is finished, then disable
+ * conversion and IRQ, and read the cached data.
+ */
+ reinit_completion(&pvt->conversion);
+
+ eic7700_pvt_set_mode(pvt, pvt_info[type].mode);
+ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, PVT_ENA_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(&pvt->conversion, timeout);
+
+ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
+ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
+
+ if (!ret)
+ synchronize_irq(pvt->irq);
+
+ data = READ_ONCE(pvt->data_cache);
+
+ 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 const struct hwmon_channel_info *pvt_channel_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL),
+ NULL
+};
+
+static umode_t eic7700_pvt_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int ch)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_label:
+ return 0444;
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_label:
+ return 0444;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int eic7700_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);
+ int ret;
+
+ ret = pm_runtime_get_sync(pvt->dev);
+ if (ret < 0) {
+ dev_err(pvt->dev, "Failed to resume PVT device: %d\n", ret);
+ pm_runtime_put_noidle(pvt->dev);
+ return ret;
+ }
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = eic7700_pvt_read_data(pvt, ch, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+ break;
+ case hwmon_in:
+ if (attr == hwmon_in_input)
+ ret = eic7700_pvt_read_data(pvt, PVT_VOLT + ch, val);
+ else
+ ret = -EOPNOTSUPP;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ pm_runtime_mark_last_busy(pvt->dev);
+ pm_runtime_put_autosuspend(pvt->dev);
+ return ret;
+}
+
+static int eic7700_pvt_hwmon_read_string(struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ int ch, const char **str)
+{
+ switch (type) {
+ case hwmon_temp:
+ if (attr == hwmon_temp_label) {
+ *str = pvt_info[ch].label;
+ return 0;
+ }
+ break;
+ case hwmon_in:
+ if (attr == hwmon_in_label) {
+ *str = pvt_info[PVT_VOLT + ch].label;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops pvt_hwmon_ops = {
+ .is_visible = eic7700_pvt_hwmon_is_visible,
+ .read = eic7700_pvt_hwmon_read,
+ .read_string = eic7700_pvt_hwmon_read_string
+};
+
+static const struct hwmon_chip_info pvt_hwmon_info = {
+ .ops = &pvt_hwmon_ops,
+ .info = pvt_channel_info
+};
+
+static struct pvt_hwmon *eic7700_pvt_create_data(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pvt_hwmon *pvt;
+
+ pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL);
+ if (!pvt)
+ return ERR_PTR(-ENOMEM);
+
+ pvt->dev = dev;
+ init_completion(&pvt->conversion);
+
+ return pvt;
+}
+
+static int eic7700_pvt_init_iface(struct pvt_hwmon *pvt)
+{
+ /*
+ * Make sure controller are disabled so not to accidentally have ISR
+ * executed before the driver data is fully initialized. Clear the IRQ
+ * status as well.
+ */
+ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0);
+ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
+ readl(pvt->regs + PVT_INT);
+ readl(pvt->regs + PVT_DATA);
+
+ /* Setup default sensor mode and temperature trim. */
+ eic7700_pvt_set_mode(pvt, pvt_info[PVT_TEMP].mode);
+
+ /*
+ * Max conversion latency (~333 µs) derived from PVT spec:
+ * maximum sampling rate = 3000 samples/sec.
+ */
+ pvt->timeout = ns_to_ktime(PVT_TOUT_MIN);
+
+ eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF);
+
+ return 0;
+}
+
+static int eic7700_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,
+ eic7700_pvt_hard_isr, NULL,
+ IRQF_TRIGGER_HIGH, "pvt", pvt);
+ if (ret) {
+ dev_err(pvt->dev, "Couldn't request PVT IRQ\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int eic7700_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;
+}
+
+static void eic7700_pvt_disable_pm_runtime(void *data)
+{
+ struct pvt_hwmon *pvt = data;
+
+ pm_runtime_dont_use_autosuspend(pvt->dev);
+ pm_runtime_disable(pvt->dev);
+
+ if (!pm_runtime_status_suspended(pvt->dev)) {
+ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
+ pm_runtime_set_suspended(pvt->dev);
+ }
+}
+
+static int eic7700_pvt_probe(struct platform_device *pdev)
+{
+ struct reset_control *rst;
+ struct pvt_hwmon *pvt;
+ int i, ret;
+
+ pvt = eic7700_pvt_create_data(pdev);
+ if (IS_ERR(pvt))
+ return PTR_ERR(pvt);
+
+ platform_set_drvdata(pdev, pvt);
+
+ pvt->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pvt->regs))
+ return PTR_ERR(pvt->regs);
+
+ for (i = 0; i < PVT_CLK_NUM; i++)
+ pvt->clks[i].id = pvt_clk_names[i];
+
+ ret = devm_clk_bulk_get(&pdev->dev, PVT_CLK_NUM, pvt->clks);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Couldn't get clock descriptors\n");
+
+ rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(pvt->dev, PTR_ERR(rst),
+ "Couldn't get reset control\n");
+
+ ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks);
+ if (ret)
+ return dev_err_probe(pvt->dev, ret,
+ "Failed to enable clocks\n");
+
+ ret = eic7700_pvt_init_iface(pvt);
+ if (ret) {
+ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_PM))
+ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ ret = devm_add_action_or_reset(pvt->dev, eic7700_pvt_disable_pm_runtime,
+ pvt);
+ if (ret) {
+ pm_runtime_put_noidle(&pdev->dev);
+ return dev_err_probe(&pdev->dev, ret,
+ "Can't register PM cleanup\n");
+ }
+
+ ret = eic7700_pvt_request_irq(pvt);
+ if (ret)
+ goto err_put_pm_runtime;
+
+ ret = eic7700_pvt_create_hwmon(pvt);
+ if (ret)
+ goto err_put_pm_runtime;
+
+ pm_runtime_put_autosuspend(&pdev->dev);
+
+ return 0;
+
+err_put_pm_runtime:
+ pm_runtime_put_noidle(&pdev->dev);
+ return ret;
+}
+
+static int __maybe_unused eic7700_pvt_runtime_resume(struct device *dev)
+{
+ struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks);
+ if (ret) {
+ dev_err(dev, "Failed to enable clocks: %d\n", ret);
+ return ret;
+ }
+
+ eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF);
+
+ return 0;
+}
+
+static int __maybe_unused eic7700_pvt_runtime_suspend(struct device *dev)
+{
+ struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks);
+
+ return 0;
+}
+
+static const struct dev_pm_ops eic7700_pvt_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(eic7700_pvt_runtime_suspend, eic7700_pvt_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id pvt_of_match[] = {
+ { .compatible = "eswin,eic7700-pvt"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, pvt_of_match);
+
+static struct platform_driver pvt_driver = {
+ .probe = eic7700_pvt_probe,
+ .driver = {
+ .name = "eic7700-pvt",
+ .of_match_table = pvt_of_match,
+ .pm = pm_ptr(&eic7700_pvt_pm_ops),
+ },
+};
+module_platform_driver(pvt_driver);
+
+MODULE_AUTHOR("Yulin Lu <luyulin@eswincomputing.com>");
+MODULE_AUTHOR("Huan He <hehuan1@eswincomputing.com>");
+MODULE_DESCRIPTION("Eswin eic7700 PVT driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/eic7700-pvt.h b/drivers/hwmon/eic7700-pvt.h
new file mode 100644
index 000000000000..fb10f9e4e93a
--- /dev/null
+++ b/drivers/hwmon/eic7700-pvt.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ESWIN EIC7700 Voltage, Temperature sensor driver
+ *
+ * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.
+ */
+#ifndef __HWMON_EIC7700_PVT_H__
+#define __HWMON_EIC7700_PVT_H__
+
+#include <linux/completion.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+
+/* ESWIN EIC7700 PVT registers and their bitfields */
+#define PVT_TRIM 0x04
+#define PVT_MODE 0x08
+#define PVT_MODE_MASK GENMASK(2, 0)
+#define PVT_CTRL_MODE_TEMP 0x0
+#define PVT_CTRL_MODE_VOLT 0x4
+#define PVT_ENA 0x0c
+#define PVT_ENA_EN BIT(0)
+#define PVT_INT 0x10
+#define PVT_INT_STAT BIT(0)
+#define PVT_INT_CLR BIT(1)
+#define PVT_DATA 0x14
+#define PVT_DATA_OUT GENMASK(9, 0)
+
+/*
+ * PVT sensors-related limits and default values
+ * @PVT_TEMP_CHS: Number of temperature hwmon channels.
+ * @PVT_VOLT_CHS: Number of voltage hwmon channels.
+ * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value
+ * when one is determined for ESWIN EIC7700 SoC).
+ * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds.
+ */
+#define PVT_TEMP_CHS 1
+#define PVT_VOLT_CHS 1
+#define PVT_TRIM_DEF 0
+#define PVT_TOUT_MIN (NSEC_PER_SEC / 3000)
+
+/*
+ * enum pvt_sensor_type - ESWIN EIC7700 PVT sensor types (correspond to each PVT
+ * sampling mode)
+ * @PVT_TEMP: PVT Temperature sensor.
+ * @PVT_VOLT: PVT Voltage sensor.
+ */
+enum pvt_sensor_type {
+ PVT_TEMP = 0,
+ PVT_VOLT
+};
+
+#define PVT_CLK_NUM 2
+
+/*
+ * struct pvt_sensor_info - ESWIN EIC7700 PVT sensor informational structure
+ * @channel: Sensor channel ID.
+ * @label: hwmon sensor label.
+ * @mode: PVT mode corresponding to the channel.
+ * @type: Sensor type.
+ */
+struct pvt_sensor_info {
+ int channel;
+ const char *label;
+ u32 mode;
+ enum hwmon_sensor_types type;
+};
+
+#define PVT_SENSOR_INFO(_ch, _label, _type, _mode) \
+ { \
+ .channel = _ch, \
+ .label = _label, \
+ .mode = PVT_CTRL_MODE_ ##_mode, \
+ .type = _type, \
+ }
+
+/*
+ * struct pvt_hwmon - Eswin EIC7700 PVT private data
+ * @dev: device structure of the PVT platform device.
+ * @hwmon: hwmon device structure.
+ * @regs: pointer to the Eswin EIC7700 PVT registers region.
+ * @irq: PVT events IRQ number.
+ * @clks: PVT clock descriptors.
+ * @data_cache: data cache in raw format.
+ * @conversion: data conversion completion.
+ * @timeout: conversion timeout.
+ */
+struct pvt_hwmon {
+ struct device *dev;
+ struct device *hwmon;
+ void __iomem *regs;
+ int irq;
+ struct clk_bulk_data clks[PVT_CLK_NUM];
+ u32 data_cache;
+ struct completion conversion;
+ ktime_t timeout;
+};
+
+#endif /* __HWMON_EIC7700_PVT_H__ */
diff --git a/drivers/hwmon/gxp-fan-ctrl.c b/drivers/hwmon/gxp-fan-ctrl.c
index 86e7bafd3a38..64f85d7e748e 100644
--- a/drivers/hwmon/gxp-fan-ctrl.c
+++ b/drivers/hwmon/gxp-fan-ctrl.c
@@ -1,59 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */
+/* Copyright (C) 2023 Hewlett-Packard Enterprise Development Company, L.P. */
-#include <linux/bits.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#define OFS_FAN_INST 0 /* Is 0 because plreg base will be set at INST */
-#define OFS_FAN_FAIL 2 /* Is 2 bytes after base */
-#define OFS_SEVSTAT 0 /* Is 0 because fn2 base will be set at SEVSTAT */
-#define POWER_BIT 24
-
struct gxp_fan_ctrl_drvdata {
void __iomem *base;
- void __iomem *plreg;
- void __iomem *fn2;
};
-static bool fan_installed(struct device *dev, int fan)
-{
- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev);
- u8 val;
-
- val = readb(drvdata->plreg + OFS_FAN_INST);
-
- return !!(val & BIT(fan));
-}
-
-static long fan_failed(struct device *dev, int fan)
-{
- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev);
- u8 val;
-
- val = readb(drvdata->plreg + OFS_FAN_FAIL);
-
- return !!(val & BIT(fan));
-}
-
-static long fan_enabled(struct device *dev, int fan)
-{
- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev);
- u32 val;
-
- /*
- * Check the power status as if the platform is off the value
- * reported for the PWM will be incorrect. Report fan as
- * disabled.
- */
- val = readl(drvdata->fn2 + OFS_SEVSTAT);
-
- return !!((val & BIT(POWER_BIT)) && fan_installed(dev, fan));
-}
-
static int gxp_pwm_write(struct device *dev, u32 attr, int channel, long val)
{
struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev);
@@ -80,37 +37,11 @@ static int gxp_fan_ctrl_write(struct device *dev, enum hwmon_sensor_types type,
}
}
-static int gxp_fan_read(struct device *dev, u32 attr, int channel, long *val)
-{
- switch (attr) {
- case hwmon_fan_enable:
- *val = fan_enabled(dev, channel);
- return 0;
- case hwmon_fan_fault:
- *val = fan_failed(dev, channel);
- return 0;
- default:
- return -EOPNOTSUPP;
- }
-}
-
static int gxp_pwm_read(struct device *dev, u32 attr, int channel, long *val)
{
struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev);
- u32 reg;
- /*
- * Check the power status of the platform. If the platform is off
- * the value reported for the PWM will be incorrect. In this case
- * report a PWM of zero.
- */
-
- reg = readl(drvdata->fn2 + OFS_SEVSTAT);
-
- if (reg & BIT(POWER_BIT))
- *val = fan_installed(dev, channel) ? readb(drvdata->base + channel) : 0;
- else
- *val = 0;
+ *val = readb(drvdata->base + channel);
return 0;
}
@@ -119,8 +50,6 @@ static int gxp_fan_ctrl_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
- case hwmon_fan:
- return gxp_fan_read(dev, attr, channel, val);
case hwmon_pwm:
return gxp_pwm_read(dev, attr, channel, val);
default:
@@ -135,16 +64,6 @@ static umode_t gxp_fan_ctrl_is_visible(const void *_data,
umode_t mode = 0;
switch (type) {
- case hwmon_fan:
- switch (attr) {
- case hwmon_fan_enable:
- case hwmon_fan_fault:
- mode = 0444;
- break;
- default:
- break;
- }
- break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
@@ -168,15 +87,6 @@ static const struct hwmon_ops gxp_fan_ctrl_ops = {
};
static const struct hwmon_channel_info * const gxp_fan_ctrl_info[] = {
- HWMON_CHANNEL_INFO(fan,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE,
- HWMON_F_FAULT | HWMON_F_ENABLE),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
@@ -211,18 +121,6 @@ static int gxp_fan_ctrl_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(drvdata->base),
"failed to map base\n");
- drvdata->plreg = devm_platform_ioremap_resource_byname(pdev,
- "pl");
- if (IS_ERR(drvdata->plreg))
- return dev_err_probe(dev, PTR_ERR(drvdata->plreg),
- "failed to map plreg\n");
-
- drvdata->fn2 = devm_platform_ioremap_resource_byname(pdev,
- "fn2");
- if (IS_ERR(drvdata->fn2))
- return dev_err_probe(dev, PTR_ERR(drvdata->fn2),
- "failed to map fn2\n");
-
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"hpe_gxp_fan_ctrl",
drvdata,
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 449a72c6b40b..5c6dc2c370d8 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -123,6 +123,7 @@ static const struct regmap_config ina2xx_regmap_config = {
enum ina2xx_ids {
ina219,
ina226,
+ ina232,
ina234,
ina260,
sy24655
@@ -197,6 +198,20 @@ static const struct ina2xx_config ina2xx_config[] = {
.current_shift = 4,
.has_update_interval = true,
},
+ [ina232] = {
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_value = 2048,
+ .shunt_div = 400,
+ .shunt_voltage_shift = 0,
+ .bus_voltage_shift = 0,
+ .bus_voltage_lsb = 1600,
+ .power_lsb_factor = 32,
+ .has_alerts = true,
+ .has_ishunt = false,
+ .has_power_average = false,
+ .current_shift = 0,
+ .has_update_interval = true,
+ },
[ina260] = {
.config_default = INA260_CONFIG_DEFAULT,
.shunt_div = 400,
@@ -1014,6 +1029,7 @@ static const struct i2c_device_id ina2xx_id[] = {
{ .name = "ina226", .driver_data = ina226 },
{ .name = "ina230", .driver_data = ina226 },
{ .name = "ina231", .driver_data = ina226 },
+ { .name = "ina232", .driver_data = ina232 },
{ .name = "ina234", .driver_data = ina234 },
{ .name = "ina260", .driver_data = ina260 },
{ .name = "sy24655", .driver_data = sy24655 },
@@ -1047,6 +1063,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
.data = (void *)ina226
},
{
+ .compatible = "ti,ina232",
+ .data = (void *)ina232
+ },
+ {
.compatible = "ti,ina234",
.data = (void *)ina234
},
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index c6186505661f..ae3b0a33d676 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -2918,10 +2918,8 @@ static int lm90_probe(struct i2c_client *client)
err = devm_request_threaded_irq(dev, client->irq,
NULL, lm90_irq_thread,
IRQF_ONESHOT, "lm90", client);
- if (err < 0) {
- dev_err(dev, "cannot request IRQ %d\n", client->irq);
+ if (err < 0)
return err;
- }
}
return 0;
diff --git a/drivers/hwmon/ltc4283.c b/drivers/hwmon/ltc4283.c
index 9b85293ea664..9ec409bc2648 100644
--- a/drivers/hwmon/ltc4283.c
+++ b/drivers/hwmon/ltc4283.c
@@ -1772,6 +1772,7 @@ static const struct of_device_id ltc4283_of_match[] = {
{ .compatible = "adi,ltc4283" },
{ }
};
+MODULE_DEVICE_TABLE(of, ltc4283_of_match);
static const struct i2c_device_id ltc4283_i2c_id[] = {
{ "ltc4283" },
diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
index aea0b8659f5f..df24dec78e77 100644
--- a/drivers/hwmon/npcm750-pwm-fan.c
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -991,10 +991,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
sprintf(name, "NPCM7XX-FAN-MD%d", i);
ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr,
0, name, (void *)data);
- if (ret) {
- dev_err(dev, "register IRQ fan%d failed\n", i);
+ if (ret)
return ret;
- }
}
for_each_child_of_node_scoped(np, child) {
diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h
index 92a7ee1925bc..5eae9681e385 100644
--- a/drivers/hwmon/peci/common.h
+++ b/drivers/hwmon/peci/common.h
@@ -31,7 +31,7 @@ struct peci_sensor_data {
/**
* peci_sensor_need_update() - check whether sensor update is needed or not
- * @sensor: pointer to sensor data struct
+ * @state: pointer to sensor state struct
*
* Return: true if update is needed, false if not.
*/
@@ -44,7 +44,7 @@ static inline bool peci_sensor_need_update(struct peci_sensor_state *state)
/**
* peci_sensor_mark_updated() - mark the sensor is updated
- * @sensor: pointer to sensor data struct
+ * @state: pointer to sensor state struct
*/
static inline void peci_sensor_mark_updated(struct peci_sensor_state *state)
{
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index c8cda160b5f8..9b4bfdd00310 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -52,8 +52,8 @@ config SENSORS_ADM1275
help
If you say yes here you get hardware monitoring support for Analog
Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
- ADM1293, ADM1294 and SQ24905C Hot-Swap Controller and
- Digital Power Monitors.
+ ADM1293, ADM1294, ROHM BD12780, ROHM BD12790, and SQ24905C
+ Hot-Swap Controller and Digital Power Monitors.
This driver can also be built as a module. If so, the module will
be called adm1275.
@@ -434,8 +434,9 @@ config SENSORS_MAX34440
tristate "Maxim MAX34440 and compatibles"
help
If you say yes here you get hardware monitoring support for Maxim
- MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461.
- Other compatible are ADPM12160, and ADPM12200.
+ MAX34440, MAX34441, MAX34446, MAX34451, MAX34452, MAX34460, and
+ MAX34461. Other compatible devices are ADPM12160, ADPM12200, and
+ ADPM12250.
This driver can also be built as a module. If so, the module will
be called max34440.
@@ -607,6 +608,15 @@ config SENSORS_MPQ7932
This driver can also be built as a module. If so, the module will
be called mpq7932.
+config SENSORS_MPQ82D00
+ tristate "MPS MPQ82D00"
+ help
+ If you say yes here you get hardware monitoring functionality support
+ for power management IC MPS MPQ82D00.
+
+ This driver can also be built as a module. If so, the module will
+ be called mpq82d00.
+
config SENSORS_MPQ8785
tristate "MPS MPQ8785"
help
@@ -677,6 +687,25 @@ config SENSORS_STEF48H28
This driver can also be built as a module. If so, the module will
be called stef48h28.
+config SENSORS_SQ24860
+ tristate "Silergy SQ24860"
+ help
+ If you say yes here you get hardware monitoring support for Silergy
+ SQ24860 eFuse.
+
+ This driver can also be built as a module. If so, the module will
+ be called sq24860.
+
+config SENSORS_SQ24860_REGULATOR
+ bool "Regulator support for SQ24860"
+ depends on SENSORS_SQ24860 && REGULATOR
+ default SENSORS_SQ24860
+ help
+ If you say yes here you get regulator support for Silergy SQ24860.
+ The regulator is registered through the PMBus regulator framework and
+ can be used to control the output exposed by the device.
+ This option is only useful if regulator framework support is needed.
+
config SENSORS_STPDDC60
tristate "ST STPDDC60"
help
@@ -803,6 +832,16 @@ config SENSORS_XDPE1A2G7B
This driver can also be built as a module. If so, the module will
be called xdpe1a2g7b.
+config SENSORS_XDPE1A2G7B_REGULATOR
+ bool "Regulator support for XDPE1A2G7B and compatibles"
+ depends on SENSORS_XDPE1A2G7B && REGULATOR
+ help
+ If you say yes here you get regulator support for Infineon
+ XDPE1A2G5B and XDPE1A2G7B multi-phase digital controllers.
+
+ This enables the controllers to be used as regulator devices,
+ providing voltage control through the regulator framework.
+
config SENSORS_XDPE122
tristate "Infineon XDPE122 family"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index ffc05f493213..080d366809f3 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -59,12 +59,14 @@ obj-$(CONFIG_SENSORS_MP5990) += mp5990.o
obj-$(CONFIG_SENSORS_MP9941) += mp9941.o
obj-$(CONFIG_SENSORS_MP9945) += mp9945.o
obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o
+obj-$(CONFIG_SENSORS_MPQ82D00) += mpq82d00.o
obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o
obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o
obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o
obj-$(CONFIG_SENSORS_STEF48H28) += stef48h28.o
+obj-$(CONFIG_SENSORS_SQ24860) += sq24860.o
obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o
obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o
obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o
diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c
index d283005d92ae..5222e7aeca44 100644
--- a/drivers/hwmon/pmbus/acbel-fsg032.c
+++ b/drivers/hwmon/pmbus/acbel-fsg032.c
@@ -52,6 +52,7 @@ static const struct i2c_device_id acbel_fsg032_id[] = {
{ .name = "acbel_fsg032" },
{ }
};
+MODULE_DEVICE_TABLE(i2c, acbel_fsg032_id);
static struct pmbus_driver_info acbel_fsg032_info = {
.pages = 1,
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index d3c3ff85dba3..174f9bcba7e2 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -19,7 +19,7 @@
#include "pmbus.h"
enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
- adm1293, adm1294, sq24905c };
+ adm1293, adm1294, bd12780, bd12790, sq24905c };
#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
@@ -47,6 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
#define ADM1278_VOUT_EN BIT(1)
#define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
+/* The BD127[89]0 data sheets mark TSFILT bit as reserved. */
+#define BD12780_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
#define ADM1293_IRANGE_25 0
#define ADM1293_IRANGE_50 BIT(6)
@@ -134,6 +136,30 @@ static const struct coefficients adm1272_coefficients[] = {
};
+/*
+ * BD12790 coefficients derived from preliminary datasheet, Table 1 (p.18)
+ * and the PMBus direct-format relationship X = (Y * 10^(-R) - b) / m.
+ *
+ * Voltage: V[V] = 14.77e-3 * code (60V) / 24.62e-3 * code (100V)
+ * -> m = 6770, R=-2 / m = 4062, R=-2
+ * Current: code = I[A] * RS * 132802.1 + 2048 (15mV) / * 66401.06 + 2048 (30mV)
+ * -> m = 1328, b = 2048 * 10^(-R) = 20480, R=-1 / m = 664, same b and R
+ * Power: code = k * RS * PIN, k = 35119.94 / 17559.97 / 21071.44 / 10535.72
+ * -> m = round(k * 10^(-3-R)), R=-2 for 60V/15mV, R=-3 for the other three
+ * Temperature: code = 4.2 * T + 3188 -> m = 42, b = 3188 * 10 = 31880, R=-1
+ */
+static const struct coefficients bd12790_coefficients[] = {
+ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */
+ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */
+ [2] = { 1328, 20480, -1 }, /* current, vsense range 15mV */
+ [3] = { 664, 20480, -1 }, /* current, vsense range 30mV */
+ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */
+ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */
+ [6] = { 17560, 0, -3 }, /* power, vrange 60V, irange 30mV */
+ [7] = { 10536, 0, -3 }, /* power, vrange 100V, irange 30mV */
+ [8] = { 42, 31880, -1 }, /* temperature */
+};
+
static const struct coefficients adm1275_coefficients[] = {
[0] = { 19199, 0, -2 }, /* voltage, vrange set */
[1] = { 6720, 0, -1 }, /* voltage, vrange not set */
@@ -487,6 +513,8 @@ static const struct i2c_device_id adm1275_id[] = {
{ .name = "adm1281", .driver_data = adm1281 },
{ .name = "adm1293", .driver_data = adm1293 },
{ .name = "adm1294", .driver_data = adm1294 },
+ { .name = "bd12780", .driver_data = bd12780 },
+ { .name = "bd12790", .driver_data = bd12790 },
{ .name = "mc09c", .driver_data = sq24905c },
{ }
};
@@ -494,12 +522,13 @@ MODULE_DEVICE_TABLE(i2c, adm1275_id);
/* Enable VOUT & TEMP1 if not enabled (disabled by default) */
static int adm1275_enable_vout_temp(struct adm1275_data *data,
- struct i2c_client *client, int config)
+ struct i2c_client *client, int config,
+ u16 defconfig)
{
int ret;
- if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) {
- config |= ADM1278_PMON_DEFCONFIG;
+ if ((config & defconfig) != defconfig) {
+ config |= defconfig;
ret = adm1275_write_pmon_config(data, client, config);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n");
@@ -535,7 +564,8 @@ static int adm1275_probe(struct i2c_client *client)
return ret;
}
if ((ret != 3 || strncmp(block_buffer, "ADI", 3)) &&
- (ret != 2 || strncmp(block_buffer, "SY", 2))) {
+ (ret != 2 || strncmp(block_buffer, "SY", 2)) &&
+ (ret != 4 || strncmp(block_buffer, "ROHM", 4))) {
dev_err(&client->dev, "Unsupported Manufacturer ID\n");
return -ENODEV;
}
@@ -562,6 +592,7 @@ static int adm1275_probe(struct i2c_client *client)
if (mid->driver_data == adm1272 || mid->driver_data == adm1273 ||
mid->driver_data == adm1278 || mid->driver_data == adm1281 ||
mid->driver_data == adm1293 || mid->driver_data == adm1294 ||
+ mid->driver_data == bd12780 || mid->driver_data == bd12790 ||
mid->driver_data == sq24905c)
config_read_fn = i2c_smbus_read_word_data;
else
@@ -642,6 +673,7 @@ static int adm1275_probe(struct i2c_client *client)
data->have_power_sampling = true;
coefficients = adm1272_coefficients;
+
vindex = (config & ADM1275_VRANGE) ? 1 : 0;
cindex = (config & ADM1272_IRANGE) ? 3 : 2;
/* pindex depends on the combination of the above */
@@ -666,7 +698,53 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- ret = adm1275_enable_vout_temp(data, client, config);
+ ret = adm1275_enable_vout_temp(data, client, config,
+ ADM1278_PMON_DEFCONFIG);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+ break;
+
+ /*
+ * The BD12790 is almost identical to the adm1272. Only the defconfig
+ * and coefficients have minor differences.
+ */
+ case bd12790:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = bd12790_coefficients;
+
+ vindex = (config & ADM1275_VRANGE) ? 1 : 0;
+ cindex = (config & ADM1272_IRANGE) ? 3 : 2;
+ /* pindex depends on the combination of the above */
+ switch (config & (ADM1275_VRANGE | ADM1272_IRANGE)) {
+ case 0:
+ default:
+ pindex = 4;
+ break;
+ case ADM1275_VRANGE:
+ pindex = 5;
+ break;
+ case ADM1272_IRANGE:
+ pindex = 6;
+ break;
+ case ADM1275_VRANGE | ADM1272_IRANGE:
+ pindex = 7;
+ break;
+ }
+ tindex = 8;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(data, client, config,
+ BD12780_PMON_DEFCONFIG);
if (ret)
return ret;
@@ -728,12 +806,44 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- ret = adm1275_enable_vout_temp(data, client, config);
+ ret = adm1275_enable_vout_temp(data, client, config,
+ ADM1278_PMON_DEFCONFIG);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+ break;
+
+ /*
+ * The BD12780 is almost functionally identical with the adm1278 above.
+ * Only differences visible to the driver are lack of TSFILT bits and
+ * different identification register contents.
+ */
+ case bd12780:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = adm1278_coefficients;
+ vindex = 0;
+ cindex = 1;
+ pindex = 2;
+ tindex = 3;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(data, client, config,
+ BD12780_PMON_DEFCONFIG);
if (ret)
return ret;
if (config & ADM1278_VIN_EN)
info->func[0] |= PMBUS_HAVE_VIN;
+
break;
case adm1293:
case adm1294:
@@ -870,9 +980,27 @@ static int adm1275_probe(struct i2c_client *client)
return pmbus_do_probe(client, info);
}
+static const struct of_device_id adm1275_of_match[] = {
+ { .compatible = "adi,adm1075", },
+ { .compatible = "adi,adm1272", },
+ { .compatible = "adi,adm1273", },
+ { .compatible = "adi,adm1275", },
+ { .compatible = "adi,adm1276", },
+ { .compatible = "adi,adm1278", },
+ { .compatible = "adi,adm1281", },
+ { .compatible = "adi,adm1293", },
+ { .compatible = "adi,adm1294", },
+ { .compatible = "rohm,bd12780", },
+ { .compatible = "rohm,bd12790", },
+ { .compatible = "silergy,mc09c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adm1275_of_match);
+
static struct i2c_driver adm1275_driver = {
.driver = {
.name = "adm1275",
+ .of_match_table = adm1275_of_match,
},
.probe = adm1275_probe,
.id_table = adm1275_id,
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index 2be50cf1bbde..f20991001727 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -34,6 +34,7 @@ enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i };
#define LM25066_READ_AVG_PIN 0xdf
#define LM25066_DEV_SETUP_CL BIT(4) /* Current limit */
+#define LM25066_DEV_SETUP_CL_CFG BIT(2) /* Current limit configuration */
#define LM25066_SAMPLES_FOR_AVG_MAX 4096
@@ -484,6 +485,42 @@ static int lm25066_probe(struct i2c_client *client)
data->id = (enum chips)(unsigned long)i2c_get_match_data(client);
+ if (data->id != lm25056) {
+ int config_new = config;
+ const char *cl_setting;
+ int ret;
+
+ if (!of_property_read_string(client->dev.of_node,
+ "ti,current-range", &cl_setting)) {
+ config_new |= LM25066_DEV_SETUP_CL_CFG;
+ if (strcmp(cl_setting, "high") == 0) {
+ if (data->id == lm25066)
+ config_new |= LM25066_DEV_SETUP_CL;
+ else
+ config_new &= ~LM25066_DEV_SETUP_CL;
+ } else if (strcmp(cl_setting, "low") == 0) {
+ if (data->id == lm25066)
+ config_new &= ~LM25066_DEV_SETUP_CL;
+ else
+ config_new |= LM25066_DEV_SETUP_CL;
+ } else {
+ dev_err(&client->dev,
+ "invalid current-range setting: %s\n",
+ cl_setting);
+ return -EINVAL;
+ }
+ }
+
+ if (config_new != config) {
+ ret = i2c_smbus_write_byte_data(client,
+ LM25066_DEVICE_SETUP,
+ config_new);
+ if (ret < 0)
+ return ret;
+ config = config_new;
+ }
+ }
+
info = &data->info;
info->pages = 1;
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index e56057e9273c..866d25ae8971 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -23,6 +23,7 @@ enum chips {
max34441,
max34446,
max34451,
+ max34452,
max34460,
max34461,
};
@@ -59,6 +60,7 @@ enum chips {
#define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A
#define MAX34451ETNA6_MFR_REV 0x0012
+#define MAX34451ETNA8_MFR_REV 0x0014
#define MAX34451_MFR_CHANNEL_CONFIG 0xe4
#define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f
@@ -105,16 +107,21 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
case PMBUS_UT_FAULT_LIMIT:
case PMBUS_MFR_MAX_TEMP_1:
/*
- * MAX34451/ADPM family do not support VIN/IIN limit registers,
- * manufacturer-specific min/max registers, or undercurrent/
- * undertemperature fault limits. Accessing these triggers CML
- * error and asserts ALERT.
+ * MAX34451/MAX34452/ADPM family do not support VIN/IIN limit
+ * registers, manufacturer-specific min/max registers, or
+ * undercurrent/undertemperature fault limits. Accessing these
+ * triggers CML error and asserts ALERT.
*/
- if (data->id == max34451 || data->id == adpm12160 ||
- data->id == adpm12200 || data->id == adpm12250)
+ if (data->id == max34451 || data->id == max34452 ||
+ data->id == adpm12160 || data->id == adpm12200 ||
+ data->id == adpm12250)
return -ENXIO;
ret = -ENODATA;
break;
+ case PMBUS_VOUT_OV_WARN_LIMIT:
+ if (data->id == max34452)
+ return -ENXIO;
+ return -ENODATA;
case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, page, phase,
MAX34440_MFR_VOUT_MIN);
@@ -125,8 +132,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
break;
case PMBUS_VIRT_READ_IOUT_AVG:
if (data->id != max34446 && data->id != max34451 &&
- data->id != adpm12160 && data->id != adpm12200 &&
- data->id != adpm12250)
+ data->id != max34452 && data->id != adpm12160 &&
+ data->id != adpm12200 && data->id != adpm12250)
return -ENXIO;
ret = pmbus_read_word_data(client, page, phase,
MAX34446_MFR_IOUT_AVG);
@@ -191,6 +198,10 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit,
word);
break;
+ case PMBUS_VOUT_OV_WARN_LIMIT:
+ if (data->id == max34452)
+ return -ENXIO;
+ return -ENODATA;
case PMBUS_VIRT_RESET_POUT_HISTORY:
ret = pmbus_write_word_data(client, page,
MAX34446_MFR_POUT_PEAK, 0);
@@ -211,8 +222,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
ret = pmbus_write_word_data(client, page,
MAX34440_MFR_IOUT_PEAK, 0);
if (!ret && (data->id == max34446 || data->id == max34451 ||
- data->id == adpm12160 || data->id == adpm12200 ||
- data->id == adpm12250))
+ data->id == max34452 || data->id == adpm12160 ||
+ data->id == adpm12200 || data->id == adpm12250))
ret = pmbus_write_word_data(client, page,
MAX34446_MFR_IOUT_AVG, 0);
@@ -280,12 +291,13 @@ static int max34451_read_byte_data(struct i2c_client *client, int page, int reg)
case PMBUS_STATUS_BYTE:
case PMBUS_STATUS_OTHER:
/*
- * MAX34451/ADPM family do not support STATUS_BYTE or
+ * MAX34451/MAX34452/ADPM family do not support STATUS_BYTE or
* STATUS_OTHER registers. Accessing them triggers CML
* error and asserts ALERT.
*/
- if (data->id == max34451 || data->id == adpm12160 ||
- data->id == adpm12200 || data->id == adpm12250)
+ if (data->id == max34451 || data->id == max34452 ||
+ data->id == adpm12160 || data->id == adpm12200 ||
+ data->id == adpm12250)
return -ENXIO;
return -ENODATA;
default:
@@ -303,12 +315,13 @@ static int max34451_write_byte_data(struct i2c_client *client, int page,
case PMBUS_STATUS_BYTE:
case PMBUS_STATUS_OTHER:
/*
- * MAX34451/ADPM family do not support STATUS_BYTE or
+ * MAX34451/MAX34452/ADPM family do not support STATUS_BYTE or
* STATUS_OTHER registers. Writing to them triggers CML
* error and asserts ALERT.
*/
- if (data->id == max34451 || data->id == adpm12160 ||
- data->id == adpm12200 || data->id == adpm12250)
+ if (data->id == max34451 || data->id == max34452 ||
+ data->id == adpm12160 || data->id == adpm12200 ||
+ data->id == adpm12250)
return -ENXIO;
return -ENODATA;
default:
@@ -339,16 +352,28 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
if (rv < 0)
return rv;
- if (rv >= MAX34451ETNA6_MFR_REV) {
+ if (data->id == max34451 && rv >= MAX34451ETNA6_MFR_REV) {
max34451_na6 = true;
data->info.format[PSC_VOLTAGE_IN] = direct;
data->info.format[PSC_CURRENT_IN] = direct;
- data->info.m[PSC_VOLTAGE_IN] = 1;
+
data->info.b[PSC_VOLTAGE_IN] = 0;
- data->info.R[PSC_VOLTAGE_IN] = 3;
- data->info.m[PSC_CURRENT_IN] = 1;
data->info.b[PSC_CURRENT_IN] = 0;
- data->info.R[PSC_CURRENT_IN] = 2;
+ if (rv >= MAX34451ETNA8_MFR_REV) {
+ data->info.m[PSC_VOLTAGE_IN] = 125;
+ data->info.R[PSC_VOLTAGE_IN] = 0;
+ data->info.m[PSC_VOLTAGE_OUT] = 125;
+ data->info.R[PSC_VOLTAGE_OUT] = 0;
+ data->info.m[PSC_CURRENT_IN] = 250;
+ data->info.R[PSC_CURRENT_IN] = -1;
+ data->info.m[PSC_CURRENT_OUT] = 250;
+ data->info.R[PSC_CURRENT_OUT] = -1;
+ } else {
+ data->info.m[PSC_VOLTAGE_IN] = 1;
+ data->info.R[PSC_VOLTAGE_IN] = 3;
+ data->info.m[PSC_CURRENT_IN] = 1;
+ data->info.R[PSC_CURRENT_IN] = 2;
+ }
data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
}
@@ -665,6 +690,32 @@ static struct pmbus_driver_info max34440_info[] = {
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
+ [max34452] = {
+ .pages = 21,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 2,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ /* func 0-15 is set dynamically before probing */
+ .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_byte_data = max34451_read_byte_data,
+ .read_word_data = max34440_read_word_data,
+ .write_byte_data = max34451_write_byte_data,
+ .write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
+ },
[max34460] = {
.pages = 18,
.format[PSC_VOLTAGE_OUT] = direct,
@@ -748,7 +799,7 @@ static int max34440_probe(struct i2c_client *client)
data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT;
- if (data->id == max34451) {
+ if (data->id == max34451 || data->id == max34452) {
rv = max34451_set_supported_funcs(client, data);
if (rv)
return rv;
@@ -769,6 +820,7 @@ static const struct i2c_device_id max34440_id[] = {
{ .name = "max34441", .driver_data = max34441 },
{ .name = "max34446", .driver_data = max34446 },
{ .name = "max34451", .driver_data = max34451 },
+ { .name = "max34452", .driver_data = max34452 },
{ .name = "max34460", .driver_data = max34460 },
{ .name = "max34461", .driver_data = max34461 },
{ }
diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c
index 5393f7aeea0f..8e5193933ceb 100644
--- a/drivers/hwmon/pmbus/mp2975.c
+++ b/drivers/hwmon/pmbus/mp2975.c
@@ -176,7 +176,6 @@ mp2975_vid2direct(int vrf, int val)
static u16 mp2975_data2reg_linear11(s64 val)
{
s16 exponent = 0, mantissa;
- bool negative = false;
/* simple case */
if (val == 0)
@@ -196,10 +195,6 @@ static u16 mp2975_data2reg_linear11(s64 val)
/* Convert mantissa from milli-units to units */
mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff);
- /* restore sign */
- if (negative)
- mantissa = -mantissa;
-
/* Convert to 5 bit exponent, 11 bit mantissa */
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
}
diff --git a/drivers/hwmon/pmbus/mpq82d00.c b/drivers/hwmon/pmbus/mpq82d00.c
new file mode 100644
index 000000000000..d51fcffcc6d9
--- /dev/null
+++ b/drivers/hwmon/pmbus/mpq82d00.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Synchronous Step-Down Converter(MPQ82D00)
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define MPQ82D00_VOUT_DIV 64
+
+#define MPQ82D00_PAGE_NUM 1
+
+#define MPQ82D00_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+struct mpq82d00_data {
+ struct pmbus_driver_info info;
+ int vout_scale;
+};
+
+#define to_mpq82d00_data(x) container_of(x, struct mpq82d00_data, info)
+
+static u16 mpq82d00_linear_exp_transfer(u16 word, u16 expect_exponent)
+{
+ s16 exponent, mantissa, target_exponent;
+
+ exponent = ((s16)word) >> 11;
+ mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
+ target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11;
+
+ /*
+ * The MPQ82D00 does not support negtive limit value, if a negtive
+ * limit value is written, the limit value will become to 0. And
+ * the maximum positive limit value is limitted to 0x3FF.
+ */
+ if (mantissa < 0) {
+ mantissa = 0;
+ } else {
+ if (exponent > target_exponent) {
+ mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ?
+ mantissa << (exponent - target_exponent) :
+ 0x3FF;
+ } else {
+ mantissa = clamp_val(mantissa >> (target_exponent - exponent),
+ 0, 0x3FF);
+ }
+ }
+
+ return mantissa | ((expect_exponent << 11) & 0xf800);
+}
+
+static int mpq82d00_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /*
+ * The MPQ82D00 does not follow standard PMBus protocol completely,
+ * and the calculation of vout in this driver is based on direct
+ * format. As a result, the format of vout is enforced to direct.
+ */
+ ret = PB_VOUT_MODE_DIRECT;
+ break;
+ default:
+ /*
+ * These registers are not explicitly handled by the driver,
+ * as a result, return -ENODATA directly.
+ */
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int mpq82d00_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 mpq82d00_data *data = to_mpq82d00_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_STATUS_WORD:
+ case PMBUS_READ_VIN:
+ case PMBUS_READ_IOUT:
+ case PMBUS_READ_POUT:
+ case PMBUS_READ_PIN:
+ case PMBUS_READ_TEMPERATURE_1:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ /*
+ * These registers are not explicitly handled by the driver,
+ * as a result, return -ENODATA directly.
+ */
+ ret = -ENODATA;
+ break;
+ case PMBUS_READ_VOUT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * data->vout_scale,
+ MPQ82D00_VOUT_DIV);
+ break;
+ default:
+ /*
+ * The MPQ82D00 do not support other telemetry and limit
+ * value reading, so, return -EINVAL directly.
+ */
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mpq82d00_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_OT_FAULT_LIMIT:
+ /*
+ * The PMBUS_OT_FAULT_LIMIT of MPQ82D00 is linear11 format,
+ * and the exponent is a constant value(5'b00000), so the
+ * exponent of word parameter should be converted to 5'b00000.
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ mpq82d00_linear_exp_transfer(word, 0x00));
+ break;
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ /*
+ * The PMBUS_IOUT_OC_FAULT_LIMIT of MPQ82D00 is linear11 format,
+ * and the exponent is a constant value(5'b11100), so the
+ * exponent of word parameter should be converted to 5'b11100.
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ mpq82d00_linear_exp_transfer(word, 0x1C));
+ break;
+ default:
+ /*
+ * The MPQ82D00 do not support other limit value configuration,
+ * so, return -EINVAL directly.
+ */
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mpq82d00_identify(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+ struct mpq82d00_data *data = to_mpq82d00_data(info);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The MPQ82D00 supports three vout mode. If PMBUS_VOUT_MODE
+ * bit5 is 1, the vout scale is 5mv/LSB.If PMBUS_VOUT_MODE bit5
+ * is 0, it is linear mode, the vout scale is 1.953125mv/LSB.
+ */
+ if (FIELD_GET(GENMASK(5, 5), ret))
+ data->vout_scale = 320;
+ else
+ data->vout_scale = 125;
+
+ return 0;
+}
+
+static struct pmbus_driver_info mpq82d00_info = {
+ .pages = MPQ82D00_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_POWER] = linear,
+
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .b[PSC_VOLTAGE_OUT] = 0,
+
+ .func[0] = MPQ82D00_RAIL1_FUNC,
+ .read_word_data = mpq82d00_read_word_data,
+ .read_byte_data = mpq82d00_read_byte_data,
+ .write_word_data = mpq82d00_write_word_data,
+ .identify = mpq82d00_identify,
+};
+
+static int mpq82d00_probe(struct i2c_client *client)
+{
+ struct mpq82d00_data *data;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct mpq82d00_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(&data->info, &mpq82d00_info, sizeof(mpq82d00_info));
+
+ return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id mpq82d00_id[] = {
+ {.name = "mpq82d00"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mpq82d00_id);
+
+static const struct of_device_id __maybe_unused mpq82d00_of_match[] = {
+ {.compatible = "mps,mpq82d00"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mpq82d00_of_match);
+
+static struct i2c_driver mpq82d00_driver = {
+ .driver = {
+ .name = "mpq82d00",
+ .of_match_table = mpq82d00_of_match,
+ },
+ .probe = mpq82d00_probe,
+ .id_table = mpq82d00_id,
+};
+
+module_i2c_driver(mpq82d00_driver);
+
+MODULE_AUTHOR("Wensheng Wang <wenswang@yeah.net");
+MODULE_DESCRIPTION("PMBus driver for MPS MPQ82D00 device");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 0081f16c3a95..24c4d502733c 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -3497,10 +3497,8 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
/* Register notifiers */
err = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler,
IRQF_ONESHOT, "pmbus-irq", data);
- if (err) {
- dev_err(dev, "failed to request an irq %d\n", err);
+ if (err)
return err;
- }
return 0;
}
diff --git a/drivers/hwmon/pmbus/sq24860.c b/drivers/hwmon/pmbus/sq24860.c
new file mode 100644
index 000000000000..30202a4b34cf
--- /dev/null
+++ b/drivers/hwmon/pmbus/sq24860.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Ziming Zhu <ziming.zhu@silergycorp.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+
+#include "pmbus.h"
+
+#define SQ24860_IIN_CAL_GAIN 0x38
+#define SQ24860_READ_VAUX 0xd0
+#define SQ24860_READ_VIN_MIN 0xd1
+#define SQ24860_READ_VIN_PEAK 0xd2
+#define SQ24860_READ_IIN_PEAK 0xd4
+#define SQ24860_READ_PIN_PEAK 0xd5
+#define SQ24860_READ_TEMP_AVG 0xd6
+#define SQ24860_READ_TEMP_PEAK 0xd7
+#define SQ24860_READ_VOUT_MIN 0xda
+#define SQ24860_READ_VIN_AVG 0xdc
+#define SQ24860_READ_VOUT_AVG 0xdd
+#define SQ24860_READ_IIN_AVG 0xde
+#define SQ24860_READ_PIN_AVG 0xdf
+#define SQ24860_VIREF 0xe0
+#define SQ24860_PK_MIN_AVG 0xea
+#define PK_MIN_AVG_RST_PEAK BIT(7)
+#define PK_MIN_AVG_RST_AVG BIT(6)
+#define PK_MIN_AVG_RST_MIN BIT(5)
+#define PK_MIN_AVG_AVG_CNT GENMASK(2, 0)
+#define SQ24860_MFR_WRITE_PROTECT 0xf8
+#define SQ24860_UNLOCKED BIT(7)
+
+#define SQ24860_8B_SHIFT 2
+#define SQ24860_IIN_OCF_NUM 1000000
+#define SQ24860_IIN_OCF_DIV 129278
+#define SQ24860_IIN_OCF_OFF 165
+
+#define PK_MIN_AVG_RST_MASK (PK_MIN_AVG_RST_PEAK | \
+ PK_MIN_AVG_RST_AVG | \
+ PK_MIN_AVG_RST_MIN)
+#define SQ24860_MAX_SAMPLES BIT(FIELD_MAX(PK_MIN_AVG_AVG_CNT))
+/*
+ * Arbitrary default Rimon value: 1.6kOhm
+ */
+#define SQ24860_DEFAULT_RIMON 1600000000
+#define SQ24860_GIMON 18180
+
+#define SQ24860_VAUX_DIV 20
+
+static int sq24860_write_iin_cal_gain(struct i2c_client *client, u32 rimon)
+{
+ u64 temp = 6400ULL * 1000000000ULL * 1000ULL;
+ u64 denom;
+ u64 word;
+
+ if (!rimon)
+ return -EINVAL;
+
+ denom = (u64)rimon * SQ24860_GIMON;
+ word = div64_u64(temp, denom);
+ if (!word || word > U16_MAX)
+ return -EINVAL;
+
+ return i2c_smbus_write_word_data(client, SQ24860_IIN_CAL_GAIN,
+ (u16)word);
+}
+
+static int sq24860_mfr_write_protect_set(struct i2c_client *client,
+ u8 protect)
+{
+ u8 val;
+
+ switch (protect) {
+ case 0:
+ val = 0xa2;
+ break;
+ case PB_WP_ALL:
+ val = 0x0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmbus_write_byte_data(client, -1, SQ24860_MFR_WRITE_PROTECT,
+ val);
+}
+
+static int sq24860_mfr_write_protect_get(struct i2c_client *client)
+{
+ int ret = pmbus_read_byte_data(client, -1, SQ24860_MFR_WRITE_PROTECT);
+
+ if (ret < 0)
+ return ret;
+
+ return (ret & SQ24860_UNLOCKED) ? 0 : PB_WP_ALL;
+}
+
+static int sq24860_read_word_data(struct i2c_client *client,
+ int page, int phase, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MAX:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_VIN_PEAK);
+ break;
+
+ case PMBUS_VIRT_READ_VIN_MIN:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_VIN_MIN);
+ break;
+
+ case PMBUS_VIRT_READ_VIN_AVG:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_VIN_AVG);
+ break;
+
+ case PMBUS_VIRT_READ_VOUT_MIN:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_VOUT_MIN);
+ break;
+
+ case PMBUS_VIRT_READ_VOUT_AVG:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_VOUT_AVG);
+ break;
+
+ case PMBUS_VIRT_READ_IIN_AVG:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_IIN_AVG);
+ break;
+
+ case PMBUS_VIRT_READ_IIN_MAX:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_IIN_PEAK);
+ break;
+
+ case PMBUS_VIRT_READ_TEMP_AVG:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_TEMP_AVG);
+ break;
+
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_TEMP_PEAK);
+ break;
+
+ case PMBUS_VIRT_READ_PIN_AVG:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_PIN_AVG);
+ break;
+
+ case PMBUS_VIRT_READ_PIN_MAX:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_PIN_PEAK);
+ break;
+
+ case PMBUS_VIRT_READ_VMON:
+ ret = pmbus_read_word_data(client, page, phase,
+ SQ24860_READ_VAUX);
+ if (ret < 0)
+ break;
+ ret = DIV_ROUND_CLOSEST(ret, SQ24860_VAUX_DIV);
+ break;
+
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ /*
+ * These registers provide an 8 bits value instead of a
+ * 10bits one. Just shifting twice the register value is
+ * enough to make the sensor type conversion work, even
+ * if the datasheet provides different m, b and R for
+ * those.
+ */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ break;
+ ret <<= SQ24860_8B_SHIFT;
+ break;
+
+ case PMBUS_IIN_OC_FAULT_LIMIT:
+ /*
+ * VIREF directly sets the over-current limit at which the eFuse
+ * will turn the FET off and trigger a fault. Expose it through
+ * this generic property instead of a manufacturer specific one.
+ */
+ ret = pmbus_read_byte_data(client, page, SQ24860_VIREF);
+ if (ret < 0)
+ break;
+ ret = DIV_ROUND_CLOSEST(ret * SQ24860_IIN_OCF_NUM,
+ SQ24860_IIN_OCF_DIV);
+ ret += SQ24860_IIN_OCF_OFF;
+ break;
+
+ case PMBUS_VIRT_SAMPLES:
+ ret = pmbus_read_byte_data(client, page, SQ24860_PK_MIN_AVG);
+ if (ret < 0)
+ break;
+ ret = BIT(FIELD_GET(PK_MIN_AVG_AVG_CNT, ret));
+ break;
+
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = 0;
+ break;
+
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int sq24860_write_word_data(struct i2c_client *client,
+ int page, int reg, u16 value)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ value = max_t(s16, (s16)value, 0);
+ value >>= SQ24860_8B_SHIFT;
+ value = clamp_val(value, 0, 0xff);
+ ret = pmbus_write_word_data(client, page, reg, value);
+ break;
+
+ case PMBUS_IIN_OC_FAULT_LIMIT:
+ value = max_t(s16, (s16)value, SQ24860_IIN_OCF_OFF);
+ value -= SQ24860_IIN_OCF_OFF;
+ value = DIV_ROUND_CLOSEST(((unsigned int)value) * SQ24860_IIN_OCF_DIV,
+ SQ24860_IIN_OCF_NUM);
+ value = clamp_val(value, 0, 0x3f);
+ ret = pmbus_write_byte_data(client, page, SQ24860_VIREF, value);
+ break;
+
+ case PMBUS_VIRT_SAMPLES:
+ value = clamp_val(value, 1, SQ24860_MAX_SAMPLES);
+ value = ilog2(value);
+ ret = pmbus_update_byte_data(client, page, SQ24860_PK_MIN_AVG,
+ PK_MIN_AVG_AVG_CNT,
+ FIELD_PREP(PK_MIN_AVG_AVG_CNT, value));
+ break;
+
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ /*
+ * SQ24860 has history resets based on MIN/AVG/PEAK instead of per
+ * sensor type. Exposing this quirk in hwmon is not desirable so
+ * reset MIN, AVG and PEAK together. Even is there effectively only
+ * one reset, which resets everything, expose the 5 entries so
+ * userspace is not required map a sensor type to another to trigger
+ * a reset
+ */
+ ret = pmbus_update_byte_data(client, 0, SQ24860_PK_MIN_AVG,
+ PK_MIN_AVG_RST_MASK,
+ PK_MIN_AVG_RST_MASK);
+ break;
+
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int sq24860_read_byte_data(struct i2c_client *client,
+ int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_WRITE_PROTECT:
+ ret = sq24860_mfr_write_protect_get(client);
+ break;
+
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int sq24860_write_byte_data(struct i2c_client *client,
+ int page, int reg, u8 byte)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_WRITE_PROTECT:
+ ret = sq24860_mfr_write_protect_set(client, byte);
+ break;
+
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_SENSORS_SQ24860_REGULATOR)
+static const struct regulator_desc sq24860_reg_desc[] = {
+ PMBUS_REGULATOR_ONE_NODE("vout"),
+};
+#endif
+
+static const struct pmbus_driver_info sq24860_base_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .m[PSC_VOLTAGE_IN] = 64,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .m[PSC_VOLTAGE_OUT] = 64,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 0,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 0,
+ /*
+ * Current and power measurements depend on the calibration gain
+ * programmed from the board-specific IMON resistor value.
+ */
+ .format[PSC_CURRENT_IN] = direct,
+ .m[PSC_CURRENT_IN] = 16,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = 0,
+ .format[PSC_POWER] = direct,
+ .m[PSC_POWER] = 2,
+ .b[PSC_POWER] = 0,
+ .R[PSC_POWER] = 0,
+ .func[0] = PMBUS_HAVE_VIN |
+ PMBUS_HAVE_VOUT |
+ PMBUS_HAVE_VMON |
+ PMBUS_HAVE_IIN |
+ PMBUS_HAVE_PIN |
+ PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_SAMPLES,
+ .read_word_data = sq24860_read_word_data,
+ .write_word_data = sq24860_write_word_data,
+ .read_byte_data = sq24860_read_byte_data,
+ .write_byte_data = sq24860_write_byte_data,
+
+#if IS_ENABLED(CONFIG_SENSORS_SQ24860_REGULATOR)
+ .reg_desc = sq24860_reg_desc,
+ .num_regulators = ARRAY_SIZE(sq24860_reg_desc),
+#endif
+};
+
+static const struct i2c_device_id sq24860_i2c_id[] = {
+ { "sq24860" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sq24860_i2c_id);
+
+static const struct of_device_id sq24860_of_match[] = {
+ { .compatible = "silergy,sq24860" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sq24860_of_match);
+
+static int sq24860_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct pmbus_driver_info *info;
+ u32 rimon;
+ int ret;
+
+ if (device_property_read_u32(dev, "silergy,rimon-micro-ohms", &rimon))
+ rimon = SQ24860_DEFAULT_RIMON;
+ ret = sq24860_write_iin_cal_gain(client, rimon);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to set gain\n");
+ info = devm_kmemdup(dev, &sq24860_base_info, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ return pmbus_do_probe(client, info);
+}
+
+static struct i2c_driver sq24860_driver = {
+ .driver = {
+ .name = "sq24860",
+ .of_match_table = sq24860_of_match,
+ },
+ .probe = sq24860_probe,
+ .id_table = sq24860_i2c_id,
+};
+module_i2c_driver(sq24860_driver);
+
+MODULE_AUTHOR("Ziming Zhu <ziming.zhu@silergycorp.com>");
+MODULE_DESCRIPTION("PMBUS driver for SQ24860 eFuse");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c
index 971e7b73752e..9455ef443b2b 100644
--- a/drivers/hwmon/pmbus/xdpe1a2g7b.c
+++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c
@@ -57,6 +57,13 @@ static int xdpe1a2g7b_identify(struct i2c_client *client,
return 0;
}
+#if IS_ENABLED(CONFIG_SENSORS_XDPE1A2G7B_REGULATOR)
+static const struct regulator_desc xdpe1a2g7b_reg_desc[] = {
+ PMBUS_REGULATOR("vout", 0),
+ PMBUS_REGULATOR("vout", 1),
+};
+#endif
+
static struct pmbus_driver_info xdpe1a2g7b_info = {
.pages = XDPE1A2G7B_PAGE_NUM,
.identify = xdpe1a2g7b_identify,
@@ -72,6 +79,10 @@ static struct pmbus_driver_info xdpe1a2g7b_info = {
.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,
+#if IS_ENABLED(CONFIG_SENSORS_XDPE1A2G7B_REGULATOR)
+ .num_regulators = XDPE1A2G7B_PAGE_NUM,
+ .reg_desc = xdpe1a2g7b_reg_desc,
+#endif
};
static int xdpe1a2g7b_probe(struct i2c_client *client)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 37f37813ea51..3b87f65bae05 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -628,12 +628,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (tach->irq > 0) {
ret = devm_request_irq(dev, tach->irq, pulse_handler,
IRQF_NO_THREAD, pdev->name, tach);
- if (ret) {
- dev_err(dev,
- "Failed to request interrupt: %d\n",
- ret);
+ if (ret)
return ret;
- }
}
if (!ctx->pulses_per_revolution[i]) {
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index 101cebbe68e4..7bb2105d1ae2 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -981,10 +981,8 @@ static int sht15_probe(struct platform_device *pdev)
IRQF_TRIGGER_FALLING,
"sht15 data",
data);
- if (ret) {
- dev_err(&pdev->dev, "failed to get irq for data line\n");
+ if (ret)
goto err_release_reg;
- }
disable_irq_nosync(gpiod_to_irq(data->data));
ret = sht15_connection_reset(data);
if (ret)
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
index 4d90f89a9929..b46de050e20a 100644
--- a/drivers/hwmon/sht3x.c
+++ b/drivers/hwmon/sht3x.c
@@ -62,7 +62,7 @@ static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 };
#define SHT3X_MAX_HUMIDITY 100000
enum sht3x_chips {
- sht3x,
+ sht3x = 1,
sts3x,
};
@@ -940,8 +940,19 @@ static const struct i2c_device_id sht3x_ids[] = {
MODULE_DEVICE_TABLE(i2c, sht3x_ids);
+static const struct of_device_id sht3x_of_match[] = {
+ { .compatible = "sensirion,sht30", .data = (void *)(uintptr_t)sht3x },
+ { .compatible = "sensirion,sts30", .data = (void *)(uintptr_t)sts3x },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, sht3x_of_match);
+
static struct i2c_driver sht3x_i2c_driver = {
- .driver.name = "sht3x",
+ .driver = {
+ .name = "sht3x",
+ .of_match_table = sht3x_of_match,
+ },
.probe = sht3x_probe,
.id_table = sht3x_ids,
};
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index ca0ff525ea29..83aafe4bd3e7 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -695,7 +695,7 @@ static int tmp401_probe(struct i2c_client *client)
info->type = hwmon_chip;
info->config = data->chip_channel_config;
- data->chip_channel_config[0] = HWMON_C_UPDATE_INTERVAL;
+ data->chip_channel_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL;
info = &data->temp_info;
info->type = hwmon_temp;
diff --git a/drivers/hwmon/tvs-mpfs.c b/drivers/hwmon/tvs-mpfs.c
new file mode 100644
index 000000000000..aad00676518b
--- /dev/null
+++ b/drivers/hwmon/tvs-mpfs.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Lars Randers <lranders@mail.dk>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/hwmon.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <linux/math.h>
+#include <linux/mfd/syscon.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MPFS_TVS_CTRL 0x08
+#define MPFS_TVS_OUTPUT0 0x24
+#define MPFS_TVS_OUTPUT1 0x28
+
+#define MPFS_TVS_CTRL_TEMP_VALID BIT(19)
+#define MPFS_TVS_CTRL_V2P5_VALID BIT(18)
+#define MPFS_TVS_CTRL_V1P8_VALID BIT(17)
+#define MPFS_TVS_CTRL_V1P05_VALID BIT(16)
+
+#define MPFS_TVS_CTRL_TEMP_ENABLE BIT(3)
+#define MPFS_TVS_CTRL_V2P5_ENABLE BIT(2)
+#define MPFS_TVS_CTRL_V1P8_ENABLE BIT(1)
+#define MPFS_TVS_CTRL_V1P05_ENABLE BIT(0)
+#define MPFS_TVS_CTRL_ENABLE_ALL GENMASK(3, 0)
+
+/*
+ * For all of these the value in millivolts is stored in 16 bits, with an upper
+ * sign bit and a lower 3 bits of decimal. These masks discard the sign bit and
+ * decimal places, because if Linux is running these voltages cannot be negative
+ * and so avoid having to convert to two's complement.
+ */
+#define MPFS_OUTPUT0_V1P8_MASK GENMASK(30, 19)
+#define MPFS_OUTPUT0_V1P05_MASK GENMASK(14, 3)
+#define MPFS_OUTPUT1_V2P5_MASK GENMASK(14, 3)
+
+/*
+ * The register map claims that the temperature is stored in bits 31:16, but
+ * application note "AN4682: PolarFire FPGA Temperature and Voltage Sensor"
+ * says that 31 is reserved. Temperature is in kelvin, so what's probably a
+ * sign bit has no value anyway.
+ */
+#define MPFS_OUTPUT1_TEMP_MASK GENMASK(30, 16)
+
+#define MPFS_TVS_INTERVAL_MASK GENMASK(15, 8)
+#define MPFS_TVS_INTERVAL_OFFSET 8
+/* The interval register is in increments of 32 us */
+#define MPFS_TVS_INTERVAL_SCALE 32
+/* with 254 usable increments of 32 us available, 8 ms is the integer limit */
+#define MPFS_TVS_INTERVAL_MAX_MS 8
+
+/* 273.1875 in 11.4 fixed-point notation */
+#define MPFS_TVS_K_TO_C 0x1113
+
+enum mpfs_tvs_sensors {
+ SENSOR_V1P05 = 0,
+ SENSOR_V1P8,
+ SENSOR_V2P5,
+};
+
+static const char * const mpfs_tvs_voltage_labels[] = { "1P05", "1P8", "2P5" };
+
+struct mpfs_tvs {
+ struct regmap *regmap;
+};
+
+static int mpfs_tvs_voltage_read(struct mpfs_tvs *data, u32 attr,
+ int channel, long *val)
+{
+ u32 tmp, control;
+
+ if (attr != hwmon_in_input && attr != hwmon_in_enable)
+ return -EOPNOTSUPP;
+
+ regmap_read(data->regmap, MPFS_TVS_CTRL, &control);
+
+ switch (channel) {
+ case SENSOR_V2P5:
+ if (attr == hwmon_in_enable) {
+ *val = FIELD_GET(MPFS_TVS_CTRL_V2P5_ENABLE, control);
+ break;
+ }
+
+ if (!(control & MPFS_TVS_CTRL_V2P5_VALID))
+ return -ENODATA;
+
+ regmap_read(data->regmap, MPFS_TVS_OUTPUT1, &tmp);
+ *val = FIELD_GET(MPFS_OUTPUT1_V2P5_MASK, tmp);
+ break;
+ case SENSOR_V1P8:
+ if (attr == hwmon_in_enable) {
+ *val = FIELD_GET(MPFS_TVS_CTRL_V1P8_ENABLE, control);
+ break;
+ }
+
+ if (!(control & MPFS_TVS_CTRL_V1P8_VALID))
+ return -ENODATA;
+
+ regmap_read(data->regmap, MPFS_TVS_OUTPUT0, &tmp);
+ *val = FIELD_GET(MPFS_OUTPUT0_V1P8_MASK, tmp);
+ break;
+ case SENSOR_V1P05:
+ if (attr == hwmon_in_enable) {
+ *val = FIELD_GET(MPFS_TVS_CTRL_V1P05_ENABLE, control);
+ break;
+ }
+
+ if (!(control & MPFS_TVS_CTRL_V1P05_VALID))
+ return -ENODATA;
+
+ regmap_read(data->regmap, MPFS_TVS_OUTPUT0, &tmp);
+ *val = FIELD_GET(MPFS_OUTPUT0_V1P05_MASK, tmp);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mpfs_tvs_voltage_write(struct mpfs_tvs *data, u32 attr,
+ int channel, long val)
+{
+ u32 tmp;
+
+ if (attr != hwmon_in_enable)
+ return -EOPNOTSUPP;
+
+ if (val > 1 || val < 0)
+ return -EINVAL;
+
+ switch (channel) {
+ case SENSOR_V2P5:
+ tmp = FIELD_PREP(MPFS_TVS_CTRL_V2P5_ENABLE, val);
+ regmap_update_bits(data->regmap, MPFS_TVS_CTRL,
+ MPFS_TVS_CTRL_V2P5_ENABLE, tmp);
+ break;
+ case SENSOR_V1P8:
+ tmp = FIELD_PREP(MPFS_TVS_CTRL_V1P8_ENABLE, val);
+ regmap_update_bits(data->regmap, MPFS_TVS_CTRL,
+ MPFS_TVS_CTRL_V1P8_ENABLE, tmp);
+ break;
+ case SENSOR_V1P05:
+ tmp = FIELD_PREP(MPFS_TVS_CTRL_V1P05_ENABLE, val);
+ regmap_update_bits(data->regmap, MPFS_TVS_CTRL,
+ MPFS_TVS_CTRL_V1P05_ENABLE, tmp);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mpfs_tvs_temp_read(struct mpfs_tvs *data, u32 attr, long *val)
+{
+ u32 tmp, control;
+
+ if (attr != hwmon_temp_input && attr != hwmon_temp_enable)
+ return -EOPNOTSUPP;
+
+ regmap_read(data->regmap, MPFS_TVS_CTRL, &control);
+
+ if (attr == hwmon_temp_enable) {
+ *val = FIELD_GET(MPFS_TVS_CTRL_TEMP_ENABLE, control);
+ return 0;
+ }
+
+ if (!(control & MPFS_TVS_CTRL_TEMP_VALID))
+ return -ENODATA;
+
+ regmap_read(data->regmap, MPFS_TVS_OUTPUT1, &tmp);
+ *val = FIELD_GET(MPFS_OUTPUT1_TEMP_MASK, tmp);
+ *val -= MPFS_TVS_K_TO_C;
+ *val = (1000 * *val) >> 4; /* fixed point (11.4) to millidegrees */
+
+ return 0;
+}
+
+static int mpfs_tvs_temp_write(struct mpfs_tvs *data, u32 attr, long val)
+{
+ u32 tmp;
+
+ if (attr != hwmon_temp_enable)
+ return -EOPNOTSUPP;
+
+ if (val > 1 || val < 0)
+ return -EINVAL;
+
+ tmp = FIELD_PREP(MPFS_TVS_CTRL_TEMP_ENABLE, val);
+ regmap_update_bits(data->regmap, MPFS_TVS_CTRL,
+ MPFS_TVS_CTRL_TEMP_ENABLE, tmp);
+
+ return 0;
+}
+
+static int mpfs_tvs_interval_read(struct mpfs_tvs *data, u32 attr, long *val)
+{
+ u32 tmp;
+
+ if (attr != hwmon_chip_update_interval)
+ return -EOPNOTSUPP;
+
+ regmap_read(data->regmap, MPFS_TVS_CTRL, &tmp);
+ *val = FIELD_GET(MPFS_TVS_INTERVAL_MASK, tmp);
+ *val *= MPFS_TVS_INTERVAL_SCALE;
+ *val = roundup(*val, 1000);
+ *val /= 1000;
+
+ return 0;
+}
+
+static int mpfs_tvs_interval_write(struct mpfs_tvs *data, u32 attr, long val)
+{
+ long temp = val;
+
+ if (attr != hwmon_chip_update_interval)
+ return -EOPNOTSUPP;
+
+ temp = clamp(temp, 0, MPFS_TVS_INTERVAL_MAX_MS);
+
+ temp *= 1000;
+ temp /= MPFS_TVS_INTERVAL_SCALE;
+
+ temp <<= MPFS_TVS_INTERVAL_OFFSET;
+ regmap_update_bits(data->regmap, MPFS_TVS_CTRL,
+ MPFS_TVS_INTERVAL_MASK, temp);
+
+ return 0;
+}
+
+static umode_t mpfs_tvs_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_chip && attr == hwmon_chip_update_interval)
+ return 0644;
+
+ if (type == hwmon_temp) {
+ switch (attr) {
+ case hwmon_temp_enable:
+ return 0644;
+ case hwmon_temp_input:
+ case hwmon_temp_label:
+ return 0444;
+ default:
+ return 0;
+ }
+ }
+
+ if (type == hwmon_in) {
+ switch (attr) {
+ case hwmon_in_enable:
+ return 0644;
+ case hwmon_in_input:
+ case hwmon_in_label:
+ return 0444;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int mpfs_tvs_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct mpfs_tvs *data = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ return mpfs_tvs_temp_read(data, attr, val);
+ case hwmon_in:
+ return mpfs_tvs_voltage_read(data, attr, channel, val);
+ case hwmon_chip:
+ return mpfs_tvs_interval_read(data, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mpfs_tvs_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct mpfs_tvs *data = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ return mpfs_tvs_temp_write(data, attr, val);
+ case hwmon_in:
+ return mpfs_tvs_voltage_write(data, attr, channel, val);
+ case hwmon_chip:
+ return mpfs_tvs_interval_write(data, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mpfs_tvs_read_labels(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel,
+ const char **str)
+{
+ switch (type) {
+ case hwmon_temp:
+ *str = "Die Temp";
+ return 0;
+ case hwmon_in:
+ *str = mpfs_tvs_voltage_labels[channel];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops mpfs_tvs_ops = {
+ .is_visible = mpfs_tvs_is_visible,
+ .read_string = mpfs_tvs_read_labels,
+ .read = mpfs_tvs_read,
+ .write = mpfs_tvs_write,
+};
+
+static const struct hwmon_channel_info *mpfs_tvs_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_ENABLE),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE,
+ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE,
+ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE),
+ NULL
+};
+
+static const struct hwmon_chip_info mpfs_tvs_chip_info = {
+ .ops = &mpfs_tvs_ops,
+ .info = mpfs_tvs_info,
+};
+
+static int mpfs_tvs_probe(struct platform_device *pdev)
+{
+ struct device *hwmon_dev;
+ struct mpfs_tvs *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = device_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(data->regmap),
+ "Failed to find syscon regmap\n");
+
+ /*
+ * It's an MMIO regmap with no resources, there's nothing that can fail
+ * and return an error
+ */
+ regmap_write(data->regmap, MPFS_TVS_CTRL, MPFS_TVS_CTRL_ENABLE_ALL);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "mpfs_tvs",
+ data,
+ &mpfs_tvs_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev),
+ "hwmon device registration failed.\n");
+
+ return 0;
+}
+
+static struct platform_driver mpfs_tvs_driver = {
+ .probe = mpfs_tvs_probe,
+ .driver = {
+ .name = "mpfs-tvs",
+ },
+};
+module_platform_driver(mpfs_tvs_driver);
+
+MODULE_AUTHOR("Lars Randers <lranders@mail.dk>");
+MODULE_DESCRIPTION("PolarFire SoC temperature & voltage sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
index 11c5d80428cd..38b140c23c88 100644
--- a/drivers/hwmon/xgene-hwmon.c
+++ b/drivers/hwmon/xgene-hwmon.c
@@ -133,10 +133,6 @@ static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
init_completion(&ctx->rd_complete);
ctx->resp_pending = true;
- /* Write signature for subspace */
- WRITE_ONCE(generic_comm_base->signature,
- cpu_to_le32(PCC_SIGNATURE | ctx->mbox_idx));
-
/* Write to the shared command region */
WRITE_ONCE(generic_comm_base->command,
cpu_to_le16(MSG_TYPE(msg[0]) | PCC_CMD_GENERATE_DB_INTR));
diff --git a/drivers/hwmon/yogafan.c b/drivers/hwmon/yogafan.c
index 605cc928f21f..0a41b8672b56 100644
--- a/drivers/hwmon/yogafan.c
+++ b/drivers/hwmon/yogafan.c
@@ -71,6 +71,12 @@ static const struct yogafan_config legion_16bit_dual_cfg = {
.paths = { "\\_SB.PCI0.LPC0.EC0.FANS", "\\_SB.PCI0.LPC0.EC0.FA2S" }
};
+static const struct yogafan_config loq_15iax9_8bit_dual_cfg = {
+ .multiplier = 100,
+ .fan_count = 2,
+ .paths = { "\\_SB.PC00.LPCB.EC0.FA1S", "\\_SB.PC00.LPCB.EC0.FA2S" }
+};
+
static void apply_rllag_filter(struct yoga_fan_data *data, int idx, long raw_rpm)
{
ktime_t now = ktime_get_boottime();
@@ -194,6 +200,14 @@ static const struct dmi_system_id yogafan_quirks[] = {
},
.driver_data = (void *)&ideapad_8bit_fan0_cfg,
},
+ {
+ .ident = "Lenovo LOQ 15IAX9",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "LOQ 15IAX9"),
+ },
+ .driver_data = (void *)&loq_15iax9_8bit_dual_cfg,
+ },
{ }
};
MODULE_DEVICE_TABLE(dmi, yogafan_quirks);