diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-22 22:31:09 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-22 22:31:09 +0300 |
commit | 241ed6ab74f94eb3d64ee6b950cd8091b1213225 (patch) | |
tree | ddbdc90494c64b4390ac3c62f8fbe102ee1b19a9 | |
parent | 6c71297eaf713ece684a367ce9aff06069d715b9 (diff) | |
parent | 5720a18baa4686d56d0a235e6ecbcc55f8d716d7 (diff) | |
download | linux-241ed6ab74f94eb3d64ee6b950cd8091b1213225.tar.xz |
Merge tag 'hwmon-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
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, 2574 insertions, 777 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-hwmon b/Documentation/ABI/testing/sysfs-class-hwmon index 7271781a23b2..638f4c6d4ec7 100644 --- a/Documentation/ABI/testing/sysfs-class-hwmon +++ b/Documentation/ABI/testing/sysfs-class-hwmon @@ -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. diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml index d794deb08bb7..ca2b47320689 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml @@ -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; }; }; ... diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml index 43b4f4f57b49..4f8e11bd5142 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,adm1266.yaml @@ -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>; }; }; ... diff --git a/Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml b/Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml index f2f99afb3a3b..0cf3ed6212a6 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml @@ -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 index 000000000000..5cb66e97e816 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2945.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml index bf04151b63d2..152935334c76 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml @@ -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>; + }; }; ... diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml index 64a8fcb7bc46..dba74f400bc2 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml @@ -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>; + }; }; }; ... diff --git a/Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml b/Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml index 7598b083979c..353d81d89bf5 100644 --- a/Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml +++ b/Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml @@ -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>; }; }; ... diff --git a/Documentation/devicetree/bindings/hwmon/amd,sbtsi.yaml b/Documentation/devicetree/bindings/hwmon/amd,sbtsi.yaml index 446b09f1ce94..75088244a274 100644 --- a/Documentation/devicetree/bindings/hwmon/amd,sbtsi.yaml +++ b/Documentation/devicetree/bindings/hwmon/amd,sbtsi.yaml @@ -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 index 000000000000..4a52aac6be72 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/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"; + }; diff --git a/Documentation/devicetree/bindings/hwmon/iio-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/iio-hwmon.yaml index e1ccbd30e0eb..c54b5986b365 100644 --- a/Documentation/devicetree/bindings/hwmon/iio-hwmon.yaml +++ b/Documentation/devicetree/bindings/hwmon/iio-hwmon.yaml @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml index e1719839faf0..7b9d48d6d6da 100644 --- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml +++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml @@ -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>; + }; }; - }; }; diff --git a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml index 6a1920712fb9..3d0146e20d3e 100644 --- a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml +++ b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml @@ -131,7 +131,7 @@ additionalProperties: false examples: - | - thermistor0 { + thermistor { compatible = "murata,ncp18wb473"; io-channels = <&gpadc 0x06>; pullup-uv = <1800000>; diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml index 2f0620ecccc9..cd8dcd797031 100644 --- a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml +++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml @@ -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 index 000000000000..306f67315835 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/nxp,mc34vr500.yaml @@ -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>; + }; + }; diff --git a/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml b/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml index 1502b22c77cc..fde5225ce012 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml @@ -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>; + }; }; diff --git a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml index 3bc8e73dfbf0..bce68a326919 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml @@ -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>; + }; }; diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 6d1dc1e2484a..6f482a254a1d 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -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 diff --git a/Documentation/hwmon/aht10.rst b/Documentation/hwmon/aht10.rst index 482262ca117c..4e198c5eb683 100644 --- a/Documentation/hwmon/aht10.rst +++ b/Documentation/hwmon/aht10.rst @@ -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 diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst index 637bdbc8fcad..7d0d015b1a52 100644 --- a/Documentation/hwmon/aquacomputer_d5next.rst +++ b/Documentation/hwmon/aquacomputer_d5next.rst @@ -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. diff --git a/Documentation/hwmon/aspeed-pwm-tacho.rst b/Documentation/hwmon/aspeed-pwm-tacho.rst index 6dcec845fbc7..f7bbe96f4bc8 100644 --- a/Documentation/hwmon/aspeed-pwm-tacho.rst +++ b/Documentation/hwmon/aspeed-pwm-tacho.rst @@ -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: diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 02f4ad314a1e..a4039f2f9ca4 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -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> diff --git a/Documentation/hwmon/corsair-psu.rst b/Documentation/hwmon/corsair-psu.rst index 6a03edb551a8..c389bd21f4f2 100644 --- a/Documentation/hwmon/corsair-psu.rst +++ b/Documentation/hwmon/corsair-psu.rst @@ -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 diff --git a/Documentation/hwmon/ftsteutates.rst b/Documentation/hwmon/ftsteutates.rst index 58a2483d8d0d..b3bfec36661d 100644 --- a/Documentation/hwmon/ftsteutates.rst +++ b/Documentation/hwmon/ftsteutates.rst @@ -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:: diff --git a/Documentation/hwmon/gsc-hwmon.rst b/Documentation/hwmon/gsc-hwmon.rst index ffac392a7129..e9ab27940d02 100644 --- a/Documentation/hwmon/gsc-hwmon.rst +++ b/Documentation/hwmon/gsc-hwmon.rst @@ -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 index 000000000000..ae3397e81c04 --- /dev/null +++ b/Documentation/hwmon/gxp-fan-ctrl.rst @@ -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 +======================= =========================================================== diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst index f3276b3a381a..5451a6d4c874 100644 --- a/Documentation/hwmon/hwmon-kernel-api.rst +++ b/Documentation/hwmon/hwmon-kernel-api.rst @@ -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 diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index fe2cc6b73634..96ce91b7d9a8 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -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 diff --git a/Documentation/hwmon/it87.rst b/Documentation/hwmon/it87.rst index 2d83f23bee93..5cef4f265000 100644 --- a/Documentation/hwmon/it87.rst +++ b/Documentation/hwmon/it87.rst @@ -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. diff --git a/Documentation/hwmon/ltc2978.rst b/Documentation/hwmon/ltc2978.rst index b99a63965cfb..edf24e5e1e11 100644 --- a/Documentation/hwmon/ltc2978.rst +++ b/Documentation/hwmon/ltc2978.rst @@ -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. diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst index 6a4eef8efbaf..c8c63a053e40 100644 --- a/Documentation/hwmon/max16601.rst +++ b/Documentation/hwmon/max16601.rst @@ -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. diff --git a/Documentation/hwmon/max6697.rst b/Documentation/hwmon/max6697.rst index ffc5a7d8d33b..90ca224c446a 100644 --- a/Documentation/hwmon/max6697.rst +++ b/Documentation/hwmon/max6697.rst @@ -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 index 000000000000..f82d872477ac --- /dev/null +++ b/Documentation/hwmon/mc34vr500.rst @@ -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 diff --git a/Documentation/hwmon/menf21bmc.rst b/Documentation/hwmon/menf21bmc.rst index 978691d5956d..e7f3f67b38d4 100644 --- a/Documentation/hwmon/menf21bmc.rst +++ b/Documentation/hwmon/menf21bmc.rst @@ -7,7 +7,7 @@ Supported chips: Prefix: 'menf21bmc_hwmon' - Adresses scanned: - + Addresses scanned: - Author: Andreas Werner <andreas.werner@men.de> diff --git a/Documentation/hwmon/oxp-sensors.rst b/Documentation/hwmon/oxp-sensors.rst index 39c588ec5c50..566a8d5bde08 100644 --- a/Documentation/hwmon/oxp-sensors.rst +++ b/Documentation/hwmon/oxp-sensors.rst @@ -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 diff --git a/Documentation/hwmon/pmbus-core.rst b/Documentation/hwmon/pmbus-core.rst index 84c5a4e40c40..cff93adf6e42 100644 --- a/Documentation/hwmon/pmbus-core.rst +++ b/Documentation/hwmon/pmbus-core.rst @@ -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. diff --git a/Documentation/hwmon/sht4x.rst b/Documentation/hwmon/sht4x.rst index c318e5582ead..daf21e763425 100644 --- a/Documentation/hwmon/sht4x.rst +++ b/Documentation/hwmon/sht4x.rst @@ -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 diff --git a/Documentation/hwmon/smm665.rst b/Documentation/hwmon/smm665.rst index a0e27f62b57b..481e69d8bf39 100644 --- a/Documentation/hwmon/smm665.rst +++ b/Documentation/hwmon/smm665.rst @@ -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 diff --git a/Documentation/hwmon/stpddc60.rst b/Documentation/hwmon/stpddc60.rst index 7f7ce7f7871b..0c31f78fe58b 100644 --- a/Documentation/hwmon/stpddc60.rst +++ b/Documentation/hwmon/stpddc60.rst @@ -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. diff --git a/Documentation/hwmon/submitting-patches.rst b/Documentation/hwmon/submitting-patches.rst index d953ee763c96..6482c4f137dc 100644 --- a/Documentation/hwmon/submitting-patches.rst +++ b/Documentation/hwmon/submitting-patches.rst @@ -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. diff --git a/Documentation/hwmon/vexpress.rst b/Documentation/hwmon/vexpress.rst index 8c861c8151ac..7fc99f75b3b1 100644 --- a/Documentation/hwmon/vexpress.rst +++ b/Documentation/hwmon/vexpress.rst @@ -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 diff --git a/Documentation/hwmon/via686a.rst b/Documentation/hwmon/via686a.rst index 7ab9ddebcf79..8c52a9207bcb 100644 --- a/Documentation/hwmon/via686a.rst +++ b/Documentation/hwmon/via686a.rst @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index a02437913f01..4dacb54557e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 497cbfb460e5..0c4cdb21ae13 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -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 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4f71d9807dae..88712b5031c8 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -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 diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index d76f3441ecf1..9babd69d54a3 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -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) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 9cc10080160b..12682a610ce7 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -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> @@ -21,14 +22,20 @@ #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) }, { } }; diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index a901e4e33d81..2768b7511684 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -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), {}, }; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index ca7a9b373bbd..30d77f451937 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -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) diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 6ad055e5868e..f65467fbd86c 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -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; diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index f5b8e724a8ca..25afd9167a34 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -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 index 000000000000..0014b8b0fd41 --- /dev/null +++ b/drivers/hwmon/gxp-fan-ctrl.c @@ -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"); diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c index d9394e19fea8..3a7582824f94 100644 --- a/drivers/hwmon/hih6130.c +++ b/drivers/hwmon/hih6130.c @@ -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 diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 1837cccd993c..db066b368918 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -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, diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 3aa40893fc09..4c8a80847891 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -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); diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index 6e82f7200d1c..2f0323c14bab 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -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, + }, { } }; diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 9997f76b1f4a..66f7ceaa7c3f 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -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 @@ -63,15 +64,7 @@ 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) @@ -3311,6 +3345,27 @@ static int it87_dmi_cb(const struct dmi_system_id *dmi_entry) } /* + * 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 * has reported instant system power-off when changing @@ -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); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index 9adebb59f604..3494f7261ebf 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -58,6 +58,22 @@ #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, <c2945_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 index 000000000000..6268e973049c --- /dev/null +++ b/drivers/hwmon/mc34vr500.c @@ -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, ®); + 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, ®); + 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, ®); + 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"); diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index b48bd7c961d6..96017cc8da7e 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -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; diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index da9ec6983e13..c54233f0369b 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -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); } diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index bf43f73dc835..76c6b564d7fc 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -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); } /* diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index 533f38b0b4e9..2b93ba89610a 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -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 */ {}, }; diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c index f84ec8f8eda9..36872b57912a 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/hwmon/oxp-sensors.c @@ -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, }; @@ -62,6 +64,20 @@ static const struct dmi_system_id dmi_table[] = { }, { .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"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"), }, @@ -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. diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c index 57470fda5f6c..30850a479f61 100644 --- a/drivers/hwmon/peci/cputemp.c +++ b/drivers/hwmon/peci/cputemp.c @@ -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; diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 89668af67206..59d9a7430499 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -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 diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 0002dbe22d52..3ae019916267 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -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 diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 79f480b4425d..91df8e895147 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -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[] = { diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index b628405e6586..6724f723f74c 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -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 index 000000000000..ff939881dc3b --- /dev/null +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -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); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 10fb17879f8e..713ea7915425 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -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 index 000000000000..c3e781319cd1 --- /dev/null +++ b/drivers/hwmon/pmbus/tda38640.c @@ -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); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index ae4d14257a11..32a41fc56fc9 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -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. diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index e23dbf287233..f50b90198f23 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -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 diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 14325f93c6b2..c1b62384b6ee 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -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, |