]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Merge tag 'hwmon-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 22 Feb 2023 19:31:09 +0000 (11:31 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 22 Feb 2023 19:31:09 +0000 (11:31 -0800)
Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Infineon TDA38640 Voltage Regulator

   - NXP MC34VR500 PMIC

   - GXP fan controller

   - MPQ7932 Power Management IC

  New chip or board support added to existing drivers:

   - it87: IT87952E; also other cleanup/improvements

   - intel-m10-bmc-hwmon: N6000

   - pmbus/max16601: MAX16600

   - aquacomputer_d5next: Aquacomputer Aquastream Ultimate, Aquacomputer
     Poweradjust 3, Aquacomputer Aquaero

   - nct6775: Support for B650/B660/X670 ASUS boards

   - oxp-sensors: AYANEO AIR and AIR Pro

  Other notable changes:

   - Various kernel documentation fixes

   - Various devicetree bindings fixes

   - Explicitly deprecated [devm_]hwmon_device_register_with_groups

   - ftsteutates: Support for fanX_fault and other cleanup

   - ltc2945: Support for setting shunt resistor and other cleanup/fixes

   - coretemp: Avoid RDMSR interrupts to isolated CPUs, and simplify
     platform device handling

  ... and various other minor cleanups and fixes"

* tag 'hwmon-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (66 commits)
  hwmon: Deprecate [devm_]hwmon_device_register_with_groups
  hwmon: (mlxreg-fan) Return zero speed for broken fan
  hwmon: (gxp-fan-ctrl) use devm_platform_get_and_ioremap_resource()
  hwmon: (aquacomputer_d5next) Add support for Aquacomputer Aquastream Ultimate
  hwmon: (aquacomputer_d5next) Add support for Aquacomputer Poweradjust 3
  hwmon: (iio_hwmon) use dev_err_probe
  hwmon: intel-m10-bmc-hwmon: Add N6000 sensors
  Docs/hwmon/index: Add missing SPDX License Identifier
  hwmon: (it87) Updated documentation for recent updates to it87
  hwmon: (it87) Add new chipset IT87952E
  hwmon: (it87) Allow multiple chip IDs for force_id
  hwmon: (it87) Add chip_id in some info message
  hwmon: (it87) List full chip model name
  hwmon: (it87) Disable configuration exit for certain chips
  hwmon: (it87) Allow disabling exiting of configuration mode
  Documentation: hwmon: correct spelling
  hwmon: (pmbus/max16601) Add support for MAX16600
  hwmon: (ltc2945) Allow setting shunt resistor
  hwmon: (ltc2945) Handle error case in ltc2945_value_store
  hwmon: (ltc2945) Add devicetree match table
  ...

75 files changed:
Documentation/ABI/testing/sysfs-class-hwmon
Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml
Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml
Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml
Documentation/devicetree/bindings/hwmon/adi,ltc2945.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml
Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml
Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml
Documentation/devicetree/bindings/hwmon/amd,sbtsi.yaml
Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/hwmon/iio-hwmon.yaml
Documentation/devicetree/bindings/hwmon/national,lm90.yaml
Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml
Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml
Documentation/devicetree/bindings/hwmon/nxp,mc34vr500.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml
Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
Documentation/devicetree/bindings/trivial-devices.yaml
Documentation/hwmon/aht10.rst
Documentation/hwmon/aquacomputer_d5next.rst
Documentation/hwmon/aspeed-pwm-tacho.rst
Documentation/hwmon/asus_ec_sensors.rst
Documentation/hwmon/corsair-psu.rst
Documentation/hwmon/ftsteutates.rst
Documentation/hwmon/gsc-hwmon.rst
Documentation/hwmon/gxp-fan-ctrl.rst [new file with mode: 0644]
Documentation/hwmon/hwmon-kernel-api.rst
Documentation/hwmon/index.rst
Documentation/hwmon/it87.rst
Documentation/hwmon/ltc2978.rst
Documentation/hwmon/max16601.rst
Documentation/hwmon/max6697.rst
Documentation/hwmon/mc34vr500.rst [new file with mode: 0644]
Documentation/hwmon/menf21bmc.rst
Documentation/hwmon/oxp-sensors.rst
Documentation/hwmon/pmbus-core.rst
Documentation/hwmon/sht4x.rst
Documentation/hwmon/smm665.rst
Documentation/hwmon/stpddc60.rst
Documentation/hwmon/submitting-patches.rst
Documentation/hwmon/vexpress.rst
Documentation/hwmon/via686a.rst
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/aht10.c
drivers/hwmon/aquacomputer_d5next.c
drivers/hwmon/asus-ec-sensors.c
drivers/hwmon/coretemp.c
drivers/hwmon/emc2305.c
drivers/hwmon/ftsteutates.c
drivers/hwmon/gxp-fan-ctrl.c [new file with mode: 0644]
drivers/hwmon/hih6130.c
drivers/hwmon/ibmpex.c
drivers/hwmon/iio_hwmon.c
drivers/hwmon/intel-m10-bmc-hwmon.c
drivers/hwmon/it87.c
drivers/hwmon/ltc2945.c
drivers/hwmon/mc34vr500.c [new file with mode: 0644]
drivers/hwmon/mlxreg-fan.c
drivers/hwmon/nct6775-core.c
drivers/hwmon/nct6775-platform.c
drivers/hwmon/nzxt-smart2.c
drivers/hwmon/oxp-sensors.c
drivers/hwmon/peci/cputemp.c
drivers/hwmon/pmbus/Kconfig
drivers/hwmon/pmbus/Makefile
drivers/hwmon/pmbus/ltc2978.c
drivers/hwmon/pmbus/max16601.c
drivers/hwmon/pmbus/mpq7932.c [new file with mode: 0644]
drivers/hwmon/pmbus/pmbus.h
drivers/hwmon/pmbus/tda38640.c [new file with mode: 0644]
drivers/hwmon/sht15.c
drivers/hwmon/sht21.c
include/linux/hwmon.h

index 7271781a23b2844f4879448a7e6beb691cb36257..638f4c6d4ec704478e6dc1c9091a63028bfeeb7d 100644 (file)
@@ -276,6 +276,15 @@ Description:
 
                RW
 
+What:          /sys/class/hwmon/hwmonX/fanY_fault
+Description:
+               Reports if a fan has reported failure.
+
+               - 1: Failed
+               - 0: Ok
+
+               RO
+
 What:          /sys/class/hwmon/hwmonX/pwmY
 Description:
                Pulse width modulation fan control.
index d794deb08bb729225a7464ce285c07cc8793932c..ca2b47320689c3482372768bdbb412c7ab9037fe 100644 (file)
@@ -52,16 +52,16 @@ examples:
   - |
     #include <dt-bindings/gpio/gpio.h>
     #include <dt-bindings/interrupt-controller/irq.h>
-    i2c0 {
+    i2c {
         #address-cells = <1>;
         #size-cells = <0>;
 
         pwmon@5a {
-                compatible = "adi,adm1177";
-                reg = <0x5a>;
-                shunt-resistor-micro-ohms = <50000>; /* 50 mOhm */
-                adi,shutdown-threshold-microamp = <1059000>; /* 1.059 A */
-                adi,vrange-high-enable;
+            compatible = "adi,adm1177";
+            reg = <0x5a>;
+            shunt-resistor-micro-ohms = <50000>; /* 50 mOhm */
+            adi,shutdown-threshold-microamp = <1059000>; /* 1.059 A */
+            adi,vrange-high-enable;
         };
     };
 ...
index 43b4f4f57b499cd3f155a4680bc2665f8aa448c8..4f8e11bd5142bb424167c8d012de16b3e62e21d2 100644 (file)
@@ -39,13 +39,13 @@ additionalProperties: false
 
 examples:
   - |
-    i2c0 {
+    i2c {
         #address-cells = <1>;
         #size-cells = <0>;
 
         adm1266@40 {
-                compatible = "adi,adm1266";
-                reg = <0x40>;
+            compatible = "adi,adm1266";
+            reg = <0x40>;
         };
     };
 ...
index f2f99afb3a3ba7798fef1bcbe56d942ebc455c07..0cf3ed6212a6a9cee6c57110b1ae03c01ba46bd2 100644 (file)
@@ -49,15 +49,15 @@ additionalProperties: false
 examples:
   - |
     fpga_axi: fpga-axi {
-            #address-cells = <0x2>;
-            #size-cells = <0x1>;
-
-            axi_fan_control: axi-fan-control@80000000 {
-                    compatible = "adi,axi-fan-control-1.00.a";
-                    reg = <0x0 0x80000000 0x10000>;
-                    clocks = <&clk 71>;
-                    interrupts = <0 110 0>;
-                    pulses-per-revolution = <2>;
-            };
+        #address-cells = <0x2>;
+        #size-cells = <0x1>;
+
+        axi_fan_control: axi-fan-control@80000000 {
+            compatible = "adi,axi-fan-control-1.00.a";
+            reg = <0x0 0x80000000 0x10000>;
+            clocks = <&clk 71>;
+            interrupts = <0 110 0>;
+            pulses-per-revolution = <2>;
+        };
     };
 ...
diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2945.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2945.yaml
new file mode 100644 (file)
index 0000000..5cb66e9
--- /dev/null
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,ltc2945.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices LTC2945 wide range i2c power monitor
+
+maintainers:
+  - Guenter Roeck <linux@roeck-us.net>
+
+description: |
+  Analog Devices LTC2945 wide range i2c power monitor over I2C.
+
+  https://www.analog.com/media/en/technical-documentation/data-sheets/LTC2945.pdf
+
+properties:
+  compatible:
+    enum:
+      - adi,ltc2945
+
+  reg:
+    maxItems: 1
+
+  shunt-resistor-micro-ohms:
+    description:
+      Shunt resistor value in micro-Ohms
+    default: 1000
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        power-monitor@6e {
+            compatible = "adi,ltc2945";
+            reg = <0x6e>;
+            /* 10 milli-Ohm shunt resistor */
+            shunt-resistor-micro-ohms = <10000>;
+        };
+    };
+...
index bf04151b63d2b4e474b2bc8c6fcca6e192eb09fe..152935334c76acb7f61aff60c82f29c757c051b3 100644 (file)
@@ -87,15 +87,15 @@ additionalProperties: false
 examples:
   - |
     spi {
-           #address-cells = <1>;
-           #size-cells = <0>;
-
-           ltc2947_spi: ltc2947@0 {
-                   compatible = "adi,ltc2947";
-                   reg = <0>;
-                   /* accumulation takes place always for energ1/charge1. */
-                   /* accumulation only on positive current for energy2/charge2. */
-                   adi,accumulator-ctl-pol = <0 1>;
-           };
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        ltc2947_spi: ltc2947@0 {
+            compatible = "adi,ltc2947";
+            reg = <0>;
+            /* accumulation takes place always for energ1/charge1. */
+            /* accumulation only on positive current for energy2/charge2. */
+            adi,accumulator-ctl-pol = <0 1>;
+        };
     };
 ...
index 64a8fcb7bc463d739dec79e5cb61d6ab8817a00d..dba74f400bc2914d3c75aaf29fd2feb481d8ab54 100644 (file)
@@ -55,26 +55,26 @@ additionalProperties: false
 
 examples:
   - |
-    i2c1 {
+    i2c {
         #address-cells = <1>;
         #size-cells = <0>;
 
-        ltc2992@6F {
-                #address-cells = <1>;
-                #size-cells = <0>;
+        ltc2992@6f {
+            #address-cells = <1>;
+            #size-cells = <0>;
 
-                compatible = "adi,ltc2992";
-                reg = <0x6F>;
+            compatible = "adi,ltc2992";
+            reg = <0x6f>;
 
-                channel@0 {
-                        reg = <0x0>;
-                        shunt-resistor-micro-ohms = <10000>;
-                };
+            channel@0 {
+                reg = <0x0>;
+                shunt-resistor-micro-ohms = <10000>;
+            };
 
-                channel@1 {
-                        reg = <0x1>;
-                        shunt-resistor-micro-ohms = <10000>;
-                };
+            channel@1 {
+                reg = <0x1>;
+                shunt-resistor-micro-ohms = <10000>;
+            };
         };
     };
 ...
index 7598b083979c73c35f769ddba6cdd5047855122a..353d81d89bf545323e0fca596a8d1fb3e5f5593a 100644 (file)
@@ -41,13 +41,13 @@ additionalProperties: false
 
 examples:
   - |
-    i2c0 {
+    i2c {
         #address-cells = <1>;
         #size-cells = <0>;
 
         sbrmi@3c {
-                compatible = "amd,sbrmi";
-                reg = <0x3c>;
+            compatible = "amd,sbrmi";
+            reg = <0x3c>;
         };
     };
 ...
index 446b09f1ce94a457684d766a65e0602f79bc03cb..75088244a274c5b95bf72df1265b873a3286cfa8 100644 (file)
@@ -42,13 +42,13 @@ additionalProperties: false
 
 examples:
   - |
-    i2c0 {
+    i2c {
         #address-cells = <1>;
         #size-cells = <0>;
 
         sbtsi@4c {
-                compatible = "amd,sbtsi";
-                reg = <0x4c>;
+            compatible = "amd,sbtsi";
+            reg = <0x4c>;
         };
     };
 ...
diff --git a/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml b/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml
new file mode 100644 (file)
index 0000000..4a52aac
--- /dev/null
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/hpe,gxp-fan-ctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: HPE GXP Fan Controller
+
+maintainers:
+  - Nick Hawkins <nick.hawkins@hpe.com>
+
+description: |
+  The HPE GXP fan controller controls the fans through an external CPLD
+  device that connects to the fans.
+
+properties:
+  compatible:
+    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
+
+required:
+  - compatible
+  - reg
+  - reg-names
+
+additionalProperties: false
+
+examples:
+  - |
+    fan-controller@1000c00 {
+      compatible = "hpe,gxp-fan-ctrl";
+      reg = <0x1000c00 0x200>, <0xd1000000 0xff>, <0x80200000 0x100000>;
+      reg-names = "base", "pl", "fn2";
+    };
index e1ccbd30e0ebd6c4de54cd029531f730f28d1867..c54b5986b36527ead39f2672183c99928e93c38c 100644 (file)
@@ -31,7 +31,7 @@ additionalProperties: false
 
 examples:
   - |
-      iio-hwmon {
-          compatible = "iio-hwmon";
-          io-channels = <&adc 1>, <&adc 2>;
-      };
+    iio-hwmon {
+        compatible = "iio-hwmon";
+        io-channels = <&adc 1>, <&adc 2>;
+    };
index e1719839faf03ed424737f0004299501773a61a3..7b9d48d6d6da721c981940497b04484feef6d124 100644 (file)
@@ -198,30 +198,30 @@ examples:
     };
   - |
     i2c {
-      #address-cells = <1>;
-      #size-cells = <0>;
-
-      sensor@4c {
-        compatible = "adi,adt7481";
-        reg = <0x4c>;
         #address-cells = <1>;
         #size-cells = <0>;
 
-        channel@0 {
-          reg = <0x0>;
-          label = "local";
-        };
-
-        channel@1 {
-          reg = <0x1>;
-          label = "front";
-          temperature-offset-millicelsius = <4000>;
-        };
-
-        channel@2 {
-          reg = <0x2>;
-          label = "back";
-          temperature-offset-millicelsius = <750>;
+        sensor@4c {
+            compatible = "adi,adt7481";
+            reg = <0x4c>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            channel@0 {
+                reg = <0x0>;
+                label = "local";
+            };
+
+            channel@1 {
+                reg = <0x1>;
+                label = "front";
+                temperature-offset-millicelsius = <4000>;
+            };
+
+            channel@2 {
+                reg = <0x2>;
+                label = "back";
+                temperature-offset-millicelsius = <750>;
+            };
         };
-      };
     };
index 6a1920712fb9f6fcb87307070c7ab33c2043102f..3d0146e20d3e10747c3049911b9419e9ccdab83d 100644 (file)
@@ -131,7 +131,7 @@ additionalProperties: false
 
 examples:
   - |
-    thermistor0 {
+    thermistor {
       compatible = "murata,ncp18wb473";
       io-channels = <&gpadc 0x06>;
       pullup-uv = <1800000>;
index 2f0620ecccc9771d24f9fd262ba9bfc79d643615..cd8dcd7970311b2a308b57f40e9527eecdeea23f 100644 (file)
@@ -123,23 +123,23 @@ examples:
             #size-cells = <0>;
 
             channel@0 { /* LTD */
-              reg = <0>;
+                reg = <0>;
             };
 
             channel@1 { /* RTD1 */
-              reg = <1>;
-              sensor-type = "voltage";
+                reg = <1>;
+                sensor-type = "voltage";
             };
 
             channel@2 { /* RTD2 */
-              reg = <2>;
-              sensor-type = "temperature";
-              temperature-mode = "thermal-diode";
+                reg = <2>;
+                sensor-type = "temperature";
+                temperature-mode = "thermal-diode";
             };
 
             channel@3 { /* RTD3 */
-              reg = <3>;
-              sensor-type = "temperature";
+                reg = <3>;
+                sensor-type = "temperature";
             };
         };
     };
diff --git a/Documentation/devicetree/bindings/hwmon/nxp,mc34vr500.yaml b/Documentation/devicetree/bindings/hwmon/nxp,mc34vr500.yaml
new file mode 100644 (file)
index 0000000..306f673
--- /dev/null
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/nxp,mc34vr500.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP MC34VR500 hwmon sensor
+
+maintainers:
+  - Mario Kicherer <dev@kicherer.org>
+
+properties:
+  compatible:
+    enum:
+      - nxp,mc34vr500
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      pmic@8 {
+        compatible = "nxp,mc34vr500";
+        reg = <0x08>;
+      };
+    };
index 1502b22c77ccb6711912de5ab967345b4c267bd5..fde5225ce012e870284f3a34b6f20ddf152fcacb 100644 (file)
@@ -77,15 +77,15 @@ additionalProperties: false
 examples:
   - |
     i2c {
-          #address-cells = <1>;
-          #size-cells = <0>;
-
-          tmp513@5c {
-              compatible = "ti,tmp513";
-              reg = <0x5C>;
-              shunt-resistor-micro-ohms = <330000>;
-              ti,bus-range-microvolt = <32000000>;
-              ti,pga-gain = <8>;
-              ti,nfactor = <0x1 0xF3 0x00>;
-          };
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        tmp513@5c {
+            compatible = "ti,tmp513";
+            reg = <0x5c>;
+            shunt-resistor-micro-ohms = <330000>;
+            ti,bus-range-microvolt = <32000000>;
+            ti,pga-gain = <8>;
+            ti,nfactor = <0x1 0xf3 0x00>;
+        };
     };
index 3bc8e73dfbf09f4d9d8f43152822dd9eb40341af..bce68a3269198dd752dac1069487c1eaa18acfc9 100644 (file)
@@ -40,12 +40,12 @@ additionalProperties: false
 examples:
   - |
     i2c {
-          #address-cells = <1>;
-          #size-cells = <0>;
-
-          tps23861@30 {
-              compatible = "ti,tps23861";
-              reg = <0x30>;
-              shunt-resistor-micro-ohms = <255000>;
-          };
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        tps23861@30 {
+            compatible = "ti,tps23861";
+            reg = <0x30>;
+            shunt-resistor-micro-ohms = <255000>;
+        };
     };
index 6d1dc1e2484a765d1df42eeacf0ccca00aa84adf..6f482a254a1dded65fa2e310fc7feb7db5c261c7 100644 (file)
@@ -143,6 +143,8 @@ properties:
           - infineon,slb9645tt
             # Infineon SLB9673 I2C TPM 2.0
           - infineon,slb9673
+            # Infineon TDA38640 Voltage Regulator
+          - infineon,tda38640
             # Infineon TLV493D-A1B6 I2C 3D Magnetic Sensor
           - infineon,tlv493d-a1b6
             # Infineon Multi-phase Digital VR Controller xdpe11280
index 482262ca117c5a44cca08740c067a46a1e9093f8..4e198c5eb68343901b036007054c2dc6d86a6dbf 100644 (file)
@@ -38,7 +38,7 @@ Sysfs entries
 -------------
 
 =============== ============================================
-temp1_input     Measured temperature in millidegrees Celcius
+temp1_input     Measured temperature in millidegrees Celsius
 humidity1_input Measured humidity in %H
 update_interval The minimum interval for polling the sensor,
                 in milliseconds. Writable. Must be at
index 637bdbc8fcad43ca915b9fb96450bb8e1a897850..7d0d015b1a528477c5a3492b0850831a731b2d7c 100644 (file)
@@ -5,12 +5,15 @@ Kernel driver aquacomputer-d5next
 
 Supported devices:
 
+* Aquacomputer Aquaero 5/6 fan controllers
 * Aquacomputer D5 Next watercooling pump
 * Aquacomputer Farbwerk RGB controller
 * Aquacomputer Farbwerk 360 RGB controller
 * Aquacomputer Octo fan controller
 * Aquacomputer Quadro fan controller
 * Aquacomputer High Flow Next sensor
+* Aquacomputer Aquastream Ultimate watercooling pump
+* Aquacomputer Poweradjust 3 fan controller
 
 Author: Aleksa Savic
 
@@ -20,6 +23,10 @@ Description
 This driver exposes hardware sensors of listed Aquacomputer devices, which
 communicate through proprietary USB HID protocols.
 
+The Aquaero devices expose eight physical, eight virtual and four calculated
+virtual temperature sensors, as well as two flow sensors. The fans expose their
+speed (in RPM), power, voltage and current.
+
 For the D5 Next pump, available sensors are pump and fan speed, power, voltage
 and current, as well as coolant temperature and eight virtual temp sensors. Also
 available through debugfs are the serial number, firmware version and power-on
@@ -48,6 +55,12 @@ The High Flow Next exposes +5V voltages, water quality, conductivity and flow re
 A temperature sensor can be connected to it, in which case it provides its reading
 and an estimation of the dissipated/absorbed power in the liquid cooling loop.
 
+The Aquastream Ultimate pump exposes coolant temp and an external temp sensor, along
+with speed, power, voltage and current of both the pump and optionally connected fan.
+It also exposes pressure and flow speed readings.
+
+The Poweradjust 3 controller exposes a single external temperature sensor.
+
 Depending on the device, not all sysfs and debugfs entries will be available.
 Writing to virtual temperature sensors is not currently supported.
 
index 6dcec845fbc738824f555b486465a37fd60cee36..f7bbe96f4bc8778849e40c0bc2c255b886d92651 100644 (file)
@@ -10,7 +10,7 @@ Authors:
 Description:
 ------------
 This driver implements support for ASPEED AST2400/2500 PWM and Fan Tacho
-controller. The PWM controller supports upto 8 PWM outputs. The Fan tacho
+controller. The PWM controller supports up to 8 PWM outputs. The Fan tacho
 controller supports up to 16 tachometer inputs.
 
 The driver provides the following sensor accesses in sysfs:
index 02f4ad314a1eb9e021cf906e8a7c191cf8610401..a4039f2f9ca4f454e4ef8f74ac63da2ee26695d2 100644 (file)
@@ -23,6 +23,7 @@ Supported boards:
  * ROG STRIX X570-I GAMING
  * ROG STRIX Z690-A GAMING WIFI D4
  * ROG ZENITH II EXTREME
+ * ROG ZENITH II EXTREME ALPHA
 
 Authors:
     - Eugene Shalygin <eugene.shalygin@gmail.com>
index 6a03edb551a871e3cbe96464d2b7c6f7b83a036b..c389bd21f4f2ffb959ea0567b4bbcfdd1b2dc56a 100644 (file)
@@ -40,7 +40,7 @@ This driver implements the sysfs interface for the Corsair PSUs with a HID proto
 interface of the HXi and RMi series.
 These power supplies provide access to a micro-controller with 2 attached
 temperature sensors, 1 fan rpm sensor, 4 sensors for volt levels, 4 sensors for
-power usage and 4 sensors for current levels and addtional non-sensor information
+power usage and 4 sensors for current levels and additional non-sensor information
 like uptimes.
 
 Sysfs entries
index 58a2483d8d0da999d31a5d92594379f5346d5e31..b3bfec36661dc4eb96085859058cf2fc663b2076 100644 (file)
@@ -22,6 +22,15 @@ enhancements. It can monitor up to 4 voltages, 16 temperatures and
 8 fans. It also contains an integrated watchdog which is currently
 implemented in this driver.
 
+The ``pwmX_auto_channels_temp`` attributes show which temperature sensor
+is currently driving which fan channel. This value might dynamically change
+during runtime depending on the temperature sensor selected by
+the fan control circuit.
+
+The 4 voltages require a board-specific multiplier, since the BMC can
+only measure voltages up to 3.3V and thus relies on voltage dividers.
+Consult your motherboard manual for details.
+
 To clear a temperature or fan alarm, execute the following command with the
 correct path to the alarm file::
 
index ffac392a7129944cb0f40cbdd081098e75aa2a9e..e9ab27940d02066f1de6b9e055bc423b638cf432 100644 (file)
@@ -31,7 +31,7 @@ Temperature Monitoring
 
 Temperatures are measured with 12-bit or 10-bit resolution and are scaled
 either internally or by the driver depending on the GSC version and firmware.
-The values returned by the driver reflect millidegree Celcius:
+The values returned by the driver reflect millidegree Celsius:
 
 tempX_input                Measured temperature.
 tempX_label                Name of temperature input.
@@ -41,8 +41,8 @@ PWM Output Control
 ------------------
 
 The GSC features 1 PWM output that operates in automatic mode where the
-PWM value will be scalled depending on 6 temperature boundaries.
-The tempeature boundaries are read-write and in millidegree Celcius and the
+PWM value will be scaled depending on 6 temperature boundaries.
+The tempeature boundaries are read-write and in millidegree Celsius and the
 read-only PWM values range from 0 (off) to 255 (full speed).
 Fan speed will be set to minimum (off) when the temperature sensor reads
 less than pwm1_auto_point1_temp and maximum when the temperature sensor
diff --git a/Documentation/hwmon/gxp-fan-ctrl.rst b/Documentation/hwmon/gxp-fan-ctrl.rst
new file mode 100644 (file)
index 0000000..ae3397e
--- /dev/null
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver gxp-fan-ctrl
+==========================
+
+Supported chips:
+
+  * HPE GXP SOC
+
+Author: Nick Hawkins <nick.hawkins@hpe.com>
+
+
+Description
+-----------
+
+gxp-fan-ctrl is a driver which provides fan control for the hpe gxp soc.
+The driver allows the gathering of fan status and the use of fan
+PWM control.
+
+
+Sysfs attributes
+----------------
+
+======================= ===========================================================
+pwm[0-7]               Fan 0 to 7 respective PWM value (0-255)
+fan[0-7]_fault         Fan 0 to 7 respective fault status: 1 fail, 0 ok
+fan[0-7]_enable         Fan 0 to 7 respective enabled status: 1 enabled, 0 disabled
+======================= ===========================================================
index f3276b3a381a14256f409cb41355df481f2b53e7..5451a6d4c874be9690f362b719e46495462ec214 100644 (file)
@@ -57,7 +57,7 @@ register/unregister functions::
 hwmon_device_register_with_groups registers a hardware monitoring device.
 The first parameter of this function is a pointer to the parent device.
 The name parameter is a pointer to the hwmon device name. The registration
-function wil create a name sysfs attribute pointing to this name.
+function will create a name sysfs attribute pointing to this name.
 The drvdata parameter is the pointer to the local driver data.
 hwmon_device_register_with_groups will attach this pointer to the newly
 allocated hwmon device. The pointer can be retrieved by the driver using
@@ -299,7 +299,7 @@ Parameters:
 
 Return value:
        The file mode for this attribute. Typically, this will be 0 (the
-       attribute will not be created), S_IRUGO, or 'S_IRUGO | S_IWUSR'.
+       attribute will not be created), 0444, or 0644.
 
 ::
 
@@ -360,7 +360,7 @@ functions is used.
 The header file linux/hwmon-sysfs.h provides a number of useful macros to
 declare and use hardware monitoring sysfs attributes.
 
-In many cases, you can use the exsting define DEVICE_ATTR or its variants
+In many cases, you can use the existing define DEVICE_ATTR or its variants
 DEVICE_ATTR_{RW,RO,WO} to declare such attributes. This is feasible if an
 attribute has no additional context. However, in many cases there will be
 additional information such as a sensor index which will need to be passed
index fe2cc6b73634c37b6e1402d9c8bace21b068ac6b..96ce91b7d9a85ca8731eebe42d9fb97308a266f4 100644 (file)
@@ -1,3 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
 =========================
 Linux Hardware Monitoring
 =========================
@@ -73,6 +75,7 @@ Hardware Monitoring Kernel Drivers
    g762
    gsc-hwmon
    gl518sm
+   gxp-fan-ctrl
    hih6130
    ibmaem
    ibm-cffps
@@ -144,6 +147,7 @@ Hardware Monitoring Kernel Drivers
    max6697
    max8688
    mc13783-adc
+   mc34vr500
    mcp3021
    menf21bmc
    mlxreg-fan
index 2d83f23bee9345f0cdafa00f483522c26f7877eb..5cef4f26500028a591006aa410d06e99c6c45cc7 100644 (file)
@@ -145,6 +145,22 @@ Supported chips:
 
     Datasheet: Not publicly available
 
+  * IT8792E/IT8795E
+
+    Prefix: 'it8792'
+
+    Addresses scanned: from Super I/O config space (8 I/O ports)
+
+    Datasheet: Not publicly available
+
+  * IT87952E
+
+    Prefix: 'it87952'
+
+    Addresses scanned: from Super I/O config space (8 I/O ports)
+
+    Datasheet: Not publicly available
+
   * SiS950   [clone of IT8705F]
 
     Prefix: 'it87'
@@ -162,7 +178,7 @@ Authors:
 Module Parameters
 -----------------
 
-* update_vbat: int
+* update_vbat bool
     0 if vbat should report power on value, 1 if vbat should be updated after
     each read. Default is 0. On some boards the battery voltage is provided
     by either the battery or the onboard power supply. Only the first reading
@@ -171,11 +187,31 @@ Module Parameters
     the chip so can be read at any time. Excessive reading may decrease
     battery life but no information is given in the datasheet.
 
-* fix_pwm_polarity int
+* fix_pwm_polarity bool
     Force PWM polarity to active high (DANGEROUS). Some chips are
     misconfigured by BIOS - PWM values would be inverted. This option tries
     to fix this. Please contact your BIOS manufacturer and ask him for fix.
 
+* force_id short, short
+
+  Force multiple chip ID to specified value, separated by ','.
+  For example "force_id=0x8689,0x8633".  A value of 0 is ignored
+  for that chip.
+  Note: A single force_id value (e.g. "force_id=0x8689") is used for
+  all chips, to only set the first chip use "force_id=0x8689,0".
+  Should only be used for testing.
+
+* ignore_resource_conflict bool
+
+  Similar to acpi_enforce_resources=lax, but only affects this driver.
+  ACPI resource conflicts are ignored if this parameter is provided and
+  set to 1.
+  Provided since there are reports that system-wide acpi_enfore_resources=lax
+  can result in boot failures on some systems.
+  Note: This is inherently risky since it means that both ACPI and this driver
+  may access the chip at the same time. This can result in race conditions and,
+  worst case, result in unexpected system reboots.
+
 
 Hardware Interfaces
 -------------------
@@ -193,8 +229,8 @@ Description
 
 This driver implements support for the IT8603E, IT8620E, IT8623E, IT8628E,
 IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F,
-IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, and
-SiS950 chips.
+IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E,
+IT8792E/IT8795E, IT87952E and SiS950 chips.
 
 These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
 joysticks and other miscellaneous stuff. For hardware monitoring, they
@@ -238,7 +274,8 @@ of the fan is not supported (value 0 of pwmX_enable).
 The IT8620E and IT8628E are custom designs, hardware monitoring part is similar
 to IT8728F. It only supports 16-bit fan mode. Both chips support up to 6 fans.
 
-The IT8790E supports up to 3 fans. 16-bit fan mode is always enabled.
+The IT8790E, IT8792E/IT8795E and IT87952E support up to 3 fans. 16-bit fan
+mode is always enabled.
 
 The IT8732F supports a closed-loop mode for fan control, but this is not
 currently implemented by the driver.
index b99a63965cfb78721caf3cba835cdb8366fc8847..edf24e5e1e118cad15264e391f7928b85f7914ce 100644 (file)
@@ -333,7 +333,7 @@ temp[N]_input               Measured temperature.
                        - On LTC3883, temp1 reports an external temperature,
                          and temp2 reports the chip temperature.
 
-temp[N]_min            Mimimum temperature.
+temp[N]_min            Minimum temperature.
 
                        LTC2972, LTC2974, LCT2977, LTM2980, LTC2978,
                        LTC2979, and LTM2987 only.
index 6a4eef8efbaf48fa4a9cae685a3b809ab0e11ae4..c8c63a053e4041fe79f9dde3b7f7922811df5bf8 100644 (file)
@@ -13,6 +13,14 @@ Supported chips:
 
     Datasheet: Not published
 
+  * Maxim MAX16600
+
+    Prefix: 'max16600'
+
+    Addresses scanned: -
+
+    Datasheet: Not published
+
   * Maxim MAX16601
 
     Prefix: 'max16601'
@@ -36,7 +44,8 @@ Description
 -----------
 
 This driver supports the MAX16508 VR13 Dual-Output Voltage Regulator
-as well as the MAX16601 VR13.HC Dual-Output Voltage Regulator chipsets.
+as well as the MAX16600, MAX16601, and MAX16602 VR13.HC Dual-Output
+Voltage Regulator chipsets.
 
 The driver is a client driver to the core PMBus driver.
 Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
index ffc5a7d8d33b700e63ae4079cab4c62de376792e..90ca224c446ae4d49f017b4dcaf14f3fe2ce994a 100644 (file)
@@ -73,7 +73,7 @@ Description
 This driver implements support for several MAX6697 compatible temperature sensor
 chips. The chips support one local temperature sensor plus four, six, or seven
 remote temperature sensors. Remote temperature sensors are diode-connected
-thermal transitors, except for MAX6698 which supports three diode-connected
+thermal transistors, except for MAX6698 which supports three diode-connected
 thermal transistors plus three thermistors in addition to the local temperature
 sensor.
 
diff --git a/Documentation/hwmon/mc34vr500.rst b/Documentation/hwmon/mc34vr500.rst
new file mode 100644 (file)
index 0000000..f82d872
--- /dev/null
@@ -0,0 +1,32 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver mc34vr500
+=======================
+
+Supported Chips:
+
+  * NXP MC34VR500
+
+    Prefix: 'mc34vr500'
+
+    Datasheet: https://www.nxp.com/docs/en/data-sheet/MC34VR500.pdf
+
+Author: Mario Kicherer <dev@kicherer.org>
+
+Description
+-----------
+
+This driver implements initial support for the NXP MC34VR500 PMIC. The MC34VR500
+monitors the temperature, input voltage and output currents and provides
+corresponding alarms. For the temperature, the chip can send interrupts if
+the temperature rises above one of the following values: 110°, 120°, 125° and
+130° Celsius. For the input voltage, an interrupt is sent when the voltage
+drops below 2.8V.
+
+Currently, this driver only implements the input voltage and temperature
+alarms. The interrupts are mapped as follows:
+
+<= 2.8V  -> in0_min_alarm
+>110°c   -> temp1_max_alarm
+>120°c   -> temp1_crit_alarm
+>130°c   -> temp1_emergency_alarm
index 978691d5956dbfef72bb89b7a6d485d213bfbed7..e7f3f67b38d4b04985026d9b1fb273fa2cac3548 100644 (file)
@@ -7,7 +7,7 @@ Supported chips:
 
          Prefix: 'menf21bmc_hwmon'
 
-         Adresses scanned: -
+         Addresses scanned: -
 
 Author: Andreas Werner <andreas.werner@men.de>
 
index 39c588ec5c5060a613f35b7f0f7db1bbb739cfd3..566a8d5bde08d8cbb7fa90f3f08f5908b0913613 100644 (file)
@@ -3,18 +3,21 @@
 Kernel driver oxp-sensors
 =========================
 
-Author:
+Authors:
+    - Derek John Clark <derekjohn.clark@gmail.com>
     - Joaquín Ignacio Aramendía <samsagax@gmail.com>
 
 Description:
 ------------
 
-One X Player devices from One Netbook provide fan readings and fan control
-through its Embedded Controller.
+Handheld devices from One Netbook and Aya Neo provide fan readings and fan
+control through their embedded controllers.
 
-Currently only supports AMD boards from the One X Player and AOK ZOE lineup.
-Intel boards could be supported if we could figure out the EC registers and
-values to write to since the EC layout and model is different.
+Currently only supports AMD boards from One X Player, AOK ZOE, and some Aya
+Neo devices. One X Player Intel boards could be supported if we could figure
+out the EC registers and values to write to since the EC layout and model is
+different. Aya Neo devices preceding the AIR may not be supportable as the EC
+model is different and do not appear to have manual control capabilities.
 
 Supported devices
 -----------------
@@ -22,6 +25,8 @@ Supported devices
 Currently the driver supports the following handhelds:
 
  - AOK ZOE A1
+ - Aya Neo AIR
+ - Aya Neo AIR Pro
  - OneXPlayer AMD
  - OneXPlayer mini AMD
  - OneXPlayer mini AMD PRO
index 84c5a4e40c409c3632595add8ad50dadbbe2dc41..cff93adf6e42e5d3cbbe2da1503b886bd058d819 100644 (file)
@@ -174,7 +174,7 @@ Read byte from page <page>, register <reg>.
   int (*read_word_data)(struct i2c_client *client, int page, int phase,
                         int reg);
 
-Read word from page <page>, phase <pase>, register <reg>. If the chip does not
+Read word from page <page>, phase <phase>, register <reg>. If the chip does not
 support multiple phases, the phase parameter can be ignored. If the chip
 supports multiple phases, a phase value of 0xff indicates all phases.
 
index c318e5582ead7ebf624cdf413f035a6250f70c25..daf21e763425563deae36b435cf5c8ac06a3a840 100644 (file)
@@ -37,7 +37,7 @@ Sysfs entries
 -------------
 
 =============== ============================================
-temp1_input     Measured temperature in millidegrees Celcius
+temp1_input     Measured temperature in millidegrees Celsius
 humidity1_input Measured humidity in %H
 update_interval The minimum interval for polling the sensor,
                 in milliseconds. Writable. Must be at least
index a0e27f62b57b2159325adc26f0b7e31b77cad6ed..481e69d8bf393725e47c5fde839870c5b200982a 100644 (file)
@@ -180,7 +180,7 @@ in9_crit_alarm              AIN1 critical alarm
 in10_crit_alarm                AIN2 critical alarm
 
 temp1_input            Chip temperature
-temp1_min              Mimimum chip temperature
+temp1_min              Minimum chip temperature
 temp1_max              Maximum chip temperature
 temp1_crit             Critical chip temperature
 temp1_crit_alarm       Temperature critical alarm
index 7f7ce7f7871b21c119a6f43bb3cc77af4fc081c1..0c31f78fe58bd54c35b36a745d1212f086043df7 100644 (file)
@@ -39,7 +39,7 @@ output voltage as a positive or negative offset in the interval 50mV to 400mV
 in 50mV steps. This means that the absolute values of the limits will change
 when the commanded output voltage changes. Also, care should be taken when
 writing to those limits since in the worst case the commanded output voltage
-could change at the same time as the limit is written to, wich will lead to
+could change at the same time as the limit is written to, which will lead to
 unpredictable results.
 
 
index d953ee763c96eec4e65df39a842e21fef9ba0217..6482c4f137dcb4052a4286dc2094c2274f5a89b1 100644 (file)
@@ -126,7 +126,7 @@ increase the chances of your change being accepted.
 * Use devm_hwmon_device_register_with_info() or, if your driver needs a remove
   function, hwmon_device_register_with_info() to register your driver with the
   hwmon subsystem. Try using devm_add_action() instead of a remove function if
-  possible. Do not use hwmon_device_register().
+  possible. Do not use any of the deprecated registration functions.
 
 * Your driver should be buildable as module. If not, please be prepared to
   explain why it has to be built into the kernel.
index 8c861c8151ac56bf72f251674466f7fcd50e0d6d..7fc99f75b3b1660c3cf0534861e61240c065068f 100644 (file)
@@ -27,7 +27,7 @@ Versatile Express platform (http://www.arm.com/versatileexpress/) is a
 reference & prototyping system for ARM Ltd. processors. It can be set up
 from a wide range of boards, each of them containing (apart of the main
 chip/FPGA) a number of microcontrollers responsible for platform
-configuration and control. Theses microcontrollers can also monitor the
+configuration and control. These microcontrollers can also monitor the
 board and its environment by a number of internal and external sensors,
 providing information about power lines voltages and currents, board
 temperature and power usage. Some of them also calculate consumed energy
index 7ab9ddebcf7920a19f633cdd2f19a37e5a3e9e01..8c52a9207bcb5f10bb409db8ee3ec4b6410634d5 100644 (file)
@@ -58,7 +58,7 @@ representable value is around 2600 RPM.
 
 Voltage sensors (also known as IN sensors) report their values in volts.
 An alarm is triggered if the voltage has crossed a programmable minimum
-or maximum limit. Voltages are internally scalled, so each voltage channel
+or maximum limit. Voltages are internally scaled, so each voltage channel
 has a different resolution and range.
 
 If an alarm triggers, it will remain triggered until the hardware register
index a02437913f010d9cf58f1f75449d08f2aad39b41..4dacb54557e1ffb427497202b82f42bc6e2e71ba 100644 (file)
@@ -2234,13 +2234,16 @@ ARM/HPE GXP ARCHITECTURE
 M:     Jean-Marie Verdun <verdun@hpe.com>
 M:     Nick Hawkins <nick.hawkins@hpe.com>
 S:     Maintained
+F:     Documentation/hwmon/gxp-fan-ctrl.rst
 F:     Documentation/devicetree/bindings/arm/hpe,gxp.yaml
+F:     Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml
 F:     Documentation/devicetree/bindings/spi/hpe,gxp-spifi.yaml
 F:     Documentation/devicetree/bindings/timer/hpe,gxp-timer.yaml
 F:     arch/arm/boot/dts/hpe-bmc*
 F:     arch/arm/boot/dts/hpe-gxp*
 F:     arch/arm/mach-hpe/
 F:     drivers/clocksource/timer-gxp.c
+F:     drivers/hwmon/gxp-fan-ctrl.c
 F:     drivers/spi/spi-gxp.c
 F:     drivers/watchdog/gxp-wdt.c
 
@@ -14046,6 +14049,7 @@ M:      Saravanan Sekar <sravanhome@gmail.com>
 S:     Maintained
 F:     Documentation/devicetree/bindings/mfd/mps,mp2629.yaml
 F:     Documentation/devicetree/bindings/regulator/mps,mp*.yaml
+F:     drivers/hwmon/pmbus/mpq7932.c
 F:     drivers/iio/adc/mp2629_adc.c
 F:     drivers/mfd/mp2629.c
 F:     drivers/power/supply/mp2629_charger.c
@@ -15481,6 +15485,7 @@ F:      drivers/mtd/nand/onenand/
 F:     include/linux/mtd/onenand*.h
 
 ONEXPLAYER FAN DRIVER
+M:     Derek John Clark <derekjohn.clark@gmail.com>
 M:     Joaquín Ignacio Aramendía <samsagax@gmail.com>
 L:     linux-hwmon@vger.kernel.org
 S:     Maintained
index 497cbfb460e5df928e74681bd0c05cfeca34cdb1..0c4cdb21ae1384e6cbcb11fbf1bb879bc0f5671f 100644 (file)
@@ -714,6 +714,15 @@ config SENSORS_GPIO_FAN
          This driver can also be built as a module. If so, the module
          will be called gpio-fan.
 
+config SENSORS_GXP_FAN_CTRL
+       tristate "HPE GXP fan controller"
+       depends on ARCH_HPE_GXP || COMPILE_TEST
+       help
+         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.
+
 config SENSORS_HIH6130
        tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor"
        depends on I2C
@@ -1166,6 +1175,13 @@ config SENSORS_MAX31790
          This driver can also be built as a module. If so, the module
          will be called max31790.
 
+config SENSORS_MC34VR500
+       tristate "NXP MC34VR500 hardware monitoring driver"
+       depends on I2C
+       help
+         If you say yes here you get support for the temperature and input
+         voltage sensors of the NXP MC34VR500.
+
 config SENSORS_MCP3021
        tristate "Microchip MCP3021 and compatibles"
        depends on I2C
@@ -1516,7 +1532,7 @@ config SENSORS_NCT6775_CORE
 config SENSORS_NCT6775
        tristate "Platform driver for Nuvoton NCT6775F and compatibles"
        depends on !PPC
-       depends on ACPI_WMI || ACPI_WMI=n
+       depends on ACPI || ACPI=n
        select HWMON_VID
        select SENSORS_NCT6775_CORE
        help
index 4f71d9807dae6fc38905d37e17b1c4f27ae47424..88712b5031c87c26e8b1a3c56e294bd0e7c4bb00 100644 (file)
@@ -83,6 +83,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)  += gl520sm.o
 obj-$(CONFIG_SENSORS_GSC)      += gsc-hwmon.o
 obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
+obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o
 obj-$(CONFIG_SENSORS_HIH6130)  += hih6130.o
 obj-$(CONFIG_SENSORS_ULTRA45)  += ultra45_env.o
 obj-$(CONFIG_SENSORS_I5500)    += i5500_temp.o
@@ -149,6 +150,7 @@ obj-$(CONFIG_SENSORS_MAX6650)       += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
 obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
+obj-$(CONFIG_SENSORS_MC34VR500)        += mc34vr500.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
 obj-$(CONFIG_SENSORS_TC654)    += tc654.o
 obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o
index d76f3441ecf1a5e9eed6ba92797b63645ed58f06..9babd69d54a35c8151638987831e531663a30c89 100644 (file)
@@ -79,7 +79,6 @@ struct aht10_data {
 
 /**
  * aht10_init() - Initialize an AHT10 chip
- * @client: the i2c client associated with the AHT10
  * @data: the data associated with this AHT10 chip
  * Return: 0 if succesfull, 1 if not
  */
@@ -124,7 +123,7 @@ static int aht10_polltime_expired(struct aht10_data *data)
 
 /**
  * aht10_read_values() - read and parse the raw data from the AHT10
- * @aht10_data: the struct aht10_data to use for the lock
+ * @data: the struct aht10_data to use for the lock
  * Return: 0 if succesfull, 1 if not
  */
 static int aht10_read_values(struct aht10_data *data)
index 9cc10080160b00d53490e9538558f39ce4ed43a5..12682a610ce7944a32b497f8d8b31e6c6dbf5d53 100644 (file)
@@ -1,10 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo,
- * Quadro, High Flow Next)
+ * Quadro, High Flow Next, Aquaero, Aquastream Ultimate)
  *
  * Aquacomputer devices send HID reports (with ID 0x01) every second to report
- * sensor values.
+ * sensor values, except for devices that communicate through the
+ * legacy way (currently, Poweradjust 3).
  *
  * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
  * Copyright 2022 Jack Doan <me@jackdoan.com>
 #include <asm/unaligned.h>
 
 #define USB_VENDOR_ID_AQUACOMPUTER     0x0c70
+#define USB_PRODUCT_ID_AQUAERO         0xf001
 #define USB_PRODUCT_ID_FARBWERK                0xf00a
 #define USB_PRODUCT_ID_QUADRO          0xf00d
 #define USB_PRODUCT_ID_D5NEXT          0xf00e
 #define USB_PRODUCT_ID_FARBWERK360     0xf010
 #define USB_PRODUCT_ID_OCTO            0xf011
 #define USB_PRODUCT_ID_HIGHFLOWNEXT    0xf012
+#define USB_PRODUCT_ID_AQUASTREAMULT   0xf00b
+#define USB_PRODUCT_ID_POWERADJUST3    0xf0bd
 
-enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext };
+enum kinds {
+       d5next, farbwerk, farbwerk360, octo, quadro,
+       highflownext, aquaero, poweradjust3, aquastreamult
+};
 
 static const char *const aqc_device_names[] = {
        [d5next] = "d5next",
@@ -36,16 +43,17 @@ static const char *const aqc_device_names[] = {
        [farbwerk360] = "farbwerk360",
        [octo] = "octo",
        [quadro] = "quadro",
-       [highflownext] = "highflownext"
+       [highflownext] = "highflownext",
+       [aquaero] = "aquaero",
+       [aquastreamult] = "aquastreamultimate",
+       [poweradjust3] = "poweradjust3"
 };
 
 #define DRIVER_NAME                    "aquacomputer_d5next"
 
 #define STATUS_REPORT_ID               0x01
 #define STATUS_UPDATE_INTERVAL         (2 * HZ)        /* In seconds */
-#define SERIAL_FIRST_PART              3
-#define SERIAL_SECOND_PART             5
-#define FIRMWARE_VERSION               13
+#define SERIAL_PART_OFFSET             2
 
 #define CTRL_REPORT_ID                 0x03
 
@@ -59,8 +67,14 @@ static u8 secondary_ctrl_report[] = {
        0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
 };
 
-/* Sensor sizes and offsets for all Aquacomputer devices */
-#define AQC_TEMP_SENSOR_SIZE           0x02
+/* Report IDs for legacy devices */
+#define POWERADJUST3_STATUS_REPORT_ID  0x03
+
+/* Info, sensor sizes and offsets for most Aquacomputer devices */
+#define AQC_SERIAL_START               0x3
+#define AQC_FIRMWARE_VERSION           0xD
+
+#define AQC_SENSOR_SIZE                        0x02
 #define AQC_TEMP_SENSOR_DISCONNECTED   0x7FFF
 #define AQC_FAN_PERCENT_OFFSET         0x00
 #define AQC_FAN_VOLTAGE_OFFSET         0x02
@@ -68,6 +82,26 @@ static u8 secondary_ctrl_report[] = {
 #define AQC_FAN_POWER_OFFSET           0x06
 #define AQC_FAN_SPEED_OFFSET           0x08
 
+/* Specs of the Aquaero fan controllers */
+#define AQUAERO_SERIAL_START                   0x07
+#define AQUAERO_FIRMWARE_VERSION               0x0B
+#define AQUAERO_NUM_FANS                       4
+#define AQUAERO_NUM_SENSORS                    8
+#define AQUAERO_NUM_VIRTUAL_SENSORS            8
+#define AQUAERO_NUM_CALC_VIRTUAL_SENSORS       4
+#define AQUAERO_NUM_FLOW_SENSORS               2
+
+/* Sensor report offsets for Aquaero fan controllers */
+#define AQUAERO_SENSOR_START                   0x65
+#define AQUAERO_VIRTUAL_SENSOR_START           0x85
+#define AQUAERO_CALC_VIRTUAL_SENSOR_START      0x95
+#define AQUAERO_FLOW_SENSORS_START             0xF9
+#define AQUAERO_FAN_VOLTAGE_OFFSET             0x04
+#define AQUAERO_FAN_CURRENT_OFFSET             0x06
+#define AQUAERO_FAN_POWER_OFFSET               0x08
+#define AQUAERO_FAN_SPEED_OFFSET               0x00
+static u16 aquaero_sensor_fan_offsets[] = { 0x167, 0x173, 0x17f, 0x18B };
+
 /* Specs of the D5 Next pump */
 #define D5NEXT_NUM_FANS                        2
 #define D5NEXT_NUM_SENSORS             1
@@ -82,12 +116,32 @@ static u8 secondary_ctrl_report[] = {
 #define D5NEXT_5V_VOLTAGE              0x39
 #define D5NEXT_12V_VOLTAGE             0x37
 #define D5NEXT_VIRTUAL_SENSORS_START   0x3f
-static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
+static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
 
 /* Control report offsets for the D5 Next pump */
 #define D5NEXT_TEMP_CTRL_OFFSET                0x2D    /* Temperature sensor offsets location */
 static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */
 
+/* Specs of the Aquastream Ultimate pump */
+/* Pump does not follow the standard structure, so only consider the fan */
+#define AQUASTREAMULT_NUM_FANS         1
+#define AQUASTREAMULT_NUM_SENSORS      2
+
+/* Sensor report offsets for the Aquastream Ultimate pump */
+#define AQUASTREAMULT_SENSOR_START             0x2D
+#define AQUASTREAMULT_PUMP_OFFSET              0x51
+#define AQUASTREAMULT_PUMP_VOLTAGE             0x3D
+#define AQUASTREAMULT_PUMP_CURRENT             0x53
+#define AQUASTREAMULT_PUMP_POWER               0x55
+#define AQUASTREAMULT_FAN_OFFSET               0x41
+#define AQUASTREAMULT_PRESSURE_OFFSET          0x57
+#define AQUASTREAMULT_FLOW_SENSOR_OFFSET       0x37
+#define AQUASTREAMULT_FAN_VOLTAGE_OFFSET       0x02
+#define AQUASTREAMULT_FAN_CURRENT_OFFSET       0x00
+#define AQUASTREAMULT_FAN_POWER_OFFSET         0x04
+#define AQUASTREAMULT_FAN_SPEED_OFFSET         0x06
+static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET };
+
 /* Spec and sensor report offset for the Farbwerk RGB controller */
 #define FARBWERK_NUM_SENSORS           4
 #define FARBWERK_SENSOR_START          0x2f
@@ -114,7 +168,7 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };      /* Pump and fan speed (fr
 #define OCTO_POWER_CYCLES              0x18
 #define OCTO_SENSOR_START              0x3D
 #define OCTO_VIRTUAL_SENSORS_START     0x45
-static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
+static u16 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
 
 /* Control report offsets for the Octo */
 #define OCTO_TEMP_CTRL_OFFSET          0xA
@@ -125,6 +179,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0
 #define QUADRO_NUM_FANS                        4
 #define QUADRO_NUM_SENSORS             4
 #define QUADRO_NUM_VIRTUAL_SENSORS     16
+#define QUADRO_NUM_FLOW_SENSORS                1
 #define QUADRO_CTRL_REPORT_SIZE                0x3c1
 
 /* Sensor report offsets for the Quadro */
@@ -132,7 +187,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0
 #define QUADRO_SENSOR_START            0x34
 #define QUADRO_VIRTUAL_SENSORS_START   0x3c
 #define QUADRO_FLOW_SENSOR_OFFSET      0x6e
-static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
+static u16 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
 
 /* Control report offsets for the Quadro */
 #define QUADRO_TEMP_CTRL_OFFSET                0xA
@@ -141,6 +196,7 @@ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed
 
 /* Specs of High Flow Next flow sensor */
 #define HIGHFLOWNEXT_NUM_SENSORS       2
+#define HIGHFLOWNEXT_NUM_FLOW_SENSORS  1
 
 /* Sensor report offsets for the High Flow Next */
 #define HIGHFLOWNEXT_SENSOR_START      85
@@ -151,6 +207,13 @@ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed
 #define HIGHFLOWNEXT_5V_VOLTAGE                97
 #define HIGHFLOWNEXT_5V_VOLTAGE_USB    99
 
+/* Specs of the Poweradjust 3 */
+#define POWERADJUST3_NUM_SENSORS       1
+#define POWERADJUST3_SENSOR_REPORT_SIZE        0x32
+
+/* Sensor report offsets for the Poweradjust 3 */
+#define POWERADJUST3_SENSOR_START      0x03
+
 /* Labels for D5 Next */
 static const char *const label_d5next_temp[] = {
        "Coolant temp"
@@ -178,12 +241,16 @@ static const char *const label_d5next_current[] = {
        "Fan current"
 };
 
-/* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */
+/* Labels for Aquaero, Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */
 static const char *const label_temp_sensors[] = {
        "Sensor 1",
        "Sensor 2",
        "Sensor 3",
-       "Sensor 4"
+       "Sensor 4",
+       "Sensor 5",
+       "Sensor 6",
+       "Sensor 7",
+       "Sensor 8"
 };
 
 static const char *const label_virtual_temp_sensors[] = {
@@ -205,6 +272,13 @@ static const char *const label_virtual_temp_sensors[] = {
        "Virtual sensor 16",
 };
 
+static const char *const label_aquaero_calc_temp_sensors[] = {
+       "Calc. virtual sensor 1",
+       "Calc. virtual sensor 2",
+       "Calc. virtual sensor 3",
+       "Calc. virtual sensor 4"
+};
+
 /* Labels for Octo and Quadro (except speed) */
 static const char *const label_fan_speed[] = {
        "Fan 1 speed",
@@ -259,6 +333,16 @@ static const char *const label_quadro_speeds[] = {
        "Flow speed [dL/h]"
 };
 
+/* Labels for Aquaero fan speeds */
+static const char *const label_aquaero_speeds[] = {
+       "Fan 1 speed",
+       "Fan 2 speed",
+       "Fan 3 speed",
+       "Fan 4 speed",
+       "Flow sensor 1 [dL/h]",
+       "Flow sensor 2 [dL/h]"
+};
+
 /* Labels for High Flow Next */
 static const char *const label_highflownext_temp_sensors[] = {
        "Coolant temp",
@@ -280,6 +364,70 @@ static const char *const label_highflownext_voltage[] = {
        "+5V USB voltage"
 };
 
+/* Labels for Aquastream Ultimate */
+static const char *const label_aquastreamult_temp[] = {
+       "Coolant temp",
+       "External temp"
+};
+
+static const char *const label_aquastreamult_speeds[] = {
+       "Fan speed",
+       "Pump speed",
+       "Pressure [mbar]",
+       "Flow speed [dL/h]"
+};
+
+static const char *const label_aquastreamult_power[] = {
+       "Fan power",
+       "Pump power"
+};
+
+static const char *const label_aquastreamult_voltages[] = {
+       "Fan voltage",
+       "Pump voltage"
+};
+
+static const char *const label_aquastreamult_current[] = {
+       "Fan current",
+       "Pump current"
+};
+
+/* Labels for Poweradjust 3 */
+static const char *const label_poweradjust3_temp_sensors[] = {
+       "External sensor"
+};
+
+struct aqc_fan_structure_offsets {
+       u8 voltage;
+       u8 curr;
+       u8 power;
+       u8 speed;
+};
+
+/* Fan structure offsets for Aquaero */
+static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = {
+       .voltage = AQUAERO_FAN_VOLTAGE_OFFSET,
+       .curr = AQUAERO_FAN_CURRENT_OFFSET,
+       .power = AQUAERO_FAN_POWER_OFFSET,
+       .speed = AQUAERO_FAN_SPEED_OFFSET
+};
+
+/* Fan structure offsets for Aquastream Ultimate */
+static struct aqc_fan_structure_offsets aqc_aquastreamult_fan_structure = {
+       .voltage = AQUASTREAMULT_FAN_VOLTAGE_OFFSET,
+       .curr = AQUASTREAMULT_FAN_CURRENT_OFFSET,
+       .power = AQUASTREAMULT_FAN_POWER_OFFSET,
+       .speed = AQUASTREAMULT_FAN_SPEED_OFFSET
+};
+
+/* Fan structure offsets for all devices except those above */
+static struct aqc_fan_structure_offsets aqc_general_fan_structure = {
+       .voltage = AQC_FAN_VOLTAGE_OFFSET,
+       .curr = AQC_FAN_CURRENT_OFFSET,
+       .power = AQC_FAN_POWER_OFFSET,
+       .speed = AQC_FAN_SPEED_OFFSET
+};
+
 struct aqc_data {
        struct hid_device *hdev;
        struct device *hwmon_dev;
@@ -288,6 +436,8 @@ struct aqc_data {
        enum kinds kind;
        const char *name;
 
+       int status_report_id;   /* Used for legacy devices, report is stored in buffer */
+
        int buffer_size;
        u8 *buffer;
        int checksum_start;
@@ -295,26 +445,32 @@ struct aqc_data {
        int checksum_offset;
 
        int num_fans;
-       u8 *fan_sensor_offsets;
+       u16 *fan_sensor_offsets;
        u16 *fan_ctrl_offsets;
        int num_temp_sensors;
        int temp_sensor_start_offset;
        int num_virtual_temp_sensors;
        int virtual_temp_sensor_start_offset;
+       int num_calc_virt_temp_sensors;
+       int calc_virt_temp_sensor_start_offset;
        u16 temp_ctrl_offset;
        u16 power_cycle_count_offset;
-       u8 flow_sensor_offset;
+       int num_flow_sensors;
+       u8 flow_sensors_start_offset;
        u8 flow_pulses_ctrl_offset;
+       struct aqc_fan_structure_offsets *fan_structure;
 
        /* General info, same across all devices */
+       u8 serial_number_start_offset;
        u32 serial_number[2];
+       u8 firmware_version_offset;
        u16 firmware_version;
 
        /* How many times the device was powered on, if available */
        u32 power_cycles;
 
        /* Sensor values */
-       s32 temp_input[20];     /* Max 4 physical and 16 virtual */
+       s32 temp_input[20];     /* Max 4 physical and 16 virtual or 8 physical and 12 virtual */
        u16 speed_input[8];
        u32 power_input[8];
        u16 voltage_input[8];
@@ -323,6 +479,7 @@ struct aqc_data {
        /* Label values */
        const char *const *temp_label;
        const char *const *virtual_temp_label;
+       const char *const *calc_virt_temp_label;        /* For Aquaero */
        const char *const *speed_label;
        const char *const *power_label;
        const char *const *voltage_label;
@@ -443,7 +600,9 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                        }
                }
 
-               if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors)
+               if (channel <
+                   priv->num_temp_sensors + priv->num_virtual_temp_sensors +
+                   priv->num_calc_virt_temp_sensors)
                        switch (attr) {
                        case hwmon_temp_label:
                        case hwmon_temp_input:
@@ -467,6 +626,14 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                case hwmon_fan_input:
                case hwmon_fan_label:
                        switch (priv->kind) {
+                       case aquastreamult:
+                               /*
+                                * Special case to support pump RPM, fan RPM,
+                                * pressure and flow sensor
+                                */
+                               if (channel < 4)
+                                       return 0444;
+                               break;
                        case highflownext:
                                /* Special case to support flow sensor, water quality
                                 * and conductivity
@@ -474,9 +641,10 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                                if (channel < 3)
                                        return 0444;
                                break;
+                       case aquaero:
                        case quadro:
-                               /* Special case to support flow sensor */
-                               if (channel < priv->num_fans + 1)
+                               /* Special case to support flow sensors */
+                               if (channel < priv->num_fans + priv->num_flow_sensors)
                                        return 0444;
                                break;
                        default:
@@ -496,6 +664,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                break;
        case hwmon_power:
                switch (priv->kind) {
+               case aquastreamult:
+                       /* Special case to support pump and fan power */
+                       if (channel < 2)
+                               return 0444;
+                       break;
                case highflownext:
                        /* Special case to support one power sensor */
                        if (channel == 0)
@@ -508,8 +681,17 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                }
                break;
        case hwmon_curr:
-               if (channel < priv->num_fans)
-                       return 0444;
+               switch (priv->kind) {
+               case aquastreamult:
+                       /* Special case to support pump and fan current */
+                       if (channel < 2)
+                               return 0444;
+                       break;
+               default:
+                       if (channel < priv->num_fans)
+                               return 0444;
+                       break;
+               }
                break;
        case hwmon_in:
                switch (priv->kind) {
@@ -518,6 +700,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                        if (channel < priv->num_fans + 2)
                                return 0444;
                        break;
+               case aquastreamult:
                case highflownext:
                        /* Special case to support two voltage sensors */
                        if (channel < 2)
@@ -536,14 +719,49 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
        return 0;
 }
 
+/* Read device sensors by manually requesting the sensor report (legacy way) */
+static int aqc_legacy_read(struct aqc_data *priv)
+{
+       int ret, i, sensor_value;
+
+       mutex_lock(&priv->mutex);
+
+       memset(priv->buffer, 0x00, priv->buffer_size);
+       ret = hid_hw_raw_request(priv->hdev, priv->status_report_id, priv->buffer,
+                                priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+       if (ret < 0)
+               goto unlock_and_return;
+
+       /* Temperature sensor readings */
+       for (i = 0; i < priv->num_temp_sensors; i++) {
+               sensor_value = get_unaligned_le16(priv->buffer + priv->temp_sensor_start_offset +
+                                                 i * AQC_SENSOR_SIZE);
+               priv->temp_input[i] = sensor_value * 10;
+       }
+
+       priv->updated = jiffies;
+
+unlock_and_return:
+       mutex_unlock(&priv->mutex);
+       return ret;
+}
+
 static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
                    int channel, long *val)
 {
        int ret;
        struct aqc_data *priv = dev_get_drvdata(dev);
 
-       if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL))
-               return -ENODATA;
+       if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) {
+               if (priv->status_report_id != 0) {
+                       /* Legacy devices require manual reads */
+                       ret = aqc_legacy_read(priv);
+                       if (ret < 0)
+                               return -ENODATA;
+               } else {
+                       return -ENODATA;
+               }
+       }
 
        switch (type) {
        case hwmon_temp:
@@ -557,7 +775,7 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
                case hwmon_temp_offset:
                        ret =
                            aqc_get_ctrl_val(priv, priv->temp_ctrl_offset +
-                                            channel * AQC_TEMP_SENSOR_SIZE, val);
+                                            channel * AQC_SENSOR_SIZE, val);
                        if (ret < 0)
                                return ret;
 
@@ -611,12 +829,20 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32
 {
        struct aqc_data *priv = dev_get_drvdata(dev);
 
+       /* Number of sensors that are not calculated */
+       int num_non_calc_sensors = priv->num_temp_sensors + priv->num_virtual_temp_sensors;
+
        switch (type) {
        case hwmon_temp:
-               if (channel < priv->num_temp_sensors)
+               if (channel < priv->num_temp_sensors) {
                        *str = priv->temp_label[channel];
-               else
-                       *str = priv->virtual_temp_label[channel - priv->num_temp_sensors];
+               } else {
+                       if (priv->kind == aquaero && channel >= num_non_calc_sensors)
+                               *str =
+                                   priv->calc_virt_temp_label[channel - num_non_calc_sensors];
+                       else
+                               *str = priv->virtual_temp_label[channel - priv->num_temp_sensors];
+               }
                break;
        case hwmon_fan:
                *str = priv->speed_label[channel];
@@ -651,7 +877,7 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
                        val = clamp_val(val, -15000, 15000) / 10;
                        ret =
                            aqc_set_ctrl_val(priv, priv->temp_ctrl_offset +
-                                            channel * AQC_TEMP_SENSOR_SIZE, val);
+                                            channel * AQC_SENSOR_SIZE, val);
                        if (ret < 0)
                                return ret;
                        break;
@@ -789,15 +1015,16 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
        priv = hid_get_drvdata(hdev);
 
        /* Info provided with every report */
-       priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART);
-       priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
-       priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
+       priv->serial_number[0] = get_unaligned_be16(data + priv->serial_number_start_offset);
+       priv->serial_number[1] = get_unaligned_be16(data + priv->serial_number_start_offset +
+                                                   SERIAL_PART_OFFSET);
+       priv->firmware_version = get_unaligned_be16(data + priv->firmware_version_offset);
 
        /* Physical temperature sensor readings */
        for (i = 0; i < priv->num_temp_sensors; i++) {
                sensor_value = get_unaligned_be16(data +
                                                  priv->temp_sensor_start_offset +
-                                                 i * AQC_TEMP_SENSOR_SIZE);
+                                                 i * AQC_SENSOR_SIZE);
                if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
                        priv->temp_input[i] = -ENODATA;
                else
@@ -808,7 +1035,7 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
        for (j = 0; j < priv->num_virtual_temp_sensors; j++) {
                sensor_value = get_unaligned_be16(data +
                                                  priv->virtual_temp_sensor_start_offset +
-                                                 j * AQC_TEMP_SENSOR_SIZE);
+                                                 j * AQC_SENSOR_SIZE);
                if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
                        priv->temp_input[i] = -ENODATA;
                else
@@ -819,15 +1046,24 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
        /* Fan speed and related readings */
        for (i = 0; i < priv->num_fans; i++) {
                priv->speed_input[i] =
-                   get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET);
+                   get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
+                                      priv->fan_structure->speed);
                priv->power_input[i] =
                    get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
-                                      AQC_FAN_POWER_OFFSET) * 10000;
+                                      priv->fan_structure->power) * 10000;
                priv->voltage_input[i] =
                    get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
-                                      AQC_FAN_VOLTAGE_OFFSET) * 10;
+                                      priv->fan_structure->voltage) * 10;
                priv->current_input[i] =
-                   get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET);
+                   get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
+                                      priv->fan_structure->curr);
+       }
+
+       /* Flow sensor readings */
+       for (j = 0; j < priv->num_flow_sensors; j++) {
+               priv->speed_input[i] = get_unaligned_be16(data + priv->flow_sensors_start_offset +
+                                                         j * AQC_SENSOR_SIZE);
+               i++;
        }
 
        if (priv->power_cycle_count_offset != 0)
@@ -835,13 +1071,35 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 
        /* Special-case sensor readings */
        switch (priv->kind) {
+       case aquaero:
+               /* Read calculated virtual temp sensors */
+               i = priv->num_temp_sensors + priv->num_virtual_temp_sensors;
+               for (j = 0; j < priv->num_calc_virt_temp_sensors; j++) {
+                       sensor_value = get_unaligned_be16(data +
+                                       priv->calc_virt_temp_sensor_start_offset +
+                                       j * AQC_SENSOR_SIZE);
+                       if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
+                               priv->temp_input[i] = -ENODATA;
+                       else
+                               priv->temp_input[i] = sensor_value * 10;
+                       i++;
+               }
+               break;
+       case aquastreamult:
+               priv->speed_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_OFFSET);
+               priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET);
+               priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET);
+
+               priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000;
+
+               priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10;
+
+               priv->current_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_CURRENT);
+               break;
        case d5next:
                priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
                priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10;
                break;
-       case quadro:
-               priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset);
-               break;
        case highflownext:
                /* If external temp sensor is not connected, its power reading is also N/A */
                if (priv->temp_input[1] == -ENODATA)
@@ -854,7 +1112,6 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
                priv->voltage_input[1] =
                    get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10;
 
-               priv->speed_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_FLOW);
                priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY);
                priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY);
                break;
@@ -907,9 +1164,13 @@ static void aqc_debugfs_init(struct aqc_data *priv)
                  dev_name(&priv->hdev->dev));
 
        priv->debugfs = debugfs_create_dir(name, NULL);
-       debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
-       debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
 
+       if (priv->serial_number_start_offset != 0)
+               debugfs_create_file("serial_number", 0444, priv->debugfs, priv,
+                                   &serial_number_fops);
+       if (priv->firmware_version_offset != 0)
+               debugfs_create_file("firmware_version", 0444, priv->debugfs, priv,
+                                   &firmware_version_fops);
        if (priv->power_cycle_count_offset != 0)
                debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
 }
@@ -949,6 +1210,45 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto fail_and_stop;
 
        switch (hdev->product) {
+       case USB_PRODUCT_ID_AQUAERO:
+               /*
+                * Aquaero presents itself as three HID devices under the same product ID:
+                * "aquaero keyboard/mouse", "aquaero System Control" and "aquaero Device",
+                * which is the one we want to communicate with. Unlike most other Aquacomputer
+                * devices, Aquaero does not return meaningful data when explicitly requested
+                * using GET_FEATURE_REPORT.
+                *
+                * The difference between "aquaero Device" and the other two is in the collections
+                * they present. The two other devices have the type of the second element in
+                * their respective collections set to 1, while the real device has it set to 0.
+                */
+               if (hdev->collection[1].type != 0) {
+                       ret = -ENODEV;
+                       goto fail_and_close;
+               }
+
+               priv->kind = aquaero;
+
+               priv->num_fans = AQUAERO_NUM_FANS;
+               priv->fan_sensor_offsets = aquaero_sensor_fan_offsets;
+
+               priv->num_temp_sensors = AQUAERO_NUM_SENSORS;
+               priv->temp_sensor_start_offset = AQUAERO_SENSOR_START;
+               priv->num_virtual_temp_sensors = AQUAERO_NUM_VIRTUAL_SENSORS;
+               priv->virtual_temp_sensor_start_offset = AQUAERO_VIRTUAL_SENSOR_START;
+               priv->num_calc_virt_temp_sensors = AQUAERO_NUM_CALC_VIRTUAL_SENSORS;
+               priv->calc_virt_temp_sensor_start_offset = AQUAERO_CALC_VIRTUAL_SENSOR_START;
+               priv->num_flow_sensors = AQUAERO_NUM_FLOW_SENSORS;
+               priv->flow_sensors_start_offset = AQUAERO_FLOW_SENSORS_START;
+
+               priv->temp_label = label_temp_sensors;
+               priv->virtual_temp_label = label_virtual_temp_sensors;
+               priv->calc_virt_temp_label = label_aquaero_calc_temp_sensors;
+               priv->speed_label = label_aquaero_speeds;
+               priv->power_label = label_fan_power;
+               priv->voltage_label = label_fan_voltage;
+               priv->current_label = label_fan_current;
+               break;
        case USB_PRODUCT_ID_D5NEXT:
                priv->kind = d5next;
 
@@ -1034,11 +1334,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
                priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS;
                priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START;
+               priv->num_flow_sensors = QUADRO_NUM_FLOW_SENSORS;
+               priv->flow_sensors_start_offset = QUADRO_FLOW_SENSOR_OFFSET;
+
                priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET;
 
                priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
 
-               priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
                priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET;
                priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
 
@@ -1056,6 +1358,8 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
                priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS;
                priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START;
+               priv->num_flow_sensors = HIGHFLOWNEXT_NUM_FLOW_SENSORS;
+               priv->flow_sensors_start_offset = HIGHFLOWNEXT_FLOW;
 
                priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
 
@@ -1064,10 +1368,57 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->power_label = label_highflownext_power;
                priv->voltage_label = label_highflownext_voltage;
                break;
+       case USB_PRODUCT_ID_AQUASTREAMULT:
+               priv->kind = aquastreamult;
+
+               priv->num_fans = AQUASTREAMULT_NUM_FANS;
+               priv->fan_sensor_offsets = aquastreamult_sensor_fan_offsets;
+
+               priv->num_temp_sensors = AQUASTREAMULT_NUM_SENSORS;
+               priv->temp_sensor_start_offset = AQUASTREAMULT_SENSOR_START;
+
+               priv->temp_label = label_aquastreamult_temp;
+               priv->speed_label = label_aquastreamult_speeds;
+               priv->power_label = label_aquastreamult_power;
+               priv->voltage_label = label_aquastreamult_voltages;
+               priv->current_label = label_aquastreamult_current;
+               break;
+       case USB_PRODUCT_ID_POWERADJUST3:
+               priv->kind = poweradjust3;
+
+               priv->num_fans = 0;
+
+               priv->num_temp_sensors = POWERADJUST3_NUM_SENSORS;
+               priv->temp_sensor_start_offset = POWERADJUST3_SENSOR_START;
+               priv->buffer_size = POWERADJUST3_SENSOR_REPORT_SIZE;
+
+               priv->temp_label = label_poweradjust3_temp_sensors;
+               break;
        default:
                break;
        }
 
+       switch (priv->kind) {
+       case aquaero:
+               priv->serial_number_start_offset = AQUAERO_SERIAL_START;
+               priv->firmware_version_offset = AQUAERO_FIRMWARE_VERSION;
+
+               priv->fan_structure = &aqc_aquaero_fan_structure;
+               break;
+       case poweradjust3:
+               priv->status_report_id = POWERADJUST3_STATUS_REPORT_ID;
+               break;
+       default:
+               priv->serial_number_start_offset = AQC_SERIAL_START;
+               priv->firmware_version_offset = AQC_FIRMWARE_VERSION;
+
+               if (priv->kind == aquastreamult)
+                       priv->fan_structure = &aqc_aquastreamult_fan_structure;
+               else
+                       priv->fan_structure = &aqc_general_fan_structure;
+               break;
+       }
+
        if (priv->buffer_size != 0) {
                priv->checksum_start = 0x01;
                priv->checksum_length = priv->buffer_size - 3;
@@ -1115,12 +1466,15 @@ static void aqc_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id aqc_table[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUAERO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) },
        { }
 };
 
index a901e4e33d81d0430df620e06fd2d9adf428b23e..2768b7511684f8a225e14ced58beb91fb0190203 100644 (file)
@@ -299,6 +299,7 @@ static const struct ec_board_info board_info_pro_art_x570_creator_wifi = {
        .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
                SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
                SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+       .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
        .family = family_amd_500_series,
 };
 
@@ -466,6 +467,8 @@ static const struct dmi_system_id dmi_table[] = {
                                        &board_info_strix_z690_a_gaming_wifi_d4),
        DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME",
                                        &board_info_zenith_ii_extreme),
+       DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME ALPHA",
+                                       &board_info_zenith_ii_extreme),
        {},
 };
 
index ca7a9b373bbd6365e07a609d1919c2f2e84a52ce..30d77f451937a12373f8ddfd5e4801347bd771a6 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/msr.h>
 #include <asm/processor.h>
 #include <asm/cpu_device_id.h>
+#include <linux/sched/isolation.h>
 
 #define DRVNAME        "coretemp"
 
@@ -502,6 +503,9 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
        u32 eax, edx;
        int err, index, attr_no;
 
+       if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
+               return 0;
+
        /*
         * Find attr number for sysfs:
         * We map the attr number to core id of the CPU
@@ -588,66 +592,49 @@ static void coretemp_remove_core(struct platform_data *pdata, int indx)
                ida_free(&pdata->ida, indx - BASE_SYSFS_ATTR_NO);
 }
 
-static int coretemp_probe(struct platform_device *pdev)
+static int coretemp_device_add(int zoneid)
 {
-       struct device *dev = &pdev->dev;
+       struct platform_device *pdev;
        struct platform_data *pdata;
+       int err;
 
        /* Initialize the per-zone data structures */
-       pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL);
+       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return -ENOMEM;
 
-       pdata->pkg_id = pdev->id;
+       pdata->pkg_id = zoneid;
        ida_init(&pdata->ida);
-       platform_set_drvdata(pdev, pdata);
-
-       pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME,
-                                                                 pdata, NULL);
-       return PTR_ERR_OR_ZERO(pdata->hwmon_dev);
-}
 
-static int coretemp_remove(struct platform_device *pdev)
-{
-       struct platform_data *pdata = platform_get_drvdata(pdev);
-       int i;
+       pdev = platform_device_alloc(DRVNAME, zoneid);
+       if (!pdev) {
+               err = -ENOMEM;
+               goto err_free_pdata;
+       }
 
-       for (i = MAX_CORE_DATA - 1; i >= 0; --i)
-               if (pdata->core_data[i])
-                       coretemp_remove_core(pdata, i);
+       err = platform_device_add(pdev);
+       if (err)
+               goto err_put_dev;
 
-       ida_destroy(&pdata->ida);
+       platform_set_drvdata(pdev, pdata);
+       zone_devices[zoneid] = pdev;
        return 0;
-}
 
-static struct platform_driver coretemp_driver = {
-       .driver = {
-               .name = DRVNAME,
-       },
-       .probe = coretemp_probe,
-       .remove = coretemp_remove,
-};
+err_put_dev:
+       platform_device_put(pdev);
+err_free_pdata:
+       kfree(pdata);
+       return err;
+}
 
-static struct platform_device *coretemp_device_add(unsigned int cpu)
+static void coretemp_device_remove(int zoneid)
 {
-       int err, zoneid = topology_logical_die_id(cpu);
-       struct platform_device *pdev;
-
-       if (zoneid < 0)
-               return ERR_PTR(-ENOMEM);
-
-       pdev = platform_device_alloc(DRVNAME, zoneid);
-       if (!pdev)
-               return ERR_PTR(-ENOMEM);
-
-       err = platform_device_add(pdev);
-       if (err) {
-               platform_device_put(pdev);
-               return ERR_PTR(err);
-       }
+       struct platform_device *pdev = zone_devices[zoneid];
+       struct platform_data *pdata = platform_get_drvdata(pdev);
 
-       zone_devices[zoneid] = pdev;
-       return pdev;
+       ida_destroy(&pdata->ida);
+       kfree(pdata);
+       platform_device_unregister(pdev);
 }
 
 static int coretemp_cpu_online(unsigned int cpu)
@@ -671,7 +658,10 @@ static int coretemp_cpu_online(unsigned int cpu)
        if (!cpu_has(c, X86_FEATURE_DTHERM))
                return -ENODEV;
 
-       if (!pdev) {
+       pdata = platform_get_drvdata(pdev);
+       if (!pdata->hwmon_dev) {
+               struct device *hwmon;
+
                /* Check the microcode version of the CPU */
                if (chk_ucode_version(cpu))
                        return -EINVAL;
@@ -682,9 +672,11 @@ static int coretemp_cpu_online(unsigned int cpu)
                 * online. So, initialize per-pkg data structures and
                 * then bring this core online.
                 */
-               pdev = coretemp_device_add(cpu);
-               if (IS_ERR(pdev))
-                       return PTR_ERR(pdev);
+               hwmon = hwmon_device_register_with_groups(&pdev->dev, DRVNAME,
+                                                         pdata, NULL);
+               if (IS_ERR(hwmon))
+                       return PTR_ERR(hwmon);
+               pdata->hwmon_dev = hwmon;
 
                /*
                 * Check whether pkgtemp support is available.
@@ -694,7 +686,6 @@ static int coretemp_cpu_online(unsigned int cpu)
                        coretemp_add_core(pdev, cpu, 1);
        }
 
-       pdata = platform_get_drvdata(pdev);
        /*
         * Check whether a thread sibling is already online. If not add the
         * interface for this CPU core.
@@ -713,18 +704,14 @@ static int coretemp_cpu_offline(unsigned int cpu)
        struct temp_data *tdata;
        int i, indx = -1, target;
 
-       /*
-        * Don't execute this on suspend as the device remove locks
-        * up the machine.
-        */
+       /* No need to tear down any interfaces for suspend */
        if (cpuhp_tasks_frozen)
                return 0;
 
        /* If the physical CPU device does not exist, just return */
-       if (!pdev)
-               return 0;
-
        pd = platform_get_drvdata(pdev);
+       if (!pd->hwmon_dev)
+               return 0;
 
        for (i = 0; i < NUM_REAL_CORES; i++) {
                if (pd->cpu_map[i] == topology_core_id(cpu)) {
@@ -756,13 +743,14 @@ static int coretemp_cpu_offline(unsigned int cpu)
        }
 
        /*
-        * If all cores in this pkg are offline, remove the device. This
-        * will invoke the platform driver remove function, which cleans up
-        * the rest.
+        * If all cores in this pkg are offline, remove the interface.
         */
+       tdata = pd->core_data[PKG_SYSFS_ATTR_NO];
        if (cpumask_empty(&pd->cpumask)) {
-               zone_devices[topology_logical_die_id(cpu)] = NULL;
-               platform_device_unregister(pdev);
+               if (tdata)
+                       coretemp_remove_core(pd, PKG_SYSFS_ATTR_NO);
+               hwmon_device_unregister(pd->hwmon_dev);
+               pd->hwmon_dev = NULL;
                return 0;
        }
 
@@ -770,7 +758,6 @@ static int coretemp_cpu_offline(unsigned int cpu)
         * Check whether this core is the target for the package
         * interface. We need to assign it to some other cpu.
         */
-       tdata = pd->core_data[PKG_SYSFS_ATTR_NO];
        if (tdata && tdata->cpu == cpu) {
                target = cpumask_first(&pd->cpumask);
                mutex_lock(&tdata->update_lock);
@@ -789,7 +776,7 @@ static enum cpuhp_state coretemp_hp_online;
 
 static int __init coretemp_init(void)
 {
-       int err;
+       int i, err;
 
        /*
         * CPUID.06H.EAX[0] indicates whether the CPU has thermal
@@ -805,20 +792,22 @@ static int __init coretemp_init(void)
        if (!zone_devices)
                return -ENOMEM;
 
-       err = platform_driver_register(&coretemp_driver);
-       if (err)
-               goto outzone;
+       for (i = 0; i < max_zones; i++) {
+               err = coretemp_device_add(i);
+               if (err)
+                       goto outzone;
+       }
 
        err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online",
                                coretemp_cpu_online, coretemp_cpu_offline);
        if (err < 0)
-               goto outdrv;
+               goto outzone;
        coretemp_hp_online = err;
        return 0;
 
-outdrv:
-       platform_driver_unregister(&coretemp_driver);
 outzone:
+       while (i--)
+               coretemp_device_remove(i);
        kfree(zone_devices);
        return err;
 }
@@ -826,8 +815,11 @@ module_init(coretemp_init)
 
 static void __exit coretemp_exit(void)
 {
+       int i;
+
        cpuhp_remove_state(coretemp_hp_online);
-       platform_driver_unregister(&coretemp_driver);
+       for (i = 0; i < max_zones; i++)
+               coretemp_device_remove(i);
        kfree(zone_devices);
 }
 module_exit(coretemp_exit)
index 6ad055e5868e6c817c37b3c1ba29cb0bf402511a..f65467fbd86c55cf12a966ab353712976ccf59e5 100644 (file)
@@ -59,10 +59,11 @@ static const struct i2c_device_id emc2305_ids[] = {
 MODULE_DEVICE_TABLE(i2c, emc2305_ids);
 
 /**
- * @cdev: cooling device;
- * @curr_state: cooling current state;
- * @last_hwmon_state: last cooling state updated by hwmon subsystem;
- * @last_thermal_state: last cooling state updated by thermal subsystem;
+ * struct emc2305_cdev_data - device-specific cooling device state
+ * @cdev: cooling device
+ * @cur_state: cooling current state
+ * @last_hwmon_state: last cooling state updated by hwmon subsystem
+ * @last_thermal_state: last cooling state updated by thermal subsystem
  *
  * The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit
  * speed feature. The purpose of this feature is to provides ability to limit fan speed
@@ -86,13 +87,14 @@ struct emc2305_cdev_data {
 };
 
 /**
- * @client: i2c client;
- * @hwmon_dev: hwmon device;
- * @max_state: maximum cooling state of the cooling device;
- * @pwm_num: number of PWM channels;
- * @pwm_separate: separate PWM settings for every channel;
- * @pwm_min: array of minimum PWM per channel;
- * @cdev_data: array of cooling devices data;
+ * struct emc2305_data - device-specific data
+ * @client: i2c client
+ * @hwmon_dev: hwmon device
+ * @max_state: maximum cooling state of the cooling device
+ * @pwm_num: number of PWM channels
+ * @pwm_separate: separate PWM settings for every channel
+ * @pwm_min: array of minimum PWM per channel
+ * @cdev_data: array of cooling devices data
  */
 struct emc2305_data {
        struct i2c_client *client;
index f5b8e724a8ca1964bcb3341653e035e15c283163..25afd9167a346d03d7a180b338f4c7b85fe971cf 100644 (file)
@@ -6,17 +6,14 @@
  *               Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>
  */
 #include <linux/err.h>
-#include <linux/fs.h>
 #include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/jiffies.h>
+#include <linux/math.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/sysfs.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 
 #define FTS_DEVICE_ID_REG              0x0000
@@ -48,6 +45,8 @@
 #define FTS_NO_TEMP_SENSORS            0x10
 #define FTS_NO_VOLT_SENSORS            0x04
 
+#define FTS_FAN_SOURCE_INVALID         0xff
+
 static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
 
 static const struct i2c_device_id fts_id[] = {
@@ -187,7 +186,7 @@ static int fts_update_device(struct fts_data *data)
                        data->fan_source[i] = err;
                } else {
                        data->fan_input[i] = 0;
-                       data->fan_source[i] = 0;
+                       data->fan_source[i] = FTS_FAN_SOURCE_INVALID;
                }
        }
 
@@ -336,373 +335,243 @@ static int fts_watchdog_init(struct fts_data *data)
        /* max timeout 255 minutes. */
        data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC;
 
-       return watchdog_register_device(&data->wdd);
-}
-
-/*****************************************************************************/
-/* SysFS handler functions                                                  */
-/*****************************************************************************/
-static ssize_t in_value_show(struct device *dev,
-                            struct device_attribute *devattr, char *buf)
-{
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
-
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
-
-       return sprintf(buf, "%u\n", data->volt[index]);
+       return devm_watchdog_register_device(&data->client->dev, &data->wdd);
 }
 
-static ssize_t temp_value_show(struct device *dev,
-                              struct device_attribute *devattr, char *buf)
+static umode_t fts_is_visible(const void *devdata, enum hwmon_sensor_types type, u32 attr,
+                             int channel)
 {
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
-
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
-
-       return sprintf(buf, "%u\n", data->temp_input[index]);
-}
-
-static ssize_t temp_fault_show(struct device *dev,
-                              struct device_attribute *devattr, char *buf)
-{
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
-
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
-
-       /* 00h Temperature = Sensor Error */
-       return sprintf(buf, "%d\n", data->temp_input[index] == 0);
-}
-
-static ssize_t temp_alarm_show(struct device *dev,
-                              struct device_attribute *devattr, char *buf)
-{
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
-
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
+       switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_input:
+               case hwmon_temp_fault:
+                       return 0444;
+               case hwmon_temp_alarm:
+                       return 0644;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_fan:
+               switch (attr) {
+               case hwmon_fan_input:
+               case hwmon_fan_fault:
+                       return 0444;
+               case hwmon_fan_alarm:
+                       return 0644;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_pwm:
+       case hwmon_in:
+               return 0444;
+       default:
+               break;
+       }
 
-       return sprintf(buf, "%u\n", !!(data->temp_alarm & BIT(index)));
+       return 0;
 }
 
-static ssize_t
-temp_alarm_store(struct device *dev, struct device_attribute *devattr,
-                const char *buf, size_t count)
+static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+                   long *val)
 {
        struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       long ret;
+       int ret = fts_update_device(data);
 
-       ret = fts_update_device(data);
        if (ret < 0)
                return ret;
 
-       if (kstrtoul(buf, 10, &ret) || ret != 0)
-               return -EINVAL;
-
-       mutex_lock(&data->update_lock);
-       ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(index));
-       if (ret < 0)
-               goto error;
-
-       ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(index),
-                            ret | 0x1);
-       if (ret < 0)
-               goto error;
+       switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_input:
+                       *val = (data->temp_input[channel] - 64) * 1000;
 
-       data->valid = false;
-       ret = count;
-error:
-       mutex_unlock(&data->update_lock);
-       return ret;
-}
-
-static ssize_t fan_value_show(struct device *dev,
-                             struct device_attribute *devattr, char *buf)
-{
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
-
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
+                       return 0;
+               case hwmon_temp_alarm:
+                       *val = !!(data->temp_alarm & BIT(channel));
 
-       return sprintf(buf, "%u\n", data->fan_input[index]);
-}
+                       return 0;
+               case hwmon_temp_fault:
+                       /* 00h Temperature = Sensor Error */;
+                       *val = (data->temp_input[channel] == 0);
 
-static ssize_t fan_source_show(struct device *dev,
-                              struct device_attribute *devattr, char *buf)
-{
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_fan:
+               switch (attr) {
+               case hwmon_fan_input:
+                       *val = data->fan_input[channel] * 60;
 
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
+                       return 0;
+               case hwmon_fan_alarm:
+                       *val = !!(data->fan_alarm & BIT(channel));
 
-       return sprintf(buf, "%u\n", data->fan_source[index]);
-}
+                       return 0;
+               case hwmon_fan_fault:
+                       *val = !(data->fan_present & BIT(channel));
 
-static ssize_t fan_alarm_show(struct device *dev,
-                             struct device_attribute *devattr, char *buf)
-{
-       struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       int err;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_pwm:
+               switch (attr) {
+               case hwmon_pwm_auto_channels_temp:
+                       if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID)
+                               *val = 0;
+                       else
+                               *val = BIT(data->fan_source[channel]);
+
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_input:
+                       *val = DIV_ROUND_CLOSEST(data->volt[channel] * 3300, 255);
 
-       err = fts_update_device(data);
-       if (err < 0)
-               return err;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
 
-       return sprintf(buf, "%d\n", !!(data->fan_alarm & BIT(index)));
+       return -EOPNOTSUPP;
 }
 
-static ssize_t
-fan_alarm_store(struct device *dev, struct device_attribute *devattr,
-               const char *buf, size_t count)
+static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+                    long val)
 {
        struct fts_data *data = dev_get_drvdata(dev);
-       int index = to_sensor_dev_attr(devattr)->index;
-       long ret;
+       int ret = fts_update_device(data);
 
-       ret = fts_update_device(data);
        if (ret < 0)
                return ret;
 
-       if (kstrtoul(buf, 10, &ret) || ret != 0)
-               return -EINVAL;
-
-       mutex_lock(&data->update_lock);
-       ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(index));
-       if (ret < 0)
-               goto error;
-
-       ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(index),
-                            ret | 0x1);
-       if (ret < 0)
-               goto error;
+       switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_alarm:
+                       if (val)
+                               return -EINVAL;
+
+                       mutex_lock(&data->update_lock);
+                       ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(channel));
+                       if (ret >= 0)
+                               ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel),
+                                                    ret | 0x1);
+                       if (ret >= 0)
+                               data->valid = false;
+
+                       mutex_unlock(&data->update_lock);
+                       if (ret < 0)
+                               return ret;
+
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_fan:
+               switch (attr) {
+               case hwmon_fan_alarm:
+                       if (val)
+                               return -EINVAL;
+
+                       mutex_lock(&data->update_lock);
+                       ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(channel));
+                       if (ret >= 0)
+                               ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel),
+                                                    ret | 0x1);
+                       if (ret >= 0)
+                               data->valid = false;
+
+                       mutex_unlock(&data->update_lock);
+                       if (ret < 0)
+                               return ret;
+
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
 
-       data->valid = false;
-       ret = count;
-error:
-       mutex_unlock(&data->update_lock);
-       return ret;
+       return -EOPNOTSUPP;
 }
 
-/*****************************************************************************/
-/* SysFS structs                                                            */
-/*****************************************************************************/
-
-/* Temperature sensors */
-static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0);
-static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1);
-static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2);
-static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_value, 3);
-static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_value, 4);
-static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_value, 5);
-static SENSOR_DEVICE_ATTR_RO(temp7_input, temp_value, 6);
-static SENSOR_DEVICE_ATTR_RO(temp8_input, temp_value, 7);
-static SENSOR_DEVICE_ATTR_RO(temp9_input, temp_value, 8);
-static SENSOR_DEVICE_ATTR_RO(temp10_input, temp_value, 9);
-static SENSOR_DEVICE_ATTR_RO(temp11_input, temp_value, 10);
-static SENSOR_DEVICE_ATTR_RO(temp12_input, temp_value, 11);
-static SENSOR_DEVICE_ATTR_RO(temp13_input, temp_value, 12);
-static SENSOR_DEVICE_ATTR_RO(temp14_input, temp_value, 13);
-static SENSOR_DEVICE_ATTR_RO(temp15_input, temp_value, 14);
-static SENSOR_DEVICE_ATTR_RO(temp16_input, temp_value, 15);
-
-static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
-static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
-static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
-static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
-static SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4);
-static SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5);
-static SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6);
-static SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7);
-static SENSOR_DEVICE_ATTR_RO(temp9_fault, temp_fault, 8);
-static SENSOR_DEVICE_ATTR_RO(temp10_fault, temp_fault, 9);
-static SENSOR_DEVICE_ATTR_RO(temp11_fault, temp_fault, 10);
-static SENSOR_DEVICE_ATTR_RO(temp12_fault, temp_fault, 11);
-static SENSOR_DEVICE_ATTR_RO(temp13_fault, temp_fault, 12);
-static SENSOR_DEVICE_ATTR_RO(temp14_fault, temp_fault, 13);
-static SENSOR_DEVICE_ATTR_RO(temp15_fault, temp_fault, 14);
-static SENSOR_DEVICE_ATTR_RO(temp16_fault, temp_fault, 15);
-
-static SENSOR_DEVICE_ATTR_RW(temp1_alarm, temp_alarm, 0);
-static SENSOR_DEVICE_ATTR_RW(temp2_alarm, temp_alarm, 1);
-static SENSOR_DEVICE_ATTR_RW(temp3_alarm, temp_alarm, 2);
-static SENSOR_DEVICE_ATTR_RW(temp4_alarm, temp_alarm, 3);
-static SENSOR_DEVICE_ATTR_RW(temp5_alarm, temp_alarm, 4);
-static SENSOR_DEVICE_ATTR_RW(temp6_alarm, temp_alarm, 5);
-static SENSOR_DEVICE_ATTR_RW(temp7_alarm, temp_alarm, 6);
-static SENSOR_DEVICE_ATTR_RW(temp8_alarm, temp_alarm, 7);
-static SENSOR_DEVICE_ATTR_RW(temp9_alarm, temp_alarm, 8);
-static SENSOR_DEVICE_ATTR_RW(temp10_alarm, temp_alarm, 9);
-static SENSOR_DEVICE_ATTR_RW(temp11_alarm, temp_alarm, 10);
-static SENSOR_DEVICE_ATTR_RW(temp12_alarm, temp_alarm, 11);
-static SENSOR_DEVICE_ATTR_RW(temp13_alarm, temp_alarm, 12);
-static SENSOR_DEVICE_ATTR_RW(temp14_alarm, temp_alarm, 13);
-static SENSOR_DEVICE_ATTR_RW(temp15_alarm, temp_alarm, 14);
-static SENSOR_DEVICE_ATTR_RW(temp16_alarm, temp_alarm, 15);
-
-static struct attribute *fts_temp_attrs[] = {
-       &sensor_dev_attr_temp1_input.dev_attr.attr,
-       &sensor_dev_attr_temp2_input.dev_attr.attr,
-       &sensor_dev_attr_temp3_input.dev_attr.attr,
-       &sensor_dev_attr_temp4_input.dev_attr.attr,
-       &sensor_dev_attr_temp5_input.dev_attr.attr,
-       &sensor_dev_attr_temp6_input.dev_attr.attr,
-       &sensor_dev_attr_temp7_input.dev_attr.attr,
-       &sensor_dev_attr_temp8_input.dev_attr.attr,
-       &sensor_dev_attr_temp9_input.dev_attr.attr,
-       &sensor_dev_attr_temp10_input.dev_attr.attr,
-       &sensor_dev_attr_temp11_input.dev_attr.attr,
-       &sensor_dev_attr_temp12_input.dev_attr.attr,
-       &sensor_dev_attr_temp13_input.dev_attr.attr,
-       &sensor_dev_attr_temp14_input.dev_attr.attr,
-       &sensor_dev_attr_temp15_input.dev_attr.attr,
-       &sensor_dev_attr_temp16_input.dev_attr.attr,
-
-       &sensor_dev_attr_temp1_fault.dev_attr.attr,
-       &sensor_dev_attr_temp2_fault.dev_attr.attr,
-       &sensor_dev_attr_temp3_fault.dev_attr.attr,
-       &sensor_dev_attr_temp4_fault.dev_attr.attr,
-       &sensor_dev_attr_temp5_fault.dev_attr.attr,
-       &sensor_dev_attr_temp6_fault.dev_attr.attr,
-       &sensor_dev_attr_temp7_fault.dev_attr.attr,
-       &sensor_dev_attr_temp8_fault.dev_attr.attr,
-       &sensor_dev_attr_temp9_fault.dev_attr.attr,
-       &sensor_dev_attr_temp10_fault.dev_attr.attr,
-       &sensor_dev_attr_temp11_fault.dev_attr.attr,
-       &sensor_dev_attr_temp12_fault.dev_attr.attr,
-       &sensor_dev_attr_temp13_fault.dev_attr.attr,
-       &sensor_dev_attr_temp14_fault.dev_attr.attr,
-       &sensor_dev_attr_temp15_fault.dev_attr.attr,
-       &sensor_dev_attr_temp16_fault.dev_attr.attr,
-
-       &sensor_dev_attr_temp1_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp2_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp3_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp4_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp5_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp6_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp7_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp8_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp9_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp10_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp11_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp12_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp13_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp14_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp15_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp16_alarm.dev_attr.attr,
-       NULL
-};
-
-/* Fans */
-static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_value, 0);
-static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_value, 1);
-static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_value, 2);
-static SENSOR_DEVICE_ATTR_RO(fan4_input, fan_value, 3);
-static SENSOR_DEVICE_ATTR_RO(fan5_input, fan_value, 4);
-static SENSOR_DEVICE_ATTR_RO(fan6_input, fan_value, 5);
-static SENSOR_DEVICE_ATTR_RO(fan7_input, fan_value, 6);
-static SENSOR_DEVICE_ATTR_RO(fan8_input, fan_value, 7);
-
-static SENSOR_DEVICE_ATTR_RO(fan1_source, fan_source, 0);
-static SENSOR_DEVICE_ATTR_RO(fan2_source, fan_source, 1);
-static SENSOR_DEVICE_ATTR_RO(fan3_source, fan_source, 2);
-static SENSOR_DEVICE_ATTR_RO(fan4_source, fan_source, 3);
-static SENSOR_DEVICE_ATTR_RO(fan5_source, fan_source, 4);
-static SENSOR_DEVICE_ATTR_RO(fan6_source, fan_source, 5);
-static SENSOR_DEVICE_ATTR_RO(fan7_source, fan_source, 6);
-static SENSOR_DEVICE_ATTR_RO(fan8_source, fan_source, 7);
-
-static SENSOR_DEVICE_ATTR_RW(fan1_alarm, fan_alarm, 0);
-static SENSOR_DEVICE_ATTR_RW(fan2_alarm, fan_alarm, 1);
-static SENSOR_DEVICE_ATTR_RW(fan3_alarm, fan_alarm, 2);
-static SENSOR_DEVICE_ATTR_RW(fan4_alarm, fan_alarm, 3);
-static SENSOR_DEVICE_ATTR_RW(fan5_alarm, fan_alarm, 4);
-static SENSOR_DEVICE_ATTR_RW(fan6_alarm, fan_alarm, 5);
-static SENSOR_DEVICE_ATTR_RW(fan7_alarm, fan_alarm, 6);
-static SENSOR_DEVICE_ATTR_RW(fan8_alarm, fan_alarm, 7);
-
-static struct attribute *fts_fan_attrs[] = {
-       &sensor_dev_attr_fan1_input.dev_attr.attr,
-       &sensor_dev_attr_fan2_input.dev_attr.attr,
-       &sensor_dev_attr_fan3_input.dev_attr.attr,
-       &sensor_dev_attr_fan4_input.dev_attr.attr,
-       &sensor_dev_attr_fan5_input.dev_attr.attr,
-       &sensor_dev_attr_fan6_input.dev_attr.attr,
-       &sensor_dev_attr_fan7_input.dev_attr.attr,
-       &sensor_dev_attr_fan8_input.dev_attr.attr,
-
-       &sensor_dev_attr_fan1_source.dev_attr.attr,
-       &sensor_dev_attr_fan2_source.dev_attr.attr,
-       &sensor_dev_attr_fan3_source.dev_attr.attr,
-       &sensor_dev_attr_fan4_source.dev_attr.attr,
-       &sensor_dev_attr_fan5_source.dev_attr.attr,
-       &sensor_dev_attr_fan6_source.dev_attr.attr,
-       &sensor_dev_attr_fan7_source.dev_attr.attr,
-       &sensor_dev_attr_fan8_source.dev_attr.attr,
-
-       &sensor_dev_attr_fan1_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan2_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan3_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan4_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan5_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan6_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan7_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan8_alarm.dev_attr.attr,
-       NULL
+static const struct hwmon_ops fts_ops = {
+       .is_visible = fts_is_visible,
+       .read = fts_read,
+       .write = fts_write,
 };
 
-/* Voltages */
-static SENSOR_DEVICE_ATTR_RO(in1_input, in_value, 0);
-static SENSOR_DEVICE_ATTR_RO(in2_input, in_value, 1);
-static SENSOR_DEVICE_ATTR_RO(in3_input, in_value, 2);
-static SENSOR_DEVICE_ATTR_RO(in4_input, in_value, 3);
-static struct attribute *fts_voltage_attrs[] = {
-       &sensor_dev_attr_in1_input.dev_attr.attr,
-       &sensor_dev_attr_in2_input.dev_attr.attr,
-       &sensor_dev_attr_in3_input.dev_attr.attr,
-       &sensor_dev_attr_in4_input.dev_attr.attr,
+static const struct hwmon_channel_info *fts_info[] = {
+       HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT
+                          ),
+       HWMON_CHANNEL_INFO(fan,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT
+                          ),
+       HWMON_CHANNEL_INFO(pwm,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP,
+                          HWMON_PWM_AUTO_CHANNELS_TEMP
+                          ),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT,
+                          HWMON_I_INPUT,
+                          HWMON_I_INPUT,
+                          HWMON_I_INPUT
+                          ),
        NULL
 };
 
-static const struct attribute_group fts_voltage_attr_group = {
-       .attrs = fts_voltage_attrs
-};
-
-static const struct attribute_group fts_temp_attr_group = {
-       .attrs = fts_temp_attrs
-};
-
-static const struct attribute_group fts_fan_attr_group = {
-       .attrs = fts_fan_attrs
-};
-
-static const struct attribute_group *fts_attr_groups[] = {
-       &fts_voltage_attr_group,
-       &fts_temp_attr_group,
-       &fts_fan_attr_group,
-       NULL
+static const struct hwmon_chip_info fts_chip_info = {
+       .ops = &fts_ops,
+       .info = fts_info,
 };
 
 /*****************************************************************************/
@@ -744,13 +613,6 @@ static int fts_detect(struct i2c_client *client,
        return 0;
 }
 
-static void fts_remove(struct i2c_client *client)
-{
-       struct fts_data *data = dev_get_drvdata(&client->dev);
-
-       watchdog_unregister_device(&data->wdd);
-}
-
 static int fts_probe(struct i2c_client *client)
 {
        u8 revision;
@@ -793,10 +655,8 @@ static int fts_probe(struct i2c_client *client)
                return err;
        revision = err;
 
-       hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
-                                                          "ftsteutates",
-                                                          data,
-                                                          fts_attr_groups);
+       hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, "ftsteutates", data,
+                                                        &fts_chip_info, NULL);
        if (IS_ERR(hwmon_dev))
                return PTR_ERR(hwmon_dev);
 
@@ -819,7 +679,6 @@ static struct i2c_driver fts_driver = {
        },
        .id_table = fts_id,
        .probe_new = fts_probe,
-       .remove = fts_remove,
        .detect = fts_detect,
        .address_list = normal_i2c,
 };
diff --git a/drivers/hwmon/gxp-fan-ctrl.c b/drivers/hwmon/gxp-fan-ctrl.c
new file mode 100644 (file)
index 0000000..0014b8b
--- /dev/null
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2022 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/of_device.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);
+
+       switch (attr) {
+       case hwmon_pwm_input:
+               if (val > 255 || val < 0)
+                       return -EINVAL;
+               writeb(val, drvdata->base + channel);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int gxp_fan_ctrl_write(struct device *dev, enum hwmon_sensor_types type,
+                             u32 attr, int channel, long val)
+{
+       switch (type) {
+       case hwmon_pwm:
+               return gxp_pwm_write(dev, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+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;
+
+       return 0;
+}
+
+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:
+               return -EOPNOTSUPP;
+       }
+}
+
+static umode_t gxp_fan_ctrl_is_visible(const void *_data,
+                                      enum hwmon_sensor_types type,
+                                      u32 attr, int channel)
+{
+       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:
+                       mode = 0644;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return mode;
+}
+
+static const struct hwmon_ops gxp_fan_ctrl_ops = {
+       .is_visible = gxp_fan_ctrl_is_visible,
+       .read = gxp_fan_ctrl_read,
+       .write = gxp_fan_ctrl_write,
+};
+
+static const struct hwmon_channel_info *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,
+                          HWMON_PWM_INPUT,
+                          HWMON_PWM_INPUT,
+                          HWMON_PWM_INPUT,
+                          HWMON_PWM_INPUT,
+                          HWMON_PWM_INPUT,
+                          HWMON_PWM_INPUT),
+       NULL
+};
+
+static const struct hwmon_chip_info gxp_fan_ctrl_chip_info = {
+       .ops = &gxp_fan_ctrl_ops,
+       .info = gxp_fan_ctrl_info,
+
+};
+
+static int gxp_fan_ctrl_probe(struct platform_device *pdev)
+{
+       struct gxp_fan_ctrl_drvdata *drvdata;
+       struct device *dev = &pdev->dev;
+       struct device *hwmon_dev;
+
+       drvdata = devm_kzalloc(dev, sizeof(struct gxp_fan_ctrl_drvdata),
+                              GFP_KERNEL);
+       if (!drvdata)
+               return -ENOMEM;
+
+       drvdata->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+       if (IS_ERR(drvdata->base))
+               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,
+                                                        &gxp_fan_ctrl_chip_info,
+                                                        NULL);
+
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct of_device_id gxp_fan_ctrl_of_match[] = {
+       { .compatible = "hpe,gxp-fan-ctrl", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, gxp_fan_ctrl_of_match);
+
+static struct platform_driver gxp_fan_ctrl_driver = {
+       .probe          = gxp_fan_ctrl_probe,
+       .driver = {
+               .name   = "gxp-fan-ctrl",
+               .of_match_table = gxp_fan_ctrl_of_match,
+       },
+};
+module_platform_driver(gxp_fan_ctrl_driver);
+
+MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>");
+MODULE_DESCRIPTION("HPE GXP fan controller");
+MODULE_LICENSE("GPL");
index d9394e19fea801feebcc331cf3e0cf51b1f26700..3a7582824f945dc519486820d9a40c9f098adc5d 100644 (file)
@@ -150,7 +150,7 @@ out:
 }
 
 /**
- * hih6130_show_temperature() - show temperature measurement value in sysfs
+ * hih6130_temperature_show() - show temperature measurement value in sysfs
  * @dev: device
  * @attr: device attribute
  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
@@ -172,7 +172,7 @@ static ssize_t hih6130_temperature_show(struct device *dev,
 }
 
 /**
- * hih6130_show_humidity() - show humidity measurement value in sysfs
+ * hih6130_humidity_show() - show humidity measurement value in sysfs
  * @dev: device
  * @attr: device attribute
  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
index 1837cccd993c8fd13f015903fbf392aef9dccc46..db066b3689187d810c1734ecedfbbbcbe7516305 100644 (file)
@@ -546,7 +546,7 @@ static void ibmpex_bmc_gone(int iface)
 
 static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 {
-       struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+       struct ibmpex_bmc_data *data = user_msg_data;
 
        if (msg->msgid != data->tx_msgid) {
                dev_err(data->bmc_device,
index 3aa40893fc095abc5a177dc7b3d62cbed9557e2b..4c8a8084789127843516b4eb2417805ea70022da 100644 (file)
@@ -77,9 +77,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
 
        channels = devm_iio_channel_get_all(dev);
        if (IS_ERR(channels)) {
-               if (PTR_ERR(channels) == -ENODEV)
-                       return -EPROBE_DEFER;
-               return PTR_ERR(channels);
+               ret = PTR_ERR(channels);
+               if (ret == -ENODEV)
+                       ret = -EPROBE_DEFER;
+               return dev_err_probe(dev, ret,
+                                    "Failed to get channels\n");
        }
 
        st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
index 6e82f7200d1cc54be282a90248f1ef10706c2d74..2f0323c14bab296c8a6fd6de004497c43a80c002 100644 (file)
@@ -340,6 +340,231 @@ static const struct m10bmc_hwmon_board_data n5010bmc_hwmon_bdata = {
        .hinfo = n5010bmc_hinfo,
 };
 
+static const struct m10bmc_sdata n6000bmc_temp_tbl[] = {
+       { 0x444, 0x448, 0x44c, 0x0, 0x0, 500, "FPGA E-TILE Temperature #1" },
+       { 0x450, 0x454, 0x458, 0x0, 0x0, 500, "FPGA E-TILE Temperature #2" },
+       { 0x45c, 0x460, 0x464, 0x0, 0x0, 500, "FPGA E-TILE Temperature #3" },
+       { 0x468, 0x46c, 0x470, 0x0, 0x0, 500, "FPGA E-TILE Temperature #4" },
+       { 0x474, 0x478, 0x47c, 0x0, 0x0, 500, "FPGA P-TILE Temperature" },
+       { 0x484, 0x488, 0x48c, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #1" },
+       { 0x490, 0x494, 0x498, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #2" },
+       { 0x49c, 0x4a0, 0x4a4, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #3" },
+       { 0x4a8, 0x4ac, 0x4b0, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #4" },
+       { 0x4b4, 0x4b8, 0x4bc, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #5" },
+       { 0x4c0, 0x4c4, 0x4c8, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #1" },
+       { 0x4cc, 0x4d0, 0x4d4, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #2" },
+       { 0x4d8, 0x4dc, 0x4e0, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #3" },
+       { 0x4e4, 0x4e8, 0x4ec, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #4" },
+       { 0x4f0, 0x4f4, 0x4f8, 0x52c, 0x0, 500, "Board Top Near FPGA Temperature" },
+       { 0x4fc, 0x500, 0x504, 0x52c, 0x0, 500, "Board Bottom Near CVL Temperature" },
+       { 0x508, 0x50c, 0x510, 0x52c, 0x0, 500, "Board Top East Near VRs Temperature" },
+       { 0x514, 0x518, 0x51c, 0x52c, 0x0, 500, "Columbiaville Die Temperature" },
+       { 0x520, 0x524, 0x528, 0x52c, 0x0, 500, "Board Rear Side Temperature" },
+       { 0x530, 0x534, 0x538, 0x52c, 0x0, 500, "Board Front Side Temperature" },
+       { 0x53c, 0x540, 0x544, 0x0, 0x0, 500, "QSFP1 Case Temperature" },
+       { 0x548, 0x54c, 0x550, 0x0, 0x0, 500, "QSFP2 Case Temperature" },
+       { 0x554, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage Phase 0 VR Temperature" },
+       { 0x560, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage Phase 1 VR Temperature" },
+       { 0x56c, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage Phase 2 VR Temperature" },
+       { 0x578, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage VR Controller Temperature" },
+       { 0x584, 0x0, 0x0, 0x0, 0x0, 500, "FPGA VCCH VR Temperature" },
+       { 0x590, 0x0, 0x0, 0x0, 0x0, 500, "FPGA VCC_1V2 VR Temperature" },
+       { 0x59c, 0x0, 0x0, 0x0, 0x0, 500, "FPGA VCCH, VCC_1V2 VR Controller Temperature" },
+       { 0x5a8, 0x0, 0x0, 0x0, 0x0, 500, "3V3 VR Temperature" },
+       { 0x5b4, 0x0, 0x0, 0x0, 0x0, 500, "CVL Core Voltage VR Temperature" },
+       { 0x5c4, 0x5c8, 0x5cc, 0x5c0, 0x0, 500, "FPGA P-Tile Temperature [Remote]" },
+       { 0x5d0, 0x5d4, 0x5d8, 0x5c0, 0x0, 500, "FPGA E-Tile Temperature [Remote]" },
+       { 0x5dc, 0x5e0, 0x5e4, 0x5c0, 0x0, 500, "FPGA SDM Temperature [Remote]" },
+       { 0x5e8, 0x5ec, 0x5f0, 0x5c0, 0x0, 500, "FPGA Corner Temperature [Remote]" },
+};
+
+static const struct m10bmc_sdata n6000bmc_in_tbl[] = {
+       { 0x5f4, 0x0, 0x0, 0x0, 0x0, 1, "Inlet 12V PCIe Rail Voltage" },
+       { 0x60c, 0x0, 0x0, 0x0, 0x0, 1, "Inlet 12V Aux Rail Voltage" },
+       { 0x624, 0x0, 0x0, 0x0, 0x0, 1, "Inlet 3V3 PCIe Rail Voltage" },
+       { 0x63c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage Rail Voltage" },
+       { 0x644, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCH Rail Voltage" },
+       { 0x64c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCC_1V2 Rail Voltage" },
+       { 0x654, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCH_GXER_1V1, VCCA_1V8 Voltage" },
+       { 0x664, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCIO_1V2 Voltage" },
+       { 0x674, 0x0, 0x0, 0x0, 0x0, 1, "CVL Non Core Rails Inlet Voltage" },
+       { 0x684, 0x0, 0x0, 0x0, 0x0, 1, "MAX10 & Board CLK PWR 3V3 Inlet Voltage" },
+       { 0x694, 0x0, 0x0, 0x0, 0x0, 1, "CVL Core Voltage Rail Voltage" },
+       { 0x6ac, 0x0, 0x0, 0x0, 0x0, 1, "Board 3V3 VR Voltage" },
+       { 0x6b4, 0x0, 0x0, 0x0, 0x0, 1, "QSFP 3V3 Rail Voltage" },
+       { 0x6c4, 0x0, 0x0, 0x0, 0x0, 1, "QSFP (Primary) Supply Rail Voltage" },
+       { 0x6c8, 0x0, 0x0, 0x0, 0x0, 1, "QSFP (Secondary) Supply Rail Voltage" },
+       { 0x6cc, 0x0, 0x0, 0x0, 0x0, 1, "VCCCLK_GXER_2V5 Voltage" },
+       { 0x6d0, 0x0, 0x0, 0x0, 0x0, 1, "AVDDH_1V1_CVL Voltage" },
+       { 0x6d4, 0x0, 0x0, 0x0, 0x0, 1, "VDDH_1V8_CVL Voltage" },
+       { 0x6d8, 0x0, 0x0, 0x0, 0x0, 1, "VCCA_PLL Voltage" },
+       { 0x6e0, 0x0, 0x0, 0x0, 0x0, 1, "VCCRT_GXER_0V9 Voltage" },
+       { 0x6e8, 0x0, 0x0, 0x0, 0x0, 1, "VCCRT_GXPL_0V9 Voltage" },
+       { 0x6f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCH_GXPL_1V8 Voltage" },
+       { 0x6f4, 0x0, 0x0, 0x0, 0x0, 1, "VCCPT_1V8 Voltage" },
+       { 0x6fc, 0x0, 0x0, 0x0, 0x0, 1, "VCC_3V3_M10 Voltage" },
+       { 0x700, 0x0, 0x0, 0x0, 0x0, 1, "VCC_1V8_M10 Voltage" },
+       { 0x704, 0x0, 0x0, 0x0, 0x0, 1, "VCC_1V2_EMIF1_2_3 Voltage" },
+       { 0x70c, 0x0, 0x0, 0x0, 0x0, 1, "VCC_1V2_EMIF4_5 Voltage" },
+       { 0x714, 0x0, 0x0, 0x0, 0x0, 1, "VCCA_1V8 Voltage" },
+       { 0x718, 0x0, 0x0, 0x0, 0x0, 1, "VCCH_GXER_1V1 Voltage" },
+       { 0x71c, 0x0, 0x0, 0x0, 0x0, 1, "AVDD_ETH_0V9_CVL Voltage" },
+       { 0x720, 0x0, 0x0, 0x0, 0x0, 1, "AVDD_PCIE_0V9_CVL Voltage" },
+};
+
+static const struct m10bmc_sdata n6000bmc_curr_tbl[] = {
+       { 0x600, 0x604, 0x608, 0x0, 0x0, 1, "Inlet 12V PCIe Rail Current" },
+       { 0x618, 0x61c, 0x620, 0x0, 0x0, 1, "Inlet 12V Aux Rail Current" },
+       { 0x630, 0x634, 0x638, 0x0, 0x0, 1, "Inlet 3V3 PCIe Rail Current" },
+       { 0x640, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage Rail Current" },
+       { 0x648, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCH Rail Current" },
+       { 0x650, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCC_1V2 Rail Current" },
+       { 0x658, 0x65c, 0x660, 0x0, 0x0, 1, "FPGA VCCH_GXER_1V1, VCCA_1V8 Current" },
+       { 0x668, 0x66c, 0x670, 0x0, 0x0, 1, "FPGA VCCIO_1V2 Current" },
+       { 0x678, 0x67c, 0x680, 0x0, 0x0, 1, "CVL Non Core Rails Inlet Current" },
+       { 0x688, 0x68c, 0x690, 0x0, 0x0, 1, "MAX10 & Board CLK PWR 3V3 Inlet Current" },
+       { 0x698, 0x0, 0x0, 0x0, 0x0, 1, "CVL Core Voltage Rail Current" },
+       { 0x6b0, 0x0, 0x0, 0x0, 0x0, 1, "Board 3V3 VR Current" },
+       { 0x6b8, 0x6bc, 0x6c0, 0x0, 0x0, 1, "QSFP 3V3 Rail Current" },
+};
+
+static const struct m10bmc_sdata n6000bmc_power_tbl[] = {
+       { 0x724, 0x0, 0x0, 0x0, 0x0, 1, "Board Power" },
+};
+
+static const struct hwmon_channel_info *n6000bmc_hinfo[] = {
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL),
+       HWMON_CHANNEL_INFO(curr,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT |
+                          HWMON_C_LABEL),
+       HWMON_CHANNEL_INFO(power,
+                          HWMON_P_INPUT | HWMON_P_LABEL),
+       NULL
+};
+
+static const struct m10bmc_hwmon_board_data n6000bmc_hwmon_bdata = {
+       .tables = {
+               [hwmon_temp] = n6000bmc_temp_tbl,
+               [hwmon_in] = n6000bmc_in_tbl,
+               [hwmon_curr] = n6000bmc_curr_tbl,
+               [hwmon_power] = n6000bmc_power_tbl,
+       },
+
+       .hinfo = n6000bmc_hinfo,
+};
+
 static umode_t
 m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
                        u32 attr, int channel)
@@ -549,6 +774,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
                .name = "n5010bmc-hwmon",
                .driver_data = (unsigned long)&n5010bmc_hwmon_bdata,
        },
+       {
+               .name = "n6000bmc-hwmon",
+               .driver_data = (unsigned long)&n6000bmc_hwmon_bdata,
+       },
        { }
 };
 
index 9997f76b1f4aa345f3c46657960bb362f851cf0d..66f7ceaa7c3f5fa6328c905f5ca64b8504f25b3c 100644 (file)
@@ -34,6 +34,7 @@
  *            IT8786E  Super I/O chip w/LPC interface
  *            IT8790E  Super I/O chip w/LPC interface
  *            IT8792E  Super I/O chip w/LPC interface
+ *            IT87952E  Super I/O chip w/LPC interface
  *            Sis950   A clone of the IT8705F
  *
  *  Copyright (C) 2001 Chris Gauthron
 
 enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732,
             it8771, it8772, it8781, it8782, it8783, it8786, it8790,
-            it8792, it8603, it8620, it8622, it8628 };
-
-static unsigned short force_id;
-module_param(force_id, ushort, 0);
-MODULE_PARM_DESC(force_id, "Override the detected device ID");
-
-static bool ignore_resource_conflict;
-module_param(ignore_resource_conflict, bool, 0);
-MODULE_PARM_DESC(ignore_resource_conflict, "Ignore ACPI resource conflict");
+            it8792, it8603, it8620, it8622, it8628, it87952 };
 
 static struct platform_device *it87_pdev[2];
 
@@ -87,6 +80,14 @@ static struct platform_device *it87_pdev[2];
 #define        DEVID   0x20    /* Register: Device ID */
 #define        DEVREV  0x22    /* Register: Device Revision */
 
+static inline void __superio_enter(int ioreg)
+{
+       outb(0x87, ioreg);
+       outb(0x01, ioreg);
+       outb(0x55, ioreg);
+       outb(ioreg == REG_4E ? 0xaa : 0x55, ioreg);
+}
+
 static inline int superio_inb(int ioreg, int reg)
 {
        outb(reg, ioreg);
@@ -124,17 +125,16 @@ static inline int superio_enter(int ioreg)
        if (!request_muxed_region(ioreg, 2, DRVNAME))
                return -EBUSY;
 
-       outb(0x87, ioreg);
-       outb(0x01, ioreg);
-       outb(0x55, ioreg);
-       outb(ioreg == REG_4E ? 0xaa : 0x55, ioreg);
+       __superio_enter(ioreg);
        return 0;
 }
 
-static inline void superio_exit(int ioreg)
+static inline void superio_exit(int ioreg, bool noexit)
 {
-       outb(0x02, ioreg);
-       outb(0x02, ioreg + 1);
+       if (!noexit) {
+               outb(0x02, ioreg);
+               outb(0x02, ioreg + 1);
+       }
        release_region(ioreg, 2);
 }
 
@@ -161,6 +161,7 @@ static inline void superio_exit(int ioreg)
 #define IT8622E_DEVID 0x8622
 #define IT8623E_DEVID 0x8623
 #define IT8628E_DEVID 0x8628
+#define IT87952E_DEVID 0x8695
 #define IT87_ACT_REG  0x30
 #define IT87_BASE_REG 0x60
 
@@ -176,6 +177,13 @@ static inline void superio_exit(int ioreg)
 #define IT87_SIO_VID_REG       0xfc    /* VID value */
 #define IT87_SIO_BEEP_PIN_REG  0xf6    /* Beep pin mapping */
 
+/* Force chip IDs to specified values. Should only be used for testing */
+static unsigned short force_id[2];
+static unsigned int force_id_cnt;
+
+/* ACPI resource conflicts are ignored if this parameter is set to 1 */
+static bool ignore_resource_conflict;
+
 /* Update battery voltage after every reading if true */
 static bool update_vbat;
 
@@ -272,7 +280,7 @@ static const u8 IT87_REG_AUTO_BASE[] = { 0x60, 0x68, 0x70, 0x78, 0xa0, 0xa8 };
 
 struct it87_devices {
        const char *name;
-       const char * const suffix;
+       const char * const model;
        u32 features;
        u8 peci_mask;
        u8 old_peci_mask;
@@ -297,28 +305,35 @@ struct it87_devices {
 #define FEAT_PWM_FREQ2         BIT(16) /* Separate pwm freq 2 */
 #define FEAT_SIX_TEMP          BIT(17) /* Up to 6 temp sensors */
 #define FEAT_VIN3_5V           BIT(18) /* VIN3 connected to +5V */
+/*
+ * Disabling configuration mode on some chips can result in system
+ * hang-ups and access failures to the Super-IO chip at the
+ * second SIO address. Never exit configuration mode on these
+ * chips to avoid the problem.
+ */
+#define FEAT_CONF_NOEXIT       BIT(19) /* Chip should not exit conf mode */
 
 static const struct it87_devices it87_devices[] = {
        [it87] = {
                .name = "it87",
-               .suffix = "F",
+               .model = "IT87F",
                .features = FEAT_OLD_AUTOPWM,   /* may need to overwrite */
        },
        [it8712] = {
                .name = "it8712",
-               .suffix = "F",
+               .model = "IT8712F",
                .features = FEAT_OLD_AUTOPWM | FEAT_VID,
                                                /* may need to overwrite */
        },
        [it8716] = {
                .name = "it8716",
-               .suffix = "F",
+               .model = "IT8716F",
                .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
                  | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2,
        },
        [it8718] = {
                .name = "it8718",
-               .suffix = "F",
+               .model = "IT8718F",
                .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
                  | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS
                  | FEAT_PWM_FREQ2,
@@ -326,7 +341,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8720] = {
                .name = "it8720",
-               .suffix = "F",
+               .model = "IT8720F",
                .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
                  | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS
                  | FEAT_PWM_FREQ2,
@@ -334,7 +349,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8721] = {
                .name = "it8721",
-               .suffix = "F",
+               .model = "IT8721F",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
                  | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL
@@ -344,7 +359,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8728] = {
                .name = "it8728",
-               .suffix = "F",
+               .model = "IT8728F",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
                  | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2,
@@ -352,7 +367,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8732] = {
                .name = "it8732",
-               .suffix = "F",
+               .model = "IT8732F",
                .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
                  | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
@@ -361,7 +376,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8771] = {
                .name = "it8771",
-               .suffix = "E",
+               .model = "IT8771E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
                  | FEAT_PWM_FREQ2,
@@ -373,7 +388,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8772] = {
                .name = "it8772",
-               .suffix = "E",
+               .model = "IT8772E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
                  | FEAT_PWM_FREQ2,
@@ -385,28 +400,28 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8781] = {
                .name = "it8781",
-               .suffix = "F",
+               .model = "IT8781F",
                .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
                  | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2,
                .old_peci_mask = 0x4,
        },
        [it8782] = {
                .name = "it8782",
-               .suffix = "F",
+               .model = "IT8782F",
                .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
                  | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2,
                .old_peci_mask = 0x4,
        },
        [it8783] = {
                .name = "it8783",
-               .suffix = "E/F",
+               .model = "IT8783E/F",
                .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
                  | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2,
                .old_peci_mask = 0x4,
        },
        [it8786] = {
                .name = "it8786",
-               .suffix = "E",
+               .model = "IT8786E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
                  | FEAT_PWM_FREQ2,
@@ -414,24 +429,24 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8790] = {
                .name = "it8790",
-               .suffix = "E",
+               .model = "IT8790E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
-                 | FEAT_PWM_FREQ2,
+                 | FEAT_PWM_FREQ2 | FEAT_CONF_NOEXIT,
                .peci_mask = 0x07,
        },
        [it8792] = {
                .name = "it8792",
-               .suffix = "E",
+               .model = "IT8792E/IT8795E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
-                 | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
+                 | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_CONF_NOEXIT,
                .peci_mask = 0x07,
                .old_peci_mask = 0x02,  /* Actually reports PCH */
        },
        [it8603] = {
                .name = "it8603",
-               .suffix = "E",
+               .model = "IT8603E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
                  | FEAT_AVCC3 | FEAT_PWM_FREQ2,
@@ -439,7 +454,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8620] = {
                .name = "it8620",
-               .suffix = "E",
+               .model = "IT8620E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
                  | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
@@ -448,7 +463,7 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8622] = {
                .name = "it8622",
-               .suffix = "E",
+               .model = "IT8622E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
                  | FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
@@ -457,13 +472,22 @@ static const struct it87_devices it87_devices[] = {
        },
        [it8628] = {
                .name = "it8628",
-               .suffix = "E",
+               .model = "IT8628E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
                  | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
                  | FEAT_SIX_TEMP | FEAT_VIN3_5V,
                .peci_mask = 0x07,
        },
+       [it87952] = {
+               .name = "it87952",
+               .model = "IT87952E",
+               .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
+                 | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+                 | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_CONF_NOEXIT,
+               .peci_mask = 0x07,
+               .old_peci_mask = 0x02,  /* Actually reports PCH */
+       },
 };
 
 #define has_16bit_fans(data)   ((data)->features & FEAT_16BIT_FANS)
@@ -490,6 +514,7 @@ static const struct it87_devices it87_devices[] = {
 #define has_pwm_freq2(data)    ((data)->features & FEAT_PWM_FREQ2)
 #define has_six_temp(data)     ((data)->features & FEAT_SIX_TEMP)
 #define has_vin3_5v(data)      ((data)->features & FEAT_VIN3_5V)
+#define has_conf_noexit(data)  ((data)->features & FEAT_CONF_NOEXIT)
 
 struct it87_sio_data {
        int sioaddr;
@@ -2397,11 +2422,11 @@ static const struct attribute_group it87_group_auto_pwm = {
 
 /* SuperIO detection - will change isa_address if a chip is found */
 static int __init it87_find(int sioaddr, unsigned short *address,
-                           struct it87_sio_data *sio_data)
+                           struct it87_sio_data *sio_data, int chip_cnt)
 {
        int err;
        u16 chip_type;
-       const struct it87_devices *config;
+       const struct it87_devices *config = NULL;
 
        err = superio_enter(sioaddr);
        if (err)
@@ -2413,8 +2438,12 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        if (chip_type == 0xffff)
                goto exit;
 
-       if (force_id)
-               chip_type = force_id;
+       if (force_id_cnt == 1) {
+               /* If only one value given use for all chips */
+               if (force_id[0])
+                       chip_type = force_id[0];
+       } else if (force_id[chip_cnt])
+               chip_type = force_id[chip_cnt];
 
        switch (chip_type) {
        case IT8705F_DEVID:
@@ -2479,6 +2508,9 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        case IT8628E_DEVID:
                sio_data->type = it8628;
                break;
+       case IT87952E_DEVID:
+               sio_data->type = it87952;
+               break;
        case 0xffff:    /* No device at all */
                goto exit;
        default:
@@ -2486,27 +2518,29 @@ static int __init it87_find(int sioaddr, unsigned short *address,
                goto exit;
        }
 
+       config = &it87_devices[sio_data->type];
+
        superio_select(sioaddr, PME);
        if (!(superio_inb(sioaddr, IT87_ACT_REG) & 0x01)) {
-               pr_info("Device not activated, skipping\n");
+               pr_info("Device (chip %s ioreg 0x%x) not activated, skipping\n",
+                       config->model, sioaddr);
                goto exit;
        }
 
        *address = superio_inw(sioaddr, IT87_BASE_REG) & ~(IT87_EXTENT - 1);
        if (*address == 0) {
-               pr_info("Base address not set, skipping\n");
+               pr_info("Base address not set (chip %s ioreg 0x%x), skipping\n",
+                       config->model, sioaddr);
                goto exit;
        }
 
        err = 0;
        sio_data->sioaddr = sioaddr;
        sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f;
-       pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
-               it87_devices[sio_data->type].suffix,
+       pr_info("Found %s chip at 0x%x, revision %d\n",
+               it87_devices[sio_data->type].model,
                *address, sio_data->revision);
 
-       config = &it87_devices[sio_data->type];
-
        /* in7 (VSB or VCCH5V) is always internal on some chips */
        if (has_in7_internal(config))
                sio_data->internal |= BIT(1);
@@ -2824,7 +2858,7 @@ static int __init it87_find(int sioaddr, unsigned short *address,
                sio_data->skip_pwm |= dmi_data->skip_pwm;
 
 exit:
-       superio_exit(sioaddr);
+       superio_exit(sioaddr, config ? has_conf_noexit(config) : false);
        return err;
 }
 
@@ -3210,7 +3244,7 @@ static void it87_resume_sio(struct platform_device *pdev)
                             reg2c);
        }
 
-       superio_exit(data->sioaddr);
+       superio_exit(data->sioaddr, has_conf_noexit(data));
 }
 
 static int it87_resume(struct device *dev)
@@ -3310,6 +3344,27 @@ static int it87_dmi_cb(const struct dmi_system_id *dmi_entry)
        return 1;
 }
 
+/*
+ * On various Gigabyte AM4 boards (AB350, AX370), the second Super-IO chip
+ * (IT8792E) needs to be in configuration mode before accessing the first
+ * due to a bug in IT8792E which otherwise results in LPC bus access errors.
+ * This needs to be done before accessing the first Super-IO chip since
+ * the second chip may have been accessed prior to loading this driver.
+ *
+ * The problem is also reported to affect IT8795E, which is used on X299 boards
+ * and has the same chip ID as IT8792E (0x8733). It also appears to affect
+ * systems with IT8790E, which is used on some Z97X-Gaming boards as well as
+ * Z87X-OC.
+ * DMI entries for those systems will be added as they become available and
+ * as the problem is confirmed to affect those boards.
+ */
+static int it87_sio_force(const struct dmi_system_id *dmi_entry)
+{
+       __superio_enter(REG_4E);
+
+       return it87_dmi_cb(dmi_entry);
+};
+
 /*
  * On the Shuttle SN68PT, FAN_CTL2 is apparently not
  * connected to a fan, but to something else. One user
@@ -3332,7 +3387,34 @@ static struct it87_dmi_data nvidia_fn68pt = {
                .driver_data = data, \
        }
 
+#define IT87_DMI_MATCH_GBT(name, cb, data) \
+       IT87_DMI_MATCH_VND("Gigabyte Technology Co., Ltd.", name, cb, data)
+
 static const struct dmi_system_id it87_dmi_table[] __initconst = {
+       IT87_DMI_MATCH_GBT("AB350", it87_sio_force, NULL),
+               /* ? + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("AX370", it87_sio_force, NULL),
+               /* ? + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("Z97X-Gaming G1", it87_sio_force, NULL),
+               /* ? + IT8790E */
+       IT87_DMI_MATCH_GBT("TRX40 AORUS XTREME", it87_sio_force, NULL),
+               /* IT8688E + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("Z390 AORUS ULTRA-CF", it87_sio_force, NULL),
+               /* IT8688E + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("B550 AORUS PRO AC", it87_sio_force, NULL),
+               /* IT8688E + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("X570 AORUS MASTER", it87_sio_force, NULL),
+               /* IT8688E + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("X570 AORUS PRO", it87_sio_force, NULL),
+               /* IT8688E + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("X570 AORUS PRO WIFI", it87_sio_force, NULL),
+               /* IT8688E + IT8792E/IT8795E */
+       IT87_DMI_MATCH_GBT("X570S AERO G", it87_sio_force, NULL),
+               /* IT8689E + IT87952E */
+       IT87_DMI_MATCH_GBT("Z690 AORUS PRO DDR4", it87_sio_force, NULL),
+               /* IT8689E + IT87952E */
+       IT87_DMI_MATCH_GBT("Z690 AORUS PRO", it87_sio_force, NULL),
+               /* IT8689E + IT87952E */
        IT87_DMI_MATCH_VND("nVIDIA", "FN68PT", it87_dmi_cb, &nvidia_fn68pt),
        { }
 
@@ -3356,7 +3438,7 @@ static int __init sm_it87_init(void)
        for (i = 0; i < ARRAY_SIZE(sioaddr); i++) {
                memset(&sio_data, 0, sizeof(struct it87_sio_data));
                isa_address[i] = 0;
-               err = it87_find(sioaddr[i], &isa_address[i], &sio_data);
+               err = it87_find(sioaddr[i], &isa_address[i], &sio_data, i);
                if (err || isa_address[i] == 0)
                        continue;
                /*
@@ -3404,11 +3486,20 @@ static void __exit sm_it87_exit(void)
 
 MODULE_AUTHOR("Chris Gauthron, Jean Delvare <jdelvare@suse.de>");
 MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver");
+
+module_param_array(force_id, ushort, &force_id_cnt, 0);
+MODULE_PARM_DESC(force_id, "Override one or more detected device ID(s)");
+
+module_param(ignore_resource_conflict, bool, 0);
+MODULE_PARM_DESC(ignore_resource_conflict, "Ignore ACPI resource conflict");
+
 module_param(update_vbat, bool, 0);
 MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
+
 module_param(fix_pwm_polarity, bool, 0);
 MODULE_PARM_DESC(fix_pwm_polarity,
                 "Force PWM polarity to active high (DANGEROUS)");
+
 MODULE_LICENSE("GPL");
 
 module_init(sm_it87_init);
index 9adebb59f6042b1fc9ea9eae1166d4b02221393d..3494f7261ebf5ed93c3ee492b9aa564b82867d72 100644 (file)
 #define CONTROL_MULT_SELECT    (1 << 0)
 #define CONTROL_TEST_MODE      (1 << 4)
 
+static const struct of_device_id __maybe_unused ltc2945_of_match[] = {
+       { .compatible = "adi,ltc2945" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ltc2945_of_match);
+
+/**
+ * struct ltc2945_data - LTC2945 device data
+ * @regmap: regmap device
+ * @shunt_resistor: shunt resistor value in micro ohms (1000 by default)
+ */
+struct ltc2945_data {
+       struct regmap *regmap;
+       u32 shunt_resistor;
+};
+
 static inline bool is_power_reg(u8 reg)
 {
        return reg < LTC2945_SENSE_H;
@@ -66,7 +82,9 @@ static inline bool is_power_reg(u8 reg)
 /* Return the value from the given register in uW, mV, or mA */
 static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
 {
-       struct regmap *regmap = dev_get_drvdata(dev);
+       struct ltc2945_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       u32 shunt_resistor = data->shunt_resistor;
        unsigned int control;
        u8 buf[3];
        long long val;
@@ -78,10 +96,10 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
                return ret;
 
        if (is_power_reg(reg)) {
-               /* power */
+               /* 24-bit power */
                val = (buf[0] << 16) + (buf[1] << 8) + buf[2];
        } else {
-               /* current, voltage */
+               /* 12-bit current, voltage */
                val = (buf[0] << 4) + (buf[1] >> 4);
        }
 
@@ -92,9 +110,7 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
        case LTC2945_MAX_POWER_THRES_H:
        case LTC2945_MIN_POWER_THRES_H:
                /*
-                * Convert to uW by assuming current is measured with
-                * an 1mOhm sense resistor, similar to current
-                * measurements.
+                * Convert to uW
                 * Control register bit 0 selects if voltage at SENSE+/VDD
                 * or voltage at ADIN is used to measure power.
                 */
@@ -108,6 +124,14 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
                        /* 0.5 mV * 25 uV = 0.0125 uV resolution. */
                        val = (val * 25LL) >> 1;
                }
+               val *= 1000;
+               /* Overflow check: Assuming max 24-bit power, val is at most 53 bits right now. */
+               val = DIV_ROUND_CLOSEST_ULL(val, shunt_resistor);
+               /*
+                * Overflow check: After division, depending on shunt resistor,
+                * val can still be > 32 bits so returning long long makes sense
+                */
+
                break;
        case LTC2945_VIN_H:
        case LTC2945_MAX_VIN_H:
@@ -130,14 +154,11 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
        case LTC2945_MIN_SENSE_H:
        case LTC2945_MAX_SENSE_THRES_H:
        case LTC2945_MIN_SENSE_THRES_H:
-               /*
-                * 25 uV resolution. Convert to current as measured with
-                * an 1 mOhm sense resistor, in mA. If a different sense
-                * resistor is installed, calculate the actual current by
-                * dividing the reported current by the sense resistor value
-                * in mOhm.
-                */
-               val *= 25;
+               /* 25 uV resolution. Convert to mA. */
+               val *= 25 * 1000;
+               /* Overflow check: Assuming max 12-bit sense, val is at most 27 bits right now */
+               val = DIV_ROUND_CLOSEST_ULL(val, shunt_resistor);
+               /* Overflow check: After division, <= 27 bits */
                break;
        default:
                return -EINVAL;
@@ -145,13 +166,18 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
        return val;
 }
 
-static int ltc2945_val_to_reg(struct device *dev, u8 reg,
-                             unsigned long val)
+static long long ltc2945_val_to_reg(struct device *dev, u8 reg,
+                                   unsigned long long val)
 {
-       struct regmap *regmap = dev_get_drvdata(dev);
+       struct ltc2945_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
+       u32 shunt_resistor = data->shunt_resistor;
        unsigned int control;
        int ret;
 
+       /* Ensure we don't overflow */
+       val = clamp_val(val, 0, U32_MAX);
+
        switch (reg) {
        case LTC2945_POWER_H:
        case LTC2945_MAX_POWER_H:
@@ -159,9 +185,6 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg,
        case LTC2945_MAX_POWER_THRES_H:
        case LTC2945_MIN_POWER_THRES_H:
                /*
-                * Convert to register value by assuming current is measured
-                * with an 1mOhm sense resistor, similar to current
-                * measurements.
                 * Control register bit 0 selects if voltage at SENSE+/VDD
                 * or voltage at ADIN is used to measure power, which in turn
                 * determines register calculations.
@@ -171,14 +194,16 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg,
                        return ret;
                if (control & CONTROL_MULT_SELECT) {
                        /* 25 mV * 25 uV = 0.625 uV resolution. */
-                       val = DIV_ROUND_CLOSEST(val, 625);
+                       val *= shunt_resistor;
+                       /* Overflow check: Assuming 32-bit val and shunt resistor, val <= 64bits */
+                       val = DIV_ROUND_CLOSEST_ULL(val, 625 * 1000);
+                       /* Overflow check: val is now <= 44 bits */
                } else {
-                       /*
-                        * 0.5 mV * 25 uV = 0.0125 uV resolution.
-                        * Divide first to avoid overflow;
-                        * accept loss of accuracy.
-                        */
-                       val = DIV_ROUND_CLOSEST(val, 25) * 2;
+                       /* 0.5 mV * 25 uV = 0.0125 uV resolution. */
+                       val *= shunt_resistor;
+                       /* Overflow check: Assuming 32-bit val and shunt resistor, val <= 64bits */
+                       val = DIV_ROUND_CLOSEST_ULL(val, 25 * 1000) * 2;
+                       /* Overflow check: val is now <= 51 bits */
                }
                break;
        case LTC2945_VIN_H:
@@ -187,7 +212,7 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg,
        case LTC2945_MAX_VIN_THRES_H:
        case LTC2945_MIN_VIN_THRES_H:
                /* 25 mV resolution. */
-               val /= 25;
+               val = DIV_ROUND_CLOSEST_ULL(val, 25);
                break;
        case LTC2945_ADIN_H:
        case LTC2945_MAX_ADIN_H:
@@ -202,14 +227,11 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg,
        case LTC2945_MIN_SENSE_H:
        case LTC2945_MAX_SENSE_THRES_H:
        case LTC2945_MIN_SENSE_THRES_H:
-               /*
-                * 25 uV resolution. Convert to current as measured with
-                * an 1 mOhm sense resistor, in mA. If a different sense
-                * resistor is installed, calculate the actual current by
-                * dividing the reported current by the sense resistor value
-                * in mOhm.
-                */
-               val = DIV_ROUND_CLOSEST(val, 25);
+               /* 25 uV resolution. Convert to  mA. */
+               val *= shunt_resistor;
+               /* Overflow check: Assuming 32-bit val and 32-bit shunt resistor, val is 64bits */
+               val = DIV_ROUND_CLOSEST_ULL(val, 25 * 1000);
+               /* Overflow check: val is now <= 50 bits */
                break;
        default:
                return -EINVAL;
@@ -234,20 +256,23 @@ static ssize_t ltc2945_value_store(struct device *dev,
                                   const char *buf, size_t count)
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct regmap *regmap = dev_get_drvdata(dev);
+       struct ltc2945_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
        u8 reg = attr->index;
-       unsigned long val;
+       unsigned int val;
        u8 regbuf[3];
        int num_regs;
-       int regval;
+       long long regval;
        int ret;
 
-       ret = kstrtoul(buf, 10, &val);
+       ret = kstrtouint(buf, 10, &val);
        if (ret)
                return ret;
 
        /* convert to register value, then clamp and write result */
        regval = ltc2945_val_to_reg(dev, reg, val);
+       if (regval < 0)
+               return regval;
        if (is_power_reg(reg)) {
                regval = clamp_val(regval, 0, 0xffffff);
                regbuf[0] = regval >> 16;
@@ -269,7 +294,8 @@ static ssize_t ltc2945_history_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct regmap *regmap = dev_get_drvdata(dev);
+       struct ltc2945_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
        u8 reg = attr->index;
        int num_regs = is_power_reg(reg) ? 3 : 2;
        u8 buf_min[3] = { 0xff, 0xff, 0xff };
@@ -321,7 +347,8 @@ static ssize_t ltc2945_bool_show(struct device *dev,
                                 struct device_attribute *da, char *buf)
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct regmap *regmap = dev_get_drvdata(dev);
+       struct ltc2945_data *data = dev_get_drvdata(dev);
+       struct regmap *regmap = data->regmap;
        unsigned int fault;
        int ret;
 
@@ -450,6 +477,12 @@ static int ltc2945_probe(struct i2c_client *client)
        struct device *dev = &client->dev;
        struct device *hwmon_dev;
        struct regmap *regmap;
+       struct ltc2945_data *data;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       dev_set_drvdata(dev, data);
 
        regmap = devm_regmap_init_i2c(client, &ltc2945_regmap_config);
        if (IS_ERR(regmap)) {
@@ -457,11 +490,19 @@ static int ltc2945_probe(struct i2c_client *client)
                return PTR_ERR(regmap);
        }
 
+       data->regmap = regmap;
+       if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",
+                                    &data->shunt_resistor))
+               data->shunt_resistor = 1000;
+
+       if (data->shunt_resistor == 0)
+               return -EINVAL;
+
        /* Clear faults */
        regmap_write(regmap, LTC2945_FAULT, 0x00);
 
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
-                                                          regmap,
+                                                          data,
                                                           ltc2945_groups);
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }
@@ -475,8 +516,9 @@ MODULE_DEVICE_TABLE(i2c, ltc2945_id);
 
 static struct i2c_driver ltc2945_driver = {
        .driver = {
-                  .name = "ltc2945",
-                  },
+               .name = "ltc2945",
+               .of_match_table = of_match_ptr(ltc2945_of_match),
+       },
        .probe_new = ltc2945_probe,
        .id_table = ltc2945_id,
 };
diff --git a/drivers/hwmon/mc34vr500.c b/drivers/hwmon/mc34vr500.c
new file mode 100644 (file)
index 0000000..6268e97
--- /dev/null
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * An hwmon driver for the NXP MC34VR500 PMIC
+ *
+ * Author: Mario Kicherer <dev@kicherer.org>
+ */
+
+#include <linux/bits.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#define MC34VR500_I2C_ADDR             0x08
+#define MC34VR500_DEVICEID_VALUE       0x14
+
+/* INTSENSE0 */
+#define ENS_BIT                BIT(0)
+#define LOWVINS_BIT    BIT(1)
+#define THERM110S_BIT  BIT(2)
+#define THERM120S_BIT  BIT(3)
+#define THERM125S_BIT  BIT(4)
+#define THERM130S_BIT  BIT(5)
+
+#define MC34VR500_DEVICEID     0x00
+
+#define MC34VR500_SILICONREVID 0x03
+#define MC34VR500_FABID                0x04
+#define MC34VR500_INTSTAT0     0x05
+#define MC34VR500_INTMASK0     0x06
+#define MC34VR500_INTSENSE0    0x07
+
+struct mc34vr500_data {
+       struct device *hwmon_dev;
+       struct regmap *regmap;
+};
+
+static irqreturn_t mc34vr500_process_interrupt(int irq, void *userdata)
+{
+       struct mc34vr500_data *data = (struct mc34vr500_data *)userdata;
+       unsigned int reg;
+       int ret;
+
+       ret = regmap_read(data->regmap, MC34VR500_INTSTAT0, &reg);
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       if (reg) {
+               if (reg & LOWVINS_BIT)
+                       hwmon_notify_event(data->hwmon_dev, hwmon_in,
+                                          hwmon_in_min_alarm, 0);
+
+               if (reg & THERM110S_BIT)
+                       hwmon_notify_event(data->hwmon_dev, hwmon_temp,
+                                          hwmon_temp_max_alarm, 0);
+
+               if (reg & THERM120S_BIT)
+                       hwmon_notify_event(data->hwmon_dev, hwmon_temp,
+                                          hwmon_temp_crit_alarm, 0);
+
+               if (reg & THERM130S_BIT)
+                       hwmon_notify_event(data->hwmon_dev, hwmon_temp,
+                                          hwmon_temp_emergency_alarm, 0);
+
+               /* write 1 to clear */
+               regmap_write(data->regmap, MC34VR500_INTSTAT0, LOWVINS_BIT |
+                            THERM110S_BIT | THERM120S_BIT | THERM130S_BIT);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static umode_t mc34vr500_is_visible(const void *data,
+                                   enum hwmon_sensor_types type,
+                                   u32 attr, int channel)
+{
+       switch (attr) {
+       case hwmon_in_min_alarm:
+       case hwmon_temp_max_alarm:
+       case hwmon_temp_crit_alarm:
+       case hwmon_temp_emergency_alarm:
+               return 0444;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int mc34vr500_alarm_read(struct mc34vr500_data *data, int index,
+                               long *val)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = regmap_read(data->regmap, MC34VR500_INTSENSE0, &reg);
+       if (ret < 0)
+               return ret;
+
+       *val = !!(reg & index);
+
+       return 0;
+}
+
+static int mc34vr500_read(struct device *dev, enum hwmon_sensor_types type,
+                         u32 attr, int channel, long *val)
+{
+       struct mc34vr500_data *data = dev_get_drvdata(dev);
+
+       switch (type) {
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_min_alarm:
+                       return mc34vr500_alarm_read(data, LOWVINS_BIT, val);
+               default:
+                       return -EOPNOTSUPP;
+               }
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_max_alarm:
+                       return mc34vr500_alarm_read(data, THERM110S_BIT, val);
+               case hwmon_temp_crit_alarm:
+                       return mc34vr500_alarm_read(data, THERM120S_BIT, val);
+               case hwmon_temp_emergency_alarm:
+                       return mc34vr500_alarm_read(data, THERM130S_BIT, val);
+               default:
+                       return -EOPNOTSUPP;
+               }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static const struct hwmon_channel_info *mc34vr500_info[] = {
+       HWMON_CHANNEL_INFO(in, HWMON_I_MIN_ALARM),
+       HWMON_CHANNEL_INFO(temp, HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM
+                          | HWMON_T_EMERGENCY_ALARM),
+       NULL,
+};
+
+static const struct hwmon_ops mc34vr500_hwmon_ops = {
+       .is_visible = mc34vr500_is_visible,
+       .read = mc34vr500_read,
+};
+
+static const struct hwmon_chip_info mc34vr500_chip_info = {
+       .ops = &mc34vr500_hwmon_ops,
+       .info = mc34vr500_info,
+};
+
+static const struct regmap_config mc34vr500_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = MC34VR500_INTSENSE0,
+};
+
+static int mc34vr500_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct mc34vr500_data *data;
+       struct device *hwmon_dev;
+       int ret;
+       unsigned int reg, revid, fabid;
+       struct regmap *regmap;
+
+       regmap = devm_regmap_init_i2c(client, &mc34vr500_regmap_config);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       data = devm_kzalloc(dev, sizeof(struct mc34vr500_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->regmap = regmap;
+
+       ret = regmap_read(regmap, MC34VR500_DEVICEID, &reg);
+       if (ret < 0)
+               return ret;
+
+       if (reg != MC34VR500_DEVICEID_VALUE)
+               return -ENODEV;
+
+       ret = regmap_read(regmap, MC34VR500_SILICONREVID, &revid);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read(regmap, MC34VR500_FABID, &fabid);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(dev, "mc34vr500: revid 0x%x fabid 0x%x\n", revid, fabid);
+
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+                                                        data,
+                                                        &mc34vr500_chip_info,
+                                                        NULL);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
+
+       data->hwmon_dev = hwmon_dev;
+
+       if (client->irq) {
+               ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                               mc34vr500_process_interrupt,
+                                               IRQF_TRIGGER_RISING |
+                                               IRQF_ONESHOT |
+                                               IRQF_SHARED,
+                                               dev_name(dev), data);
+               if (ret)
+                       return ret;
+
+               /* write 1 to clear interrupts */
+               ret = regmap_write(regmap, MC34VR500_INTSTAT0, LOWVINS_BIT |
+                                  THERM110S_BIT | THERM120S_BIT |
+                                  THERM130S_BIT);
+               if (ret)
+                       return ret;
+
+               /* unmask interrupts */
+               ret = regmap_write(regmap, MC34VR500_INTMASK0,
+                                  (unsigned int) ~(LOWVINS_BIT | THERM110S_BIT |
+                                                   THERM120S_BIT | THERM130S_BIT));
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id mc34vr500_id[] = {
+       { "mc34vr500", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, mc34vr500_id);
+
+static const struct of_device_id __maybe_unused mc34vr500_of_match[] = {
+       { .compatible = "nxp,mc34vr500" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, mc34vr500_of_match);
+
+static struct i2c_driver mc34vr500_driver = {
+       .driver = {
+                  .name = "mc34vr500",
+                  .of_match_table = of_match_ptr(mc34vr500_of_match),
+                   },
+       .probe_new = mc34vr500_probe,
+       .id_table = mc34vr500_id,
+};
+
+module_i2c_driver(mc34vr500_driver);
+
+MODULE_AUTHOR("Mario Kicherer <dev@kicherer.org>");
+
+MODULE_DESCRIPTION("MC34VR500 driver");
+MODULE_LICENSE("GPL");
index b48bd7c961d665c4bab51f627f524f88ae415cf6..96017cc8da7ecd5b79e9068bccce9be1315f674e 100644 (file)
@@ -155,6 +155,12 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
                        if (err)
                                return err;
 
+                       if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) {
+                               /* FAN is broken - return zero for FAN speed. */
+                               *val = 0;
+                               return 0;
+                       }
+
                        *val = MLXREG_FAN_GET_RPM(regval, fan->divider,
                                                  fan->samples);
                        break;
index da9ec6983e139b1485c52042edbea38417993031..c54233f0369b2487376d13a76b453f191dc5c0f4 100644 (file)
@@ -1150,7 +1150,7 @@ static int nct6775_write_fan_div(struct nct6775_data *data, int nr)
        if (err)
                return err;
        reg &= 0x70 >> oddshift;
-       reg |= data->fan_div[nr] & (0x7 << oddshift);
+       reg |= (data->fan_div[nr] & 0x7) << oddshift;
        return nct6775_write_value(data, fandiv_reg, reg);
 }
 
index bf43f73dc835fbc582e5f3d154b3e3153456389d..76c6b564d7fc46f8901509ce81cf24b996247f0e 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
-#include <linux/wmi.h>
 
 #include "nct6775.h"
 
@@ -107,40 +106,51 @@ struct nct6775_sio_data {
        void (*sio_exit)(struct nct6775_sio_data *sio_data);
 };
 
-#define ASUSWMI_MONITORING_GUID                "466747A0-70EC-11DE-8A39-0800200C9A66"
+#define ASUSWMI_METHOD                 "WMBD"
 #define ASUSWMI_METHODID_RSIO          0x5253494F
 #define ASUSWMI_METHODID_WSIO          0x5753494F
 #define ASUSWMI_METHODID_RHWM          0x5248574D
 #define ASUSWMI_METHODID_WHWM          0x5748574D
 #define ASUSWMI_UNSUPPORTED_METHOD     0xFFFFFFFE
+#define ASUSWMI_DEVICE_HID             "PNP0C14"
+#define ASUSWMI_DEVICE_UID             "ASUSWMI"
+#define ASUSMSI_DEVICE_UID             "AsusMbSwInterface"
+
+#if IS_ENABLED(CONFIG_ACPI)
+/*
+ * ASUS boards have only one device with WMI "WMBD" method and have provided
+ * access to only one SuperIO chip at 0x0290.
+ */
+static struct acpi_device *asus_acpi_dev;
+#endif
 
 static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
 {
-#if IS_ENABLED(CONFIG_ACPI_WMI)
+#if IS_ENABLED(CONFIG_ACPI)
+       acpi_handle handle = acpi_device_handle(asus_acpi_dev);
        u32 args = bank | (reg << 8) | (val << 16);
-       struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
-       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct acpi_object_list input;
+       union acpi_object params[3];
+       unsigned long long result;
        acpi_status status;
-       union acpi_object *obj;
-       u32 tmp = ASUSWMI_UNSUPPORTED_METHOD;
-
-       status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
-                                    method_id, &input, &output);
 
+       params[0].type = ACPI_TYPE_INTEGER;
+       params[0].integer.value = 0;
+       params[1].type = ACPI_TYPE_INTEGER;
+       params[1].integer.value = method_id;
+       params[2].type = ACPI_TYPE_BUFFER;
+       params[2].buffer.length = sizeof(args);
+       params[2].buffer.pointer = (void *)&args;
+       input.count = 3;
+       input.pointer = params;
+
+       status = acpi_evaluate_integer(handle, ASUSWMI_METHOD, &input, &result);
        if (ACPI_FAILURE(status))
                return -EIO;
 
-       obj = output.pointer;
-       if (obj && obj->type == ACPI_TYPE_INTEGER)
-               tmp = obj->integer.value;
-
        if (retval)
-               *retval = tmp;
-
-       kfree(obj);
+               *retval = (u32)result & 0xFFFFFFFF;
 
-       if (tmp == ASUSWMI_UNSUPPORTED_METHOD)
-               return -ENODEV;
        return 0;
 #else
        return -EOPNOTSUPP;
@@ -1099,6 +1109,91 @@ static const char * const asus_wmi_boards[] = {
        "TUF GAMING Z490-PLUS (WI-FI)",
 };
 
+static const char * const asus_msi_boards[] = {
+       "EX-B660M-V5 PRO D4",
+       "PRIME B650-PLUS",
+       "PRIME B650M-A",
+       "PRIME B650M-A AX",
+       "PRIME B650M-A II",
+       "PRIME B650M-A WIFI",
+       "PRIME B650M-A WIFI II",
+       "PRIME B660M-A D4",
+       "PRIME B660M-A WIFI D4",
+       "PRIME X670-P",
+       "PRIME X670-P WIFI",
+       "PRIME X670E-PRO WIFI",
+       "Pro B660M-C-D4",
+       "ProArt B660-CREATOR D4",
+       "ProArt X670E-CREATOR WIFI",
+       "ROG CROSSHAIR X670E EXTREME",
+       "ROG CROSSHAIR X670E GENE",
+       "ROG CROSSHAIR X670E HERO",
+       "ROG MAXIMUS XIII EXTREME GLACIAL",
+       "ROG MAXIMUS Z690 EXTREME",
+       "ROG MAXIMUS Z690 EXTREME GLACIAL",
+       "ROG STRIX B650-A GAMING WIFI",
+       "ROG STRIX B650E-E GAMING WIFI",
+       "ROG STRIX B650E-F GAMING WIFI",
+       "ROG STRIX B650E-I GAMING WIFI",
+       "ROG STRIX B660-A GAMING WIFI D4",
+       "ROG STRIX B660-F GAMING WIFI",
+       "ROG STRIX B660-G GAMING WIFI",
+       "ROG STRIX B660-I GAMING WIFI",
+       "ROG STRIX X670E-A GAMING WIFI",
+       "ROG STRIX X670E-E GAMING WIFI",
+       "ROG STRIX X670E-F GAMING WIFI",
+       "ROG STRIX X670E-I GAMING WIFI",
+       "ROG STRIX Z590-A GAMING WIFI II",
+       "ROG STRIX Z690-A GAMING WIFI D4",
+       "TUF GAMING B650-PLUS",
+       "TUF GAMING B650-PLUS WIFI",
+       "TUF GAMING B650M-PLUS",
+       "TUF GAMING B650M-PLUS WIFI",
+       "TUF GAMING B660M-PLUS WIFI",
+       "TUF GAMING X670E-PLUS",
+       "TUF GAMING X670E-PLUS WIFI",
+       "TUF GAMING Z590-PLUS WIFI",
+};
+
+#if IS_ENABLED(CONFIG_ACPI)
+/*
+ * Callback for acpi_bus_for_each_dev() to find the right device
+ * by _UID and _HID and return 1 to stop iteration.
+ */
+static int nct6775_asuswmi_device_match(struct device *dev, void *data)
+{
+       struct acpi_device *adev = to_acpi_device(dev);
+       const char *uid = acpi_device_uid(adev);
+       const char *hid = acpi_device_hid(adev);
+
+       if (hid && !strcmp(hid, ASUSWMI_DEVICE_HID) && uid && !strcmp(uid, data)) {
+               asus_acpi_dev = adev;
+               return 1;
+       }
+
+       return 0;
+}
+#endif
+
+static enum sensor_access nct6775_determine_access(const char *device_uid)
+{
+#if IS_ENABLED(CONFIG_ACPI)
+       u8 tmp;
+
+       acpi_bus_for_each_dev(nct6775_asuswmi_device_match, (void *)device_uid);
+       if (!asus_acpi_dev)
+               return access_direct;
+
+       /* if reading chip id via ACPI succeeds, use WMI "WMBD" method for access */
+       if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) {
+               pr_debug("Using Asus WMBD method of %s to access %#x chip.\n", device_uid, tmp);
+               return access_asuswmi;
+       }
+#endif
+
+       return access_direct;
+}
+
 static int __init sensors_nct6775_platform_init(void)
 {
        int i, err;
@@ -1109,7 +1204,6 @@ static int __init sensors_nct6775_platform_init(void)
        int sioaddr[2] = { 0x2e, 0x4e };
        enum sensor_access access = access_direct;
        const char *board_vendor, *board_name;
-       u8 tmp;
 
        err = platform_driver_register(&nct6775_driver);
        if (err)
@@ -1122,15 +1216,13 @@ static int __init sensors_nct6775_platform_init(void)
            !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) {
                err = match_string(asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards),
                                   board_name);
-               if (err >= 0) {
-                       /* if reading chip id via WMI succeeds, use WMI */
-                       if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) {
-                               pr_info("Using Asus WMI to access %#x chip.\n", tmp);
-                               access = access_asuswmi;
-                       } else {
-                               pr_err("Can't read ChipID by Asus WMI.\n");
-                       }
-               }
+               if (err >= 0)
+                       access = nct6775_determine_access(ASUSWMI_DEVICE_UID);
+
+               err = match_string(asus_msi_boards, ARRAY_SIZE(asus_msi_boards),
+                                  board_name);
+               if (err >= 0)
+                       access = nct6775_determine_access(ASUSMSI_DEVICE_UID);
        }
 
        /*
index 533f38b0b4e9bfe1af3a38250d744649fa872ba1..2b93ba89610ae0f47c5bab295230fb02beab9099 100644 (file)
@@ -791,6 +791,7 @@ static const struct hid_device_id nzxt_smart2_hid_id_table[] = {
        { HID_USB_DEVICE(0x1e71, 0x2009) }, /* NZXT RGB & Fan Controller */
        { HID_USB_DEVICE(0x1e71, 0x200e) }, /* NZXT RGB & Fan Controller */
        { HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */
+       { HID_USB_DEVICE(0x1e71, 0x2019) }, /* NZXT RGB & Fan Controller */
        {},
 };
 
index f84ec8f8eda9187e5752a98ae6175595cfbccdfd..36872b57912afdeedcb5ab994fdcd4e8aa129101 100644 (file)
@@ -1,12 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Platform driver for OXP Handhelds that expose fan reading and control
- * via hwmon sysfs.
+ * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose
+ * fan reading and control via hwmon sysfs.
  *
- * Old boards have the same DMI strings and they are told appart by the
- * boot cpu vendor (Intel/AMD). Currently only AMD boards are supported
- * but the code is made to be simple to add other handheld boards in the
- * future.
+ * Old OXP boards have the same DMI strings and they are told apart by
+ * the boot cpu vendor (Intel/AMD). Currently only AMD boards are
+ * supported but the code is made to be simple to add other handheld
+ * boards in the future.
  * Fan control is provided via pwm interface in the range [0-255].
  * Old AMD boards use [0-100] as range in the EC, the written value is
  * scaled to accommodate for that. Newer boards like the mini PRO and
@@ -42,6 +42,8 @@ static bool unlock_global_acpi_lock(void)
 
 enum oxp_board {
        aok_zoe_a1 = 1,
+       aya_neo_air,
+       aya_neo_air_pro,
        oxp_mini_amd,
        oxp_mini_amd_pro,
 };
@@ -60,6 +62,20 @@ static const struct dmi_system_id dmi_table[] = {
                },
                .driver_data = (void *) &(enum oxp_board) {aok_zoe_a1},
        },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
+               },
+               .driver_data = (void *) &(enum oxp_board) {aya_neo_air},
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
+               },
+               .driver_data = (void *) &(enum oxp_board) {aya_neo_air_pro},
+       },
        {
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
@@ -161,8 +177,17 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
                        ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
                        if (ret)
                                return ret;
-                       if (board == oxp_mini_amd)
+                       switch (board) {
+                       case aya_neo_air:
+                       case aya_neo_air_pro:
+                       case oxp_mini_amd:
                                *val = (*val * 255) / 100;
+                               break;
+                       case oxp_mini_amd_pro:
+                       case aok_zoe_a1:
+                       default:
+                               break;
+                       }
                        return 0;
                case hwmon_pwm_enable:
                        return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
@@ -191,8 +216,17 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
                case hwmon_pwm_input:
                        if (val < 0 || val > 255)
                                return -EINVAL;
-                       if (board == oxp_mini_amd)
+                       switch (board) {
+                       case aya_neo_air:
+                       case aya_neo_air_pro:
+                       case oxp_mini_amd:
                                val = (val * 100) / 255;
+                               break;
+                       case aok_zoe_a1:
+                       case oxp_mini_amd_pro:
+                       default:
+                               break;
+                       }
                        return write_to_ec(dev, OXP_SENSOR_PWM_REG, val);
                default:
                        break;
@@ -233,7 +267,7 @@ static int oxp_platform_probe(struct platform_device *pdev)
 
        /*
         * Have to check for AMD processor here because DMI strings are the
-        * same between Intel and AMD boards, the only way to tell them appart
+        * same between Intel and AMD boards, the only way to tell them apart
         * is the CPU.
         * Intel boards seem to have different EC registers and values to
         * read/write.
index 57470fda5f6c91f46c02ff3563f8dbd781342527..30850a479f61fdf96b5ae1cdd985b09802575bdc 100644 (file)
@@ -402,7 +402,7 @@ static int create_temp_label(struct peci_cputemp *priv)
        unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX);
        int i;
 
-       priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL);
+       priv->coretemp_label = devm_kzalloc(priv->dev, (core_max + 1) * sizeof(char *), GFP_KERNEL);
        if (!priv->coretemp_label)
                return -ENOMEM;
 
index 89668af67206084deef93e3c8a60ce9729c4506f..59d9a7430499f790ad57532710e4568712170158 100644 (file)
@@ -237,10 +237,10 @@ config SENSORS_MAX16064
          be called max16064.
 
 config SENSORS_MAX16601
-       tristate "Maxim MAX16508, MAX16601, MAX16602"
+       tristate "Maxim MAX16508, MAX16600, MAX16601, and MAX16602"
        help
          If you say yes here you get hardware monitoring support for Maxim
-         MAX16508, MAX16601 and MAX16602.
+         MAX16508, MAX16600, MAX16601, and MAX16602.
 
          This driver can also be built as a module. If so, the module will
          be called max16601.
@@ -317,6 +317,22 @@ config SENSORS_MP5023
          This driver can also be built as a module. If so, the module will
          be called mp5023.
 
+config SENSORS_MPQ7932_REGULATOR
+       bool "Regulator support for MPQ7932"
+       depends on SENSORS_MPQ7932 && REGULATOR
+       help
+         If you say yes here you get six integrated buck converter regulator
+         support for power management IC MPS MPQ7932.
+
+config SENSORS_MPQ7932
+       tristate "MPS MPQ7932"
+       help
+         If you say yes here you get hardware monitoring functionality support
+         for power management IC MPS MPQ7932.
+
+         This driver can also be built as a module. If so, the module will
+         be called mpq7932.
+
 config SENSORS_PIM4328
        tristate "Flex PIM4328 and compatibles"
        help
@@ -379,6 +395,22 @@ config SENSORS_STPDDC60
          This driver can also be built as a module. If so, the module will
          be called stpddc60.
 
+config SENSORS_TDA38640
+       tristate "Infineon TDA38640"
+       help
+         If you say yes here you get hardware monitoring support for Infineon
+         TDA38640.
+
+         This driver can also be built as a module. If so, the module will
+         be called tda38640.
+
+config SENSORS_TDA38640_REGULATOR
+       bool "Regulator support for TDA38640 and compatibles"
+       depends on SENSORS_TDA38640 && REGULATOR
+       help
+         If you say yes here you get regulator support for Infineon
+         TDA38640 as regulator.
+
 config SENSORS_TPS40422
        tristate "TI TPS40422"
        help
index 0002dbe22d528b945ebaf046ef432606e72d956e..3ae01991626779d4d55b98fc5bda3f813285cb67 100644 (file)
@@ -34,11 +34,13 @@ obj-$(CONFIG_SENSORS_MAX8688)       += max8688.o
 obj-$(CONFIG_SENSORS_MP2888)   += mp2888.o
 obj-$(CONFIG_SENSORS_MP2975)   += mp2975.o
 obj-$(CONFIG_SENSORS_MP5023)   += mp5023.o
+obj-$(CONFIG_SENSORS_MPQ7932)  += mpq7932.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_STPDDC60) += stpddc60.o
+obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o
 obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
 obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
 obj-$(CONFIG_SENSORS_TPS546D24)        += tps546d24.o
index 79f480b4425d25891379aa7a2a2cbca97b760e48..91df8e895147272d0384616c53c24f45e804c476 100644 (file)
@@ -570,14 +570,14 @@ MODULE_DEVICE_TABLE(i2c, ltc2978_id);
 #define LTC2978_N_VOLTAGES     ((LTC2978_MAX_UV / LTC2978_UV_STEP) + 1)
 
 static const struct regulator_desc ltc2978_reg_desc[] = {
-       PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
-       PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
+       PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
+       PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0),
 };
 
 static const struct regulator_desc ltc2978_reg_desc_default[] = {
index b628405e6586c39a92aa58d4780a795cea9087bb..6724f723f74ced0c1b5aea16b02755b872bd8e6e 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Hardware monitoring driver for Maxim MAX16508, MAX16601 and MAX16602.
+ * Hardware monitoring driver for Maxim MAX16508, MAX16600, MAX16601,
+ * and MAX16602.
  *
  * Implementation notes:
  *
@@ -31,7 +32,7 @@
 
 #include "pmbus.h"
 
-enum chips { max16508, max16601, max16602 };
+enum chips { max16508, max16600, max16601, max16602 };
 
 #define REG_DEFAULT_NUM_POP    0xc4
 #define REG_SETPT_DVID         0xd1
@@ -202,7 +203,7 @@ static int max16601_identify(struct i2c_client *client,
        else
                info->vrm_version[0] = vr12;
 
-       if (data->id != max16601 && data->id != max16602)
+       if (data->id != max16600 && data->id != max16601 && data->id != max16602)
                return 0;
 
        reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP);
@@ -263,6 +264,7 @@ static void max16601_remove(void *_data)
 
 static const struct i2c_device_id max16601_id[] = {
        {"max16508", max16508},
+       {"max16600", max16600},
        {"max16601", max16601},
        {"max16602", max16602},
        {}
@@ -281,11 +283,13 @@ static int max16601_get_id(struct i2c_client *client)
                return -ENODEV;
 
        /*
-        * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" or
-        * MAX16602y.xx or "MAX16500y.xx".cdxxcccccccccc
+        * PMBUS_IC_DEVICE_ID is expected to return MAX1660[012]y.xx" or
+        * "MAX16500y.xx".cdxxcccccccccc
         */
        if (!strncmp(buf, "MAX16500", 8)) {
                id = max16508;
+       } else if (!strncmp(buf, "MAX16600", 8)) {
+               id = max16600;
        } else if (!strncmp(buf, "MAX16601", 8)) {
                id = max16601;
        } else if (!strncmp(buf, "MAX16602", 8)) {
diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c
new file mode 100644 (file)
index 0000000..ff93988
--- /dev/null
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * mpq7932.c - hwmon with optional regulator driver for mps mpq7932
+ * Copyright 2022 Monolithic Power Systems, Inc
+ *
+ * Author: Saravanan Sekar <saravanan@linumiz.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+#define MPQ7932_BUCK_UV_MIN            206250
+#define MPQ7932_UV_STEP                        6250
+#define MPQ7932_N_VOLTAGES             256
+#define MPQ7932_VOUT_MAX               0xFF
+#define MPQ7932_NUM_PAGES              6
+
+#define MPQ7932_TON_DELAY              0x60
+#define MPQ7932_VOUT_STARTUP_SLEW      0xA3
+#define MPQ7932_VOUT_SHUTDOWN_SLEW     0xA5
+#define MPQ7932_VOUT_SLEW_MASK         GENMASK(1, 0)
+#define MPQ7932_TON_DELAY_MASK         GENMASK(4, 0)
+
+struct mpq7932_data {
+       struct pmbus_driver_info info;
+       struct pmbus_platform_data pdata;
+};
+
+#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR)
+static struct regulator_desc mpq7932_regulators_desc[] = {
+       PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES,
+                            MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+       PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES,
+                            MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+       PMBUS_REGULATOR_STEP("buck", 2, MPQ7932_N_VOLTAGES,
+                            MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+       PMBUS_REGULATOR_STEP("buck", 3, MPQ7932_N_VOLTAGES,
+                            MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+       PMBUS_REGULATOR_STEP("buck", 4, MPQ7932_N_VOLTAGES,
+                            MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+       PMBUS_REGULATOR_STEP("buck", 5, MPQ7932_N_VOLTAGES,
+                            MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+};
+#endif
+
+static int mpq7932_write_word_data(struct i2c_client *client, int page, int reg,
+                                  u16 word)
+{
+       switch (reg) {
+       /*
+        * chip supports only byte access for VOUT_COMMAND otherwise
+        * access results -EREMOTEIO
+        */
+       case PMBUS_VOUT_COMMAND:
+               return pmbus_write_byte_data(client, page, reg, word & 0xFF);
+
+       default:
+               return -ENODATA;
+       }
+}
+
+static int mpq7932_read_word_data(struct i2c_client *client, int page,
+                                 int phase, int reg)
+{
+       switch (reg) {
+       /*
+        * chip supports neither (PMBUS_VOUT_MARGIN_HIGH, PMBUS_VOUT_MARGIN_LOW)
+        * nor (PMBUS_MFR_VOUT_MIN, PMBUS_MFR_VOUT_MAX). As a result set voltage
+        * fails due to error in pmbus_regulator_get_low_margin, so faked.
+        */
+       case PMBUS_MFR_VOUT_MIN:
+               return 0;
+
+       case PMBUS_MFR_VOUT_MAX:
+               return MPQ7932_VOUT_MAX;
+
+       /*
+        * chip supports only byte access for VOUT_COMMAND otherwise
+        * access results in -EREMOTEIO
+        */
+       case PMBUS_READ_VOUT:
+               return pmbus_read_byte_data(client, page, PMBUS_VOUT_COMMAND);
+
+       default:
+               return -ENODATA;
+       }
+}
+
+static int mpq7932_probe(struct i2c_client *client)
+{
+       struct mpq7932_data *data;
+       struct pmbus_driver_info *info;
+       struct device *dev = &client->dev;
+       int i;
+
+       data = devm_kzalloc(dev, sizeof(struct mpq7932_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       info = &data->info;
+       info->pages = MPQ7932_NUM_PAGES;
+       info->format[PSC_VOLTAGE_OUT] = direct;
+       info->m[PSC_VOLTAGE_OUT] = 160;
+       info->b[PSC_VOLTAGE_OUT] = -33;
+       for (i = 0; i < info->pages; i++) {
+               info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+                               | PMBUS_HAVE_STATUS_TEMP;
+       }
+
+#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR)
+       info->num_regulators = ARRAY_SIZE(mpq7932_regulators_desc);
+       info->reg_desc = mpq7932_regulators_desc;
+#endif
+
+       info->read_word_data = mpq7932_read_word_data;
+       info->write_word_data = mpq7932_write_word_data;
+
+       data->pdata.flags = PMBUS_NO_CAPABILITY;
+       dev->platform_data = &data->pdata;
+
+       return pmbus_do_probe(client, info);
+}
+
+static const struct of_device_id mpq7932_of_match[] = {
+       { .compatible = "mps,mpq7932"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, mpq7932_of_match);
+
+static const struct i2c_device_id mpq7932_id[] = {
+       { "mpq7932", },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, mpq7932_id);
+
+static struct i2c_driver mpq7932_regulator_driver = {
+       .driver = {
+               .name = "mpq7932",
+               .of_match_table = mpq7932_of_match,
+       },
+       .probe_new = mpq7932_probe,
+       .id_table = mpq7932_id,
+};
+module_i2c_driver(mpq7932_regulator_driver);
+
+MODULE_AUTHOR("Saravanan Sekar <saravanan@linumiz.com>");
+MODULE_DESCRIPTION("MPQ7932 PMIC regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 10fb17879f8ed986cc6146897a8270ddd3d1f10b..713ea79154259be5971a3433d4da14b68070dbb2 100644 (file)
@@ -464,7 +464,7 @@ struct pmbus_driver_info {
 extern const struct regulator_ops pmbus_regulator_ops;
 
 /* Macros for filling in array of struct regulator_desc */
-#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step)  \
+#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step, _min_uV)  \
        [_id] = {                                               \
                .name = (_name # _id),                          \
                .id = (_id),                                    \
@@ -475,9 +475,10 @@ extern const struct regulator_ops pmbus_regulator_ops;
                .owner = THIS_MODULE,                           \
                .n_voltages = _voltages,                        \
                .uV_step = _step,                               \
+               .min_uV = _min_uV,                              \
        }
 
-#define PMBUS_REGULATOR(_name, _id)    PMBUS_REGULATOR_STEP(_name, _id, 0, 0)
+#define PMBUS_REGULATOR(_name, _id)   PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0)
 
 /* Function declarations */
 
diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c
new file mode 100644 (file)
index 0000000..c3e7813
--- /dev/null
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon TDA38640
+ *
+ * Copyright (c) 2023 9elements GmbH
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+#include "pmbus.h"
+
+static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {
+       PMBUS_REGULATOR("vout", 0),
+};
+
+static struct pmbus_driver_info tda38640_info = {
+       .pages = 1,
+       .format[PSC_VOLTAGE_IN] = linear,
+       .format[PSC_VOLTAGE_OUT] = linear,
+       .format[PSC_CURRENT_OUT] = linear,
+       .format[PSC_CURRENT_IN] = linear,
+       .format[PSC_POWER] = linear,
+       .format[PSC_TEMPERATURE] = linear,
+
+       .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+           | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
+           | PMBUS_HAVE_IIN
+           | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+           | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+           | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN,
+#if IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR)
+       .num_regulators = 1,
+       .reg_desc = tda38640_reg_desc,
+#endif
+};
+
+static int tda38640_probe(struct i2c_client *client)
+{
+       return pmbus_do_probe(client, &tda38640_info);
+}
+
+static const struct i2c_device_id tda38640_id[] = {
+       {"tda38640", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, tda38640_id);
+
+static const struct of_device_id __maybe_unused tda38640_of_match[] = {
+       { .compatible = "infineon,tda38640"},
+       { },
+};
+MODULE_DEVICE_TABLE(of, tda38640_of_match);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver tda38640_driver = {
+       .driver = {
+               .name = "tda38640",
+               .of_match_table = of_match_ptr(tda38640_of_match),
+       },
+       .probe_new = tda38640_probe,
+       .id_table = tda38640_id,
+};
+
+module_i2c_driver(tda38640_driver);
+
+MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>");
+MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index ae4d14257a11da2044650867b1ad8245dd62e865..32a41fc56fc96837e756c0d46dbefad4f3a2042c 100644 (file)
@@ -668,7 +668,7 @@ static inline int sht15_calc_humid(struct sht15_data *data)
 }
 
 /**
- * sht15_show_status() - show status information in sysfs
+ * sht15_status_show() - show status information in sysfs
  * @dev:       device.
  * @attr:      device attribute.
  * @buf:       sysfs buffer where information is written to.
@@ -690,7 +690,7 @@ static ssize_t sht15_status_show(struct device *dev,
 }
 
 /**
- * sht15_store_heater() - change heater state via sysfs
+ * sht15_status_store() - change heater state via sysfs
  * @dev:       device.
  * @attr:      device attribute.
  * @buf:       sysfs buffer to read the new heater state from.
@@ -725,7 +725,7 @@ static ssize_t sht15_status_store(struct device *dev,
 }
 
 /**
- * sht15_show_temp() - show temperature measurement value in sysfs
+ * sht15_temp_show() - show temperature measurement value in sysfs
  * @dev:       device.
  * @attr:      device attribute.
  * @buf:       sysfs buffer where measurement values are written to.
@@ -747,7 +747,7 @@ static ssize_t sht15_temp_show(struct device *dev,
 }
 
 /**
- * sht15_show_humidity() - show humidity measurement value in sysfs
+ * sht15_humidity_show() - show humidity measurement value in sysfs
  * @dev:       device.
  * @attr:      device attribute.
  * @buf:       sysfs buffer where measurement values are written to.
index e23dbf287233a851657ecb6579c64d16a489d063..f50b90198f2394356c8099d497912a41be3ba7ac 100644 (file)
@@ -114,7 +114,7 @@ out:
 }
 
 /**
- * sht21_show_temperature() - show temperature measurement value in sysfs
+ * sht21_temperature_show() - show temperature measurement value in sysfs
  * @dev: device
  * @attr: device attribute
  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
@@ -136,7 +136,7 @@ static ssize_t sht21_temperature_show(struct device *dev,
 }
 
 /**
- * sht21_show_humidity() - show humidity measurement value in sysfs
+ * sht21_humidity_show() - show humidity measurement value in sysfs
  * @dev: device
  * @attr: device attribute
  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
index 14325f93c6b2bbfbe4648d18f057ac23094850b5..c1b62384b6ee71c96e3d8acd59fa4ea3e42732ad 100644 (file)
@@ -436,6 +436,10 @@ struct hwmon_chip_info {
 /* hwmon_device_register() is deprecated */
 struct device *hwmon_device_register(struct device *dev);
 
+/*
+ * hwmon_device_register_with_groups() and
+ * devm_hwmon_device_register_with_groups() are deprecated.
+ */
 struct device *
 hwmon_device_register_with_groups(struct device *dev, const char *name,
                                  void *drvdata,