aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-05-24 14:23:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-05-24 14:23:10 -0700
commit076f222a690e11b433d2b1e218dbd9bdb08fb190 (patch)
treeb0d0e04f6e5aca9f7bcb6f00f00f8814ca74b5ab
parent0350785b0a092c99c5ddd2ace0260dbe7b3f919f (diff)
parent8877ecb0fc8d7662218a8e7ebb0650f320467935 (diff)
downloadnf-076f222a690e11b433d2b1e218dbd9bdb08fb190.tar.gz
Merge tag 'hwmon-for-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New drivers: - Driver for the Microchip LAN966x SoC - PMBus driver for Infineon Digital Multi-phase xdp152 family controllers Chip support added to existing drivers: - asus-ec-sensors: - Support for ROG STRIX X570-E GAMING WIFI II, PRIME X470-PRO, and ProArt X570 Creator WIFI - External temperature sensor support for ASUS WS X570-ACE - nct6775: - Support for I2C driver - Support for ASUS PRO H410T / PRIME H410M-R / ROG X570-E GAMING WIFI II - lm75: - Support for - Atmel AT30TS74 - pmbus/max16601: - Support for MAX16602 - aquacomputer_d5next: - Support for Aquacomputer Farbwerk - Support for Aquacomputer Octo - jc42: - Support for S-34TS04A Kernel API changes / clarifications: - The chip parameter of with_info API is now mandatory - New hwmon_device_register_for_thermal API call for use by the thermal subsystem Improvements: - PMBus and JC42 drivers now register with thermal subsystem - PMBus drivers now support get_voltage/set_voltage power operations - The adt7475 driver now supports pin configuration - The lm90 driver now supports setting extended range temperatures configuration with a devicetree property - The dell-smm driver now registers as cooling device - The OCC driver delays hwmon registration until requested by userspace ... and various other minor fixes and improvements" * tag 'hwmon-for-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (71 commits) hwmon: (aquacomputer_d5next) Fix an error handling path in aqc_probe() hwmon: (sl28cpld) Fix typo in comment hwmon: (pmbus) Check PEC support before reading other registers hwmon: (dimmtemp) Fix bitmap handling hwmon: (lm90) enable extended range according to DTS node dt-bindings: hwmon: lm90: add ti,extended-range-enable property dt-bindings: hwmon: lm90: add missing ti,tmp461 hwmon: (ibmaem) Directly use ida_alloc()/free() hwmon: Directly use ida_alloc()/free() hwmon: (asus-ec-sensors) fix Formula VIII definition dt-bindings: trivial-devices: Add xdp152 hwmon: (sl28cpld-hwmon) Use HWMON_CHANNEL_INFO macro hwmon: (pwm-fan) Use HWMON_CHANNEL_INFO macro hwmon: (peci/dimmtemp) Use HWMON_CHANNEL_INFO macro hwmon: (peci/cputemp) Use HWMON_CHANNEL_INFO macro hwmon: (mr75203) Use HWMON_CHANNEL_INFO macro hwmon: (ltc2992) Use HWMON_CHANNEL_INFO macro hwmon: (as370-hwmon) Use HWMON_CHANNEL_INFO macro hwmon: Make chip parameter for with_info API mandatory thermal/drivers/thermal_hwmon: Use hwmon_device_register_for_thermal() ...
-rw-r--r--Documentation/devicetree/bindings/hwmon/adt7475.yaml22
-rw-r--r--Documentation/devicetree/bindings/hwmon/lm75.yaml1
-rw-r--r--Documentation/devicetree/bindings/hwmon/microchip,lan966x.yaml53
-rw-r--r--Documentation/devicetree/bindings/hwmon/national,lm90.yaml20
-rw-r--r--Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml57
-rw-r--r--Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml105
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml4
-rw-r--r--Documentation/hwmon/aquacomputer_d5next.rst7
-rw-r--r--Documentation/hwmon/asus_ec_sensors.rst25
-rw-r--r--Documentation/hwmon/dell-smm-hwmon.rst9
-rw-r--r--Documentation/hwmon/hwmon-kernel-api.rst18
-rw-r--r--Documentation/hwmon/index.rst2
-rw-r--r--Documentation/hwmon/lan966x.rst40
-rw-r--r--Documentation/hwmon/max16601.rst8
-rw-r--r--Documentation/hwmon/xdpe152c4.rst118
-rw-r--r--MAINTAINERS15
-rw-r--r--drivers/hwmon/Kconfig55
-rw-r--r--drivers/hwmon/Makefile4
-rw-r--r--drivers/hwmon/acpi_power_meter.c17
-rw-r--r--drivers/hwmon/adt7475.c119
-rw-r--r--drivers/hwmon/aquacomputer_d5next.c466
-rw-r--r--drivers/hwmon/as370-hwmon.c12
-rw-r--r--drivers/hwmon/asus-ec-sensors.c414
-rw-r--r--drivers/hwmon/bt1-pvt.c50
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c147
-rw-r--r--drivers/hwmon/hwmon.c100
-rw-r--r--drivers/hwmon/ibmaem.c10
-rw-r--r--drivers/hwmon/intel-m10-bmc-hwmon.c11
-rw-r--r--drivers/hwmon/jc42.c8
-rw-r--r--drivers/hwmon/lan966x-hwmon.c418
-rw-r--r--drivers/hwmon/lm75.c14
-rw-r--r--drivers/hwmon/lm83.c2
-rw-r--r--drivers/hwmon/lm90.c4
-rw-r--r--drivers/hwmon/ltc2992.c86
-rw-r--r--drivers/hwmon/mr75203.c12
-rw-r--r--drivers/hwmon/nct6775-core.c (renamed from drivers/hwmon/nct6775.c)2437
-rw-r--r--drivers/hwmon/nct6775-i2c.c195
-rw-r--r--drivers/hwmon/nct6775-platform.c1229
-rw-r--r--drivers/hwmon/nct6775.h252
-rw-r--r--drivers/hwmon/occ/common.c100
-rw-r--r--drivers/hwmon/occ/common.h5
-rw-r--r--drivers/hwmon/occ/p8_i2c.c2
-rw-r--r--drivers/hwmon/occ/p9_sbe.c2
-rw-r--r--drivers/hwmon/occ/sysfs.c137
-rw-r--r--drivers/hwmon/peci/cputemp.c38
-rw-r--r--drivers/hwmon/peci/dimmtemp.c33
-rw-r--r--drivers/hwmon/pmbus/Kconfig13
-rw-r--r--drivers/hwmon/pmbus/Makefile1
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c12
-rw-r--r--drivers/hwmon/pmbus/max16601.c13
-rw-r--r--drivers/hwmon/pmbus/pmbus.h2
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c233
-rw-r--r--drivers/hwmon/pmbus/xdpe152c4.c75
-rw-r--r--drivers/hwmon/pwm-fan.c12
-rw-r--r--drivers/hwmon/sl28cpld-hwmon.c14
-rw-r--r--drivers/hwmon/tmp401.c46
-rw-r--r--drivers/thermal/thermal_hwmon.c6
-rw-r--r--include/linux/hwmon.h6
-rw-r--r--include/linux/polynomial.h35
-rw-r--r--lib/Kconfig3
-rw-r--r--lib/Makefile2
-rw-r--r--lib/polynomial.c108
62 files changed, 5114 insertions, 2350 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/adt7475.yaml b/Documentation/devicetree/bindings/hwmon/adt7475.yaml
index 7d9c083632b941..22beb37f1bf12c 100644
--- a/Documentation/devicetree/bindings/hwmon/adt7475.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adt7475.yaml
@@ -61,6 +61,26 @@ patternProperties:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
+ "adi,pin(5|10)-function":
+ description: |
+ Configures the function for pin 5 on the adi,adt7473 and adi,adt7475. Or
+ pin 10 on the adi,adt7476 and adi,adt7490.
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - pwm2
+ - smbalert#
+
+ "adi,pin(9|14)-function":
+ description: |
+ Configures the function for pin 9 on the adi,adt7473 and adi,adt7475. Or
+ pin 14 on the adi,adt7476 and adi,adt7490
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - tach4
+ - therm#
+ - smbalert#
+ - gpio
+
required:
- compatible
- reg
@@ -79,6 +99,8 @@ examples:
adi,bypass-attenuator-in0 = <1>;
adi,bypass-attenuator-in1 = <0>;
adi,pwm-active-state = <1 0 1>;
+ adi,pin10-function = "smbalert#";
+ adi,pin14-function = "tach4";
};
};
diff --git a/Documentation/devicetree/bindings/hwmon/lm75.yaml b/Documentation/devicetree/bindings/hwmon/lm75.yaml
index 72980d083c210b..8226e3b5d028e2 100644
--- a/Documentation/devicetree/bindings/hwmon/lm75.yaml
+++ b/Documentation/devicetree/bindings/hwmon/lm75.yaml
@@ -14,6 +14,7 @@ properties:
compatible:
enum:
- adi,adt75
+ - atmel,at30ts74
- dallas,ds1775
- dallas,ds75
- dallas,ds7505
diff --git a/Documentation/devicetree/bindings/hwmon/microchip,lan966x.yaml b/Documentation/devicetree/bindings/hwmon/microchip,lan966x.yaml
new file mode 100644
index 00000000000000..390dd6755ff51f
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/microchip,lan966x.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/microchip,lan966x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip LAN966x Hardware Monitor
+
+maintainers:
+ - Michael Walle <michael@walle.cc>
+
+description: |
+ Microchip LAN966x temperature monitor and fan controller
+
+properties:
+ compatible:
+ enum:
+ - microchip,lan9668-hwmon
+
+ reg:
+ items:
+ - description: PVT registers
+ - description: FAN registers
+
+ reg-names:
+ items:
+ - const: pvt
+ - const: fan
+
+ clocks:
+ maxItems: 1
+
+ '#thermal-sensor-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ hwmon: hwmon@e2010180 {
+ compatible = "microchip,lan9668-hwmon";
+ reg = <0xe2010180 0xc>,
+ <0xe20042a8 0xc>;
+ reg-names = "pvt", "fan";
+ clocks = <&sys_clk>;
+ #thermal-sensor-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml
index 30db92977937b2..b046578498524d 100644
--- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml
+++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml
@@ -34,6 +34,7 @@ properties:
- nxp,sa56004
- onnn,nct1008
- ti,tmp451
+ - ti,tmp461
- winbond,w83l771
@@ -52,10 +53,29 @@ properties:
vcc-supply:
description: phandle to the regulator that provides the +VCC supply
+ ti,extended-range-enable:
+ description: Set to enable extended range temperature.
+ type: boolean
+
required:
- compatible
- reg
+allOf:
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,adt7461
+ - adi,adt7461a
+ - ti,tmp451
+ - ti,tmp461
+ then:
+ properties:
+ ti,extended-range-enable: false
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
new file mode 100644
index 00000000000000..358b262431fc57
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+
+$id: http://devicetree.org/schemas/hwmon/nuvoton,nct6775.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NCT6775 and compatible Super I/O chips
+
+maintainers:
+ - Zev Weiss <zev@bewilderbeest.net>
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nct6106
+ - nuvoton,nct6116
+ - nuvoton,nct6775
+ - nuvoton,nct6776
+ - nuvoton,nct6779
+ - nuvoton,nct6791
+ - nuvoton,nct6792
+ - nuvoton,nct6793
+ - nuvoton,nct6795
+ - nuvoton,nct6796
+ - nuvoton,nct6797
+ - nuvoton,nct6798
+
+ reg:
+ maxItems: 1
+
+ nuvoton,tsi-channel-mask:
+ description:
+ Bitmask indicating which TSI temperature sensor channels are
+ active. LSB is TSI0, bit 1 is TSI1, etc.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ maximum: 0xff
+ default: 0
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ superio@4d {
+ compatible = "nuvoton,nct6779";
+ reg = <0x4d>;
+ nuvoton,tsi-channel-mask = <0x03>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml b/Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml
new file mode 100644
index 00000000000000..fe0ac08faa1a70
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/ti,tmp401.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TMP401, TPM411 and TMP43x temperature sensor
+
+maintainers:
+ - Guenter Roeck <linux@roeck-us.net>
+
+description: |
+ ±1°C Remote and Local temperature sensor
+
+ Datasheets:
+ https://www.ti.com/lit/ds/symlink/tmp401.pdf
+ https://www.ti.com/lit/ds/symlink/tmp411.pdf
+ https://www.ti.com/lit/ds/symlink/tmp431.pdf
+ https://www.ti.com/lit/ds/symlink/tmp435.pdf
+
+properties:
+ compatible:
+ enum:
+ - ti,tmp401
+ - ti,tmp411
+ - ti,tmp431
+ - ti,tmp432
+ - ti,tmp435
+
+ reg:
+ maxItems: 1
+
+ ti,extended-range-enable:
+ description:
+ When set, this sensor measures over extended temperature range.
+ type: boolean
+
+ ti,n-factor:
+ description:
+ value to be used for converting remote channel measurements to
+ temperature.
+ $ref: /schemas/types.yaml#/definitions/int32
+ items:
+ minimum: -128
+ maximum: 127
+
+ ti,beta-compensation:
+ description:
+ value to select beta correction range.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - ti,tmp401
+ then:
+ properties:
+ ti,n-factor: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - ti,tmp401
+ - ti,tmp411
+ then:
+ properties:
+ ti,beta-compensation: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
+ };
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@4c {
+ compatible = "ti,tmp431";
+ reg = <0x4c>;
+ ti,extended-range-enable;
+ ti,n-factor = <0x3b>;
+ ti,beta-compensation = <0x7>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 550a2e5c9e05dd..c11520347a9d2b 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -143,6 +143,10 @@ properties:
- infineon,xdpe12254
# Infineon Multi-phase Digital VR Controller xdpe12284
- infineon,xdpe12284
+ # Infineon Multi-phase Digital VR Controller xdpe15284
+ - infineon,xdpe15284
+ # Infineon Multi-phase Digital VR Controller xdpe152c4
+ - infineon,xdpe152c4
# Injoinic IP5108 2.0A Power Bank IC with I2C
- injoinic,ip5108
# Injoinic IP5109 2.1A Power Bank IC with I2C
diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst
index 3373e27b707d68..717e28226cde9c 100644
--- a/Documentation/hwmon/aquacomputer_d5next.rst
+++ b/Documentation/hwmon/aquacomputer_d5next.rst
@@ -6,7 +6,9 @@ Kernel driver aquacomputer-d5next
Supported devices:
* Aquacomputer D5 Next watercooling pump
+* Aquacomputer Farbwerk RGB controller
* Aquacomputer Farbwerk 360 RGB controller
+* Aquacomputer Octo fan controller
Author: Aleksa Savic
@@ -28,7 +30,10 @@ seems to require sending it a complete configuration. That includes addressable
RGB LEDs, for which there is no standard sysfs interface. Thus, that task is
better suited for userspace tools.
-The Farbwerk 360 exposes four temperature sensors. Depending on the device,
+The Octo exposes four temperature sensors and eight PWM controllable fans, along
+with their speed (in RPM), power, voltage and current.
+
+The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
not all sysfs and debugfs entries will be available.
Usage notes
diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst
index e7e8f1640f457b..78ca69eda87789 100644
--- a/Documentation/hwmon/asus_ec_sensors.rst
+++ b/Documentation/hwmon/asus_ec_sensors.rst
@@ -4,17 +4,20 @@ Kernel driver asus_ec_sensors
=================================
Supported boards:
- * PRIME X570-PRO,
- * Pro WS X570-ACE,
- * ROG CROSSHAIR VIII DARK HERO,
+ * PRIME X470-PRO
+ * PRIME X570-PRO
+ * Pro WS X570-ACE
+ * ProArt X570-CREATOR WIFI
+ * ROG CROSSHAIR VIII DARK HERO
* ROG CROSSHAIR VIII HERO (WI-FI)
- * ROG CROSSHAIR VIII FORMULA,
- * ROG CROSSHAIR VIII HERO,
- * ROG CROSSHAIR VIII IMPACT,
- * ROG STRIX B550-E GAMING,
- * ROG STRIX B550-I GAMING,
- * ROG STRIX X570-E GAMING,
- * ROG STRIX X570-F GAMING,
+ * ROG CROSSHAIR VIII FORMULA
+ * ROG CROSSHAIR VIII HERO
+ * ROG CROSSHAIR VIII IMPACT
+ * ROG STRIX B550-E GAMING
+ * ROG STRIX B550-I GAMING
+ * ROG STRIX X570-E GAMING
+ * ROG STRIX X570-E GAMING WIFI II
+ * ROG STRIX X570-F GAMING
* ROG STRIX X570-I GAMING
Authors:
@@ -52,3 +55,5 @@ Module Parameters
the path is mostly identical for them). If ASUS changes this path
in a future BIOS update, this parameter can be used to override
the stored in the driver value until it gets updated.
+ A special string ":GLOBAL_LOCK" can be passed to use the ACPI
+ global lock instead of a dedicated mutex.
diff --git a/Documentation/hwmon/dell-smm-hwmon.rst b/Documentation/hwmon/dell-smm-hwmon.rst
index d3323a96665d6f..e5d85e40972c28 100644
--- a/Documentation/hwmon/dell-smm-hwmon.rst
+++ b/Documentation/hwmon/dell-smm-hwmon.rst
@@ -86,6 +86,13 @@ probe the BIOS on your machine and discover the appropriate codes.
Again, when you find new codes, we'd be happy to have your patches!
+``thermal`` interface
+---------------------------
+
+The driver also exports the fans as thermal cooling devices with
+``type`` set to ``dell-smm-fan[1-3]``. This allows for easy fan control
+using one of the thermal governors.
+
Module parameters
-----------------
@@ -324,6 +331,8 @@ Reading of fan types causes erratic fan behaviour. Studio XPS 8000
Inspiron 580
+ Inspiron 3505
+
Fan-related SMM calls take too long (about 500ms). Inspiron 7720
Vostro 3360
diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst
index c41eb61081036d..f3276b3a381a14 100644
--- a/Documentation/hwmon/hwmon-kernel-api.rst
+++ b/Documentation/hwmon/hwmon-kernel-api.rst
@@ -50,6 +50,10 @@ register/unregister functions::
void devm_hwmon_device_unregister(struct device *dev);
+ char *hwmon_sanitize_name(const char *name);
+
+ char *devm_hwmon_sanitize_name(struct device *dev, const char *name);
+
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
@@ -72,7 +76,7 @@ hwmon_device_register_with_info is the most comprehensive and preferred means
to register a hardware monitoring device. It creates the standard sysfs
attributes in the hardware monitoring core, letting the driver focus on reading
from and writing to the chip instead of having to bother with sysfs attributes.
-The parent device parameter cannot be NULL with non-NULL chip info. Its
+The parent device parameter as well as the chip parameter must not be NULL. Its
parameters are described in more detail below.
devm_hwmon_device_register_with_info is similar to
@@ -95,6 +99,18 @@ All supported hwmon device registration functions only accept valid device
names. Device names including invalid characters (whitespace, '*', or '-')
will be rejected. The 'name' parameter is mandatory.
+If the driver doesn't use a static device name (for example it uses
+dev_name()), and therefore cannot make sure the name only contains valid
+characters, hwmon_sanitize_name can be used. This convenience function
+will duplicate the string and replace any invalid characters with an
+underscore. It will allocate memory for the new string and it is the
+responsibility of the caller to release the memory when the device is
+removed.
+
+devm_hwmon_sanitize_name is the resource managed version of
+hwmon_sanitize_name; the memory will be freed automatically on device
+removal.
+
Using devm_hwmon_device_register_with_info()
--------------------------------------------
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 863b7628915970..a72c16872ec2d4 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -90,6 +90,7 @@ Hardware Monitoring Kernel Drivers
jc42
k10temp
k8temp
+ lan966x
lineage-pem
lm25066
lm63
@@ -223,6 +224,7 @@ Hardware Monitoring Kernel Drivers
wm8350
xgene-hwmon
xdpe12284
+ xdpe152c4
zl6100
.. only:: subproject and html
diff --git a/Documentation/hwmon/lan966x.rst b/Documentation/hwmon/lan966x.rst
new file mode 100644
index 00000000000000..1d1724afa5d28d
--- /dev/null
+++ b/Documentation/hwmon/lan966x.rst
@@ -0,0 +1,40 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver lan966x-hwmon
+===========================
+
+Supported chips:
+
+ * Microchip LAN9668 (sensor in SoC)
+
+ Prefix: 'lan9668-hwmon'
+
+ Datasheet: https://microchip-ung.github.io/lan9668_reginfo
+
+Authors:
+
+ Michael Walle <michael@walle.cc>
+
+Description
+-----------
+
+This driver implements support for the Microchip LAN9668 on-chip
+temperature sensor as well as its fan controller. It provides one
+temperature sensor and one fan controller. The temperature range
+of the sensor is specified from -40 to +125 degrees Celsius and
+its accuracy is +/- 5 degrees Celsius. The fan controller has a
+tacho input and a PWM output with a customizable PWM output
+frequency ranging from ~20Hz to ~650kHz.
+
+No alarms are supported by the SoC.
+
+The driver exports temperature values, fan tacho input and PWM
+settings via the following sysfs files:
+
+**temp1_input**
+
+**fan1_input**
+
+**pwm1**
+
+**pwm1_freq**
diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst
index 92c0a7d7808c11..6a4eef8efbaf48 100644
--- a/Documentation/hwmon/max16601.rst
+++ b/Documentation/hwmon/max16601.rst
@@ -21,6 +21,14 @@ Supported chips:
Datasheet: Not published
+ * Maxim MAX16602
+
+ Prefix: 'max16602'
+
+ Addresses scanned: -
+
+ Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX16602.pdf
+
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/xdpe152c4.rst b/Documentation/hwmon/xdpe152c4.rst
new file mode 100644
index 00000000000000..ab92c32d4d6933
--- /dev/null
+++ b/Documentation/hwmon/xdpe152c4.rst
@@ -0,0 +1,118 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver xdpe152
+=====================
+
+Supported chips:
+
+ * Infineon XDPE152C4
+
+ Prefix: 'xdpe152c4'
+
+ * Infineon XDPE15284
+
+ Prefix: 'xdpe15284'
+
+Authors:
+
+ Greg Schwendimann <greg.schwendimann@infineon.com>
+
+Description
+-----------
+
+This driver implements support for Infineon Digital Multi-phase Controller
+XDPE152C4 and XDPE15284 dual loop voltage regulators.
+The devices are compliant with:
+
+- Intel VR13, VR13HC and VR14 rev 1.86
+ converter specification.
+- Intel SVID rev 1.93. protocol.
+- PMBus rev 1.3.1 interface.
+
+Devices support linear format for reading input and output voltage, input
+and output current, input and output power and temperature.
+
+Devices support two pages for telemetry.
+
+The driver provides for current: input, maximum and critical thresholds
+and maximum and critical alarms. Low Critical thresholds and Low critical alarm are
+supported only for current output.
+The driver exports the following attributes for via the sysfs files, where
+indexes 1, 2 are for "iin" and 3, 4 for "iout":
+
+**curr[1-4]_crit**
+
+**curr[1-4]_crit_alarm**
+
+**curr[1-4]_input**
+
+**curr[1-4]_label**
+
+**curr[1-4]_max**
+
+**curr[1-4]_max_alarm**
+
+**curr[3-4]_lcrit**
+
+**curr[3-4]_lcrit_alarm**
+
+**curr[3-4]_rated_max**
+
+The driver provides for voltage: input, critical and low critical thresholds
+and critical and low critical alarms.
+The driver exports the following attributes for via the sysfs files, where
+indexes 1, 2 are for "vin" and 3, 4 for "vout":
+
+**in[1-4]_min**
+
+**in[1-4]_crit**
+
+**in[1-4_crit_alarm**
+
+**in[1-4]_input**
+
+**in[1-4]_label**
+
+**in[1-4]_max**
+
+**in[1-4]_max_alarm**
+
+**in[1-4]_min**
+
+**in[1-4]_min_alarm**
+
+**in[3-4]_lcrit**
+
+**in[3-4]_lcrit_alarm**
+
+**in[3-4]_rated_max**
+
+**in[3-4]_rated_min**
+
+The driver provides for power: input and alarms.
+The driver exports the following attributes for via the sysfs files, where
+indexes 1, 2 are for "pin" and 3, 4 for "pout":
+
+**power[1-2]_alarm**
+
+**power[1-4]_input**
+
+**power[1-4]_label**
+
+**power[1-4]_max**
+
+**power[1-4]_rated_max**
+
+The driver provides for temperature: input, maximum and critical thresholds
+and maximum and critical alarms.
+The driver exports the following attributes for via the sysfs files:
+
+**temp[1-2]_crit**
+
+**temp[1-2]_crit_alarm**
+
+**temp[1-2]_input**
+
+**temp[1-2]_max**
+
+**temp[1-2]_max_alarm**
diff --git a/MAINTAINERS b/MAINTAINERS
index 3bf9805b10aaef..65e5fe38c9a128 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1447,6 +1447,7 @@ F: drivers/media/i2c/aptina-pll.*
AQUACOMPUTER D5 NEXT PUMP SENSOR DRIVER
M: Aleksa Savic <savicaleksa83@gmail.com>
+M: Jack Doan <me@jackdoan.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/aquacomputer_d5next.rst
@@ -13554,12 +13555,21 @@ M: Samuel Mendoza-Jonas <sam@mendozajonas.com>
S: Maintained
F: net/ncsi/
-NCT6775 HARDWARE MONITOR DRIVER
+NCT6775 HARDWARE MONITOR DRIVER - CORE & PLATFORM DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/nct6775.rst
-F: drivers/hwmon/nct6775.c
+F: drivers/hwmon/nct6775-core.c
+F: drivers/hwmon/nct6775-platform.c
+F: drivers/hwmon/nct6775.h
+
+NCT6775 HARDWARE MONITOR DRIVER - I2C DRIVER
+M: Zev Weiss <zev@bewilderbeest.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
+F: drivers/hwmon/nct6775-i2c.c
NETDEVSIM
M: Jakub Kicinski <kuba@kernel.org>
@@ -19870,6 +19880,7 @@ TMP401 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
+F: Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml
F: Documentation/hwmon/tmp401.rst
F: drivers/hwmon/tmp401.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f2b038fa3b84c3..590d3d550acba5 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -256,11 +256,14 @@ config SENSORS_AHT10
will be called aht10.
config SENSORS_AQUACOMPUTER_D5NEXT
- tristate "Aquacomputer D5 Next watercooling pump"
+ tristate "Aquacomputer D5 Next, Octo, Farbwerk, and Farbwerk 360"
depends on USB_HID
+ select CRC16
help
- If you say yes here you get support for the Aquacomputer D5 Next
- watercooling pump sensors.
+ If you say yes here you get support for sensors and fans of
+ the Aquacomputer D5 Next watercooling pump, Octo fan
+ controller, Farbwerk and Farbwerk 360 RGB controllers, where
+ available.
This driver can also be built as a module. If so, the module
will be called aquacomputer_d5next.
@@ -415,6 +418,7 @@ config SENSORS_ATXP1
config SENSORS_BT1_PVT
tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+ select POLYNOMIAL
help
If you say yes here you get support for Baikal-T1 PVT sensor
embedded into the SoC.
@@ -498,6 +502,7 @@ config SENSORS_DS1621
config SENSORS_DELL_SMM
tristate "Dell laptop SMM BIOS hwmon driver"
depends on X86
+ imply THERMAL
help
This hwmon driver adds support for reporting temperature of different
sensors and controls the fans on Dell laptops via System Management
@@ -814,6 +819,18 @@ config SENSORS_POWR1220
This driver can also be built as a module. If so, the module
will be called powr1220.
+config SENSORS_LAN966X
+ tristate "Microchip LAN966x Hardware Monitoring"
+ depends on SOC_LAN966 || COMPILE_TEST
+ select REGMAP
+ select POLYNOMIAL
+ help
+ If you say yes here you get support for temperature monitoring
+ on the Microchip LAN966x SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called lan966x-hwmon.
+
config SENSORS_LINEAGE
tristate "Lineage Compact Power Line Power Entry Module"
depends on I2C
@@ -1248,6 +1265,7 @@ config SENSORS_LM75
temperature sensor chip, with models including:
- Analog Devices ADT75
+ - Atmel (now Microchip) AT30TS74
- Dallas Semiconductor DS75, DS1775 and DS7505
- Global Mixed-mode Technology (GMT) G751
- Maxim MAX6625 and MAX6626
@@ -1457,11 +1475,23 @@ config SENSORS_NCT6683
This driver can also be built as a module. If so, the module
will be called nct6683.
+config SENSORS_NCT6775_CORE
+ tristate
+ select REGMAP
+ help
+ This module contains common code shared by the platform and
+ i2c versions of the nct6775 driver; it is not useful on its
+ own.
+
+ If built as a module, the module will be called
+ nct6775-core.
+
config SENSORS_NCT6775
- tristate "Nuvoton NCT6775F and compatibles"
+ tristate "Platform driver for Nuvoton NCT6775F and compatibles"
depends on !PPC
depends on ACPI_WMI || ACPI_WMI=n
select HWMON_VID
+ select SENSORS_NCT6775_CORE
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
@@ -1472,6 +1502,23 @@ config SENSORS_NCT6775
This driver can also be built as a module. If so, the module
will be called nct6775.
+config SENSORS_NCT6775_I2C
+ tristate "I2C driver for Nuvoton NCT6775F and compatibles"
+ depends on I2C
+ select REGMAP_I2C
+ select SENSORS_NCT6775_CORE
+ help
+ If you say yes here you get support for the hardware monitoring
+ functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
+ NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, and compatible
+ Super-I/O chips via their I2C interface.
+
+ If you're not building a kernel for a BMC, this is probably
+ not the driver you want (see CONFIG_SENSORS_NCT6775).
+
+ This driver can also be built as a module. If so, the module
+ will be called nct6775-i2c.
+
config SENSORS_NCT7802
tristate "Nuvoton NCT7802Y"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8a03289e2aa458..007e829d1d0d0c 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
+obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
obj-$(CONFIG_SENSORS_LOCHNAGAR) += lochnagar-hwmon.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
@@ -154,7 +155,10 @@ obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
+obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o
+nct6775-objs := nct6775-platform.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
+obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index c405a5869581c9..d2545a1be9fc07 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -481,7 +481,7 @@ static struct sensor_template meter_attrs[] = {
RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
- set_avg_interval, 0),
+ set_avg_interval, 0),
{},
};
@@ -530,6 +530,7 @@ static void remove_domain_devices(struct acpi_power_meter_resource *resource)
for (i = 0; i < resource->num_domain_devices; i++) {
struct acpi_device *obj = resource->domain_devices[i];
+
if (!obj)
continue;
@@ -580,7 +581,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
}
resource->holders_dir = kobject_create_and_add("measures",
- &resource->acpi_dev->dev.kobj);
+ &resource->acpi_dev->dev.kobj);
if (!resource->holders_dir) {
res = -ENOMEM;
goto exit_free;
@@ -590,7 +591,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
for (i = 0; i < pss->package.count; i++) {
struct acpi_device *obj;
- union acpi_object *element = &(pss->package.elements[i]);
+ union acpi_object *element = &pss->package.elements[i];
/* Refuse non-references */
if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
@@ -603,7 +604,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
continue;
res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj,
- kobject_name(&obj->dev.kobj));
+ kobject_name(&obj->dev.kobj));
if (res) {
acpi_dev_put(obj);
resource->domain_devices[i] = NULL;
@@ -788,7 +789,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource)
str = &resource->model_number;
for (i = 11; i < 14; i++) {
- union acpi_object *element = &(pss->package.elements[i]);
+ union acpi_object *element = &pss->package.elements[i];
if (element->type != ACPI_TYPE_STRING) {
res = -EINVAL;
@@ -868,8 +869,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
if (!device)
return -EINVAL;
- resource = kzalloc(sizeof(struct acpi_power_meter_resource),
- GFP_KERNEL);
+ resource = kzalloc(sizeof(*resource), GFP_KERNEL);
if (!resource)
return -ENOMEM;
@@ -884,7 +884,8 @@ static int acpi_power_meter_add(struct acpi_device *device)
if (res)
goto exit_free;
- resource->trip[0] = resource->trip[1] = -1;
+ resource->trip[0] = -1;
+ resource->trip[1] = -1;
res = setup_attrs(resource);
if (res)
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index 9d5b019651f2d7..ac480e6e4818b5 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -112,6 +112,8 @@
#define CONFIG3_THERM 0x02
#define CONFIG4_PINFUNC 0x03
+#define CONFIG4_THERM 0x01
+#define CONFIG4_SMBALERT 0x02
#define CONFIG4_MAXDUTY 0x08
#define CONFIG4_ATTN_IN10 0x30
#define CONFIG4_ATTN_IN43 0xC0
@@ -1460,6 +1462,96 @@ static int adt7475_update_limits(struct i2c_client *client)
return 0;
}
+static int load_config3(const struct i2c_client *client, const char *propname)
+{
+ const char *function;
+ u8 config3;
+ int ret;
+
+ ret = of_property_read_string(client->dev.of_node, propname, &function);
+ if (!ret) {
+ ret = adt7475_read(REG_CONFIG3);
+ if (ret < 0)
+ return ret;
+
+ config3 = ret & ~CONFIG3_SMBALERT;
+ if (!strcmp("pwm2", function))
+ ;
+ else if (!strcmp("smbalert#", function))
+ config3 |= CONFIG3_SMBALERT;
+ else
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, REG_CONFIG3, config3);
+ }
+
+ return 0;
+}
+
+static int load_config4(const struct i2c_client *client, const char *propname)
+{
+ const char *function;
+ u8 config4;
+ int ret;
+
+ ret = of_property_read_string(client->dev.of_node, propname, &function);
+ if (!ret) {
+ ret = adt7475_read(REG_CONFIG4);
+ if (ret < 0)
+ return ret;
+
+ config4 = ret & ~CONFIG4_PINFUNC;
+
+ if (!strcmp("tach4", function))
+ ;
+ else if (!strcmp("therm#", function))
+ config4 |= CONFIG4_THERM;
+ else if (!strcmp("smbalert#", function))
+ config4 |= CONFIG4_SMBALERT;
+ else if (!strcmp("gpio", function))
+ config4 |= CONFIG4_PINFUNC;
+ else
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, REG_CONFIG4, config4);
+ }
+
+ return 0;
+}
+
+static int load_config(const struct i2c_client *client, enum chips chip)
+{
+ int err;
+ const char *prop1, *prop2;
+
+ switch (chip) {
+ case adt7473:
+ case adt7475:
+ prop1 = "adi,pin5-function";
+ prop2 = "adi,pin9-function";
+ break;
+ case adt7476:
+ case adt7490:
+ prop1 = "adi,pin10-function";
+ prop2 = "adi,pin14-function";
+ break;
+ }
+
+ err = load_config3(client, prop1);
+ if (err) {
+ dev_err(&client->dev, "failed to configure %s\n", prop1);
+ return err;
+ }
+
+ err = load_config4(client, prop2);
+ if (err) {
+ dev_err(&client->dev, "failed to configure %s\n", prop2);
+ return err;
+ }
+
+ return 0;
+}
+
static int set_property_bit(const struct i2c_client *client, char *property,
u8 *config, u8 bit_index)
{
@@ -1477,12 +1569,12 @@ static int set_property_bit(const struct i2c_client *client, char *property,
return ret;
}
-static int load_attenuators(const struct i2c_client *client, int chip,
+static int load_attenuators(const struct i2c_client *client, enum chips chip,
struct adt7475_data *data)
{
- int ret;
-
- if (chip == adt7476 || chip == adt7490) {
+ switch (chip) {
+ case adt7476:
+ case adt7490:
set_property_bit(client, "adi,bypass-attenuator-in0",
&data->config4, 4);
set_property_bit(client, "adi,bypass-attenuator-in1",
@@ -1492,18 +1584,15 @@ static int load_attenuators(const struct i2c_client *client, int chip,
set_property_bit(client, "adi,bypass-attenuator-in4",
&data->config4, 7);
- ret = i2c_smbus_write_byte_data(client, REG_CONFIG4,
- data->config4);
- if (ret < 0)
- return ret;
- } else if (chip == adt7473 || chip == adt7475) {
+ return i2c_smbus_write_byte_data(client, REG_CONFIG4,
+ data->config4);
+ case adt7473:
+ case adt7475:
set_property_bit(client, "adi,bypass-attenuator-in1",
&data->config2, 5);
- ret = i2c_smbus_write_byte_data(client, REG_CONFIG2,
- data->config2);
- if (ret < 0)
- return ret;
+ return i2c_smbus_write_byte_data(client, REG_CONFIG2,
+ data->config2);
}
return 0;
@@ -1585,6 +1674,10 @@ static int adt7475_probe(struct i2c_client *client)
revision = adt7475_read(REG_DEVID2) & 0x07;
}
+ ret = load_config(client, chip);
+ if (ret)
+ return ret;
+
config3 = adt7475_read(REG_CONFIG3);
/* Pin PWM2 may alternatively be used for ALERT output */
if (!(config3 & CONFIG3_SMBALERT))
diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 525809cf7c9524..a0e69f7ece36e3 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -1,30 +1,37 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk 360)
+ * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo)
*
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
* sensor values.
*
* Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
+ * Copyright 2022 Jack Doan <me@jackdoan.com>
*/
+#include <linux/crc16.h>
#include <linux/debugfs.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <asm/unaligned.h>
#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
+#define USB_PRODUCT_ID_FARBWERK 0xf00a
#define USB_PRODUCT_ID_D5NEXT 0xf00e
#define USB_PRODUCT_ID_FARBWERK360 0xf010
+#define USB_PRODUCT_ID_OCTO 0xf011
-enum kinds { d5next, farbwerk360 };
+enum kinds { d5next, farbwerk, farbwerk360, octo };
static const char *const aqc_device_names[] = {
[d5next] = "d5next",
- [farbwerk360] = "farbwerk360"
+ [farbwerk] = "farbwerk",
+ [farbwerk360] = "farbwerk360",
+ [octo] = "octo"
};
#define DRIVER_NAME "aquacomputer_d5next"
@@ -35,6 +42,18 @@ static const char *const aqc_device_names[] = {
#define SERIAL_SECOND_PART 5
#define FIRMWARE_VERSION 13
+#define CTRL_REPORT_ID 0x03
+
+/* The HID report that the official software always sends
+ * after writing values, currently same for all devices
+ */
+#define SECONDARY_CTRL_REPORT_ID 0x02
+#define SECONDARY_CTRL_REPORT_SIZE 0x0B
+
+static u8 secondary_ctrl_report[] = {
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
+};
+
/* Register offsets for the D5 Next pump */
#define D5NEXT_POWER_CYCLES 24
@@ -53,14 +72,46 @@ static const char *const aqc_device_names[] = {
#define D5NEXT_PUMP_CURRENT 112
#define D5NEXT_FAN_CURRENT 99
+/* Register offsets for the Farbwerk RGB controller */
+#define FARBWERK_NUM_SENSORS 4
+#define FARBWERK_SENSOR_START 0x2f
+#define FARBWERK_SENSOR_SIZE 0x02
+#define FARBWERK_SENSOR_DISCONNECTED 0x7FFF
+
/* Register offsets for the Farbwerk 360 RGB controller */
#define FARBWERK360_NUM_SENSORS 4
-#define FARBWERK360_SENSOR_START 0x32
+#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_SENSOR_SIZE 0x02
#define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
+/* Register offsets for the Octo fan controller */
+#define OCTO_POWER_CYCLES 0x18
+#define OCTO_NUM_FANS 8
+#define OCTO_FAN_PERCENT_OFFSET 0x00
+#define OCTO_FAN_VOLTAGE_OFFSET 0x02
+#define OCTO_FAN_CURRENT_OFFSET 0x04
+#define OCTO_FAN_POWER_OFFSET 0x06
+#define OCTO_FAN_SPEED_OFFSET 0x08
+
+static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
+
+#define OCTO_NUM_SENSORS 4
+#define OCTO_SENSOR_START 0x3D
+#define OCTO_SENSOR_SIZE 0x02
+#define OCTO_SENSOR_DISCONNECTED 0x7FFF
+
+#define OCTO_CTRL_REPORT_SIZE 0x65F
+#define OCTO_CTRL_REPORT_CHECKSUM_OFFSET 0x65D
+#define OCTO_CTRL_REPORT_CHECKSUM_START 0x01
+#define OCTO_CTRL_REPORT_CHECKSUM_LENGTH 0x65C
+
+/* Fan speed registers in Octo control report (from 0-100%) */
+static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
+
/* Labels for D5 Next */
-#define L_D5NEXT_COOLANT_TEMP "Coolant temp"
+static const char *const label_d5next_temp[] = {
+ "Coolant temp"
+};
static const char *const label_d5next_speeds[] = {
"Pump speed",
@@ -83,7 +134,7 @@ static const char *const label_d5next_current[] = {
"Fan current"
};
-/* Labels for Farbwerk 360 temperature sensors */
+/* Labels for Farbwerk, Farbwerk 360 and Octo temperature sensors */
static const char *const label_temp_sensors[] = {
"Sensor 1",
"Sensor 2",
@@ -91,32 +142,182 @@ static const char *const label_temp_sensors[] = {
"Sensor 4"
};
+/* Labels for Octo */
+static const char *const label_fan_speed[] = {
+ "Fan 1 speed",
+ "Fan 2 speed",
+ "Fan 3 speed",
+ "Fan 4 speed",
+ "Fan 5 speed",
+ "Fan 6 speed",
+ "Fan 7 speed",
+ "Fan 8 speed"
+};
+
+static const char *const label_fan_power[] = {
+ "Fan 1 power",
+ "Fan 2 power",
+ "Fan 3 power",
+ "Fan 4 power",
+ "Fan 5 power",
+ "Fan 6 power",
+ "Fan 7 power",
+ "Fan 8 power"
+};
+
+static const char *const label_fan_voltage[] = {
+ "Fan 1 voltage",
+ "Fan 2 voltage",
+ "Fan 3 voltage",
+ "Fan 4 voltage",
+ "Fan 5 voltage",
+ "Fan 6 voltage",
+ "Fan 7 voltage",
+ "Fan 8 voltage"
+};
+
+static const char *const label_fan_current[] = {
+ "Fan 1 current",
+ "Fan 2 current",
+ "Fan 3 current",
+ "Fan 4 current",
+ "Fan 5 current",
+ "Fan 6 current",
+ "Fan 7 current",
+ "Fan 8 current"
+};
+
struct aqc_data {
struct hid_device *hdev;
struct device *hwmon_dev;
struct dentry *debugfs;
+ struct mutex mutex; /* Used for locking access when reading and writing PWM values */
enum kinds kind;
const char *name;
+ int buffer_size;
+ u8 *buffer;
+ int checksum_start;
+ int checksum_length;
+ int checksum_offset;
+
/* General info, same across all devices */
u32 serial_number[2];
u16 firmware_version;
- /* D5 Next specific - how many times the device was powered on */
+ /* How many times the device was powered on */
u32 power_cycles;
/* Sensor values */
s32 temp_input[4];
- u16 speed_input[2];
- u32 power_input[2];
- u16 voltage_input[3];
- u16 current_input[2];
+ u16 speed_input[8];
+ u32 power_input[8];
+ u16 voltage_input[8];
+ u16 current_input[8];
+
+ /* Label values */
+ const char *const *temp_label;
+ const char *const *speed_label;
+ const char *const *power_label;
+ const char *const *voltage_label;
+ const char *const *current_label;
unsigned long updated;
};
-static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
- int channel)
+/* Converts from centi-percent */
+static int aqc_percent_to_pwm(u16 val)
+{
+ return DIV_ROUND_CLOSEST(val * 255, 100 * 100);
+}
+
+/* Converts to centi-percent */
+static int aqc_pwm_to_percent(long val)
+{
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ return DIV_ROUND_CLOSEST(val * 100 * 100, 255);
+}
+
+/* Expects the mutex to be locked */
+static int aqc_get_ctrl_data(struct aqc_data *priv)
+{
+ int ret;
+
+ memset(priv->buffer, 0x00, priv->buffer_size);
+ ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret < 0)
+ ret = -ENODATA;
+
+ return ret;
+}
+
+/* Expects the mutex to be locked */
+static int aqc_send_ctrl_data(struct aqc_data *priv)
+{
+ int ret;
+ u16 checksum;
+
+ /* Init and xorout value for CRC-16/USB is 0xffff */
+ checksum = crc16(0xffff, priv->buffer + priv->checksum_start, priv->checksum_length);
+ checksum ^= 0xffff;
+
+ /* Place the new checksum at the end of the report */
+ put_unaligned_be16(checksum, priv->buffer + priv->checksum_offset);
+
+ /* Send the patched up report back to the device */
+ ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0)
+ return ret;
+
+ /* The official software sends this report after every change, so do it here as well */
+ ret = hid_hw_raw_request(priv->hdev, SECONDARY_CTRL_REPORT_ID, secondary_ctrl_report,
+ SECONDARY_CTRL_REPORT_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_SET_REPORT);
+ return ret;
+}
+
+/* Refreshes the control buffer and returns value at offset */
+static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
+{
+ int ret;
+
+ mutex_lock(&priv->mutex);
+
+ ret = aqc_get_ctrl_data(priv);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ ret = get_unaligned_be16(priv->buffer + offset);
+
+unlock_and_return:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val)
+{
+ int ret;
+
+ mutex_lock(&priv->mutex);
+
+ ret = aqc_get_ctrl_data(priv);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ put_unaligned_be16((u16)val, priv->buffer + offset);
+
+ ret = aqc_send_ctrl_data(priv);
+
+unlock_and_return:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel)
{
const struct aqc_data *priv = data;
@@ -127,18 +328,49 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
if (channel == 0)
return 0444;
break;
+ case farbwerk:
case farbwerk360:
+ case octo:
return 0444;
default:
break;
}
break;
+ case hwmon_pwm:
+ switch (priv->kind) {
+ case octo:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
case hwmon_fan:
case hwmon_power:
- case hwmon_in:
case hwmon_curr:
switch (priv->kind) {
case d5next:
+ if (channel < 2)
+ return 0444;
+ break;
+ case octo:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_in:
+ switch (priv->kind) {
+ case d5next:
+ if (channel < 3)
+ return 0444;
+ break;
+ case octo:
return 0444;
default:
break;
@@ -154,6 +386,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
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))
@@ -172,6 +405,19 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_power:
*val = priv->power_input[channel];
break;
+ case hwmon_pwm:
+ switch (priv->kind) {
+ case octo:
+ ret = aqc_get_ctrl_val(priv, octo_ctrl_fan_offsets[channel]);
+ if (ret < 0)
+ return ret;
+
+ *val = aqc_percent_to_pwm(ret);
+ break;
+ default:
+ break;
+ }
+ break;
case hwmon_in:
*val = priv->voltage_input[channel];
break;
@@ -192,48 +438,51 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32
switch (type) {
case hwmon_temp:
- switch (priv->kind) {
- case d5next:
- *str = L_D5NEXT_COOLANT_TEMP;
- break;
- case farbwerk360:
- *str = label_temp_sensors[channel];
- break;
- default:
- break;
- }
+ *str = priv->temp_label[channel];
break;
case hwmon_fan:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_speeds[channel];
- break;
- default:
- break;
- }
+ *str = priv->speed_label[channel];
break;
case hwmon_power:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_power[channel];
- break;
- default:
- break;
- }
+ *str = priv->power_label[channel];
break;
case hwmon_in:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_voltages[channel];
- break;
- default:
- break;
- }
+ *str = priv->voltage_label[channel];
break;
case hwmon_curr:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_current[channel];
+ *str = priv->current_label[channel];
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long val)
+{
+ int ret, pwm_value;
+ struct aqc_data *priv = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ switch (priv->kind) {
+ case octo:
+ pwm_value = aqc_pwm_to_percent(val);
+ if (pwm_value < 0)
+ return pwm_value;
+
+ ret = aqc_set_ctrl_val(priv, octo_ctrl_fan_offsets[channel],
+ pwm_value);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
break;
default:
break;
@@ -250,6 +499,7 @@ static const struct hwmon_ops aqc_hwmon_ops = {
.is_visible = aqc_is_visible,
.read = aqc_read,
.read_string = aqc_read_string,
+ .write = aqc_write
};
static const struct hwmon_channel_info *aqc_info[] = {
@@ -260,16 +510,48 @@ static const struct hwmon_channel_info *aqc_info[] = {
HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL),
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT),
HWMON_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_CHANNEL_INFO(curr,
HWMON_C_INPUT | 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_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),
NULL
};
@@ -279,8 +561,7 @@ static const struct hwmon_chip_info aqc_chip_info = {
.info = aqc_info,
};
-static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
- int size)
+static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
{
int i, sensor_value;
struct aqc_data *priv;
@@ -315,6 +596,17 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
break;
+ case farbwerk:
+ /* Temperature sensor readings */
+ for (i = 0; i < FARBWERK_NUM_SENSORS; i++) {
+ sensor_value = get_unaligned_be16(data + FARBWERK_SENSOR_START +
+ i * FARBWERK_SENSOR_SIZE);
+ if (sensor_value == FARBWERK_SENSOR_DISCONNECTED)
+ priv->temp_input[i] = -ENODATA;
+ else
+ priv->temp_input[i] = sensor_value * 10;
+ }
+ break;
case farbwerk360:
/* Temperature sensor readings */
for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) {
@@ -326,6 +618,35 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->temp_input[i] = sensor_value * 10;
}
break;
+ case octo:
+ priv->power_cycles = get_unaligned_be32(data + OCTO_POWER_CYCLES);
+
+ /* Fan speed and related readings */
+ for (i = 0; i < OCTO_NUM_FANS; i++) {
+ priv->speed_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_SPEED_OFFSET);
+ priv->power_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_POWER_OFFSET) * 10000;
+ priv->voltage_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_VOLTAGE_OFFSET) * 10;
+ priv->current_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_CURRENT_OFFSET);
+ }
+
+ /* Temperature sensor readings */
+ for (i = 0; i < OCTO_NUM_SENSORS; i++) {
+ sensor_value = get_unaligned_be16(data + OCTO_SENSOR_START +
+ i * OCTO_SENSOR_SIZE);
+ if (sensor_value == OCTO_SENSOR_DISCONNECTED)
+ priv->temp_input[i] = -ENODATA;
+ else
+ priv->temp_input[i] = sensor_value * 10;
+ }
+ break;
default:
break;
}
@@ -378,8 +699,14 @@ static void aqc_debugfs_init(struct aqc_data *priv)
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->kind == d5next)
+ switch (priv->kind) {
+ case d5next:
+ case octo:
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
+ break;
+ default:
+ break;
+ }
}
#else
@@ -419,9 +746,35 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
switch (hdev->product) {
case USB_PRODUCT_ID_D5NEXT:
priv->kind = d5next;
+
+ priv->temp_label = label_d5next_temp;
+ priv->speed_label = label_d5next_speeds;
+ priv->power_label = label_d5next_power;
+ priv->voltage_label = label_d5next_voltages;
+ priv->current_label = label_d5next_current;
+ break;
+ case USB_PRODUCT_ID_FARBWERK:
+ priv->kind = farbwerk;
+
+ priv->temp_label = label_temp_sensors;
break;
case USB_PRODUCT_ID_FARBWERK360:
priv->kind = farbwerk360;
+
+ priv->temp_label = label_temp_sensors;
+ break;
+ case USB_PRODUCT_ID_OCTO:
+ priv->kind = octo;
+ priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
+ priv->checksum_start = OCTO_CTRL_REPORT_CHECKSUM_START;
+ priv->checksum_length = OCTO_CTRL_REPORT_CHECKSUM_LENGTH;
+ priv->checksum_offset = OCTO_CTRL_REPORT_CHECKSUM_OFFSET;
+
+ priv->temp_label = label_temp_sensors;
+ priv->speed_label = label_fan_speed;
+ priv->power_label = label_fan_power;
+ priv->voltage_label = label_fan_voltage;
+ priv->current_label = label_fan_current;
break;
default:
break;
@@ -429,6 +782,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->name = aqc_device_names[priv->kind];
+ priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL);
+ if (!priv->buffer) {
+ ret = -ENOMEM;
+ goto fail_and_close;
+ }
+
+ mutex_init(&priv->mutex);
+
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv,
&aqc_chip_info, NULL);
@@ -461,7 +822,9 @@ 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_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) },
{ }
};
@@ -491,4 +854,5 @@ module_exit(aqc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
+MODULE_AUTHOR("Jack Doan <me@jackdoan.com>");
MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices");
diff --git a/drivers/hwmon/as370-hwmon.c b/drivers/hwmon/as370-hwmon.c
index 464244ba8d584a..63b5b2d6e59307 100644
--- a/drivers/hwmon/as370-hwmon.c
+++ b/drivers/hwmon/as370-hwmon.c
@@ -76,18 +76,8 @@ as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
}
}
-static const u32 as370_hwmon_temp_config[] = {
- HWMON_T_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info as370_hwmon_temp = {
- .type = hwmon_temp,
- .config = as370_hwmon_temp_config,
-};
-
static const struct hwmon_channel_info *as370_hwmon_info[] = {
- &as370_hwmon_temp,
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index b5cf0136360cdf..57e11b2bab742b 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -54,8 +54,10 @@ static char *mutex_path_override;
/* ACPI mutex for locking access to the EC for the firmware */
#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX"
-/* There are two variants of the vendor spelling */
-#define VENDOR_ASUS_UPPER_CASE "ASUSTeK COMPUTER INC."
+#define MAX_IDENTICAL_BOARD_VARIATIONS 3
+
+/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
+#define ACPI_GLOBAL_LOCK_PSEUDO_PATH ":GLOBAL_LOCK"
typedef union {
u32 value;
@@ -133,8 +135,44 @@ enum ec_sensors {
#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
#define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out)
+enum board_family {
+ family_unknown,
+ family_amd_400_series,
+ family_amd_500_series,
+};
+
/* All the known sensors for ASUS EC controllers */
-static const struct ec_sensor_info known_ec_sensors[] = {
+static const struct ec_sensor_info sensors_family_amd_400[] = {
+ [ec_sensor_temp_chipset] =
+ EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
+ [ec_sensor_temp_cpu] =
+ EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
+ [ec_sensor_temp_mb] =
+ EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
+ [ec_sensor_temp_t_sensor] =
+ EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
+ [ec_sensor_temp_vrm] =
+ EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
+ [ec_sensor_in_cpu_core] =
+ EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
+ [ec_sensor_fan_cpu_opt] =
+ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
+ [ec_sensor_fan_vrm_hs] =
+ EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
+ [ec_sensor_fan_chipset] =
+ /* no chipset fans in this generation */
+ EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00),
+ [ec_sensor_fan_water_flow] =
+ EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4),
+ [ec_sensor_curr_cpu] =
+ EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
+ [ec_sensor_temp_water_in] =
+ EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
+ [ec_sensor_temp_water_out] =
+ EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b),
+};
+
+static const struct ec_sensor_info sensors_family_amd_500[] = {
[ec_sensor_temp_chipset] =
EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
@@ -164,68 +202,134 @@ static const struct ec_sensor_info known_ec_sensors[] = {
(SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB)
#define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT)
-#define DMI_EXACT_MATCH_BOARD(vendor, name, sensors) { \
- .matches = { \
- DMI_EXACT_MATCH(DMI_BOARD_VENDOR, vendor), \
- DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
- }, \
- .driver_data = (void *)(sensors), \
-}
+struct ec_board_info {
+ const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS];
+ unsigned long sensors;
+ /*
+ * Defines which mutex to use for guarding access to the state and the
+ * hardware. Can be either a full path to an AML mutex or the
+ * pseudo-path ACPI_GLOBAL_LOCK_PSEUDO_PATH to use the global ACPI lock,
+ * or left empty to use a regular mutex object, in which case access to
+ * the hardware is not guarded.
+ */
+ const char *mutex_path;
+ enum board_family family;
+};
-static const struct dmi_system_id asus_ec_dmi_table[] __initconst = {
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "PRIME X570-PRO",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "Pro WS X570-ACE",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
- SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII DARK HERO",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
- SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII FORMULA",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG CROSSHAIR VIII HERO",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
- SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
- SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII HERO (WI-FI)",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
- SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
- SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII IMPACT",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CHIPSET |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX B550-E GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CPU_OPT),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX B550-I GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_VRM_HS |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX X570-E GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CHIPSET |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX X570-F GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX X570-I GAMING",
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
- SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
+static const struct ec_board_info board_info[] = {
+ {
+ .board_names = {"PRIME X470-PRO"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CPU_OPT |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
+ .family = family_amd_400_series,
+ },
+ {
+ .board_names = {"PRIME X570-PRO"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ProArt 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,
+ },
+ {
+ .board_names = {"Pro WS X570-ACE"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG CROSSHAIR VIII DARK HERO"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR |
+ SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
+ SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {
+ "ROG CROSSHAIR VIII FORMULA"
+ "ROG CROSSHAIR VIII HERO",
+ "ROG CROSSHAIR VIII HERO (WI-FI)",
+ },
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR |
+ SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
+ SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
+ SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG CROSSHAIR VIII IMPACT"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX B550-E GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CPU_OPT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX B550-I GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-E GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-E GAMING WIFI II"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-F GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-I GAMING"},
+ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
+ SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
{}
};
@@ -234,8 +338,49 @@ struct ec_sensor {
s32 cached_value;
};
+struct lock_data {
+ union {
+ acpi_handle aml;
+ /* global lock handle */
+ u32 glk;
+ } mutex;
+ bool (*lock)(struct lock_data *data);
+ bool (*unlock)(struct lock_data *data);
+};
+
+/*
+ * The next function pairs implement options for locking access to the
+ * state and the EC
+ */
+static bool lock_via_acpi_mutex(struct lock_data *data)
+{
+ /*
+ * ASUS DSDT does not specify that access to the EC has to be guarded,
+ * but firmware does access it via ACPI
+ */
+ return ACPI_SUCCESS(acpi_acquire_mutex(data->mutex.aml,
+ NULL, ACPI_LOCK_DELAY_MS));
+}
+
+static bool unlock_acpi_mutex(struct lock_data *data)
+{
+ return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL));
+}
+
+static bool lock_via_global_acpi_lock(struct lock_data *data)
+{
+ return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS,
+ &data->mutex.glk));
+}
+
+static bool unlock_global_acpi_lock(struct lock_data *data)
+{
+ return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk));
+}
+
struct ec_sensors_data {
- unsigned long board_sensors;
+ const struct ec_board_info *board_info;
+ const struct ec_sensor_info *sensors_info;
struct ec_sensor *sensors;
/* EC registers to read from */
u16 *registers;
@@ -244,7 +389,7 @@ struct ec_sensors_data {
u8 banks[ASUS_EC_MAX_BANK + 1];
/* in jiffies */
unsigned long last_updated;
- acpi_handle aml_mutex;
+ struct lock_data lock_data;
/* number of board EC sensors */
u8 nr_sensors;
/*
@@ -278,7 +423,7 @@ static bool is_sensor_data_signed(const struct ec_sensor_info *si)
static const struct ec_sensor_info *
get_sensor_info(const struct ec_sensors_data *state, int index)
{
- return &known_ec_sensors[state->sensors[index].info_index];
+ return state->sensors_info + state->sensors[index].info_index;
}
static int find_ec_sensor_index(const struct ec_sensors_data *ec,
@@ -301,11 +446,6 @@ static int __init bank_compare(const void *a, const void *b)
return *((const s8 *)a) - *((const s8 *)b);
}
-static int __init board_sensors_count(unsigned long sensors)
-{
- return hweight_long(sensors);
-}
-
static void __init setup_sensor_data(struct ec_sensors_data *ec)
{
struct ec_sensor *s = ec->sensors;
@@ -316,14 +456,14 @@ static void __init setup_sensor_data(struct ec_sensors_data *ec)
ec->nr_banks = 0;
ec->nr_registers = 0;
- for_each_set_bit(i, &ec->board_sensors,
- BITS_PER_TYPE(ec->board_sensors)) {
+ for_each_set_bit(i, &ec->board_info->sensors,
+ BITS_PER_TYPE(ec->board_info->sensors)) {
s->info_index = i;
s->cached_value = 0;
ec->nr_registers +=
- known_ec_sensors[s->info_index].addr.components.size;
+ ec->sensors_info[s->info_index].addr.components.size;
bank_found = false;
- bank = known_ec_sensors[s->info_index].addr.components.bank;
+ bank = ec->sensors_info[s->info_index].addr.components.bank;
for (j = 0; j < ec->nr_banks; j++) {
if (ec->banks[j] == bank) {
bank_found = true;
@@ -353,23 +493,36 @@ static void __init fill_ec_registers(struct ec_sensors_data *ec)
}
}
-static acpi_handle __init asus_hw_access_mutex(struct device *dev)
+static int __init setup_lock_data(struct device *dev)
{
const char *mutex_path;
- acpi_handle res;
int status;
+ struct ec_sensors_data *state = dev_get_drvdata(dev);
mutex_path = mutex_path_override ?
- mutex_path_override : ASUS_HW_ACCESS_MUTEX_ASMX;
+ mutex_path_override : state->board_info->mutex_path;
- status = acpi_get_handle(NULL, (acpi_string)mutex_path, &res);
- if (ACPI_FAILURE(status)) {
- dev_err(dev,
- "Could not get hardware access guard mutex '%s': error %d",
- mutex_path, status);
- return NULL;
+ if (!mutex_path || !strlen(mutex_path)) {
+ dev_err(dev, "Hardware access guard mutex name is empty");
+ return -EINVAL;
}
- return res;
+ if (!strcmp(mutex_path, ACPI_GLOBAL_LOCK_PSEUDO_PATH)) {
+ state->lock_data.mutex.glk = 0;
+ state->lock_data.lock = lock_via_global_acpi_lock;
+ state->lock_data.unlock = unlock_global_acpi_lock;
+ } else {
+ status = acpi_get_handle(NULL, (acpi_string)mutex_path,
+ &state->lock_data.mutex.aml);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev,
+ "Failed to get hardware access guard AML mutex '%s': error %d",
+ mutex_path, status);
+ return -ENOENT;
+ }
+ state->lock_data.lock = lock_via_acpi_mutex;
+ state->lock_data.unlock = unlock_acpi_mutex;
+ }
+ return 0;
}
static int asus_ec_bank_switch(u8 bank, u8 *old)
@@ -457,10 +610,11 @@ static inline s32 get_sensor_value(const struct ec_sensor_info *si, u8 *data)
static void update_sensor_values(struct ec_sensors_data *ec, u8 *data)
{
const struct ec_sensor_info *si;
- struct ec_sensor *s;
+ struct ec_sensor *s, *sensor_end;
- for (s = ec->sensors; s != ec->sensors + ec->nr_sensors; s++) {
- si = &known_ec_sensors[s->info_index];
+ sensor_end = ec->sensors + ec->nr_sensors;
+ for (s = ec->sensors; s != sensor_end; s++) {
+ si = ec->sensors_info + s->info_index;
s->cached_value = get_sensor_value(si, data);
data += si->addr.components.size;
}
@@ -471,15 +625,9 @@ static int update_ec_sensors(const struct device *dev,
{
int status;
- /*
- * ASUS DSDT does not specify that access to the EC has to be guarded,
- * but firmware does access it via ACPI
- */
- if (ACPI_FAILURE(acpi_acquire_mutex(ec->aml_mutex, NULL,
- ACPI_LOCK_DELAY_MS))) {
- dev_err(dev, "Failed to acquire AML mutex");
- status = -EBUSY;
- goto cleanup;
+ if (!ec->lock_data.lock(&ec->lock_data)) {
+ dev_warn(dev, "Failed to acquire mutex");
+ return -EBUSY;
}
status = asus_ec_block_read(dev, ec);
@@ -487,10 +635,10 @@ static int update_ec_sensors(const struct device *dev,
if (!status) {
update_sensor_values(ec, ec->read_buffer);
}
- if (ACPI_FAILURE(acpi_release_mutex(ec->aml_mutex, NULL))) {
- dev_err(dev, "Failed to release AML mutex");
- }
-cleanup:
+
+ if (!ec->lock_data.unlock(&ec->lock_data))
+ dev_err(dev, "Failed to release mutex");
+
return status;
}
@@ -597,12 +745,24 @@ static struct hwmon_chip_info asus_ec_chip_info = {
.ops = &asus_ec_hwmon_ops,
};
-static unsigned long __init get_board_sensors(void)
+static const struct ec_board_info * __init get_board_info(void)
{
- const struct dmi_system_id *dmi_entry =
- dmi_first_match(asus_ec_dmi_table);
+ const char *dmi_board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ const char *dmi_board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ const struct ec_board_info *board;
- return dmi_entry ? (unsigned long)dmi_entry->driver_data : 0;
+ if (!dmi_board_vendor || !dmi_board_name ||
+ strcasecmp(dmi_board_vendor, "ASUSTeK COMPUTER INC."))
+ return NULL;
+
+ for (board = board_info; board->sensors; board++) {
+ if (match_string(board->board_names,
+ MAX_IDENTICAL_BOARD_VARIATIONS,
+ dmi_board_name) >= 0)
+ return board;
+ }
+
+ return NULL;
}
static int __init asus_ec_probe(struct platform_device *pdev)
@@ -610,17 +770,18 @@ static int __init asus_ec_probe(struct platform_device *pdev)
const struct hwmon_channel_info **ptr_asus_ec_ci;
int nr_count[hwmon_max] = { 0 }, nr_types = 0;
struct hwmon_channel_info *asus_ec_hwmon_chan;
+ const struct ec_board_info *pboard_info;
const struct hwmon_chip_info *chip_info;
struct device *dev = &pdev->dev;
struct ec_sensors_data *ec_data;
const struct ec_sensor_info *si;
enum hwmon_sensor_types type;
- unsigned long board_sensors;
struct device *hwdev;
unsigned int i;
+ int status;
- board_sensors = get_board_sensors();
- if (!board_sensors)
+ pboard_info = get_board_info();
+ if (!pboard_info)
return -ENODEV;
ec_data = devm_kzalloc(dev, sizeof(struct ec_sensors_data),
@@ -629,11 +790,31 @@ static int __init asus_ec_probe(struct platform_device *pdev)
return -ENOMEM;
dev_set_drvdata(dev, ec_data);
- ec_data->board_sensors = board_sensors;
- ec_data->nr_sensors = board_sensors_count(ec_data->board_sensors);
+ ec_data->board_info = pboard_info;
+
+ switch (ec_data->board_info->family) {
+ case family_amd_400_series:
+ ec_data->sensors_info = sensors_family_amd_400;
+ break;
+ case family_amd_500_series:
+ ec_data->sensors_info = sensors_family_amd_500;
+ break;
+ default:
+ dev_err(dev, "Unknown board family: %d",
+ ec_data->board_info->family);
+ return -EINVAL;
+ }
+
+ ec_data->nr_sensors = hweight_long(ec_data->board_info->sensors);
ec_data->sensors = devm_kcalloc(dev, ec_data->nr_sensors,
sizeof(struct ec_sensor), GFP_KERNEL);
+ status = setup_lock_data(dev);
+ if (status) {
+ dev_err(dev, "Failed to setup state/EC locking: %d", status);
+ return status;
+ }
+
setup_sensor_data(ec_data);
ec_data->registers = devm_kcalloc(dev, ec_data->nr_registers,
sizeof(u16), GFP_KERNEL);
@@ -645,8 +826,6 @@ static int __init asus_ec_probe(struct platform_device *pdev)
fill_ec_registers(ec_data);
- ec_data->aml_mutex = asus_hw_access_mutex(dev);
-
for (i = 0; i < ec_data->nr_sensors; ++i) {
si = get_sensor_info(ec_data, i);
if (!nr_count[si->type])
@@ -703,7 +882,14 @@ static struct platform_driver asus_ec_sensors_platform_driver = {
},
};
-MODULE_DEVICE_TABLE(dmi, asus_ec_dmi_table);
+MODULE_DEVICE_TABLE(acpi, acpi_ec_ids);
+/*
+ * we use module_platform_driver_probe() rather than module_platform_driver()
+ * because the probe function (and its dependants) are marked with __init, which
+ * means we can't put it into the .probe member of the platform_driver struct
+ * above, and we can't mark the asus_ec_sensors_platform_driver object as __init
+ * because the object is referenced from the module exit code.
+ */
module_platform_driver_probe(asus_ec_sensors_platform_driver, asus_ec_probe);
module_param_named(mutex_path, mutex_path_override, charp, 0);
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
index 74ce5211eb752c..21ab172774ec58 100644
--- a/drivers/hwmon/bt1-pvt.c
+++ b/drivers/hwmon/bt1-pvt.c
@@ -26,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/polynomial.h>
#include <linux/seqlock.h>
#include <linux/sysfs.h>
#include <linux/types.h>
@@ -65,7 +66,7 @@ static const struct pvt_sensor_info pvt_info[] = {
* 48380,
* where T = [-48380, 147438] mC and N = [0, 1023].
*/
-static const struct pvt_poly __maybe_unused poly_temp_to_N = {
+static const struct polynomial __maybe_unused poly_temp_to_N = {
.total_divider = 10000,
.terms = {
{4, 18322, 10000, 10000},
@@ -76,7 +77,7 @@ static const struct pvt_poly __maybe_unused poly_temp_to_N = {
}
};
-static const struct pvt_poly poly_N_to_temp = {
+static const struct polynomial poly_N_to_temp = {
.total_divider = 1,
.terms = {
{4, -16743, 1000, 1},
@@ -97,7 +98,7 @@ static const struct pvt_poly poly_N_to_temp = {
* N = (18658e-3*V - 11572) / 10,
* V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
*/
-static const struct pvt_poly __maybe_unused poly_volt_to_N = {
+static const struct polynomial __maybe_unused poly_volt_to_N = {
.total_divider = 10,
.terms = {
{1, 18658, 1000, 1},
@@ -105,7 +106,7 @@ static const struct pvt_poly __maybe_unused poly_volt_to_N = {
}
};
-static const struct pvt_poly poly_N_to_volt = {
+static const struct polynomial poly_N_to_volt = {
.total_divider = 10,
.terms = {
{1, 100000, 18658, 1},
@@ -113,31 +114,6 @@ static const struct pvt_poly poly_N_to_volt = {
}
};
-/*
- * Here is the polynomial calculation function, which performs the
- * redistributed terms calculations. It's pretty straightforward. We walk
- * over each degree term up to the free one, and perform the redistributed
- * multiplication of the term coefficient, its divider (as for the rationale
- * fraction representation), data power and the rational fraction divider
- * leftover. Then all of this is collected in a total sum variable, which
- * value is normalized by the total divider before being returned.
- */
-static long pvt_calc_poly(const struct pvt_poly *poly, long data)
-{
- const struct pvt_poly_term *term = poly->terms;
- long tmp, ret = 0;
- int deg;
-
- do {
- tmp = term->coef;
- for (deg = 0; deg < term->deg; ++deg)
- tmp = mult_frac(tmp, data, term->divider);
- ret += tmp / term->divider_leftover;
- } while ((term++)->deg);
-
- return ret / poly->total_divider;
-}
-
static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
{
u32 old;
@@ -324,9 +300,9 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
} while (read_seqretry(&cache->data_seqlock, seq));
if (type == PVT_TEMP)
- *val = pvt_calc_poly(&poly_N_to_temp, data);
+ *val = polynomial_calc(&poly_N_to_temp, data);
else
- *val = pvt_calc_poly(&poly_N_to_volt, data);
+ *val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
@@ -345,9 +321,9 @@ static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
data = FIELD_GET(PVT_THRES_HI_MASK, data);
if (type == PVT_TEMP)
- *val = pvt_calc_poly(&poly_N_to_temp, data);
+ *val = polynomial_calc(&poly_N_to_temp, data);
else
- *val = pvt_calc_poly(&poly_N_to_volt, data);
+ *val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
@@ -360,10 +336,10 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
if (type == PVT_TEMP) {
val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
- data = pvt_calc_poly(&poly_temp_to_N, val);
+ data = polynomial_calc(&poly_temp_to_N, val);
} else {
val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
- data = pvt_calc_poly(&poly_volt_to_N, val);
+ data = polynomial_calc(&poly_volt_to_N, val);
}
/* Serialize limit update, since a part of the register is changed. */
@@ -522,9 +498,9 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
return -ETIMEDOUT;
if (type == PVT_TEMP)
- *val = pvt_calc_poly(&poly_N_to_temp, data);
+ *val = polynomial_calc(&poly_N_to_temp, data);
else
- *val = pvt_calc_poly(&poly_N_to_volt, data);
+ *val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index 84cb1ede7bc0b0..071aa6f4e109b7 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -21,14 +21,17 @@
#include <linux/errno.h>
#include <linux/hwmon.h>
#include <linux/init.h>
+#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/thermal.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -46,8 +49,11 @@
#define I8K_SMM_GET_DELL_SIG1 0xfea3
#define I8K_SMM_GET_DELL_SIG2 0xffa3
+/* in usecs */
+#define DELL_SMM_MAX_DURATION 250000
+
#define I8K_FAN_MULT 30
-#define I8K_FAN_MAX_RPM 30000
+#define I8K_FAN_RPM_THRESHOLD 1000
#define I8K_MAX_TEMP 127
#define I8K_FN_NONE 0x00
@@ -80,6 +86,11 @@ struct dell_smm_data {
int *fan_nominal_speed[DELL_SMM_NO_FANS];
};
+struct dell_smm_cooling_data {
+ u8 fan_num;
+ struct dell_smm_data *data;
+};
+
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
@@ -231,6 +242,9 @@ static int i8k_smm_func(void *par)
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
(rc ? 0xffff : regs->eax & 0xffff), duration);
+ if (duration > DELL_SMM_MAX_DURATION)
+ pr_warn_once("SMM call took %lld usecs!\n", duration);
+
return rc;
}
@@ -318,7 +332,7 @@ static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8
if (data->disallow_fan_support)
return -EINVAL;
- return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
+ return i8k_smm(&regs) ? : (regs.eax & 0xffff);
}
/*
@@ -638,9 +652,50 @@ static void __init i8k_init_procfs(struct device *dev)
#endif
-/*
- * Hwmon interface
- */
+static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+
+ *state = cdata->data->i8k_fan_max;
+
+ return 0;
+}
+
+static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+ int ret;
+
+ ret = i8k_get_fan_status(cdata->data, cdata->fan_num);
+ if (ret < 0)
+ return ret;
+
+ *state = ret;
+
+ return 0;
+}
+
+static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+ struct dell_smm_data *data = cdata->data;
+ int ret;
+
+ if (state > data->i8k_fan_max)
+ return -EINVAL;
+
+ mutex_lock(&data->i8k_mutex);
+ ret = i8k_set_fan(data, cdata->fan_num, (int)state);
+ mutex_unlock(&data->i8k_mutex);
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops dell_smm_cooling_ops = {
+ .get_max_state = dell_smm_get_max_state,
+ .get_cur_state = dell_smm_get_cur_state,
+ .set_cur_state = dell_smm_set_cur_state,
+};
static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
@@ -727,6 +782,7 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
long *val)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
+ int mult = data->i8k_fan_mult;
int ret;
switch (type) {
@@ -755,11 +811,11 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
return 0;
case hwmon_fan_min:
- *val = data->fan_nominal_speed[channel][0];
+ *val = data->fan_nominal_speed[channel][0] * mult;
return 0;
case hwmon_fan_max:
- *val = data->fan_nominal_speed[channel][data->i8k_fan_max];
+ *val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult;
return 0;
case hwmon_fan_target:
@@ -770,7 +826,7 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
if (ret > data->i8k_fan_max)
ret = data->i8k_fan_max;
- *val = data->fan_nominal_speed[channel][ret];
+ *val = data->fan_nominal_speed[channel][ret] * mult;
return 0;
default:
@@ -941,6 +997,37 @@ static const struct hwmon_chip_info dell_smm_chip_info = {
.info = dell_smm_info,
};
+static int __init dell_smm_init_cdev(struct device *dev, u8 fan_num)
+{
+ struct dell_smm_data *data = dev_get_drvdata(dev);
+ struct thermal_cooling_device *cdev;
+ struct dell_smm_cooling_data *cdata;
+ int ret = 0;
+ char *name;
+
+ name = kasprintf(GFP_KERNEL, "dell-smm-fan%u", fan_num + 1);
+ if (!name)
+ return -ENOMEM;
+
+ cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL);
+ if (cdata) {
+ cdata->fan_num = fan_num;
+ cdata->data = data;
+ cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata,
+ &dell_smm_cooling_ops);
+ if (IS_ERR(cdev)) {
+ devm_kfree(dev, cdata);
+ ret = PTR_ERR(cdev);
+ }
+ } else {
+ ret = -ENOMEM;
+ }
+
+ kfree(name);
+
+ return ret;
+}
+
static int __init dell_smm_init_hwmon(struct device *dev)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
@@ -967,6 +1054,15 @@ static int __init dell_smm_init_hwmon(struct device *dev)
continue;
data->fan[i] = true;
+
+ /* the cooling device is not critical, ignore failures */
+ if (IS_REACHABLE(CONFIG_THERMAL)) {
+ err = dell_smm_init_cdev(dev, i);
+ if (err < 0)
+ dev_warn(dev, "Failed to register cooling device for fan %u\n",
+ i + 1);
+ }
+
data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1,
sizeof(*data->fan_nominal_speed[i]),
GFP_KERNEL);
@@ -982,6 +1078,13 @@ static int __init dell_smm_init_hwmon(struct device *dev)
break;
}
data->fan_nominal_speed[i][state] = err;
+ /*
+ * Autodetect fan multiplier based on nominal rpm if multiplier
+ * was not specified as module param or in DMI. If fan reports
+ * rpm value too high then set multiplier to 1.
+ */
+ if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD)
+ data->i8k_fan_mult = 1;
}
}
@@ -1270,15 +1373,12 @@ static int __init dell_smm_probe(struct platform_device *pdev)
struct dell_smm_data *data;
const struct dmi_system_id *id, *fan_control;
int ret;
- u8 fan;
data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->i8k_mutex);
- data->i8k_fan_mult = I8K_FAN_MULT;
- data->i8k_fan_max = I8K_FAN_HIGH;
platform_set_drvdata(pdev, data);
if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
@@ -1313,7 +1413,9 @@ static int __init dell_smm_probe(struct platform_device *pdev)
fan_max = conf->fan_max;
}
- data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
+ /* All options must not be 0 */
+ data->i8k_fan_mult = fan_mult ? : I8K_FAN_MULT;
+ data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH;
data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max);
fan_control = dmi_first_match(i8k_whitelist_fan_control);
@@ -1325,25 +1427,6 @@ static int __init dell_smm_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n");
}
- if (!fan_mult) {
- /*
- * Autodetect fan multiplier based on nominal rpm
- * If fan reports rpm value too high then set multiplier to 1
- */
- for (fan = 0; fan < DELL_SMM_NO_FANS; ++fan) {
- ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max);
- if (ret < 0)
- continue;
-
- if (ret > I8K_FAN_MAX_RPM)
- data->i8k_fan_mult = 1;
- break;
- }
- } else {
- /* Fan multiplier was specified in module param or in dmi */
- data->i8k_fan_mult = fan_mult;
- }
-
ret = dell_smm_init_hwmon(&pdev->dev);
if (ret)
return ret;
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 989e2c8496dd2d..2e2cd79d89ebc9 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -764,7 +764,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
"hwmon: '%s' is not a valid name attribute, please fix\n",
name);
- id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
+ id = ida_alloc(&hwmon_ida, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
@@ -856,7 +856,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
free_hwmon:
hwmon_dev_release(hdev);
ida_remove:
- ida_simple_remove(&hwmon_ida, id);
+ ida_free(&hwmon_ida, id);
return ERR_PTR(err);
}
@@ -886,11 +886,12 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
/**
* hwmon_device_register_with_info - register w/ hwmon
- * @dev: the parent device
- * @name: hwmon name attribute
- * @drvdata: driver data to attach to created device
- * @chip: pointer to hwmon chip information
+ * @dev: the parent device (mandatory)
+ * @name: hwmon name attribute (mandatory)
+ * @drvdata: driver data to attach to created device (optional)
+ * @chip: pointer to hwmon chip information (mandatory)
* @extra_groups: pointer to list of additional non-standard attribute groups
+ * (optional)
*
* hwmon_device_unregister() must be called when the device is no
* longer needed.
@@ -903,13 +904,10 @@ hwmon_device_register_with_info(struct device *dev, const char *name,
const struct hwmon_chip_info *chip,
const struct attribute_group **extra_groups)
{
- if (!name)
- return ERR_PTR(-EINVAL);
-
- if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info))
+ if (!dev || !name || !chip)
return ERR_PTR(-EINVAL);
- if (chip && !dev)
+ if (!chip->ops || !chip->ops->is_visible || !chip->info)
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, chip, extra_groups);
@@ -917,6 +915,31 @@ hwmon_device_register_with_info(struct device *dev, const char *name,
EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
/**
+ * hwmon_device_register_for_thermal - register hwmon device for thermal subsystem
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ *
+ * The use of this function is restricted. It is provided for legacy reasons
+ * and must only be called from the thermal subsystem.
+ *
+ * hwmon_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_for_thermal(struct device *dev, const char *name,
+ void *drvdata)
+{
+ if (!name || !dev)
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_device_register(dev, name, drvdata, NULL, NULL);
+}
+EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, HWMON_THERMAL);
+
+/**
* hwmon_device_register - register w/ hwmon
* @dev: the device to register
*
@@ -945,7 +968,7 @@ void hwmon_device_unregister(struct device *dev)
if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
device_unregister(dev);
- ida_simple_remove(&hwmon_ida, id);
+ ida_free(&hwmon_ida, id);
} else
dev_dbg(dev->parent,
"hwmon_device_unregister() failed: bad class ID!\n");
@@ -1057,6 +1080,59 @@ void devm_hwmon_device_unregister(struct device *dev)
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister);
+static char *__hwmon_sanitize_name(struct device *dev, const char *old_name)
+{
+ char *name, *p;
+
+ if (dev)
+ name = devm_kstrdup(dev, old_name, GFP_KERNEL);
+ else
+ name = kstrdup(old_name, GFP_KERNEL);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ for (p = name; *p; p++)
+ if (hwmon_is_bad_char(*p))
+ *p = '_';
+
+ return name;
+}
+
+/**
+ * hwmon_sanitize_name - Replaces invalid characters in a hwmon name
+ * @name: NUL-terminated name
+ *
+ * Allocates a new string where any invalid characters will be replaced
+ * by an underscore. It is the responsibility of the caller to release
+ * the memory.
+ *
+ * Returns newly allocated name, or ERR_PTR on error.
+ */
+char *hwmon_sanitize_name(const char *name)
+{
+ return __hwmon_sanitize_name(NULL, name);
+}
+EXPORT_SYMBOL_GPL(hwmon_sanitize_name);
+
+/**
+ * devm_hwmon_sanitize_name - resource managed hwmon_sanitize_name()
+ * @dev: device to allocate memory for
+ * @name: NUL-terminated name
+ *
+ * Allocates a new string where any invalid characters will be replaced
+ * by an underscore.
+ *
+ * Returns newly allocated name, or ERR_PTR on error.
+ */
+char *devm_hwmon_sanitize_name(struct device *dev, const char *name)
+{
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_sanitize_name(dev, name);
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_sanitize_name);
+
static void __init hwmon_pci_quirks(void)
{
#if defined CONFIG_X86 && defined CONFIG_PCI
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index de6baf6ca3d1e0..5c4cf742f5ae7b 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -482,7 +482,7 @@ static void aem_delete(struct aem_data *data)
ipmi_destroy_user(data->ipmi.user);
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
- ida_simple_remove(&aem_ida, data->id);
+ ida_free(&aem_ida, data->id);
kfree(data);
}
@@ -539,7 +539,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ data->id = ida_alloc(&aem_ida, GFP_KERNEL);
if (data->id < 0)
goto id_err;
@@ -600,7 +600,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- ida_simple_remove(&aem_ida, data->id);
+ ida_free(&aem_ida, data->id);
id_err:
kfree(data);
@@ -679,7 +679,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ data->id = ida_alloc(&aem_ida, GFP_KERNEL);
if (data->id < 0)
goto id_err;
@@ -740,7 +740,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- ida_simple_remove(&aem_ida, data->id);
+ ida_free(&aem_ida, data->id);
id_err:
kfree(data);
diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c
index 7a08e4c44a4b4b..6e82f7200d1cc5 100644
--- a/drivers/hwmon/intel-m10-bmc-hwmon.c
+++ b/drivers/hwmon/intel-m10-bmc-hwmon.c
@@ -515,7 +515,6 @@ static int m10bmc_hwmon_probe(struct platform_device *pdev)
struct intel_m10bmc *m10bmc = dev_get_drvdata(pdev->dev.parent);
struct device *hwmon_dev, *dev = &pdev->dev;
struct m10bmc_hwmon *hw;
- int i;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
@@ -528,13 +527,9 @@ static int m10bmc_hwmon_probe(struct platform_device *pdev)
hw->chip.info = hw->bdata->hinfo;
hw->chip.ops = &m10bmc_hwmon_ops;
- hw->hw_name = devm_kstrdup(dev, id->name, GFP_KERNEL);
- if (!hw->hw_name)
- return -ENOMEM;
-
- for (i = 0; hw->hw_name[i]; i++)
- if (hwmon_is_bad_char(hw->hw_name[i]))
- hw->hw_name[i] = '_';
+ hw->hw_name = devm_hwmon_sanitize_name(dev, id->name);
+ if (IS_ERR(hw->hw_name))
+ return PTR_ERR(hw->hw_name);
hwmon_dev = devm_hwmon_device_register_with_info(dev, hw->hw_name,
hw, &hw->chip, NULL);
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index cb347a6bd8d93c..07f7f8b5b73d74 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -63,6 +63,7 @@ static const unsigned short normal_i2c[] = {
#define STM_MANID 0x104a /* ST Microelectronics */
#define GT_MANID 0x1c68 /* Giantec */
#define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */
+#define SI_MANID 0x1c85 /* Seiko Instruments */
/* SMBUS register */
#define SMBUS_STMOUT BIT(7) /* SMBus time-out, active low */
@@ -156,6 +157,10 @@ static const unsigned short normal_i2c[] = {
#define STTS3000_DEVID 0x0200
#define STTS3000_DEVID_MASK 0xffff
+/* Seiko Instruments */
+#define S34TS04A_DEVID 0x2221
+#define S34TS04A_DEVID_MASK 0xffff
+
static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 };
struct jc42_chips {
@@ -186,6 +191,7 @@ static struct jc42_chips jc42_chips[] = {
{ ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK },
{ ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK },
{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
+ { SI_MANID, S34TS04A_DEVID, S34TS04A_DEVID_MASK },
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
{ STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK },
@@ -443,6 +449,8 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
}
static const struct hwmon_channel_info *jc42_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_CRIT | HWMON_T_MAX_HYST |
diff --git a/drivers/hwmon/lan966x-hwmon.c b/drivers/hwmon/lan966x-hwmon.c
new file mode 100644
index 00000000000000..f41df053ac319b
--- /dev/null
+++ b/drivers/hwmon/lan966x-hwmon.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/polynomial.h>
+#include <linux/regmap.h>
+
+/*
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * are as follows:
+ *
+ * T = -3.4627e-11*(N^4) + 1.1023e-7*(N^3) + -1.9165e-4*(N^2) +
+ * 3.0604e-1*(N^1) + -5.6197e1
+ *
+ * where [-56.197, 136.402]C and N = [0, 1023].
+ *
+ * They must be accordingly altered to be suitable for the integer arithmetics.
+ * The technique is called 'factor redistribution', which just makes sure the
+ * multiplications and divisions are made so to have a result of the operations
+ * within the integer numbers limit. In addition we need to translate the
+ * formulae to accept millidegrees of Celsius. Here what it looks like after
+ * the alterations:
+ *
+ * T = -34627e-12*(N^4) + 110230e-9*(N^3) + -191650e-6*(N^2) +
+ * 306040e-3*(N^1) + -56197
+ *
+ * where T = [-56197, 136402]mC and N = [0, 1023].
+ */
+
+static const struct polynomial poly_N_to_temp = {
+ .terms = {
+ {4, -34627, 1000, 1},
+ {3, 110230, 1000, 1},
+ {2, -191650, 1000, 1},
+ {1, 306040, 1000, 1},
+ {0, -56197, 1, 1}
+ }
+};
+
+#define PVT_SENSOR_CTRL 0x0 /* unused */
+#define PVT_SENSOR_CFG 0x4
+#define SENSOR_CFG_CLK_CFG GENMASK(27, 20)
+#define SENSOR_CFG_TRIM_VAL GENMASK(13, 9)
+#define SENSOR_CFG_SAMPLE_ENA BIT(8)
+#define SENSOR_CFG_START_CAPTURE BIT(7)
+#define SENSOR_CFG_CONTINIOUS_MODE BIT(6)
+#define SENSOR_CFG_PSAMPLE_ENA GENMASK(1, 0)
+#define PVT_SENSOR_STAT 0x8
+#define SENSOR_STAT_DATA_VALID BIT(10)
+#define SENSOR_STAT_DATA GENMASK(9, 0)
+
+#define FAN_CFG 0x0
+#define FAN_CFG_DUTY_CYCLE GENMASK(23, 16)
+#define INV_POL BIT(3)
+#define GATE_ENA BIT(2)
+#define PWM_OPEN_COL_ENA BIT(1)
+#define FAN_STAT_CFG BIT(0)
+#define FAN_PWM_FREQ 0x4
+#define FAN_PWM_CYC_10US GENMASK(25, 15)
+#define FAN_PWM_FREQ_FREQ GENMASK(14, 0)
+#define FAN_CNT 0xc
+#define FAN_CNT_DATA GENMASK(15, 0)
+
+#define LAN966X_PVT_CLK 1200000 /* 1.2 MHz */
+
+struct lan966x_hwmon {
+ struct regmap *regmap_pvt;
+ struct regmap *regmap_fan;
+ struct clk *clk;
+ unsigned long clk_rate;
+};
+
+static int lan966x_hwmon_read_temp(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_pvt, PVT_SENSOR_STAT, &data);
+ if (ret < 0)
+ return ret;
+
+ if (!(data & SENSOR_STAT_DATA_VALID))
+ return -ENODATA;
+
+ *val = polynomial_calc(&poly_N_to_temp,
+ FIELD_GET(SENSOR_STAT_DATA, data));
+
+ return 0;
+}
+
+static int lan966x_hwmon_read_fan(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_fan, FAN_CNT, &data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Data is given in pulses per second. Assume two pulses
+ * per revolution.
+ */
+ *val = FIELD_GET(FAN_CNT_DATA, data) * 60 / 2;
+
+ return 0;
+}
+
+static int lan966x_hwmon_read_pwm(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_fan, FAN_CFG, &data);
+ if (ret < 0)
+ return ret;
+
+ *val = FIELD_GET(FAN_CFG_DUTY_CYCLE, data);
+
+ return 0;
+}
+
+static int lan966x_hwmon_read_pwm_freq(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned long tmp;
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_fan, FAN_PWM_FREQ, &data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Datasheet says it is sys_clk / 256 / pwm_freq. But in reality
+ * it is sys_clk / 256 / (pwm_freq + 1).
+ */
+ data = FIELD_GET(FAN_PWM_FREQ_FREQ, data) + 1;
+ tmp = DIV_ROUND_CLOSEST(hwmon->clk_rate, 256);
+ *val = DIV_ROUND_CLOSEST(tmp, data);
+
+ return 0;
+}
+
+static int lan966x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_temp:
+ return lan966x_hwmon_read_temp(dev, val);
+ case hwmon_fan:
+ return lan966x_hwmon_read_fan(dev, val);
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return lan966x_hwmon_read_pwm(dev, val);
+ case hwmon_pwm_freq:
+ return lan966x_hwmon_read_pwm_freq(dev, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lan966x_hwmon_write_pwm(struct device *dev, long val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ return regmap_update_bits(hwmon->regmap_fan, FAN_CFG,
+ FAN_CFG_DUTY_CYCLE,
+ FIELD_PREP(FAN_CFG_DUTY_CYCLE, val));
+}
+
+static int lan966x_hwmon_write_pwm_freq(struct device *dev, long val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+
+ if (val <= 0)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST(hwmon->clk_rate, val);
+ val = DIV_ROUND_CLOSEST(val, 256) - 1;
+ val = clamp_val(val, 0, FAN_PWM_FREQ_FREQ);
+
+ return regmap_update_bits(hwmon->regmap_fan, FAN_PWM_FREQ,
+ FAN_PWM_FREQ_FREQ,
+ FIELD_PREP(FAN_PWM_FREQ_FREQ, val));
+}
+
+static int lan966x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return lan966x_hwmon_write_pwm(dev, val);
+ case hwmon_pwm_freq:
+ return lan966x_hwmon_write_pwm_freq(dev, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lan966x_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ umode_t mode = 0;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ mode = 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ mode = 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_freq:
+ mode = 0644;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return mode;
+}
+
+static const struct hwmon_channel_info *lan966x_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_FREQ),
+ NULL
+};
+
+static const struct hwmon_ops lan966x_hwmon_ops = {
+ .is_visible = lan966x_hwmon_is_visible,
+ .read = lan966x_hwmon_read,
+ .write = lan966x_hwmon_write,
+};
+
+static const struct hwmon_chip_info lan966x_hwmon_chip_info = {
+ .ops = &lan966x_hwmon_ops,
+ .info = lan966x_hwmon_info,
+};
+
+static void lan966x_hwmon_disable(void *data)
+{
+ struct lan966x_hwmon *hwmon = data;
+
+ regmap_update_bits(hwmon->regmap_pvt, PVT_SENSOR_CFG,
+ SENSOR_CFG_SAMPLE_ENA | SENSOR_CFG_CONTINIOUS_MODE,
+ 0);
+}
+
+static int lan966x_hwmon_enable(struct device *dev,
+ struct lan966x_hwmon *hwmon)
+{
+ unsigned int mask = SENSOR_CFG_CLK_CFG |
+ SENSOR_CFG_SAMPLE_ENA |
+ SENSOR_CFG_START_CAPTURE |
+ SENSOR_CFG_CONTINIOUS_MODE |
+ SENSOR_CFG_PSAMPLE_ENA;
+ unsigned int val;
+ unsigned int div;
+ int ret;
+
+ /* enable continuous mode */
+ val = SENSOR_CFG_SAMPLE_ENA | SENSOR_CFG_CONTINIOUS_MODE;
+
+ /* set PVT clock to be between 1.15 and 1.25 MHz */
+ div = DIV_ROUND_CLOSEST(hwmon->clk_rate, LAN966X_PVT_CLK);
+ val |= FIELD_PREP(SENSOR_CFG_CLK_CFG, div);
+
+ ret = regmap_update_bits(hwmon->regmap_pvt, PVT_SENSOR_CFG,
+ mask, val);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, lan966x_hwmon_disable, hwmon);
+}
+
+static struct regmap *lan966x_init_regmap(struct platform_device *pdev,
+ const char *name)
+{
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ };
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource_byname(pdev, name);
+ if (IS_ERR(base))
+ return ERR_CAST(base);
+
+ regmap_config.name = name;
+
+ return devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
+}
+
+static void lan966x_clk_disable(void *data)
+{
+ struct lan966x_hwmon *hwmon = data;
+
+ clk_disable_unprepare(hwmon->clk);
+}
+
+static int lan966x_clk_enable(struct device *dev, struct lan966x_hwmon *hwmon)
+{
+ int ret;
+
+ ret = clk_prepare_enable(hwmon->clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, lan966x_clk_disable, hwmon);
+}
+
+static int lan966x_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lan966x_hwmon *hwmon;
+ struct device *hwmon_dev;
+ int ret;
+
+ hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ hwmon->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(hwmon->clk))
+ return dev_err_probe(dev, PTR_ERR(hwmon->clk),
+ "failed to get clock\n");
+
+ ret = lan966x_clk_enable(dev, hwmon);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable clock\n");
+
+ hwmon->clk_rate = clk_get_rate(hwmon->clk);
+
+ hwmon->regmap_pvt = lan966x_init_regmap(pdev, "pvt");
+ if (IS_ERR(hwmon->regmap_pvt))
+ return dev_err_probe(dev, PTR_ERR(hwmon->regmap_pvt),
+ "failed to get regmap for PVT registers\n");
+
+ hwmon->regmap_fan = lan966x_init_regmap(pdev, "fan");
+ if (IS_ERR(hwmon->regmap_fan))
+ return dev_err_probe(dev, PTR_ERR(hwmon->regmap_fan),
+ "failed to get regmap for fan registers\n");
+
+ ret = lan966x_hwmon_enable(dev, hwmon);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable sensor\n");
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+ "lan966x_hwmon", hwmon,
+ &lan966x_hwmon_chip_info, NULL);
+ if (IS_ERR(hwmon_dev))
+ return dev_err_probe(dev, PTR_ERR(hwmon_dev),
+ "failed to register hwmon device\n");
+
+ return 0;
+}
+
+static const struct of_device_id lan966x_hwmon_of_match[] = {
+ { .compatible = "microchip,lan9668-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lan966x_hwmon_of_match);
+
+static struct platform_driver lan966x_hwmon_driver = {
+ .probe = lan966x_hwmon_probe,
+ .driver = {
+ .name = "lan966x-hwmon",
+ .of_match_table = lan966x_hwmon_of_match,
+ },
+};
+module_platform_driver(lan966x_hwmon_driver);
+
+MODULE_DESCRIPTION("LAN966x Hardware Monitoring Driver");
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index afdbb63237b9e4..66dc826f7962c6 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -26,6 +26,7 @@
enum lm75_type { /* keep sorted in alphabetical order */
adt75,
+ at30ts74,
ds1775,
ds75,
ds7505,
@@ -128,6 +129,14 @@ static const struct lm75_params device_params[] = {
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 10,
},
+ [at30ts74] = {
+ .set_mask = 3 << 5, /* 12-bit mode*/
+ .default_resolution = 12,
+ .default_sample_time = 200,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 25, 50, 100, 200 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
[ds1775] = {
.clr_mask = 3 << 5,
.set_mask = 2 << 5, /* 11-bit mode */
@@ -645,6 +654,7 @@ static int lm75_probe(struct i2c_client *client)
static const struct i2c_device_id lm75_ids[] = {
{ "adt75", adt75, },
+ { "at30ts74", at30ts74, },
{ "ds1775", ds1775, },
{ "ds75", ds75, },
{ "ds7505", ds7505, },
@@ -681,6 +691,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
.data = (void *)adt75
},
{
+ .compatible = "atmel,at30ts74",
+ .data = (void *)at30ts74
+ },
+ {
.compatible = "dallas,ds1775",
.data = (void *)ds1775
},
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index 12370dcefa6aba..905f5689f9074d 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -24,10 +24,8 @@
#include <linux/init.h>
#include <linux/hwmon.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
/*
* Addresses to scan
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 1c9493c7081324..3820f0e6151081 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1707,6 +1707,7 @@ static void lm90_restore_conf(void *_data)
static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
{
+ struct device_node *np = client->dev.of_node;
int config, convrate;
convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE);
@@ -1727,6 +1728,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
/* Check Temperature Range Select */
if (data->flags & LM90_HAVE_EXTENDED_TEMP) {
+ if (of_property_read_bool(np, "ti,extended-range-enable"))
+ config |= 0x04;
+
if (config & 0x04)
data->flags |= LM90_FLAG_ADT7461_EXT;
}
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index 7352d2b3c756d7..72489d5d7eaf97 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -811,68 +811,32 @@ static const struct hwmon_ops ltc2992_hwmon_ops = {
.write = ltc2992_write,
};
-static const u32 ltc2992_chip_config[] = {
- HWMON_C_IN_RESET_HISTORY,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_chip = {
- .type = hwmon_chip,
- .config = ltc2992_chip_config,
-};
-
-static const u32 ltc2992_in_config[] = {
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_in = {
- .type = hwmon_in,
- .config = ltc2992_in_config,
-};
-
-static const u32 ltc2992_curr_config[] = {
- HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | HWMON_C_MAX |
- HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
- HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | HWMON_C_MAX |
- HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_curr = {
- .type = hwmon_curr,
- .config = ltc2992_curr_config,
-};
-
-static const u32 ltc2992_power_config[] = {
- HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | HWMON_P_MIN | HWMON_P_MAX |
- HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
- HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | HWMON_P_MIN | HWMON_P_MAX |
- HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_power = {
- .type = hwmon_power,
- .config = ltc2992_power_config,
-};
-
static const struct hwmon_channel_info *ltc2992_info[] = {
- &ltc2992_chip,
- &ltc2992_in,
- &ltc2992_curr,
- &ltc2992_power,
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_IN_RESET_HISTORY),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM),
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN |
+ HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN |
+ HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST |
+ HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
+ HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST |
+ HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM),
NULL
};
diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c
index 1ba1e31459690a..26278b0f17a989 100644
--- a/drivers/hwmon/mr75203.c
+++ b/drivers/hwmon/mr75203.c
@@ -223,16 +223,6 @@ static int pvt_read(struct device *dev, enum hwmon_sensor_types type,
}
}
-static const u32 pvt_chip_config[] = {
- HWMON_C_REGISTER_TZ,
- 0
-};
-
-static const struct hwmon_channel_info pvt_chip = {
- .type = hwmon_chip,
- .config = pvt_chip_config,
-};
-
static struct hwmon_channel_info pvt_temp = {
.type = hwmon_temp,
};
@@ -555,7 +545,7 @@ static int mr75203_probe(struct platform_device *pdev)
pvt_info = devm_kcalloc(dev, val + 2, sizeof(*pvt_info), GFP_KERNEL);
if (!pvt_info)
return -ENOMEM;
- pvt_info[0] = &pvt_chip;
+ pvt_info[0] = HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ);
index = 1;
if (ts_num) {
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775-core.c
index 2b91f7e05126ed..446964cbae4c06 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775-core.c
@@ -44,24 +44,20 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
-#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
-#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
-#include <linux/acpi.h>
#include <linux/bitops.h>
-#include <linux/dmi.h>
-#include <linux/io.h>
#include <linux/nospec.h>
-#include <linux/wmi.h>
+#include <linux/regmap.h>
#include "lm75.h"
+#include "nct6775.h"
-#define USE_ALTERNATE
+#undef DEFAULT_SYMBOL_NAMESPACE
+#define DEFAULT_SYMBOL_NAMESPACE HWMON_NCT6775
-enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
- nct6793, nct6795, nct6796, nct6797, nct6798 };
+#define USE_ALTERNATE
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
@@ -79,242 +75,6 @@ static const char * const nct6775_device_names[] = {
"nct6798",
};
-static const char * const nct6775_sio_names[] __initconst = {
- "NCT6106D",
- "NCT6116D",
- "NCT6775F",
- "NCT6776D/F",
- "NCT6779D",
- "NCT6791D",
- "NCT6792D",
- "NCT6793D",
- "NCT6795D",
- "NCT6796D",
- "NCT6797D",
- "NCT6798D",
-};
-
-static unsigned short force_id;
-module_param(force_id, ushort, 0);
-MODULE_PARM_DESC(force_id, "Override the detected device ID");
-
-static unsigned short fan_debounce;
-module_param(fan_debounce, ushort, 0);
-MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
-
-#define DRVNAME "nct6775"
-
-/*
- * Super-I/O constants and functions
- */
-
-#define NCT6775_LD_ACPI 0x0a
-#define NCT6775_LD_HWM 0x0b
-#define NCT6775_LD_VID 0x0d
-#define NCT6775_LD_12 0x12
-
-#define SIO_REG_LDSEL 0x07 /* Logical device select */
-#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
-#define SIO_REG_ENABLE 0x30 /* Logical device enable */
-#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
-
-#define SIO_NCT6106_ID 0xc450
-#define SIO_NCT6116_ID 0xd280
-#define SIO_NCT6775_ID 0xb470
-#define SIO_NCT6776_ID 0xc330
-#define SIO_NCT6779_ID 0xc560
-#define SIO_NCT6791_ID 0xc800
-#define SIO_NCT6792_ID 0xc910
-#define SIO_NCT6793_ID 0xd120
-#define SIO_NCT6795_ID 0xd350
-#define SIO_NCT6796_ID 0xd420
-#define SIO_NCT6797_ID 0xd450
-#define SIO_NCT6798_ID 0xd428
-#define SIO_ID_MASK 0xFFF8
-
-enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
-enum sensor_access { access_direct, access_asuswmi };
-
-struct nct6775_sio_data {
- int sioreg;
- int ld;
- enum kinds kind;
- enum sensor_access access;
-
- /* superio_() callbacks */
- void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val);
- int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg);
- void (*sio_select)(struct nct6775_sio_data *sio_data, int ld);
- int (*sio_enter)(struct nct6775_sio_data *sio_data);
- void (*sio_exit)(struct nct6775_sio_data *sio_data);
-};
-
-#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
-#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
-
-static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
-{
-#if IS_ENABLED(CONFIG_ACPI_WMI)
- u32 args = bank | (reg << 8) | (val << 16);
- struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- acpi_status status;
- union acpi_object *obj;
- u32 tmp = ASUSWMI_UNSUPPORTED_METHOD;
-
- status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
- method_id, &input, &output);
-
- 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);
-
- if (tmp == ASUSWMI_UNSUPPORTED_METHOD)
- return -ENODEV;
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val)
-{
- return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank,
- reg, val, NULL);
-}
-
-static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val)
-{
- u32 ret, tmp = 0;
-
- ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank,
- reg, 0, &tmp);
- *val = tmp;
- return ret;
-}
-
-static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg)
-{
- int tmp = 0;
-
- nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, sio_data->ld,
- reg, 0, &tmp);
- return tmp;
-}
-
-static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val)
-{
- nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, sio_data->ld,
- reg, val, NULL);
-}
-
-static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld)
-{
- sio_data->ld = ld;
-}
-
-static int superio_wmi_enter(struct nct6775_sio_data *sio_data)
-{
- return 0;
-}
-
-static void superio_wmi_exit(struct nct6775_sio_data *sio_data)
-{
-}
-
-static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val)
-{
- int ioreg = sio_data->sioreg;
-
- outb(reg, ioreg);
- outb(val, ioreg + 1);
-}
-
-static int superio_inb(struct nct6775_sio_data *sio_data, int reg)
-{
- int ioreg = sio_data->sioreg;
-
- outb(reg, ioreg);
- return inb(ioreg + 1);
-}
-
-static void superio_select(struct nct6775_sio_data *sio_data, int ld)
-{
- int ioreg = sio_data->sioreg;
-
- outb(SIO_REG_LDSEL, ioreg);
- outb(ld, ioreg + 1);
-}
-
-static int superio_enter(struct nct6775_sio_data *sio_data)
-{
- int ioreg = sio_data->sioreg;
-
- /*
- * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
- */
- if (!request_muxed_region(ioreg, 2, DRVNAME))
- return -EBUSY;
-
- outb(0x87, ioreg);
- outb(0x87, ioreg);
-
- return 0;
-}
-
-static void superio_exit(struct nct6775_sio_data *sio_data)
-{
- int ioreg = sio_data->sioreg;
-
- outb(0xaa, ioreg);
- outb(0x02, ioreg);
- outb(0x02, ioreg + 1);
- release_region(ioreg, 2);
-}
-
-/*
- * ISA constants
- */
-
-#define IOREGION_ALIGNMENT (~7)
-#define IOREGION_OFFSET 5
-#define IOREGION_LENGTH 2
-#define ADDR_REG_OFFSET 0
-#define DATA_REG_OFFSET 1
-
-#define NCT6775_REG_BANK 0x4E
-#define NCT6775_REG_CONFIG 0x40
-#define NCT6775_PORT_CHIPID 0x58
-
-/*
- * Not currently used:
- * REG_MAN_ID has the value 0x5ca3 for all supported chips.
- * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
- * REG_MAN_ID is at port 0x4f
- * REG_CHIP_ID is at port 0x58
- */
-
-#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/
-#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */
-#define NUM_TSI_TEMP 8 /* Max number of TSI temp register pairs */
-
-#define NUM_REG_ALARM 7 /* Max number of alarm registers */
-#define NUM_REG_BEEP 5 /* Max number of beep registers */
-
-#define NUM_FAN 7
-
/* Common and NCT6775 specific data */
/* Voltage min/max registers for nr=7..14 are in bank 5 */
@@ -333,11 +93,6 @@ static const u16 NCT6775_REG_IN[] = {
#define NCT6775_REG_DIODE 0x5E
#define NCT6775_DIODE_MASK 0x02
-#define NCT6775_REG_FANDIV1 0x506
-#define NCT6775_REG_FANDIV2 0x507
-
-#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0
-
static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */
@@ -351,10 +106,6 @@ static const s8 NCT6775_ALARM_BITS[] = {
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, -1 }; /* intrusion0, intrusion1 */
-#define FAN_ALARM_BASE 16
-#define TEMP_ALARM_BASE 24
-#define INTRUSION_ALARM_BASE 30
-
static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e };
/*
@@ -370,11 +121,6 @@ static const s8 NCT6775_BEEP_BITS[] = {
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, -1 }; /* intrusion0, intrusion1 */
-#define BEEP_ENABLE_BASE 15
-
-static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
-static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
-
/* DC or PWM output fan configuration */
static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 };
static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 };
@@ -690,8 +436,6 @@ static const u16 NCT6779_REG_TEMP_CRIT[32] = {
/* NCT6791 specific data */
-#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28
-
static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[NUM_FAN] = { 0, 0x239 };
static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[NUM_FAN] = { 0, 0x23a };
static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[NUM_FAN] = { 0, 0x23b };
@@ -1191,165 +935,6 @@ static inline unsigned int tsi_temp_from_reg(unsigned int reg)
* Data structures and manipulation thereof
*/
-struct nct6775_data {
- int addr; /* IO base of hw monitor block */
- struct nct6775_sio_data *sio_data;
- enum kinds kind;
- const char *name;
-
- const struct attribute_group *groups[7];
-
- u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
- * 3=temp_crit, 4=temp_lcrit
- */
- u8 temp_src[NUM_TEMP];
- u16 reg_temp_config[NUM_TEMP];
- const char * const *temp_label;
- u32 temp_mask;
- u32 virt_temp_mask;
-
- u16 REG_CONFIG;
- u16 REG_VBAT;
- u16 REG_DIODE;
- u8 DIODE_MASK;
-
- const s8 *ALARM_BITS;
- const s8 *BEEP_BITS;
-
- const u16 *REG_VIN;
- const u16 *REG_IN_MINMAX[2];
-
- const u16 *REG_TARGET;
- const u16 *REG_FAN;
- const u16 *REG_FAN_MODE;
- const u16 *REG_FAN_MIN;
- const u16 *REG_FAN_PULSES;
- const u16 *FAN_PULSE_SHIFT;
- const u16 *REG_FAN_TIME[3];
-
- const u16 *REG_TOLERANCE_H;
-
- const u8 *REG_PWM_MODE;
- const u8 *PWM_MODE_MASK;
-
- const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
- * [3]=pwm_max, [4]=pwm_step,
- * [5]=weight_duty_step, [6]=weight_duty_base
- */
- const u16 *REG_PWM_READ;
-
- const u16 *REG_CRITICAL_PWM_ENABLE;
- u8 CRITICAL_PWM_ENABLE_MASK;
- const u16 *REG_CRITICAL_PWM;
-
- const u16 *REG_AUTO_TEMP;
- const u16 *REG_AUTO_PWM;
-
- const u16 *REG_CRITICAL_TEMP;
- const u16 *REG_CRITICAL_TEMP_TOLERANCE;
-
- const u16 *REG_TEMP_SOURCE; /* temp register sources */
- const u16 *REG_TEMP_SEL;
- const u16 *REG_WEIGHT_TEMP_SEL;
- const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */
-
- const u16 *REG_TEMP_OFFSET;
-
- const u16 *REG_ALARM;
- const u16 *REG_BEEP;
-
- const u16 *REG_TSI_TEMP;
-
- unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
- unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
-
- struct mutex update_lock;
- bool valid; /* true if following fields are valid */
- unsigned long last_updated; /* In jiffies */
-
- /* Register values */
- u8 bank; /* current register bank */
- u8 in_num; /* number of in inputs we have */
- u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */
- unsigned int rpm[NUM_FAN];
- u16 fan_min[NUM_FAN];
- u8 fan_pulses[NUM_FAN];
- u8 fan_div[NUM_FAN];
- u8 has_pwm;
- u8 has_fan; /* some fan inputs can be disabled */
- u8 has_fan_min; /* some fans don't have min register */
- bool has_fan_div;
-
- u8 num_temp_alarms; /* 2, 3, or 6 */
- u8 num_temp_beeps; /* 2, 3, or 6 */
- u8 temp_fixed_num; /* 3 or 6 */
- u8 temp_type[NUM_TEMP_FIXED];
- s8 temp_offset[NUM_TEMP_FIXED];
- s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
- * 3=temp_crit, 4=temp_lcrit */
- s16 tsi_temp[NUM_TSI_TEMP];
- u64 alarms;
- u64 beeps;
-
- u8 pwm_num; /* number of pwm */
- u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage,
- * 1->PWM variable duty cycle
- */
- enum pwm_enable pwm_enable[NUM_FAN];
- /* 0->off
- * 1->manual
- * 2->thermal cruise mode (also called SmartFan I)
- * 3->fan speed cruise mode
- * 4->SmartFan III
- * 5->enhanced variable thermal cruise (SmartFan IV)
- */
- u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
- * [3]=pwm_max, [4]=pwm_step,
- * [5]=weight_duty_step, [6]=weight_duty_base
- */
-
- u8 target_temp[NUM_FAN];
- u8 target_temp_mask;
- u32 target_speed[NUM_FAN];
- u32 target_speed_tolerance[NUM_FAN];
- u8 speed_tolerance_limit;
-
- u8 temp_tolerance[2][NUM_FAN];
- u8 tolerance_mask;
-
- u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
-
- /* Automatic fan speed control registers */
- int auto_pwm_num;
- u8 auto_pwm[NUM_FAN][7];
- u8 auto_temp[NUM_FAN][7];
- u8 pwm_temp_sel[NUM_FAN];
- u8 pwm_weight_temp_sel[NUM_FAN];
- u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol,
- * 2->temp_base
- */
-
- u8 vid;
- u8 vrm;
-
- bool have_vid;
-
- u16 have_temp;
- u16 have_temp_fixed;
- u16 have_tsi_temp;
- u16 have_in;
-
- /* Remember extra register values over suspend/resume */
- u8 vbat;
- u8 fandiv1;
- u8 fandiv2;
- u8 sio_reg_enable;
-
- /* nct6775_*() callbacks */
- u16 (*read_value)(struct nct6775_data *data, u16 reg);
- int (*write_value)(struct nct6775_data *data, u16 reg, u16 value);
-};
-
struct sensor_device_template {
struct device_attribute dev_attr;
union {
@@ -1405,10 +990,8 @@ struct sensor_template_group {
int base;
};
-static struct attribute_group *
-nct6775_create_attr_group(struct device *dev,
- const struct sensor_template_group *tg,
- int repeat)
+static int nct6775_add_template_attr_group(struct device *dev, struct nct6775_data *data,
+ const struct sensor_template_group *tg, int repeat)
{
struct attribute_group *group;
struct sensor_device_attr_u *su;
@@ -1419,28 +1002,28 @@ nct6775_create_attr_group(struct device *dev,
int i, count;
if (repeat <= 0)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
t = tg->templates;
for (count = 0; *t; t++, count++)
;
if (count == 0)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
if (group == NULL)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs),
GFP_KERNEL);
if (attrs == NULL)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)),
GFP_KERNEL);
if (su == NULL)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
group->attrs = attrs;
group->is_visible = tg->is_visible;
@@ -1478,10 +1061,10 @@ nct6775_create_attr_group(struct device *dev,
}
}
- return group;
+ return nct6775_add_attr_group(data, group);
}
-static bool is_word_sized(struct nct6775_data *data, u16 reg)
+bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg)
{
switch (data->kind) {
case nct6106:
@@ -1538,180 +1121,81 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
}
return false;
}
+EXPORT_SYMBOL_GPL(nct6775_reg_is_word_sized);
-static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg)
-{
- u8 bank = reg >> 8;
-
- data->bank = bank;
-}
-
-static u16 nct6775_wmi_read_value(struct nct6775_data *data, u16 reg)
+/* We left-align 8-bit temperature values to make the code simpler */
+static int nct6775_read_temp(struct nct6775_data *data, u16 reg, u16 *val)
{
- int res, err, word_sized = is_word_sized(data, reg);
- u8 tmp = 0;
-
- nct6775_wmi_set_bank(data, reg);
+ int err;
- err = nct6775_asuswmi_read(data->bank, reg & 0xff, &tmp);
+ err = nct6775_read_value(data, reg, val);
if (err)
- return 0;
-
- res = tmp;
- if (word_sized) {
- err = nct6775_asuswmi_read(data->bank, (reg & 0xff) + 1, &tmp);
- if (err)
- return 0;
-
- res = (res << 8) + tmp;
- }
- return res;
-}
-
-static int nct6775_wmi_write_value(struct nct6775_data *data, u16 reg, u16 value)
-{
- int res, word_sized = is_word_sized(data, reg);
-
- nct6775_wmi_set_bank(data, reg);
-
- if (word_sized) {
- res = nct6775_asuswmi_write(data->bank, reg & 0xff, value >> 8);
- if (res)
- return res;
-
- res = nct6775_asuswmi_write(data->bank, (reg & 0xff) + 1, value);
- } else {
- res = nct6775_asuswmi_write(data->bank, reg & 0xff, value);
- }
-
- return res;
-}
-
-/*
- * On older chips, only registers 0x50-0x5f are banked.
- * On more recent chips, all registers are banked.
- * Assume that is the case and set the bank number for each access.
- * Cache the bank number so it only needs to be set if it changes.
- */
-static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
-{
- u8 bank = reg >> 8;
-
- if (data->bank != bank) {
- outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
- outb_p(bank, data->addr + DATA_REG_OFFSET);
- data->bank = bank;
- }
-}
+ return err;
-static u16 nct6775_read_value(struct nct6775_data *data, u16 reg)
-{
- int res, word_sized = is_word_sized(data, reg);
-
- nct6775_set_bank(data, reg);
- outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
- res = inb_p(data->addr + DATA_REG_OFFSET);
- if (word_sized) {
- outb_p((reg & 0xff) + 1,
- data->addr + ADDR_REG_OFFSET);
- res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET);
- }
- return res;
-}
+ if (!nct6775_reg_is_word_sized(data, reg))
+ *val <<= 8;
-static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value)
-{
- int word_sized = is_word_sized(data, reg);
-
- nct6775_set_bank(data, reg);
- outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
- if (word_sized) {
- outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
- outb_p((reg & 0xff) + 1,
- data->addr + ADDR_REG_OFFSET);
- }
- outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
return 0;
}
-/* We left-align 8-bit temperature values to make the code simpler */
-static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg)
-{
- u16 res;
-
- res = data->read_value(data, reg);
- if (!is_word_sized(data, reg))
- res <<= 8;
-
- return res;
-}
-
-static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
-{
- if (!is_word_sized(data, reg))
- value >>= 8;
- return data->write_value(data, reg, value);
-}
-
/* This function assumes that the caller holds data->update_lock */
-static void nct6775_write_fan_div(struct nct6775_data *data, int nr)
+static int nct6775_write_fan_div(struct nct6775_data *data, int nr)
{
- u8 reg;
+ u16 reg;
+ int err;
+ u16 fandiv_reg = nr < 2 ? NCT6775_REG_FANDIV1 : NCT6775_REG_FANDIV2;
+ unsigned int oddshift = (nr & 1) * 4; /* masks shift by four if nr is odd */
- switch (nr) {
- case 0:
- reg = (data->read_value(data, NCT6775_REG_FANDIV1) & 0x70)
- | (data->fan_div[0] & 0x7);
- data->write_value(data, NCT6775_REG_FANDIV1, reg);
- break;
- case 1:
- reg = (data->read_value(data, NCT6775_REG_FANDIV1) & 0x7)
- | ((data->fan_div[1] << 4) & 0x70);
- data->write_value(data, NCT6775_REG_FANDIV1, reg);
- break;
- case 2:
- reg = (data->read_value(data, NCT6775_REG_FANDIV2) & 0x70)
- | (data->fan_div[2] & 0x7);
- data->write_value(data, NCT6775_REG_FANDIV2, reg);
- break;
- case 3:
- reg = (data->read_value(data, NCT6775_REG_FANDIV2) & 0x7)
- | ((data->fan_div[3] << 4) & 0x70);
- data->write_value(data, NCT6775_REG_FANDIV2, reg);
- break;
- }
+ err = nct6775_read_value(data, fandiv_reg, &reg);
+ if (err)
+ return err;
+ reg &= 0x70 >> oddshift;
+ reg |= data->fan_div[nr] & (0x7 << oddshift);
+ return nct6775_write_value(data, fandiv_reg, reg);
}
-static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
+static int nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
{
if (data->kind == nct6775)
- nct6775_write_fan_div(data, nr);
+ return nct6775_write_fan_div(data, nr);
+ return 0;
}
-static void nct6775_update_fan_div(struct nct6775_data *data)
+static int nct6775_update_fan_div(struct nct6775_data *data)
{
- u8 i;
+ int err;
+ u16 i;
- i = data->read_value(data, NCT6775_REG_FANDIV1);
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &i);
+ if (err)
+ return err;
data->fan_div[0] = i & 0x7;
data->fan_div[1] = (i & 0x70) >> 4;
- i = data->read_value(data, NCT6775_REG_FANDIV2);
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &i);
+ if (err)
+ return err;
data->fan_div[2] = i & 0x7;
if (data->has_fan & BIT(3))
data->fan_div[3] = (i & 0x70) >> 4;
+
+ return 0;
}
-static void nct6775_update_fan_div_common(struct nct6775_data *data)
+static int nct6775_update_fan_div_common(struct nct6775_data *data)
{
if (data->kind == nct6775)
- nct6775_update_fan_div(data);
+ return nct6775_update_fan_div(data);
+ return 0;
}
-static void nct6775_init_fan_div(struct nct6775_data *data)
+static int nct6775_init_fan_div(struct nct6775_data *data)
{
- int i;
+ int i, err;
+
+ err = nct6775_update_fan_div_common(data);
+ if (err)
+ return err;
- nct6775_update_fan_div_common(data);
/*
* For all fans, start with highest divider value if the divider
* register is not initialized. This ensures that we get a
@@ -1723,19 +1207,26 @@ static void nct6775_init_fan_div(struct nct6775_data *data)
continue;
if (data->fan_div[i] == 0) {
data->fan_div[i] = 7;
- nct6775_write_fan_div_common(data, i);
+ err = nct6775_write_fan_div_common(data, i);
+ if (err)
+ return err;
}
}
+
+ return 0;
}
-static void nct6775_init_fan_common(struct device *dev,
- struct nct6775_data *data)
+static int nct6775_init_fan_common(struct device *dev,
+ struct nct6775_data *data)
{
- int i;
- u8 reg;
+ int i, err;
+ u16 reg;
- if (data->has_fan_div)
- nct6775_init_fan_div(data);
+ if (data->has_fan_div) {
+ err = nct6775_init_fan_div(data);
+ if (err)
+ return err;
+ }
/*
* If fan_min is not set (0), set it to 0xff to disable it. This
@@ -1743,23 +1234,30 @@ static void nct6775_init_fan_common(struct device *dev,
*/
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
if (data->has_fan_min & BIT(i)) {
- reg = data->read_value(data, data->REG_FAN_MIN[i]);
- if (!reg)
- data->write_value(data, data->REG_FAN_MIN[i],
- data->has_fan_div ? 0xff
- : 0xff1f);
+ err = nct6775_read_value(data, data->REG_FAN_MIN[i], &reg);
+ if (err)
+ return err;
+ if (!reg) {
+ err = nct6775_write_value(data, data->REG_FAN_MIN[i],
+ data->has_fan_div ? 0xff : 0xff1f);
+ if (err)
+ return err;
+ }
}
}
+
+ return 0;
}
-static void nct6775_select_fan_div(struct device *dev,
- struct nct6775_data *data, int nr, u16 reg)
+static int nct6775_select_fan_div(struct device *dev,
+ struct nct6775_data *data, int nr, u16 reg)
{
+ int err;
u8 fan_div = data->fan_div[nr];
u16 fan_min;
if (!data->has_fan_div)
- return;
+ return 0;
/*
* If we failed to measure the fan speed, or the reported value is not
@@ -1791,36 +1289,46 @@ static void nct6775_select_fan_div(struct device *dev,
}
if (fan_min != data->fan_min[nr]) {
data->fan_min[nr] = fan_min;
- data->write_value(data, data->REG_FAN_MIN[nr],
- fan_min);
+ err = nct6775_write_value(data, data->REG_FAN_MIN[nr], fan_min);
+ if (err)
+ return err;
}
}
data->fan_div[nr] = fan_div;
- nct6775_write_fan_div_common(data, nr);
+ err = nct6775_write_fan_div_common(data, nr);
+ if (err)
+ return err;
}
+
+ return 0;
}
-static void nct6775_update_pwm(struct device *dev)
+static int nct6775_update_pwm(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- int i, j;
- int fanmodecfg, reg;
+ int i, j, err;
+ u16 fanmodecfg, reg;
bool duty_is_dc;
for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_pwm & BIT(i)))
continue;
- duty_is_dc = data->REG_PWM_MODE[i] &&
- (data->read_value(data, data->REG_PWM_MODE[i])
- & data->PWM_MODE_MASK[i]);
+ err = nct6775_read_value(data, data->REG_PWM_MODE[i], &reg);
+ if (err)
+ return err;
+ duty_is_dc = data->REG_PWM_MODE[i] && (reg & data->PWM_MODE_MASK[i]);
data->pwm_mode[i] = !duty_is_dc;
- fanmodecfg = data->read_value(data, data->REG_FAN_MODE[i]);
+ err = nct6775_read_value(data, data->REG_FAN_MODE[i], &fanmodecfg);
+ if (err)
+ return err;
for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
if (data->REG_PWM[j] && data->REG_PWM[j][i]) {
- data->pwm[j][i] = data->read_value(data,
- data->REG_PWM[j][i]);
+ err = nct6775_read_value(data, data->REG_PWM[j][i], &reg);
+ if (err)
+ return err;
+ data->pwm[j][i] = reg;
}
}
@@ -1835,17 +1343,22 @@ static void nct6775_update_pwm(struct device *dev)
u8 t = fanmodecfg & 0x0f;
if (data->REG_TOLERANCE_H) {
- t |= (data->read_value(data,
- data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
+ err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], &reg);
+ if (err)
+ return err;
+ t |= (reg & 0x70) >> 1;
}
data->target_speed_tolerance[i] = t;
}
- data->temp_tolerance[1][i] =
- data->read_value(data,
- data->REG_CRITICAL_TEMP_TOLERANCE[i]);
+ err = nct6775_read_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[i], &reg);
+ if (err)
+ return err;
+ data->temp_tolerance[1][i] = reg;
- reg = data->read_value(data, data->REG_TEMP_SEL[i]);
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &reg);
+ if (err)
+ return err;
data->pwm_temp_sel[i] = reg & 0x1f;
/* If fan can stop, report floor as 0 */
if (reg & 0x80)
@@ -1854,7 +1367,9 @@ static void nct6775_update_pwm(struct device *dev)
if (!data->REG_WEIGHT_TEMP_SEL[i])
continue;
- reg = data->read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i], &reg);
+ if (err)
+ return err;
data->pwm_weight_temp_sel[i] = reg & 0x1f;
/* If weight is disabled, report weight source as 0 */
if (!(reg & 0x80))
@@ -1862,29 +1377,37 @@ static void nct6775_update_pwm(struct device *dev)
/* Weight temp data */
for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
- data->weight_temp[j][i] = data->read_value(data,
- data->REG_WEIGHT_TEMP[j][i]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP[j][i], &reg);
+ if (err)
+ return err;
+ data->weight_temp[j][i] = reg;
}
}
+
+ return 0;
}
-static void nct6775_update_pwm_limits(struct device *dev)
+static int nct6775_update_pwm_limits(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- int i, j;
- u8 reg;
- u16 reg_t;
+ int i, j, err;
+ u16 reg, reg_t;
for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_pwm & BIT(i)))
continue;
for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
- data->fan_time[j][i] =
- data->read_value(data, data->REG_FAN_TIME[j][i]);
+ err = nct6775_read_value(data, data->REG_FAN_TIME[j][i], &reg);
+ if (err)
+ return err;
+ data->fan_time[j][i] = reg;
}
- reg_t = data->read_value(data, data->REG_TARGET[i]);
+ err = nct6775_read_value(data, data->REG_TARGET[i], &reg_t);
+ if (err)
+ return err;
+
/* Update only in matching mode or if never updated */
if (!data->target_temp[i] ||
data->pwm_enable[i] == thermal_cruise)
@@ -1892,29 +1415,37 @@ static void nct6775_update_pwm_limits(struct device *dev)
if (!data->target_speed[i] ||
data->pwm_enable[i] == speed_cruise) {
if (data->REG_TOLERANCE_H) {
- reg_t |= (data->read_value(data,
- data->REG_TOLERANCE_H[i]) & 0x0f) << 8;
+ err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], &reg);
+ if (err)
+ return err;
+ reg_t |= (reg & 0x0f) << 8;
}
data->target_speed[i] = reg_t;
}
for (j = 0; j < data->auto_pwm_num; j++) {
- data->auto_pwm[i][j] =
- data->read_value(data,
- NCT6775_AUTO_PWM(data, i, j));
- data->auto_temp[i][j] =
- data->read_value(data,
- NCT6775_AUTO_TEMP(data, i, j));
+ err = nct6775_read_value(data, NCT6775_AUTO_PWM(data, i, j), &reg);
+ if (err)
+ return err;
+ data->auto_pwm[i][j] = reg;
+
+ err = nct6775_read_value(data, NCT6775_AUTO_TEMP(data, i, j), &reg);
+ if (err)
+ return err;
+ data->auto_temp[i][j] = reg;
}
/* critical auto_pwm temperature data */
- data->auto_temp[i][data->auto_pwm_num] =
- data->read_value(data, data->REG_CRITICAL_TEMP[i]);
+ err = nct6775_read_value(data, data->REG_CRITICAL_TEMP[i], &reg);
+ if (err)
+ return err;
+ data->auto_temp[i][data->auto_pwm_num] = reg;
switch (data->kind) {
case nct6775:
- reg = data->read_value(data,
- NCT6775_REG_CRITICAL_ENAB[i]);
+ err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[i], &reg);
+ if (err)
+ return err;
data->auto_pwm[i][data->auto_pwm_num] =
(reg & 0x02) ? 0xff : 0x00;
break;
@@ -1931,120 +1462,158 @@ static void nct6775_update_pwm_limits(struct device *dev)
case nct6796:
case nct6797:
case nct6798:
- reg = data->read_value(data,
- data->REG_CRITICAL_PWM_ENABLE[i]);
- if (reg & data->CRITICAL_PWM_ENABLE_MASK)
- reg = data->read_value(data,
- data->REG_CRITICAL_PWM[i]);
- else
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i], &reg);
+ if (err)
+ return err;
+ if (reg & data->CRITICAL_PWM_ENABLE_MASK) {
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM[i], &reg);
+ if (err)
+ return err;
+ } else {
reg = 0xff;
+ }
data->auto_pwm[i][data->auto_pwm_num] = reg;
break;
}
}
+
+ return 0;
}
static struct nct6775_data *nct6775_update_device(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- int i, j;
+ int i, j, err = 0;
+ u16 reg;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
/* Fan clock dividers */
- nct6775_update_fan_div_common(data);
+ err = nct6775_update_fan_div_common(data);
+ if (err)
+ goto out;
/* Measured voltages and limits */
for (i = 0; i < data->in_num; i++) {
if (!(data->have_in & BIT(i)))
continue;
- data->in[i][0] = data->read_value(data,
- data->REG_VIN[i]);
- data->in[i][1] = data->read_value(data,
- data->REG_IN_MINMAX[0][i]);
- data->in[i][2] = data->read_value(data,
- data->REG_IN_MINMAX[1][i]);
+ err = nct6775_read_value(data, data->REG_VIN[i], &reg);
+ if (err)
+ goto out;
+ data->in[i][0] = reg;
+
+ err = nct6775_read_value(data, data->REG_IN_MINMAX[0][i], &reg);
+ if (err)
+ goto out;
+ data->in[i][1] = reg;
+
+ err = nct6775_read_value(data, data->REG_IN_MINMAX[1][i], &reg);
+ if (err)
+ goto out;
+ data->in[i][2] = reg;
}
/* Measured fan speeds and limits */
for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
- u16 reg;
-
if (!(data->has_fan & BIT(i)))
continue;
- reg = data->read_value(data, data->REG_FAN[i]);
+ err = nct6775_read_value(data, data->REG_FAN[i], &reg);
+ if (err)
+ goto out;
data->rpm[i] = data->fan_from_reg(reg,
data->fan_div[i]);
- if (data->has_fan_min & BIT(i))
- data->fan_min[i] = data->read_value(data,
- data->REG_FAN_MIN[i]);
+ if (data->has_fan_min & BIT(i)) {
+ err = nct6775_read_value(data, data->REG_FAN_MIN[i], &reg);
+ if (err)
+ goto out;
+ data->fan_min[i] = reg;
+ }
if (data->REG_FAN_PULSES[i]) {
- data->fan_pulses[i] =
- (data->read_value(data,
- data->REG_FAN_PULSES[i])
- >> data->FAN_PULSE_SHIFT[i]) & 0x03;
+ err = nct6775_read_value(data, data->REG_FAN_PULSES[i], &reg);
+ if (err)
+ goto out;
+ data->fan_pulses[i] = (reg >> data->FAN_PULSE_SHIFT[i]) & 0x03;
}
- nct6775_select_fan_div(dev, data, i, reg);
+ err = nct6775_select_fan_div(dev, data, i, reg);
+ if (err)
+ goto out;
}
- nct6775_update_pwm(dev);
- nct6775_update_pwm_limits(dev);
+ err = nct6775_update_pwm(dev);
+ if (err)
+ goto out;
+
+ err = nct6775_update_pwm_limits(dev);
+ if (err)
+ goto out;
/* Measured temperatures and limits */
for (i = 0; i < NUM_TEMP; i++) {
if (!(data->have_temp & BIT(i)))
continue;
for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
- if (data->reg_temp[j][i])
- data->temp[j][i] = nct6775_read_temp(data,
- data->reg_temp[j][i]);
+ if (data->reg_temp[j][i]) {
+ err = nct6775_read_temp(data, data->reg_temp[j][i], &reg);
+ if (err)
+ goto out;
+ data->temp[j][i] = reg;
+ }
}
if (i >= NUM_TEMP_FIXED ||
!(data->have_temp_fixed & BIT(i)))
continue;
- data->temp_offset[i] = data->read_value(data,
- data->REG_TEMP_OFFSET[i]);
+ err = nct6775_read_value(data, data->REG_TEMP_OFFSET[i], &reg);
+ if (err)
+ goto out;
+ data->temp_offset[i] = reg;
}
for (i = 0; i < NUM_TSI_TEMP; i++) {
if (!(data->have_tsi_temp & BIT(i)))
continue;
- data->tsi_temp[i] = data->read_value(data, data->REG_TSI_TEMP[i]);
+ err = nct6775_read_value(data, data->REG_TSI_TEMP[i], &reg);
+ if (err)
+ goto out;
+ data->tsi_temp[i] = reg;
}
data->alarms = 0;
for (i = 0; i < NUM_REG_ALARM; i++) {
- u8 alarm;
+ u16 alarm;
if (!data->REG_ALARM[i])
continue;
- alarm = data->read_value(data, data->REG_ALARM[i]);
+ err = nct6775_read_value(data, data->REG_ALARM[i], &alarm);
+ if (err)
+ goto out;
data->alarms |= ((u64)alarm) << (i << 3);
}
data->beeps = 0;
for (i = 0; i < NUM_REG_BEEP; i++) {
- u8 beep;
+ u16 beep;
if (!data->REG_BEEP[i])
continue;
- beep = data->read_value(data, data->REG_BEEP[i]);
+ err = nct6775_read_value(data, data->REG_BEEP[i], &beep);
+ if (err)
+ goto out;
data->beeps |= ((u64)beep) << (i << 3);
}
data->last_updated = jiffies;
data->valid = true;
}
-
+out:
mutex_unlock(&data->update_lock);
- return data;
+ return err ? ERR_PTR(err) : data;
}
/*
@@ -2058,6 +1627,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
int index = sattr->index;
int nr = sattr->nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
}
@@ -2077,34 +1649,39 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
return err;
mutex_lock(&data->update_lock);
data->in[nr][index] = in_to_reg(val, nr);
- data->write_value(data, data->REG_IN_MINMAX[index - 1][nr],
- data->in[nr][index]);
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], data->in[nr][index]);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
-static ssize_t
-show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+ssize_t
+nct6775_show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- int nr = data->ALARM_BITS[sattr->index];
+ int nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ nr = data->ALARM_BITS[sattr->index];
return sprintf(buf, "%u\n",
(unsigned int)((data->alarms >> nr) & 0x01));
}
+EXPORT_SYMBOL_GPL(nct6775_show_alarm);
static int find_temp_source(struct nct6775_data *data, int index, int count)
{
int source = data->temp_src[index];
- int nr;
+ int nr, err;
for (nr = 0; nr < count; nr++) {
- int src;
+ u16 src;
- src = data->read_value(data,
- data->REG_TEMP_SOURCE[nr]) & 0x1f;
- if (src == source)
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[nr], &src);
+ if (err)
+ return err;
+ if ((src & 0x1f) == source)
return nr;
}
return -ENODEV;
@@ -2118,6 +1695,9 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
unsigned int alarm = 0;
int nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/*
* For temperatures, there is no fixed mapping from registers to alarm
* bits. Alarm bits are determined by the temperature source mapping.
@@ -2131,20 +1711,25 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%u\n", alarm);
}
-static ssize_t
-show_beep(struct device *dev, struct device_attribute *attr, char *buf)
+ssize_t
+nct6775_show_beep(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct nct6775_data *data = nct6775_update_device(dev);
- int nr = data->BEEP_BITS[sattr->index];
+ int nr;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ nr = data->BEEP_BITS[sattr->index];
return sprintf(buf, "%u\n",
(unsigned int)((data->beeps >> nr) & 0x01));
}
+EXPORT_SYMBOL_GPL(nct6775_show_beep);
-static ssize_t
-store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
- size_t count)
+ssize_t
+nct6775_store_beep(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct nct6775_data *data = dev_get_drvdata(dev);
@@ -2164,11 +1749,12 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
data->beeps |= (1ULL << nr);
else
data->beeps &= ~(1ULL << nr);
- data->write_value(data, data->REG_BEEP[regindex],
- (data->beeps >> (regindex << 3)) & 0xff);
+ err = nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
+EXPORT_SYMBOL_GPL(nct6775_store_beep);
static ssize_t
show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
@@ -2178,6 +1764,9 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
unsigned int beep = 0;
int nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/*
* For temperatures, there is no fixed mapping from registers to beep
* enable bits. Beep enable bits are determined by the temperature
@@ -2220,11 +1809,11 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
data->beeps |= (1ULL << bit);
else
data->beeps &= ~(1ULL << bit);
- data->write_value(data, data->REG_BEEP[regindex],
- (data->beeps >> (regindex << 3)) & 0xff);
+ err = nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_in_is_visible(struct kobject *kobj,
@@ -2237,17 +1826,14 @@ static umode_t nct6775_in_is_visible(struct kobject *kobj,
if (!(data->have_in & BIT(in)))
return 0;
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0);
-SENSOR_TEMPLATE(in_alarm, "in%d_alarm", S_IRUGO, show_alarm, NULL, 0);
-SENSOR_TEMPLATE(in_beep, "in%d_beep", S_IWUSR | S_IRUGO, show_beep, store_beep,
- 0);
-SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 0, 1);
-SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 0, 2);
+SENSOR_TEMPLATE_2(in_input, "in%d_input", 0444, show_in_reg, NULL, 0, 0);
+SENSOR_TEMPLATE(in_alarm, "in%d_alarm", 0444, nct6775_show_alarm, NULL, 0);
+SENSOR_TEMPLATE(in_beep, "in%d_beep", 0644, nct6775_show_beep, nct6775_store_beep, 0);
+SENSOR_TEMPLATE_2(in_min, "in%d_min", 0644, show_in_reg, store_in_reg, 0, 1);
+SENSOR_TEMPLATE_2(in_max, "in%d_max", 0644, show_in_reg, store_in_reg, 0, 2);
/*
* nct6775_in_is_visible uses the index into the following array
@@ -2275,6 +1861,9 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->rpm[nr]);
}
@@ -2285,6 +1874,9 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
data->fan_from_reg_min(data->fan_min[nr],
data->fan_div[nr]));
@@ -2297,6 +1889,9 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
}
@@ -2382,16 +1977,18 @@ write_div:
nr + 1, div_from_reg(data->fan_div[nr]),
div_from_reg(new_div));
data->fan_div[nr] = new_div;
- nct6775_write_fan_div_common(data, nr);
+ err = nct6775_write_fan_div_common(data, nr);
+ if (err)
+ goto write_min;
/* Give the chip time to sample a new speed value */
data->last_updated = jiffies;
}
write_min:
- data->write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
+ err = nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2399,8 +1996,12 @@ show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- int p = data->fan_pulses[sattr->index];
+ int p;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ p = data->fan_pulses[sattr->index];
return sprintf(buf, "%d\n", p ? : 4);
}
@@ -2413,7 +2014,7 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr,
int nr = sattr->index;
unsigned long val;
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2424,13 +2025,16 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->fan_pulses[nr] = val & 3;
- reg = data->read_value(data, data->REG_FAN_PULSES[nr]);
+ err = nct6775_read_value(data, data->REG_FAN_PULSES[nr], &reg);
+ if (err)
+ goto out;
reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]);
reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr];
- data->write_value(data, data->REG_FAN_PULSES[nr], reg);
+ err = nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_fan_is_visible(struct kobject *kobj,
@@ -2455,19 +2059,16 @@ static umode_t nct6775_fan_is_visible(struct kobject *kobj,
if (nr == 5 && data->kind != nct6775)
return 0;
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0);
-SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", S_IRUGO, show_alarm, NULL,
- FAN_ALARM_BASE);
-SENSOR_TEMPLATE(fan_beep, "fan%d_beep", S_IWUSR | S_IRUGO, show_beep,
- store_beep, FAN_ALARM_BASE);
-SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 0);
-SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 0);
-SENSOR_TEMPLATE(fan_div, "fan%d_div", S_IRUGO, show_fan_div, NULL, 0);
+SENSOR_TEMPLATE(fan_input, "fan%d_input", 0444, show_fan, NULL, 0);
+SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", 0444, nct6775_show_alarm, NULL, FAN_ALARM_BASE);
+SENSOR_TEMPLATE(fan_beep, "fan%d_beep", 0644, nct6775_show_beep,
+ nct6775_store_beep, FAN_ALARM_BASE);
+SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", 0644, show_fan_pulses, store_fan_pulses, 0);
+SENSOR_TEMPLATE(fan_min, "fan%d_min", 0644, show_fan_min, store_fan_min, 0);
+SENSOR_TEMPLATE(fan_div, "fan%d_div", 0444, show_fan_div, NULL, 0);
/*
* nct6775_fan_is_visible uses the index into the following array
@@ -2497,6 +2098,9 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
}
@@ -2508,6 +2112,9 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr]));
}
@@ -2528,10 +2135,9 @@ store_temp(struct device *dev, struct device_attribute *attr, const char *buf,
mutex_lock(&data->update_lock);
data->temp[index][nr] = LM75_TEMP_TO_REG(val);
- nct6775_write_temp(data, data->reg_temp[index][nr],
- data->temp[index][nr]);
+ err = nct6775_write_temp(data, data->reg_temp[index][nr], data->temp[index][nr]);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2540,6 +2146,9 @@ show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000);
}
@@ -2561,10 +2170,10 @@ store_temp_offset(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->temp_offset[nr] = val;
- data->write_value(data, data->REG_TEMP_OFFSET[nr], val);
+ err = nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2574,6 +2183,9 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
}
@@ -2586,7 +2198,11 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
int nr = sattr->index;
unsigned long val;
int err;
- u8 vbat, diode, vbit, dbit;
+ u8 vbit, dbit;
+ u16 vbat, diode;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2600,8 +2216,17 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
data->temp_type[nr] = val;
vbit = 0x02 << nr;
dbit = data->DIODE_MASK << nr;
- vbat = data->read_value(data, data->REG_VBAT) & ~vbit;
- diode = data->read_value(data, data->REG_DIODE) & ~dbit;
+
+ err = nct6775_read_value(data, data->REG_VBAT, &vbat);
+ if (err)
+ goto out;
+ vbat &= ~vbit;
+
+ err = nct6775_read_value(data, data->REG_DIODE, &diode);
+ if (err)
+ goto out;
+ diode &= ~dbit;
+
switch (val) {
case 1: /* CPU diode (diode, current mode) */
vbat |= vbit;
@@ -2613,11 +2238,13 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
case 4: /* thermistor */
break;
}
- data->write_value(data, data->REG_VBAT, vbat);
- data->write_value(data, data->REG_DIODE, diode);
-
+ err = nct6775_write_value(data, data->REG_VBAT, vbat);
+ if (err)
+ goto out;
+ err = nct6775_write_value(data, data->REG_DIODE, diode);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_temp_is_visible(struct kobject *kobj,
@@ -2656,26 +2283,19 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
if (nr > 7 && !(data->have_temp_fixed & BIT(temp)))
return 0;
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE_2(temp_input, "temp%d_input", S_IRUGO, show_temp, NULL, 0, 0);
-SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0);
-SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO | S_IWUSR, show_temp,
- store_temp, 0, 1);
-SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", S_IRUGO | S_IWUSR,
- show_temp, store_temp, 0, 2);
-SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO | S_IWUSR, show_temp,
- store_temp, 0, 3);
-SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", S_IRUGO | S_IWUSR, show_temp,
- store_temp, 0, 4);
-SENSOR_TEMPLATE(temp_offset, "temp%d_offset", S_IRUGO | S_IWUSR,
- show_temp_offset, store_temp_offset, 0);
-SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 0);
-SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", S_IRUGO, show_temp_alarm, NULL, 0);
-SENSOR_TEMPLATE(temp_beep, "temp%d_beep", S_IRUGO | S_IWUSR, show_temp_beep,
- store_temp_beep, 0);
+SENSOR_TEMPLATE_2(temp_input, "temp%d_input", 0444, show_temp, NULL, 0, 0);
+SENSOR_TEMPLATE(temp_label, "temp%d_label", 0444, show_temp_label, NULL, 0);
+SENSOR_TEMPLATE_2(temp_max, "temp%d_max", 0644, show_temp, store_temp, 0, 1);
+SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", 0644, show_temp, store_temp, 0, 2);
+SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", 0644, show_temp, store_temp, 0, 3);
+SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", 0644, show_temp, store_temp, 0, 4);
+SENSOR_TEMPLATE(temp_offset, "temp%d_offset", 0644, show_temp_offset, store_temp_offset, 0);
+SENSOR_TEMPLATE(temp_type, "temp%d_type", 0644, show_temp_type, store_temp_type, 0);
+SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", 0444, show_temp_alarm, NULL, 0);
+SENSOR_TEMPLATE(temp_beep, "temp%d_beep", 0644, show_temp_beep, store_temp_beep, 0);
/*
* nct6775_temp_is_visible uses the index into the following array
@@ -2707,6 +2327,9 @@ static ssize_t show_tsi_temp(struct device *dev, struct device_attribute *attr,
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sysfs_emit(buf, "%u\n", tsi_temp_from_reg(data->tsi_temp[sattr->index]));
}
@@ -2727,7 +2350,7 @@ static umode_t nct6775_tsi_temp_is_visible(struct kobject *kobj, struct attribut
struct nct6775_data *data = dev_get_drvdata(dev);
int temp = index / 2;
- return (data->have_tsi_temp & BIT(temp)) ? attr->mode : 0;
+ return (data->have_tsi_temp & BIT(temp)) ? nct6775_attr_mode(data, attr) : 0;
}
/*
@@ -2746,6 +2369,9 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]);
}
@@ -2758,7 +2384,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
int nr = sattr->index;
unsigned long val;
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2776,13 +2402,16 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->pwm_mode[nr] = val;
- reg = data->read_value(data, data->REG_PWM_MODE[nr]);
+ err = nct6775_read_value(data, data->REG_PWM_MODE[nr], &reg);
+ if (err)
+ goto out;
reg &= ~data->PWM_MODE_MASK[nr];
if (!val)
reg |= data->PWM_MODE_MASK[nr];
- data->write_value(data, data->REG_PWM_MODE[nr], reg);
+ err = nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2792,16 +2421,23 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
int nr = sattr->nr;
int index = sattr->index;
- int pwm;
+ int err;
+ u16 pwm;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
/*
* For automatic fan control modes, show current pwm readings.
* Otherwise, show the configured value.
*/
- if (index == 0 && data->pwm_enable[nr] > manual)
- pwm = data->read_value(data, data->REG_PWM_READ[nr]);
- else
+ if (index == 0 && data->pwm_enable[nr] > manual) {
+ err = nct6775_read_value(data, data->REG_PWM_READ[nr], &pwm);
+ if (err)
+ return err;
+ } else {
pwm = data->pwm[index][nr];
+ }
return sprintf(buf, "%d\n", pwm);
}
@@ -2819,7 +2455,7 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
int maxval[7]
= { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 };
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2828,16 +2464,21 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
mutex_lock(&data->update_lock);
data->pwm[index][nr] = val;
- data->write_value(data, data->REG_PWM[index][nr], val);
+ err = nct6775_write_value(data, data->REG_PWM[index][nr], val);
+ if (err)
+ goto out;
if (index == 2) { /* floor: disable if val == 0 */
- reg = data->read_value(data, data->REG_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0x7f;
if (val)
reg |= 0x80;
- data->write_value(data, data->REG_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
}
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
/* Returns 0 if OK, -EINVAL otherwise */
@@ -2864,40 +2505,54 @@ static int check_trip_points(struct nct6775_data *data, int nr)
return 0;
}
-static void pwm_update_registers(struct nct6775_data *data, int nr)
+static int pwm_update_registers(struct nct6775_data *data, int nr)
{
- u8 reg;
+ u16 reg;
+ int err;
switch (data->pwm_enable[nr]) {
case off:
case manual:
break;
case speed_cruise:
- reg = data->read_value(data, data->REG_FAN_MODE[nr]);
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ return err;
reg = (reg & ~data->tolerance_mask) |
(data->target_speed_tolerance[nr] & data->tolerance_mask);
- data->write_value(data, data->REG_FAN_MODE[nr], reg);
- data->write_value(data, data->REG_TARGET[nr],
- data->target_speed[nr] & 0xff);
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ if (err)
+ return err;
+ err = nct6775_write_value(data, data->REG_TARGET[nr],
+ data->target_speed[nr] & 0xff);
+ if (err)
+ return err;
if (data->REG_TOLERANCE_H) {
reg = (data->target_speed[nr] >> 8) & 0x0f;
reg |= (data->target_speed_tolerance[nr] & 0x38) << 1;
- data->write_value(data,
- data->REG_TOLERANCE_H[nr],
- reg);
+ err = nct6775_write_value(data, data->REG_TOLERANCE_H[nr], reg);
+ if (err)
+ return err;
}
break;
case thermal_cruise:
- data->write_value(data, data->REG_TARGET[nr],
- data->target_temp[nr]);
+ err = nct6775_write_value(data, data->REG_TARGET[nr], data->target_temp[nr]);
+ if (err)
+ return err;
fallthrough;
default:
- reg = data->read_value(data, data->REG_FAN_MODE[nr]);
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ return err;
reg = (reg & ~data->tolerance_mask) |
data->temp_tolerance[0][nr];
- data->write_value(data, data->REG_FAN_MODE[nr], reg);
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ if (err)
+ return err;
break;
}
+
+ return 0;
}
static ssize_t
@@ -2906,6 +2561,9 @@ show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]);
}
@@ -2943,15 +2601,22 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
* turn off pwm control: select manual mode, set pwm to maximum
*/
data->pwm[0][nr] = 255;
- data->write_value(data, data->REG_PWM[0][nr], 255);
+ err = nct6775_write_value(data, data->REG_PWM[0][nr], 255);
+ if (err)
+ goto out;
}
- pwm_update_registers(data, nr);
- reg = data->read_value(data, data->REG_FAN_MODE[nr]);
+ err = pwm_update_registers(data, nr);
+ if (err)
+ goto out;
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ goto out;
reg &= 0x0f;
reg |= pwm_enable_to_reg(val) << 4;
- data->write_value(data, data->REG_FAN_MODE[nr], reg);
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2978,6 +2643,9 @@ show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]);
}
@@ -2989,7 +2657,11 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
unsigned long val;
- int err, reg, src;
+ int err, src;
+ u16 reg;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -3002,13 +2674,16 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
src = data->temp_src[val - 1];
data->pwm_temp_sel[nr] = src;
- reg = data->read_value(data, data->REG_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0xe0;
reg |= src;
- data->write_value(data, data->REG_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3019,6 +2694,9 @@ show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return show_pwm_temp_sel_common(data, buf,
data->pwm_weight_temp_sel[index]);
}
@@ -3031,7 +2709,11 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
unsigned long val;
- int err, reg, src;
+ int err, src;
+ u16 reg;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -3047,19 +2729,24 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
if (val) {
src = data->temp_src[val - 1];
data->pwm_weight_temp_sel[nr] = src;
- reg = data->read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0xe0;
reg |= (src | 0x80);
- data->write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
} else {
data->pwm_weight_temp_sel[nr] = 0;
- reg = data->read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0x7f;
- data->write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
}
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3068,6 +2755,9 @@ show_target_temp(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000);
}
@@ -3090,9 +2780,9 @@ store_target_temp(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->target_temp[nr] = val;
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3102,6 +2792,9 @@ show_target_speed(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
fan_from_reg16(data->target_speed[nr],
data->fan_div[nr]));
@@ -3127,9 +2820,9 @@ store_target_speed(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->target_speed[nr] = speed;
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3141,6 +2834,9 @@ show_temp_tolerance(struct device *dev, struct device_attribute *attr,
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000);
}
@@ -3165,13 +2861,11 @@ store_temp_tolerance(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->temp_tolerance[index][nr] = val;
if (index)
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
else
- data->write_value(data,
- data->REG_CRITICAL_TEMP_TOLERANCE[nr],
- val);
+ err = nct6775_write_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
/*
@@ -3188,8 +2882,12 @@ show_speed_tolerance(struct device *dev, struct device_attribute *attr,
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
- int target = data->target_speed[nr];
- int tolerance = 0;
+ int target, tolerance = 0;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ target = data->target_speed[nr];
if (target) {
int low = target - data->target_speed_tolerance[nr];
@@ -3239,24 +2937,19 @@ store_speed_tolerance(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->target_speed_tolerance[nr] = val;
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
-SENSOR_TEMPLATE_2(pwm, "pwm%d", S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0);
-SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 0);
-SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 0);
-SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 0);
-SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", S_IWUSR | S_IRUGO,
- show_target_temp, store_target_temp, 0);
-SENSOR_TEMPLATE(fan_target, "fan%d_target", S_IWUSR | S_IRUGO,
- show_target_speed, store_target_speed, 0);
-SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 0);
+SENSOR_TEMPLATE_2(pwm, "pwm%d", 0644, show_pwm, store_pwm, 0, 0);
+SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", 0644, show_pwm_mode, store_pwm_mode, 0);
+SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", 0644, show_pwm_enable, store_pwm_enable, 0);
+SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", 0644, show_pwm_temp_sel, store_pwm_temp_sel, 0);
+SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", 0644, show_target_temp, store_target_temp, 0);
+SENSOR_TEMPLATE(fan_target, "fan%d_target", 0644, show_target_speed, store_target_speed, 0);
+SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", 0644, show_speed_tolerance,
+ store_speed_tolerance, 0);
/* Smart Fan registers */
@@ -3268,6 +2961,9 @@ show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000);
}
@@ -3290,23 +2986,21 @@ store_weight_temp(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->weight_temp[index][nr] = val;
- data->write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
-SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0);
+SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", 0644,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0);
SENSOR_TEMPLATE_2(pwm_weight_temp_step, "pwm%d_weight_temp_step",
- S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 0);
+ 0644, show_weight_temp, store_weight_temp, 0, 0);
SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol",
- S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 1);
+ 0644, show_weight_temp, store_weight_temp, 0, 1);
SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base",
- S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 2);
-SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step",
- S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 5);
-SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base",
- S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 6);
+ 0644, show_weight_temp, store_weight_temp, 0, 2);
+SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step", 0644, show_pwm, store_pwm, 0, 5);
+SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base", 0644, show_pwm, store_pwm, 0, 6);
static ssize_t
show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
@@ -3316,6 +3010,9 @@ show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
step_time_from_reg(data->fan_time[index][nr],
data->pwm_mode[nr]));
@@ -3339,9 +3036,9 @@ store_fan_time(struct device *dev, struct device_attribute *attr,
val = step_time_to_reg(val, data->pwm_mode[nr]);
mutex_lock(&data->update_lock);
data->fan_time[index][nr] = val;
- data->write_value(data, data->REG_FAN_TIME[index][nr], val);
+ err = nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3350,6 +3047,9 @@ show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]);
}
@@ -3363,7 +3063,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
int point = sattr->index;
unsigned long val;
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -3381,21 +3081,20 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->auto_pwm[nr][point] = val;
if (point < data->auto_pwm_num) {
- data->write_value(data,
- NCT6775_AUTO_PWM(data, nr, point),
- data->auto_pwm[nr][point]);
+ err = nct6775_write_value(data, NCT6775_AUTO_PWM(data, nr, point),
+ data->auto_pwm[nr][point]);
} else {
switch (data->kind) {
case nct6775:
/* disable if needed (pwm == 0) */
- reg = data->read_value(data,
- NCT6775_REG_CRITICAL_ENAB[nr]);
+ err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[nr], &reg);
+ if (err)
+ break;
if (val)
reg |= 0x02;
else
reg &= ~0x02;
- data->write_value(data, NCT6775_REG_CRITICAL_ENAB[nr],
- reg);
+ err = nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], reg);
break;
case nct6776:
break; /* always enabled, nothing to do */
@@ -3409,22 +3108,22 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
case nct6796:
case nct6797:
case nct6798:
- data->write_value(data, data->REG_CRITICAL_PWM[nr],
- val);
- reg = data->read_value(data,
- data->REG_CRITICAL_PWM_ENABLE[nr]);
+ err = nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val);
+ if (err)
+ break;
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], &reg);
+ if (err)
+ break;
if (val == 255)
reg &= ~data->CRITICAL_PWM_ENABLE_MASK;
else
reg |= data->CRITICAL_PWM_ENABLE_MASK;
- data->write_value(data,
- data->REG_CRITICAL_PWM_ENABLE[nr],
- reg);
+ err = nct6775_write_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], reg);
break;
}
}
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3435,6 +3134,9 @@ show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int point = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/*
* We don't know for sure if the temperature is signed or unsigned.
* Assume it is unsigned.
@@ -3462,15 +3164,14 @@ store_auto_temp(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000);
if (point < data->auto_pwm_num) {
- data->write_value(data,
- NCT6775_AUTO_TEMP(data, nr, point),
- data->auto_temp[nr][point]);
+ err = nct6775_write_value(data, NCT6775_AUTO_TEMP(data, nr, point),
+ data->auto_temp[nr][point]);
} else {
- data->write_value(data, data->REG_CRITICAL_TEMP[nr],
- data->auto_temp[nr][point]);
+ err = nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr],
+ data->auto_temp[nr][point]);
}
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
@@ -3500,65 +3201,59 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
if (api > data->auto_pwm_num)
return 0;
}
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 0, 0);
-SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", S_IWUSR | S_IRUGO,
+SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", 0644, show_fan_time, store_fan_time, 0, 0);
+SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", 0644,
show_fan_time, store_fan_time, 0, 1);
-SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", S_IWUSR | S_IRUGO,
+SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", 0644,
show_fan_time, store_fan_time, 0, 2);
-SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 1);
-SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 2);
-SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", S_IWUSR | S_IRUGO,
+SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", 0644, show_pwm, store_pwm, 0, 1);
+SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", 0644, show_pwm, store_pwm, 0, 2);
+SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", 0644,
show_temp_tolerance, store_temp_tolerance, 0, 0);
SENSOR_TEMPLATE_2(pwm_crit_temp_tolerance, "pwm%d_crit_temp_tolerance",
- S_IWUSR | S_IRUGO, show_temp_tolerance, store_temp_tolerance,
- 0, 1);
+ 0644, show_temp_tolerance, store_temp_tolerance, 0, 1);
-SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 0, 3);
+SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", 0644, show_pwm, store_pwm, 0, 3);
-SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 4);
+SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", 0644, show_pwm, store_pwm, 0, 4);
SENSOR_TEMPLATE_2(pwm_auto_point1_pwm, "pwm%d_auto_point1_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 0);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 0);
SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 0);
+ 0644, show_auto_temp, store_auto_temp, 0, 0);
SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 1);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 1);
SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 1);
+ 0644, show_auto_temp, store_auto_temp, 0, 1);
SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 2);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 2);
SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 2);
+ 0644, show_auto_temp, store_auto_temp, 0, 2);
SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 3);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 3);
SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 3);
+ 0644, show_auto_temp, store_auto_temp, 0, 3);
SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 4);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 4);
SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 4);
+ 0644, show_auto_temp, store_auto_temp, 0, 4);
SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 5);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 5);
SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 5);
+ 0644, show_auto_temp, store_auto_temp, 0, 5);
SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 6);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 6);
SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 6);
+ 0644, show_auto_temp, store_auto_temp, 0, 6);
/*
* nct6775_pwm_is_visible uses the index into the following array
@@ -3612,123 +3307,21 @@ static const struct sensor_template_group nct6775_pwm_template_group = {
.base = 1,
};
-static ssize_t
-cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct nct6775_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
-}
-
-static DEVICE_ATTR_RO(cpu0_vid);
-
-/* Case open detection */
-
-static ssize_t
-clear_caseopen(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static inline int nct6775_init_device(struct nct6775_data *data)
{
- struct nct6775_data *data = dev_get_drvdata(dev);
- struct nct6775_sio_data *sio_data = data->sio_data;
- int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
- unsigned long val;
- u8 reg;
- int ret;
-
- if (kstrtoul(buf, 10, &val) || val != 0)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- /*
- * Use CR registers to clear caseopen status.
- * The CR registers are the same for all chips, and not all chips
- * support clearing the caseopen status through "regular" registers.
- */
- ret = sio_data->sio_enter(sio_data);
- if (ret) {
- count = ret;
- goto error;
- }
-
- sio_data->sio_select(sio_data, NCT6775_LD_ACPI);
- reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
- reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
- sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
- reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
- sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
- sio_data->sio_exit(sio_data);
-
- data->valid = false; /* Force cache refresh */
-error:
- mutex_unlock(&data->update_lock);
- return count;
-}
-
-static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm,
- clear_caseopen, INTRUSION_ALARM_BASE);
-static SENSOR_DEVICE_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm,
- clear_caseopen, INTRUSION_ALARM_BASE + 1);
-static SENSOR_DEVICE_ATTR(intrusion0_beep, S_IWUSR | S_IRUGO, show_beep,
- store_beep, INTRUSION_ALARM_BASE);
-static SENSOR_DEVICE_ATTR(intrusion1_beep, S_IWUSR | S_IRUGO, show_beep,
- store_beep, INTRUSION_ALARM_BASE + 1);
-static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep,
- store_beep, BEEP_ENABLE_BASE);
-
-static umode_t nct6775_other_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct nct6775_data *data = dev_get_drvdata(dev);
-
- if (index == 0 && !data->have_vid)
- return 0;
-
- if (index == 1 || index == 2) {
- if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0)
- return 0;
- }
-
- if (index == 3 || index == 4) {
- if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
- return 0;
- }
-
- return attr->mode;
-}
-
-/*
- * nct6775_other_is_visible uses the index into the following array
- * to determine if attributes should be created or not.
- * Any change in order or content must be matched.
- */
-static struct attribute *nct6775_attributes_other[] = {
- &dev_attr_cpu0_vid.attr, /* 0 */
- &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */
- &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */
- &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */
- &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */
- &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */
-
- NULL
-};
-
-static const struct attribute_group nct6775_group_other = {
- .attrs = nct6775_attributes_other,
- .is_visible = nct6775_other_is_visible,
-};
-
-static inline void nct6775_init_device(struct nct6775_data *data)
-{
- int i;
- u8 tmp, diode;
+ int i, err;
+ u16 tmp, diode;
/* Start monitoring if needed */
if (data->REG_CONFIG) {
- tmp = data->read_value(data, data->REG_CONFIG);
- if (!(tmp & 0x01))
- data->write_value(data, data->REG_CONFIG, tmp | 0x01);
+ err = nct6775_read_value(data, data->REG_CONFIG, &tmp);
+ if (err)
+ return err;
+ if (!(tmp & 0x01)) {
+ err = nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01);
+ if (err)
+ return err;
+ }
}
/* Enable temperature sensors if needed */
@@ -3737,18 +3330,29 @@ static inline void nct6775_init_device(struct nct6775_data *data)
continue;
if (!data->reg_temp_config[i])
continue;
- tmp = data->read_value(data, data->reg_temp_config[i]);
- if (tmp & 0x01)
- data->write_value(data, data->reg_temp_config[i],
- tmp & 0xfe);
+ err = nct6775_read_value(data, data->reg_temp_config[i], &tmp);
+ if (err)
+ return err;
+ if (tmp & 0x01) {
+ err = nct6775_write_value(data, data->reg_temp_config[i], tmp & 0xfe);
+ if (err)
+ return err;
+ }
}
/* Enable VBAT monitoring if needed */
- tmp = data->read_value(data, data->REG_VBAT);
- if (!(tmp & 0x01))
- data->write_value(data, data->REG_VBAT, tmp | 0x01);
+ err = nct6775_read_value(data, data->REG_VBAT, &tmp);
+ if (err)
+ return err;
+ if (!(tmp & 0x01)) {
+ err = nct6775_write_value(data, data->REG_VBAT, tmp | 0x01);
+ if (err)
+ return err;
+ }
- diode = data->read_value(data, data->REG_DIODE);
+ err = nct6775_read_value(data, data->REG_DIODE, &diode);
+ if (err)
+ return err;
for (i = 0; i < data->temp_fixed_num; i++) {
if (!(data->have_temp_fixed & BIT(i)))
@@ -3759,241 +3363,24 @@ static inline void nct6775_init_device(struct nct6775_data *data)
else /* thermistor */
data->temp_type[i] = 4;
}
-}
-
-static void
-nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data)
-{
- bool fan3pin = false, fan4pin = false, fan4min = false;
- bool fan5pin = false, fan6pin = false, fan7pin = false;
- bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
- bool pwm6pin = false, pwm7pin = false;
-
- /* Store SIO_REG_ENABLE for use during resume */
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
-
- /* fan4 and fan5 share some pins with the GPIO and serial flash */
- if (data->kind == nct6775) {
- int cr2c = sio_data->sio_inb(sio_data, 0x2c);
-
- fan3pin = cr2c & BIT(6);
- pwm3pin = cr2c & BIT(7);
-
- /* On NCT6775, fan4 shares pins with the fdc interface */
- fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80);
- } else if (data->kind == nct6776) {
- bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80;
- const char *board_vendor, *board_name;
-
- board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
- board_name = dmi_get_system_info(DMI_BOARD_NAME);
-
- if (board_name && board_vendor &&
- !strcmp(board_vendor, "ASRock")) {
- /*
- * Auxiliary fan monitoring is not enabled on ASRock
- * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
- * Observed with BIOS version 2.00.
- */
- if (!strcmp(board_name, "Z77 Pro4-M")) {
- if ((data->sio_reg_enable & 0xe0) != 0xe0) {
- data->sio_reg_enable |= 0xe0;
- sio_data->sio_outb(sio_data, SIO_REG_ENABLE,
- data->sio_reg_enable);
- }
- }
- }
-
- if (data->sio_reg_enable & 0x80)
- fan3pin = gpok;
- else
- fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40);
-
- if (data->sio_reg_enable & 0x40)
- fan4pin = gpok;
- else
- fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01;
-
- if (data->sio_reg_enable & 0x20)
- fan5pin = gpok;
- else
- fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02;
-
- fan4min = fan4pin;
- pwm3pin = fan3pin;
- } else if (data->kind == nct6106) {
- int cr24 = sio_data->sio_inb(sio_data, 0x24);
-
- fan3pin = !(cr24 & 0x80);
- pwm3pin = cr24 & 0x08;
- } else if (data->kind == nct6116) {
- int cr1a = sio_data->sio_inb(sio_data, 0x1a);
- int cr1b = sio_data->sio_inb(sio_data, 0x1b);
- int cr24 = sio_data->sio_inb(sio_data, 0x24);
- int cr2a = sio_data->sio_inb(sio_data, 0x2a);
- int cr2b = sio_data->sio_inb(sio_data, 0x2b);
- int cr2f = sio_data->sio_inb(sio_data, 0x2f);
-
- fan3pin = !(cr2b & 0x10);
- fan4pin = (cr2b & 0x80) || // pin 1(2)
- (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66)
- fan5pin = (cr2b & 0x80) || // pin 126(127)
- (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96)
-
- pwm3pin = fan3pin && (cr24 & 0x08);
- pwm4pin = fan4pin;
- pwm5pin = fan5pin;
- } else {
- /*
- * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
- * NCT6797D, NCT6798D
- */
- int cr1a = sio_data->sio_inb(sio_data, 0x1a);
- int cr1b = sio_data->sio_inb(sio_data, 0x1b);
- int cr1c = sio_data->sio_inb(sio_data, 0x1c);
- int cr1d = sio_data->sio_inb(sio_data, 0x1d);
- int cr2a = sio_data->sio_inb(sio_data, 0x2a);
- int cr2b = sio_data->sio_inb(sio_data, 0x2b);
- int cr2d = sio_data->sio_inb(sio_data, 0x2d);
- int cr2f = sio_data->sio_inb(sio_data, 0x2f);
- bool dsw_en = cr2f & BIT(3);
- bool ddr4_en = cr2f & BIT(4);
- int cre0;
- int creb;
- int cred;
-
- sio_data->sio_select(sio_data, NCT6775_LD_12);
- cre0 = sio_data->sio_inb(sio_data, 0xe0);
- creb = sio_data->sio_inb(sio_data, 0xeb);
- cred = sio_data->sio_inb(sio_data, 0xed);
-
- fan3pin = !(cr1c & BIT(5));
- fan4pin = !(cr1c & BIT(6));
- fan5pin = !(cr1c & BIT(7));
-
- pwm3pin = !(cr1c & BIT(0));
- pwm4pin = !(cr1c & BIT(1));
- pwm5pin = !(cr1c & BIT(2));
-
- switch (data->kind) {
- case nct6791:
- fan6pin = cr2d & BIT(1);
- pwm6pin = cr2d & BIT(0);
- break;
- case nct6792:
- fan6pin = !dsw_en && (cr2d & BIT(1));
- pwm6pin = !dsw_en && (cr2d & BIT(0));
- break;
- case nct6793:
- fan5pin |= cr1b & BIT(5);
- fan5pin |= creb & BIT(5);
-
- fan6pin = !dsw_en && (cr2d & BIT(1));
- fan6pin |= creb & BIT(3);
-
- pwm5pin |= cr2d & BIT(7);
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm6pin = !dsw_en && (cr2d & BIT(0));
- pwm6pin |= creb & BIT(2);
- break;
- case nct6795:
- fan5pin |= cr1b & BIT(5);
- fan5pin |= creb & BIT(5);
-
- fan6pin = (cr2a & BIT(4)) &&
- (!dsw_en || (cred & BIT(4)));
- fan6pin |= creb & BIT(3);
-
- pwm5pin |= cr2d & BIT(7);
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
- pwm6pin |= creb & BIT(2);
- break;
- case nct6796:
- fan5pin |= cr1b & BIT(5);
- fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0));
- fan5pin |= creb & BIT(5);
-
- fan6pin = (cr2a & BIT(4)) &&
- (!dsw_en || (cred & BIT(4)));
- fan6pin |= creb & BIT(3);
-
- fan7pin = !(cr2b & BIT(2));
-
- pwm5pin |= cr2d & BIT(7);
- pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0));
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
- pwm6pin |= creb & BIT(2);
-
- pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
- break;
- case nct6797:
- fan5pin |= !ddr4_en && (cr1b & BIT(5));
- fan5pin |= creb & BIT(5);
-
- fan6pin = cr2a & BIT(4);
- fan6pin |= creb & BIT(3);
-
- fan7pin = cr1a & BIT(1);
-
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
- pwm5pin |= !ddr4_en && (cr2d & BIT(7));
-
- pwm6pin = creb & BIT(2);
- pwm6pin |= cred & BIT(2);
-
- pwm7pin = cr1d & BIT(4);
- break;
- case nct6798:
- fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3));
- fan6pin |= cr2a & BIT(4);
- fan6pin |= creb & BIT(5);
-
- fan7pin = cr1b & BIT(5);
- fan7pin |= !(cr2b & BIT(2));
- fan7pin |= creb & BIT(3);
-
- pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4));
- pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3));
- pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
- pwm7pin |= cr2d & BIT(7);
- pwm7pin |= creb & BIT(2);
- break;
- default: /* NCT6779D */
- break;
- }
-
- fan4min = fan4pin;
- }
- /* fan 1 and 2 (0x03) are always present */
- data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
- (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
- data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
- (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
- data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
- (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
+ return 0;
}
-static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
- int *available, int *mask)
+static int add_temp_sensors(struct nct6775_data *data, const u16 *regp,
+ int *available, int *mask)
{
- int i;
- u8 src;
+ int i, err;
+ u16 src;
for (i = 0; i < data->pwm_num && *available; i++) {
int index;
if (!regp[i])
continue;
- src = data->read_value(data, regp[i]);
+ err = nct6775_read_value(data, regp[i], &src);
+ if (err)
+ return err;
src &= 0x1f;
if (!src || (*mask & BIT(src)))
continue;
@@ -4001,58 +3388,36 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
continue;
index = __ffs(*available);
- data->write_value(data, data->REG_TEMP_SOURCE[index], src);
+ err = nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
+ if (err)
+ return err;
*available &= ~BIT(index);
*mask |= BIT(src);
}
+
+ return 0;
}
-static int nct6775_probe(struct platform_device *pdev)
+int nct6775_probe(struct device *dev, struct nct6775_data *data,
+ const struct regmap_config *regmapcfg)
{
- struct device *dev = &pdev->dev;
- struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
- struct nct6775_data *data;
- struct resource *res;
int i, s, err = 0;
- int src, mask, available;
+ int mask, available;
+ u16 src;
const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config;
const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit;
const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL;
int num_reg_temp, num_reg_temp_mon, num_reg_tsi_temp;
- u8 cr2a;
- struct attribute_group *group;
struct device *hwmon_dev;
struct sensor_template_group tsi_temp_tg;
- int num_attr_groups = 0;
-
- if (sio_data->access == access_direct) {
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH,
- DRVNAME))
- return -EBUSY;
- }
-
- data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data),
- GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- data->kind = sio_data->kind;
- data->sio_data = sio_data;
-
- if (sio_data->access == access_direct) {
- data->addr = res->start;
- data->read_value = nct6775_read_value;
- data->write_value = nct6775_write_value;
- } else {
- data->read_value = nct6775_wmi_read_value;
- data->write_value = nct6775_wmi_write_value;
- }
+ data->regmap = devm_regmap_init(dev, NULL, data, regmapcfg);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
mutex_init(&data->update_lock);
data->name = nct6775_device_names[data->kind];
data->bank = 0xff; /* Force initial bank selection */
- platform_set_drvdata(pdev, data);
switch (data->kind) {
case nct6106:
@@ -4596,7 +3961,10 @@ static int nct6775_probe(struct platform_device *pdev)
if (reg_temp[i] == 0)
continue;
- src = data->read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
if (!src || (mask & BIT(src)))
available |= BIT(i);
@@ -4607,8 +3975,12 @@ static int nct6775_probe(struct platform_device *pdev)
* Now find unmonitored temperature registers and enable monitoring
* if additional monitoring registers are available.
*/
- add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask);
- add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask);
+ err = add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask);
+ if (err)
+ return err;
+ err = add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask);
+ if (err)
+ return err;
mask = 0;
s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */
@@ -4616,7 +3988,10 @@ static int nct6775_probe(struct platform_device *pdev)
if (reg_temp[i] == 0)
continue;
- src = data->read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
if (!src || (mask & BIT(src)))
continue;
@@ -4676,7 +4051,10 @@ static int nct6775_probe(struct platform_device *pdev)
if (reg_temp_mon[i] == 0)
continue;
- src = data->read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
if (!src)
continue;
@@ -4760,525 +4138,68 @@ static int nct6775_probe(struct platform_device *pdev)
/* Check which TSIx_TEMP registers are active */
for (i = 0; i < num_reg_tsi_temp; i++) {
- if (data->read_value(data, data->REG_TSI_TEMP[i]))
+ u16 tmp;
+
+ err = nct6775_read_value(data, data->REG_TSI_TEMP[i], &tmp);
+ if (err)
+ return err;
+ if (tmp)
data->have_tsi_temp |= BIT(i);
}
/* Initialize the chip */
- nct6775_init_device(data);
-
- err = sio_data->sio_enter(sio_data);
+ err = nct6775_init_device(data);
if (err)
return err;
- cr2a = sio_data->sio_inb(sio_data, 0x2a);
- switch (data->kind) {
- case nct6775:
- data->have_vid = (cr2a & 0x40);
- break;
- case nct6776:
- data->have_vid = (cr2a & 0x60) == 0x40;
- break;
- case nct6106:
- case nct6116:
- case nct6779:
- case nct6791:
- case nct6792:
- case nct6793:
- case nct6795:
- case nct6796:
- case nct6797:
- case nct6798:
- break;
- }
-
- /*
- * Read VID value
- * We can get the VID input values directly at logical device D 0xe3.
- */
- if (data->have_vid) {
- sio_data->sio_select(sio_data, NCT6775_LD_VID);
- data->vid = sio_data->sio_inb(sio_data, 0xe3);
- data->vrm = vid_which_vrm();
- }
-
- if (fan_debounce) {
- u8 tmp;
-
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- tmp = sio_data->sio_inb(sio_data,
- NCT6775_REG_CR_FAN_DEBOUNCE);
- switch (data->kind) {
- case nct6106:
- case nct6116:
- tmp |= 0xe0;
- break;
- case nct6775:
- tmp |= 0x1e;
- break;
- case nct6776:
- case nct6779:
- tmp |= 0x3e;
- break;
- case nct6791:
- case nct6792:
- case nct6793:
- case nct6795:
- case nct6796:
- case nct6797:
- case nct6798:
- tmp |= 0x7e;
- break;
- }
- sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE,
- tmp);
- dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n",
- data->name);
+ if (data->driver_init) {
+ err = data->driver_init(data);
+ if (err)
+ return err;
}
- nct6775_check_fan_inputs(data, sio_data);
-
- sio_data->sio_exit(sio_data);
-
/* Read fan clock dividers immediately */
- nct6775_init_fan_common(dev, data);
+ err = nct6775_init_fan_common(dev, data);
+ if (err)
+ return err;
/* Register sysfs hooks */
- group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group,
- data->pwm_num);
- if (IS_ERR(group))
- return PTR_ERR(group);
-
- data->groups[num_attr_groups++] = group;
-
- group = nct6775_create_attr_group(dev, &nct6775_in_template_group,
- fls(data->have_in));
- if (IS_ERR(group))
- return PTR_ERR(group);
-
- data->groups[num_attr_groups++] = group;
-
- group = nct6775_create_attr_group(dev, &nct6775_fan_template_group,
- fls(data->has_fan));
- if (IS_ERR(group))
- return PTR_ERR(group);
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_pwm_template_group,
+ data->pwm_num);
+ if (err)
+ return err;
- data->groups[num_attr_groups++] = group;
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_in_template_group,
+ fls(data->have_in));
+ if (err)
+ return err;
- group = nct6775_create_attr_group(dev, &nct6775_temp_template_group,
- fls(data->have_temp));
- if (IS_ERR(group))
- return PTR_ERR(group);
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_fan_template_group,
+ fls(data->has_fan));
+ if (err)
+ return err;
- data->groups[num_attr_groups++] = group;
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_temp_template_group,
+ fls(data->have_temp));
+ if (err)
+ return err;
if (data->have_tsi_temp) {
tsi_temp_tg.templates = nct6775_tsi_temp_template;
tsi_temp_tg.is_visible = nct6775_tsi_temp_is_visible;
tsi_temp_tg.base = fls(data->have_temp) + 1;
- group = nct6775_create_attr_group(dev, &tsi_temp_tg, fls(data->have_tsi_temp));
- if (IS_ERR(group))
- return PTR_ERR(group);
-
- data->groups[num_attr_groups++] = group;
+ err = nct6775_add_template_attr_group(dev, data, &tsi_temp_tg,
+ fls(data->have_tsi_temp));
+ if (err)
+ return err;
}
- data->groups[num_attr_groups++] = &nct6775_group_other;
-
hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
-
-static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data)
-{
- int val;
-
- val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
- if (val & 0x10) {
- pr_info("Enabling hardware monitor logical device mappings.\n");
- sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
- val & ~0x10);
- }
-}
-
-static int __maybe_unused nct6775_suspend(struct device *dev)
-{
- struct nct6775_data *data = nct6775_update_device(dev);
-
- mutex_lock(&data->update_lock);
- data->vbat = data->read_value(data, data->REG_VBAT);
- if (data->kind == nct6775) {
- data->fandiv1 = data->read_value(data, NCT6775_REG_FANDIV1);
- data->fandiv2 = data->read_value(data, NCT6775_REG_FANDIV2);
- }
- mutex_unlock(&data->update_lock);
-
- return 0;
-}
-
-static int __maybe_unused nct6775_resume(struct device *dev)
-{
- struct nct6775_data *data = dev_get_drvdata(dev);
- struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
- int i, j, err = 0;
- u8 reg;
-
- mutex_lock(&data->update_lock);
- data->bank = 0xff; /* Force initial bank selection */
-
- err = sio_data->sio_enter(sio_data);
- if (err)
- goto abort;
-
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
- if (reg != data->sio_reg_enable)
- sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable);
-
- if (data->kind == nct6791 || data->kind == nct6792 ||
- data->kind == nct6793 || data->kind == nct6795 ||
- data->kind == nct6796 || data->kind == nct6797 ||
- data->kind == nct6798)
- nct6791_enable_io_mapping(sio_data);
-
- sio_data->sio_exit(sio_data);
-
- /* Restore limits */
- for (i = 0; i < data->in_num; i++) {
- if (!(data->have_in & BIT(i)))
- continue;
-
- data->write_value(data, data->REG_IN_MINMAX[0][i],
- data->in[i][1]);
- data->write_value(data, data->REG_IN_MINMAX[1][i],
- data->in[i][2]);
- }
-
- for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
- if (!(data->has_fan_min & BIT(i)))
- continue;
-
- data->write_value(data, data->REG_FAN_MIN[i],
- data->fan_min[i]);
- }
-
- for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & BIT(i)))
- continue;
-
- for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
- if (data->reg_temp[j][i])
- nct6775_write_temp(data, data->reg_temp[j][i],
- data->temp[j][i]);
- }
-
- /* Restore other settings */
- data->write_value(data, data->REG_VBAT, data->vbat);
- if (data->kind == nct6775) {
- data->write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
- data->write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
- }
-
-abort:
- /* Force re-reading all values */
- data->valid = false;
- mutex_unlock(&data->update_lock);
-
- return err;
-}
-
-static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
-
-static struct platform_driver nct6775_driver = {
- .driver = {
- .name = DRVNAME,
- .pm = &nct6775_dev_pm_ops,
- },
- .probe = nct6775_probe,
-};
-
-/* nct6775_find() looks for a '627 in the Super-I/O config space */
-static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
-{
- u16 val;
- int err;
- int addr;
-
- sio_data->access = access_direct;
- sio_data->sioreg = sioaddr;
-
- err = sio_data->sio_enter(sio_data);
- if (err)
- return err;
-
- val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) |
- sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1);
- if (force_id && val != 0xffff)
- val = force_id;
-
- switch (val & SIO_ID_MASK) {
- case SIO_NCT6106_ID:
- sio_data->kind = nct6106;
- break;
- case SIO_NCT6116_ID:
- sio_data->kind = nct6116;
- break;
- case SIO_NCT6775_ID:
- sio_data->kind = nct6775;
- break;
- case SIO_NCT6776_ID:
- sio_data->kind = nct6776;
- break;
- case SIO_NCT6779_ID:
- sio_data->kind = nct6779;
- break;
- case SIO_NCT6791_ID:
- sio_data->kind = nct6791;
- break;
- case SIO_NCT6792_ID:
- sio_data->kind = nct6792;
- break;
- case SIO_NCT6793_ID:
- sio_data->kind = nct6793;
- break;
- case SIO_NCT6795_ID:
- sio_data->kind = nct6795;
- break;
- case SIO_NCT6796_ID:
- sio_data->kind = nct6796;
- break;
- case SIO_NCT6797_ID:
- sio_data->kind = nct6797;
- break;
- case SIO_NCT6798_ID:
- sio_data->kind = nct6798;
- break;
- default:
- if (val != 0xffff)
- pr_debug("unsupported chip ID: 0x%04x\n", val);
- sio_data->sio_exit(sio_data);
- return -ENODEV;
- }
-
- /* We have a known chip, find the HWM I/O address */
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8)
- | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1);
- addr = val & IOREGION_ALIGNMENT;
- if (addr == 0) {
- pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
- sio_data->sio_exit(sio_data);
- return -ENODEV;
- }
-
- /* Activate logical device if needed */
- val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
- if (!(val & 0x01)) {
- pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
- sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01);
- }
-
- if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
- sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
- sio_data->kind == nct6796 || sio_data->kind == nct6797 ||
- sio_data->kind == nct6798)
- nct6791_enable_io_mapping(sio_data);
-
- sio_data->sio_exit(sio_data);
- pr_info("Found %s or compatible chip at %#x:%#x\n",
- nct6775_sio_names[sio_data->kind], sioaddr, addr);
-
- return addr;
-}
-
-/*
- * when Super-I/O functions move to a separate file, the Super-I/O
- * bus will manage the lifetime of the device and this module will only keep
- * track of the nct6775 driver. But since we use platform_device_alloc(), we
- * must keep track of the device
- */
-static struct platform_device *pdev[2];
-
-static const char * const asus_wmi_boards[] = {
- "ProArt X570-CREATOR WIFI",
- "Pro B550M-C",
- "Pro WS X570-ACE",
- "PRIME B360-PLUS",
- "PRIME B460-PLUS",
- "PRIME B550-PLUS",
- "PRIME B550M-A",
- "PRIME B550M-A (WI-FI)",
- "PRIME X570-P",
- "PRIME X570-PRO",
- "ROG CROSSHAIR VIII DARK HERO",
- "ROG CROSSHAIR VIII FORMULA",
- "ROG CROSSHAIR VIII HERO",
- "ROG CROSSHAIR VIII IMPACT",
- "ROG STRIX B550-A GAMING",
- "ROG STRIX B550-E GAMING",
- "ROG STRIX B550-F GAMING",
- "ROG STRIX B550-F GAMING (WI-FI)",
- "ROG STRIX B550-F GAMING WIFI II",
- "ROG STRIX B550-I GAMING",
- "ROG STRIX B550-XE GAMING (WI-FI)",
- "ROG STRIX X570-E GAMING",
- "ROG STRIX X570-F GAMING",
- "ROG STRIX X570-I GAMING",
- "ROG STRIX Z390-E GAMING",
- "ROG STRIX Z390-F GAMING",
- "ROG STRIX Z390-H GAMING",
- "ROG STRIX Z390-I GAMING",
- "ROG STRIX Z490-A GAMING",
- "ROG STRIX Z490-E GAMING",
- "ROG STRIX Z490-F GAMING",
- "ROG STRIX Z490-G GAMING",
- "ROG STRIX Z490-G GAMING (WI-FI)",
- "ROG STRIX Z490-H GAMING",
- "ROG STRIX Z490-I GAMING",
- "TUF GAMING B550M-PLUS",
- "TUF GAMING B550M-PLUS (WI-FI)",
- "TUF GAMING B550-PLUS",
- "TUF GAMING B550-PRO",
- "TUF GAMING X570-PLUS",
- "TUF GAMING X570-PLUS (WI-FI)",
- "TUF GAMING X570-PRO (WI-FI)",
- "TUF GAMING Z490-PLUS",
- "TUF GAMING Z490-PLUS (WI-FI)",
-};
-
-static int __init sensors_nct6775_init(void)
-{
- int i, err;
- bool found = false;
- int address;
- struct resource res;
- struct nct6775_sio_data sio_data;
- 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)
- return err;
-
- board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
- board_name = dmi_get_system_info(DMI_BOARD_NAME);
-
- if (board_name && board_vendor &&
- !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");
- }
- }
- }
-
- /*
- * initialize sio_data->kind and sio_data->sioreg.
- *
- * when Super-I/O functions move to a separate file, the Super-I/O
- * driver will probe 0x2e and 0x4e and auto-detect the presence of a
- * nct6775 hardware monitor, and call probe()
- */
- for (i = 0; i < ARRAY_SIZE(pdev); i++) {
- sio_data.sio_outb = superio_outb;
- sio_data.sio_inb = superio_inb;
- sio_data.sio_select = superio_select;
- sio_data.sio_enter = superio_enter;
- sio_data.sio_exit = superio_exit;
-
- address = nct6775_find(sioaddr[i], &sio_data);
- if (address <= 0)
- continue;
-
- found = true;
-
- sio_data.access = access;
-
- if (access == access_asuswmi) {
- sio_data.sio_outb = superio_wmi_outb;
- sio_data.sio_inb = superio_wmi_inb;
- sio_data.sio_select = superio_wmi_select;
- sio_data.sio_enter = superio_wmi_enter;
- sio_data.sio_exit = superio_wmi_exit;
- }
-
- pdev[i] = platform_device_alloc(DRVNAME, address);
- if (!pdev[i]) {
- err = -ENOMEM;
- goto exit_device_unregister;
- }
-
- err = platform_device_add_data(pdev[i], &sio_data,
- sizeof(struct nct6775_sio_data));
- if (err)
- goto exit_device_put;
-
- if (sio_data.access == access_direct) {
- memset(&res, 0, sizeof(res));
- res.name = DRVNAME;
- res.start = address + IOREGION_OFFSET;
- res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
- res.flags = IORESOURCE_IO;
-
- err = acpi_check_resource_conflict(&res);
- if (err) {
- platform_device_put(pdev[i]);
- pdev[i] = NULL;
- continue;
- }
-
- err = platform_device_add_resources(pdev[i], &res, 1);
- if (err)
- goto exit_device_put;
- }
-
- /* platform_device_add calls probe() */
- err = platform_device_add(pdev[i]);
- if (err)
- goto exit_device_put;
- }
- if (!found) {
- err = -ENODEV;
- goto exit_unregister;
- }
-
- return 0;
-
-exit_device_put:
- platform_device_put(pdev[i]);
-exit_device_unregister:
- while (--i >= 0) {
- if (pdev[i])
- platform_device_unregister(pdev[i]);
- }
-exit_unregister:
- platform_driver_unregister(&nct6775_driver);
- return err;
-}
-
-static void __exit sensors_nct6775_exit(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(pdev); i++) {
- if (pdev[i])
- platform_device_unregister(pdev[i]);
- }
- platform_driver_unregister(&nct6775_driver);
-}
+EXPORT_SYMBOL_GPL(nct6775_probe);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips");
+MODULE_DESCRIPTION("Core driver for NCT6775F and compatible chips");
MODULE_LICENSE("GPL");
-
-module_init(sensors_nct6775_init);
-module_exit(sensors_nct6775_exit);
diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c
new file mode 100644
index 00000000000000..e1bcd114619131
--- /dev/null
+++ b/drivers/hwmon/nct6775-i2c.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * nct6775-i2c - I2C driver for the hardware monitoring functionality of
+ * Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2022 Zev Weiss <zev@bewilderbeest.net>
+ *
+ * This driver interacts with the chip via it's "back door" i2c interface, as
+ * is often exposed to a BMC. Because the host may still be operating the
+ * chip via the ("front door") LPC interface, this driver cannot assume that
+ * it actually has full control of the chip, and in particular must avoid
+ * making any changes that could confuse the host's LPC usage of it. It thus
+ * operates in a strictly read-only fashion, with the only exception being the
+ * bank-select register (which seems, thankfully, to be replicated for the i2c
+ * interface so it doesn't affect the LPC interface).
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include "nct6775.h"
+
+static int nct6775_i2c_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ int ret;
+ u32 tmp;
+ u8 bank = reg >> 8;
+ struct nct6775_data *data = ctx;
+ struct i2c_client *client = data->driver_data;
+
+ if (bank != data->bank) {
+ ret = i2c_smbus_write_byte_data(client, NCT6775_REG_BANK, bank);
+ if (ret)
+ return ret;
+ data->bank = bank;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, reg & 0xff);
+ if (ret < 0)
+ return ret;
+ tmp = ret;
+
+ if (nct6775_reg_is_word_sized(data, reg)) {
+ ret = i2c_smbus_read_byte_data(client, (reg & 0xff) + 1);
+ if (ret < 0)
+ return ret;
+ tmp = (tmp << 8) | ret;
+ }
+
+ *val = tmp;
+ return 0;
+}
+
+/*
+ * The write operation is a dummy so as not to disturb anything being done
+ * with the chip via LPC.
+ */
+static int nct6775_i2c_write(void *ctx, unsigned int reg, unsigned int value)
+{
+ struct nct6775_data *data = ctx;
+ struct i2c_client *client = data->driver_data;
+
+ dev_dbg(&client->dev, "skipping attempted write: %02x -> %03x\n", value, reg);
+
+ /*
+ * This is a lie, but writing anything but the bank-select register is
+ * something this driver shouldn't be doing.
+ */
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = {
+ { .compatible = "nuvoton,nct6106", .data = (void *)nct6106, },
+ { .compatible = "nuvoton,nct6116", .data = (void *)nct6116, },
+ { .compatible = "nuvoton,nct6775", .data = (void *)nct6775, },
+ { .compatible = "nuvoton,nct6776", .data = (void *)nct6776, },
+ { .compatible = "nuvoton,nct6779", .data = (void *)nct6779, },
+ { .compatible = "nuvoton,nct6791", .data = (void *)nct6791, },
+ { .compatible = "nuvoton,nct6792", .data = (void *)nct6792, },
+ { .compatible = "nuvoton,nct6793", .data = (void *)nct6793, },
+ { .compatible = "nuvoton,nct6795", .data = (void *)nct6795, },
+ { .compatible = "nuvoton,nct6796", .data = (void *)nct6796, },
+ { .compatible = "nuvoton,nct6797", .data = (void *)nct6797, },
+ { .compatible = "nuvoton,nct6798", .data = (void *)nct6798, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);
+
+static const struct i2c_device_id nct6775_i2c_id[] = {
+ { "nct6106", nct6106 },
+ { "nct6116", nct6116 },
+ { "nct6775", nct6775 },
+ { "nct6776", nct6776 },
+ { "nct6779", nct6779 },
+ { "nct6791", nct6791 },
+ { "nct6792", nct6792 },
+ { "nct6793", nct6793 },
+ { "nct6795", nct6795 },
+ { "nct6796", nct6796 },
+ { "nct6797", nct6797 },
+ { "nct6798", nct6798 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id);
+
+static int nct6775_i2c_probe_init(struct nct6775_data *data)
+{
+ u32 tsi_channel_mask;
+ struct i2c_client *client = data->driver_data;
+
+ /*
+ * The i2c interface doesn't provide access to the control registers
+ * needed to determine the presence of other fans, but fans 1 and 2
+ * are (in principle) always there.
+ *
+ * In practice this is perhaps a little silly, because the system
+ * using this driver is mostly likely a BMC, and hence probably has
+ * totally separate fan tachs & pwms of its own that are actually
+ * controlling/monitoring the fans -- these are thus unlikely to be
+ * doing anything actually useful.
+ */
+ data->has_fan = 0x03;
+ data->has_fan_min = 0x03;
+ data->has_pwm = 0x03;
+
+ /*
+ * Because on a BMC this driver may be bound very shortly after power
+ * is first applied to the device, the automatic TSI channel detection
+ * in nct6775_probe() (which has already been run at this point) may
+ * not find anything if a channel hasn't yet produced a temperature
+ * reading. Augment whatever was found via autodetection (if
+ * anything) with the channels DT says should be active.
+ */
+ if (!of_property_read_u32(client->dev.of_node, "nuvoton,tsi-channel-mask",
+ &tsi_channel_mask))
+ data->have_tsi_temp |= tsi_channel_mask & GENMASK(NUM_TSI_TEMP - 1, 0);
+
+ return 0;
+}
+
+static const struct regmap_config nct6775_i2c_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = nct6775_i2c_read,
+ .reg_write = nct6775_i2c_write,
+};
+
+static int nct6775_i2c_probe(struct i2c_client *client)
+{
+ struct nct6775_data *data;
+ const struct of_device_id *of_id;
+ const struct i2c_device_id *i2c_id;
+ struct device *dev = &client->dev;
+
+ of_id = of_match_device(nct6775_i2c_of_match, dev);
+ i2c_id = i2c_match_id(nct6775_i2c_id, client);
+
+ if (of_id && (unsigned long)of_id->data != i2c_id->driver_data)
+ dev_notice(dev, "Device mismatch: %s in device tree, %s detected\n",
+ of_id->name, i2c_id->name);
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->kind = i2c_id->driver_data;
+
+ data->read_only = true;
+ data->driver_data = client;
+ data->driver_init = nct6775_i2c_probe_init;
+
+ return nct6775_probe(dev, data, &nct6775_i2c_regmap_config);
+}
+
+static struct i2c_driver nct6775_i2c_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "nct6775-i2c",
+ .of_match_table = of_match_ptr(nct6775_i2c_of_match),
+ },
+ .probe_new = nct6775_i2c_probe,
+ .id_table = nct6775_i2c_id,
+};
+
+module_i2c_driver(nct6775_i2c_driver);
+
+MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>");
+MODULE_DESCRIPTION("I2C driver for NCT6775F and compatible chips");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(HWMON_NCT6775);
diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
new file mode 100644
index 00000000000000..6d46c940189841
--- /dev/null
+++ b/drivers/hwmon/nct6775-platform.c
@@ -0,0 +1,1229 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * nct6775 - Platform driver for the hardware monitoring
+ * functionality of Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/wmi.h>
+
+#include "nct6775.h"
+
+enum sensor_access { access_direct, access_asuswmi };
+
+static const char * const nct6775_sio_names[] __initconst = {
+ "NCT6106D",
+ "NCT6116D",
+ "NCT6775F",
+ "NCT6776D/F",
+ "NCT6779D",
+ "NCT6791D",
+ "NCT6792D",
+ "NCT6793D",
+ "NCT6795D",
+ "NCT6796D",
+ "NCT6797D",
+ "NCT6798D",
+};
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static unsigned short fan_debounce;
+module_param(fan_debounce, ushort, 0);
+MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
+
+#define DRVNAME "nct6775"
+
+#define NCT6775_PORT_CHIPID 0x58
+
+/*
+ * ISA constants
+ */
+
+#define IOREGION_ALIGNMENT (~7)
+#define IOREGION_OFFSET 5
+#define IOREGION_LENGTH 2
+#define ADDR_REG_OFFSET 0
+#define DATA_REG_OFFSET 1
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define NCT6775_LD_ACPI 0x0a
+#define NCT6775_LD_HWM 0x0b
+#define NCT6775_LD_VID 0x0d
+#define NCT6775_LD_12 0x12
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_NCT6106_ID 0xc450
+#define SIO_NCT6116_ID 0xd280
+#define SIO_NCT6775_ID 0xb470
+#define SIO_NCT6776_ID 0xc330
+#define SIO_NCT6779_ID 0xc560
+#define SIO_NCT6791_ID 0xc800
+#define SIO_NCT6792_ID 0xc910
+#define SIO_NCT6793_ID 0xd120
+#define SIO_NCT6795_ID 0xd350
+#define SIO_NCT6796_ID 0xd420
+#define SIO_NCT6797_ID 0xd450
+#define SIO_NCT6798_ID 0xd428
+#define SIO_ID_MASK 0xFFF8
+
+/*
+ * Control registers
+ */
+#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0
+
+struct nct6775_sio_data {
+ int sioreg;
+ int ld;
+ enum kinds kind;
+ enum sensor_access access;
+
+ /* superio_() callbacks */
+ void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val);
+ int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg);
+ void (*sio_select)(struct nct6775_sio_data *sio_data, int ld);
+ int (*sio_enter)(struct nct6775_sio_data *sio_data);
+ void (*sio_exit)(struct nct6775_sio_data *sio_data);
+};
+
+#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
+#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
+
+static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
+{
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+ u32 args = bank | (reg << 8) | (val << 16);
+ struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ union acpi_object *obj;
+ u32 tmp = ASUSWMI_UNSUPPORTED_METHOD;
+
+ status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
+ method_id, &input, &output);
+
+ 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);
+
+ if (tmp == ASUSWMI_UNSUPPORTED_METHOD)
+ return -ENODEV;
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val)
+{
+ return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank,
+ reg, val, NULL);
+}
+
+static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val)
+{
+ u32 ret, tmp = 0;
+
+ ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank,
+ reg, 0, &tmp);
+ *val = tmp;
+ return ret;
+}
+
+static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg)
+{
+ int tmp = 0;
+
+ nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, sio_data->ld,
+ reg, 0, &tmp);
+ return tmp;
+}
+
+static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val)
+{
+ nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, sio_data->ld,
+ reg, val, NULL);
+}
+
+static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld)
+{
+ sio_data->ld = ld;
+}
+
+static int superio_wmi_enter(struct nct6775_sio_data *sio_data)
+{
+ return 0;
+}
+
+static void superio_wmi_exit(struct nct6775_sio_data *sio_data)
+{
+}
+
+static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static int superio_inb(struct nct6775_sio_data *sio_data, int reg)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static void superio_select(struct nct6775_sio_data *sio_data, int ld)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(SIO_REG_LDSEL, ioreg);
+ outb(ld, ioreg + 1);
+}
+
+static int superio_enter(struct nct6775_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ /*
+ * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
+ */
+ if (!request_muxed_region(ioreg, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x87, ioreg);
+ outb(0x87, ioreg);
+
+ return 0;
+}
+
+static void superio_exit(struct nct6775_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(0xaa, ioreg);
+ outb(0x02, ioreg);
+ outb(0x02, ioreg + 1);
+ release_region(ioreg, 2);
+}
+
+static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg)
+{
+ u8 bank = reg >> 8;
+
+ data->bank = bank;
+}
+
+static int nct6775_wmi_reg_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ struct nct6775_data *data = ctx;
+ int err, word_sized = nct6775_reg_is_word_sized(data, reg);
+ u8 tmp = 0;
+ u16 res;
+
+ nct6775_wmi_set_bank(data, reg);
+
+ err = nct6775_asuswmi_read(data->bank, reg & 0xff, &tmp);
+ if (err)
+ return err;
+
+ res = tmp;
+ if (word_sized) {
+ err = nct6775_asuswmi_read(data->bank, (reg & 0xff) + 1, &tmp);
+ if (err)
+ return err;
+
+ res = (res << 8) + tmp;
+ }
+ *val = res;
+ return 0;
+}
+
+static int nct6775_wmi_reg_write(void *ctx, unsigned int reg, unsigned int value)
+{
+ struct nct6775_data *data = ctx;
+ int res, word_sized = nct6775_reg_is_word_sized(data, reg);
+
+ nct6775_wmi_set_bank(data, reg);
+
+ if (word_sized) {
+ res = nct6775_asuswmi_write(data->bank, reg & 0xff, value >> 8);
+ if (res)
+ return res;
+
+ res = nct6775_asuswmi_write(data->bank, (reg & 0xff) + 1, value);
+ } else {
+ res = nct6775_asuswmi_write(data->bank, reg & 0xff, value);
+ }
+
+ return res;
+}
+
+/*
+ * On older chips, only registers 0x50-0x5f are banked.
+ * On more recent chips, all registers are banked.
+ * Assume that is the case and set the bank number for each access.
+ * Cache the bank number so it only needs to be set if it changes.
+ */
+static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
+{
+ u8 bank = reg >> 8;
+
+ if (data->bank != bank) {
+ outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
+ outb_p(bank, data->addr + DATA_REG_OFFSET);
+ data->bank = bank;
+ }
+}
+
+static int nct6775_reg_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ struct nct6775_data *data = ctx;
+ int word_sized = nct6775_reg_is_word_sized(data, reg);
+
+ nct6775_set_bank(data, reg);
+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+ *val = inb_p(data->addr + DATA_REG_OFFSET);
+ if (word_sized) {
+ outb_p((reg & 0xff) + 1,
+ data->addr + ADDR_REG_OFFSET);
+ *val = (*val << 8) + inb_p(data->addr + DATA_REG_OFFSET);
+ }
+ return 0;
+}
+
+static int nct6775_reg_write(void *ctx, unsigned int reg, unsigned int value)
+{
+ struct nct6775_data *data = ctx;
+ int word_sized = nct6775_reg_is_word_sized(data, reg);
+
+ nct6775_set_bank(data, reg);
+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+ if (word_sized) {
+ outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
+ outb_p((reg & 0xff) + 1,
+ data->addr + ADDR_REG_OFFSET);
+ }
+ outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
+ return 0;
+}
+
+static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data)
+{
+ int val;
+
+ val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
+ if (val & 0x10) {
+ pr_info("Enabling hardware monitor logical device mappings.\n");
+ sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
+ val & ~0x10);
+ }
+}
+
+static int __maybe_unused nct6775_suspend(struct device *dev)
+{
+ int err;
+ u16 tmp;
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ err = nct6775_read_value(data, data->REG_VBAT, &tmp);
+ if (err)
+ goto out;
+ data->vbat = tmp;
+ if (data->kind == nct6775) {
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &tmp);
+ if (err)
+ goto out;
+ data->fandiv1 = tmp;
+
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &tmp);
+ if (err)
+ goto out;
+ data->fandiv2 = tmp;
+ }
+out:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static int __maybe_unused nct6775_resume(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
+ int i, j, err = 0;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+ data->bank = 0xff; /* Force initial bank selection */
+
+ err = sio_data->sio_enter(sio_data);
+ if (err)
+ goto abort;
+
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
+ if (reg != data->sio_reg_enable)
+ sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable);
+
+ if (data->kind == nct6791 || data->kind == nct6792 ||
+ data->kind == nct6793 || data->kind == nct6795 ||
+ data->kind == nct6796 || data->kind == nct6797 ||
+ data->kind == nct6798)
+ nct6791_enable_io_mapping(sio_data);
+
+ sio_data->sio_exit(sio_data);
+
+ /* Restore limits */
+ for (i = 0; i < data->in_num; i++) {
+ if (!(data->have_in & BIT(i)))
+ continue;
+
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[0][i], data->in[i][1]);
+ if (err)
+ goto abort;
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[1][i], data->in[i][2]);
+ if (err)
+ goto abort;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+ if (!(data->has_fan_min & BIT(i)))
+ continue;
+
+ err = nct6775_write_value(data, data->REG_FAN_MIN[i], data->fan_min[i]);
+ if (err)
+ goto abort;
+ }
+
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & BIT(i)))
+ continue;
+
+ for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
+ if (data->reg_temp[j][i]) {
+ err = nct6775_write_temp(data, data->reg_temp[j][i],
+ data->temp[j][i]);
+ if (err)
+ goto abort;
+ }
+ }
+
+ /* Restore other settings */
+ err = nct6775_write_value(data, data->REG_VBAT, data->vbat);
+ if (err)
+ goto abort;
+ if (data->kind == nct6775) {
+ err = nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
+ if (err)
+ goto abort;
+ err = nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
+ }
+
+abort:
+ /* Force re-reading all values */
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
+
+static void
+nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data)
+{
+ bool fan3pin = false, fan4pin = false, fan4min = false;
+ bool fan5pin = false, fan6pin = false, fan7pin = false;
+ bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
+ bool pwm6pin = false, pwm7pin = false;
+
+ /* Store SIO_REG_ENABLE for use during resume */
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
+
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+ if (data->kind == nct6775) {
+ int cr2c = sio_data->sio_inb(sio_data, 0x2c);
+
+ fan3pin = cr2c & BIT(6);
+ pwm3pin = cr2c & BIT(7);
+
+ /* On NCT6775, fan4 shares pins with the fdc interface */
+ fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80);
+ } else if (data->kind == nct6776) {
+ bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80;
+ const char *board_vendor, *board_name;
+
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name && board_vendor &&
+ !strcmp(board_vendor, "ASRock")) {
+ /*
+ * Auxiliary fan monitoring is not enabled on ASRock
+ * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
+ * Observed with BIOS version 2.00.
+ */
+ if (!strcmp(board_name, "Z77 Pro4-M")) {
+ if ((data->sio_reg_enable & 0xe0) != 0xe0) {
+ data->sio_reg_enable |= 0xe0;
+ sio_data->sio_outb(sio_data, SIO_REG_ENABLE,
+ data->sio_reg_enable);
+ }
+ }
+ }
+
+ if (data->sio_reg_enable & 0x80)
+ fan3pin = gpok;
+ else
+ fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40);
+
+ if (data->sio_reg_enable & 0x40)
+ fan4pin = gpok;
+ else
+ fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01;
+
+ if (data->sio_reg_enable & 0x20)
+ fan5pin = gpok;
+ else
+ fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02;
+
+ fan4min = fan4pin;
+ pwm3pin = fan3pin;
+ } else if (data->kind == nct6106) {
+ int cr24 = sio_data->sio_inb(sio_data, 0x24);
+
+ fan3pin = !(cr24 & 0x80);
+ pwm3pin = cr24 & 0x08;
+ } else if (data->kind == nct6116) {
+ int cr1a = sio_data->sio_inb(sio_data, 0x1a);
+ int cr1b = sio_data->sio_inb(sio_data, 0x1b);
+ int cr24 = sio_data->sio_inb(sio_data, 0x24);
+ int cr2a = sio_data->sio_inb(sio_data, 0x2a);
+ int cr2b = sio_data->sio_inb(sio_data, 0x2b);
+ int cr2f = sio_data->sio_inb(sio_data, 0x2f);
+
+ fan3pin = !(cr2b & 0x10);
+ fan4pin = (cr2b & 0x80) || // pin 1(2)
+ (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66)
+ fan5pin = (cr2b & 0x80) || // pin 126(127)
+ (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96)
+
+ pwm3pin = fan3pin && (cr24 & 0x08);
+ pwm4pin = fan4pin;
+ pwm5pin = fan5pin;
+ } else {
+ /*
+ * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
+ * NCT6797D, NCT6798D
+ */
+ int cr1a = sio_data->sio_inb(sio_data, 0x1a);
+ int cr1b = sio_data->sio_inb(sio_data, 0x1b);
+ int cr1c = sio_data->sio_inb(sio_data, 0x1c);
+ int cr1d = sio_data->sio_inb(sio_data, 0x1d);
+ int cr2a = sio_data->sio_inb(sio_data, 0x2a);
+ int cr2b = sio_data->sio_inb(sio_data, 0x2b);
+ int cr2d = sio_data->sio_inb(sio_data, 0x2d);
+ int cr2f = sio_data->sio_inb(sio_data, 0x2f);
+ bool dsw_en = cr2f & BIT(3);
+ bool ddr4_en = cr2f & BIT(4);
+ int cre0;
+ int creb;
+ int cred;
+
+ sio_data->sio_select(sio_data, NCT6775_LD_12);
+ cre0 = sio_data->sio_inb(sio_data, 0xe0);
+ creb = sio_data->sio_inb(sio_data, 0xeb);
+ cred = sio_data->sio_inb(sio_data, 0xed);
+
+ fan3pin = !(cr1c & BIT(5));
+ fan4pin = !(cr1c & BIT(6));
+ fan5pin = !(cr1c & BIT(7));
+
+ pwm3pin = !(cr1c & BIT(0));
+ pwm4pin = !(cr1c & BIT(1));
+ pwm5pin = !(cr1c & BIT(2));
+
+ switch (data->kind) {
+ case nct6791:
+ fan6pin = cr2d & BIT(1);
+ pwm6pin = cr2d & BIT(0);
+ break;
+ case nct6792:
+ fan6pin = !dsw_en && (cr2d & BIT(1));
+ pwm6pin = !dsw_en && (cr2d & BIT(0));
+ break;
+ case nct6793:
+ fan5pin |= cr1b & BIT(5);
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = !dsw_en && (cr2d & BIT(1));
+ fan6pin |= creb & BIT(3);
+
+ pwm5pin |= cr2d & BIT(7);
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm6pin = !dsw_en && (cr2d & BIT(0));
+ pwm6pin |= creb & BIT(2);
+ break;
+ case nct6795:
+ fan5pin |= cr1b & BIT(5);
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = (cr2a & BIT(4)) &&
+ (!dsw_en || (cred & BIT(4)));
+ fan6pin |= creb & BIT(3);
+
+ pwm5pin |= cr2d & BIT(7);
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
+ pwm6pin |= creb & BIT(2);
+ break;
+ case nct6796:
+ fan5pin |= cr1b & BIT(5);
+ fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0));
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = (cr2a & BIT(4)) &&
+ (!dsw_en || (cred & BIT(4)));
+ fan6pin |= creb & BIT(3);
+
+ fan7pin = !(cr2b & BIT(2));
+
+ pwm5pin |= cr2d & BIT(7);
+ pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0));
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
+ pwm6pin |= creb & BIT(2);
+
+ pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
+ break;
+ case nct6797:
+ fan5pin |= !ddr4_en && (cr1b & BIT(5));
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = cr2a & BIT(4);
+ fan6pin |= creb & BIT(3);
+
+ fan7pin = cr1a & BIT(1);
+
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+ pwm5pin |= !ddr4_en && (cr2d & BIT(7));
+
+ pwm6pin = creb & BIT(2);
+ pwm6pin |= cred & BIT(2);
+
+ pwm7pin = cr1d & BIT(4);
+ break;
+ case nct6798:
+ fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3));
+ fan6pin |= cr2a & BIT(4);
+ fan6pin |= creb & BIT(5);
+
+ fan7pin = cr1b & BIT(5);
+ fan7pin |= !(cr2b & BIT(2));
+ fan7pin |= creb & BIT(3);
+
+ pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4));
+ pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3));
+ pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
+ pwm7pin |= cr2d & BIT(7);
+ pwm7pin |= creb & BIT(2);
+ break;
+ default: /* NCT6779D */
+ break;
+ }
+
+ fan4min = fan4pin;
+ }
+
+ /* fan 1 and 2 (0x03) are always present */
+ data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
+ (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
+ data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
+ (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
+ data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
+ (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
+}
+
+static ssize_t
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static DEVICE_ATTR_RO(cpu0_vid);
+
+/* Case open detection */
+
+static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
+static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
+
+static ssize_t
+clear_caseopen(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct nct6775_sio_data *sio_data = data->driver_data;
+ int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
+ unsigned long val;
+ u8 reg;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ /*
+ * Use CR registers to clear caseopen status.
+ * The CR registers are the same for all chips, and not all chips
+ * support clearing the caseopen status through "regular" registers.
+ */
+ ret = sio_data->sio_enter(sio_data);
+ if (ret) {
+ count = ret;
+ goto error;
+ }
+
+ sio_data->sio_select(sio_data, NCT6775_LD_ACPI);
+ reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
+ reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+ sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+ sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ sio_data->sio_exit(sio_data);
+
+ data->valid = false; /* Force cache refresh */
+error:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(intrusion0_alarm, 0644, nct6775_show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE);
+static SENSOR_DEVICE_ATTR(intrusion1_alarm, 0644, nct6775_show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE + 1);
+static SENSOR_DEVICE_ATTR(intrusion0_beep, 0644, nct6775_show_beep,
+ nct6775_store_beep, INTRUSION_ALARM_BASE);
+static SENSOR_DEVICE_ATTR(intrusion1_beep, 0644, nct6775_show_beep,
+ nct6775_store_beep, INTRUSION_ALARM_BASE + 1);
+static SENSOR_DEVICE_ATTR(beep_enable, 0644, nct6775_show_beep,
+ nct6775_store_beep, BEEP_ENABLE_BASE);
+
+static umode_t nct6775_other_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ if (index == 0 && !data->have_vid)
+ return 0;
+
+ if (index == 1 || index == 2) {
+ if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0)
+ return 0;
+ }
+
+ if (index == 3 || index == 4) {
+ if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
+ return 0;
+ }
+
+ return nct6775_attr_mode(data, attr);
+}
+
+/*
+ * nct6775_other_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct attribute *nct6775_attributes_other[] = {
+ &dev_attr_cpu0_vid.attr, /* 0 */
+ &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */
+ &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */
+ &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */
+ &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */
+ &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */
+
+ NULL
+};
+
+static const struct attribute_group nct6775_group_other = {
+ .attrs = nct6775_attributes_other,
+ .is_visible = nct6775_other_is_visible,
+};
+
+static int nct6775_platform_probe_init(struct nct6775_data *data)
+{
+ int err;
+ u8 cr2a;
+ struct nct6775_sio_data *sio_data = data->driver_data;
+
+ err = sio_data->sio_enter(sio_data);
+ if (err)
+ return err;
+
+ cr2a = sio_data->sio_inb(sio_data, 0x2a);
+ switch (data->kind) {
+ case nct6775:
+ data->have_vid = (cr2a & 0x40);
+ break;
+ case nct6776:
+ data->have_vid = (cr2a & 0x60) == 0x40;
+ break;
+ case nct6106:
+ case nct6116:
+ case nct6779:
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ break;
+ }
+
+ /*
+ * Read VID value
+ * We can get the VID input values directly at logical device D 0xe3.
+ */
+ if (data->have_vid) {
+ sio_data->sio_select(sio_data, NCT6775_LD_VID);
+ data->vid = sio_data->sio_inb(sio_data, 0xe3);
+ data->vrm = vid_which_vrm();
+ }
+
+ if (fan_debounce) {
+ u8 tmp;
+
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ tmp = sio_data->sio_inb(sio_data,
+ NCT6775_REG_CR_FAN_DEBOUNCE);
+ switch (data->kind) {
+ case nct6106:
+ case nct6116:
+ tmp |= 0xe0;
+ break;
+ case nct6775:
+ tmp |= 0x1e;
+ break;
+ case nct6776:
+ case nct6779:
+ tmp |= 0x3e;
+ break;
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ tmp |= 0x7e;
+ break;
+ }
+ sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE,
+ tmp);
+ pr_info("Enabled fan debounce for chip %s\n", data->name);
+ }
+
+ nct6775_check_fan_inputs(data, sio_data);
+
+ sio_data->sio_exit(sio_data);
+
+ return nct6775_add_attr_group(data, &nct6775_group_other);
+}
+
+static const struct regmap_config nct6775_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = nct6775_reg_read,
+ .reg_write = nct6775_reg_write,
+};
+
+static const struct regmap_config nct6775_wmi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = nct6775_wmi_reg_read,
+ .reg_write = nct6775_wmi_reg_write,
+};
+
+static int nct6775_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
+ struct nct6775_data *data;
+ struct resource *res;
+ const struct regmap_config *regmapcfg;
+
+ if (sio_data->access == access_direct) {
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, DRVNAME))
+ return -EBUSY;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->kind = sio_data->kind;
+ data->sioreg = sio_data->sioreg;
+
+ if (sio_data->access == access_direct) {
+ data->addr = res->start;
+ regmapcfg = &nct6775_regmap_config;
+ } else {
+ regmapcfg = &nct6775_wmi_regmap_config;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ data->driver_data = sio_data;
+ data->driver_init = nct6775_platform_probe_init;
+
+ return nct6775_probe(&pdev->dev, data, regmapcfg);
+}
+
+static struct platform_driver nct6775_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .pm = &nct6775_dev_pm_ops,
+ },
+ .probe = nct6775_platform_probe,
+};
+
+/* nct6775_find() looks for a '627 in the Super-I/O config space */
+static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
+{
+ u16 val;
+ int err;
+ int addr;
+
+ sio_data->access = access_direct;
+ sio_data->sioreg = sioaddr;
+
+ err = sio_data->sio_enter(sio_data);
+ if (err)
+ return err;
+
+ val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) |
+ sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1);
+ if (force_id && val != 0xffff)
+ val = force_id;
+
+ switch (val & SIO_ID_MASK) {
+ case SIO_NCT6106_ID:
+ sio_data->kind = nct6106;
+ break;
+ case SIO_NCT6116_ID:
+ sio_data->kind = nct6116;
+ break;
+ case SIO_NCT6775_ID:
+ sio_data->kind = nct6775;
+ break;
+ case SIO_NCT6776_ID:
+ sio_data->kind = nct6776;
+ break;
+ case SIO_NCT6779_ID:
+ sio_data->kind = nct6779;
+ break;
+ case SIO_NCT6791_ID:
+ sio_data->kind = nct6791;
+ break;
+ case SIO_NCT6792_ID:
+ sio_data->kind = nct6792;
+ break;
+ case SIO_NCT6793_ID:
+ sio_data->kind = nct6793;
+ break;
+ case SIO_NCT6795_ID:
+ sio_data->kind = nct6795;
+ break;
+ case SIO_NCT6796_ID:
+ sio_data->kind = nct6796;
+ break;
+ case SIO_NCT6797_ID:
+ sio_data->kind = nct6797;
+ break;
+ case SIO_NCT6798_ID:
+ sio_data->kind = nct6798;
+ break;
+ default:
+ if (val != 0xffff)
+ pr_debug("unsupported chip ID: 0x%04x\n", val);
+ sio_data->sio_exit(sio_data);
+ return -ENODEV;
+ }
+
+ /* We have a known chip, find the HWM I/O address */
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8)
+ | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1);
+ addr = val & IOREGION_ALIGNMENT;
+ if (addr == 0) {
+ pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
+ sio_data->sio_exit(sio_data);
+ return -ENODEV;
+ }
+
+ /* Activate logical device if needed */
+ val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
+ if (!(val & 0x01)) {
+ pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
+ sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01);
+ }
+
+ if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
+ sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
+ sio_data->kind == nct6796 || sio_data->kind == nct6797 ||
+ sio_data->kind == nct6798)
+ nct6791_enable_io_mapping(sio_data);
+
+ sio_data->sio_exit(sio_data);
+ pr_info("Found %s or compatible chip at %#x:%#x\n",
+ nct6775_sio_names[sio_data->kind], sioaddr, addr);
+
+ return addr;
+}
+
+/*
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * bus will manage the lifetime of the device and this module will only keep
+ * track of the nct6775 driver. But since we use platform_device_alloc(), we
+ * must keep track of the device
+ */
+static struct platform_device *pdev[2];
+
+static const char * const asus_wmi_boards[] = {
+ "PRO H410T",
+ "ProArt X570-CREATOR WIFI",
+ "Pro B550M-C",
+ "Pro WS X570-ACE",
+ "PRIME B360-PLUS",
+ "PRIME B460-PLUS",
+ "PRIME B550-PLUS",
+ "PRIME B550M-A",
+ "PRIME B550M-A (WI-FI)",
+ "PRIME H410M-R",
+ "PRIME X570-P",
+ "PRIME X570-PRO",
+ "ROG CROSSHAIR VIII DARK HERO",
+ "ROG CROSSHAIR VIII FORMULA",
+ "ROG CROSSHAIR VIII HERO",
+ "ROG CROSSHAIR VIII IMPACT",
+ "ROG STRIX B550-A GAMING",
+ "ROG STRIX B550-E GAMING",
+ "ROG STRIX B550-F GAMING",
+ "ROG STRIX B550-F GAMING (WI-FI)",
+ "ROG STRIX B550-F GAMING WIFI II",
+ "ROG STRIX B550-I GAMING",
+ "ROG STRIX B550-XE GAMING (WI-FI)",
+ "ROG STRIX X570-E GAMING",
+ "ROG STRIX X570-E GAMING WIFI II",
+ "ROG STRIX X570-F GAMING",
+ "ROG STRIX X570-I GAMING",
+ "ROG STRIX Z390-E GAMING",
+ "ROG STRIX Z390-F GAMING",
+ "ROG STRIX Z390-H GAMING",
+ "ROG STRIX Z390-I GAMING",
+ "ROG STRIX Z490-A GAMING",
+ "ROG STRIX Z490-E GAMING",
+ "ROG STRIX Z490-F GAMING",
+ "ROG STRIX Z490-G GAMING",
+ "ROG STRIX Z490-G GAMING (WI-FI)",
+ "ROG STRIX Z490-H GAMING",
+ "ROG STRIX Z490-I GAMING",
+ "TUF GAMING B550M-PLUS",
+ "TUF GAMING B550M-PLUS (WI-FI)",
+ "TUF GAMING B550-PLUS",
+ "TUF GAMING B550-PRO",
+ "TUF GAMING X570-PLUS",
+ "TUF GAMING X570-PLUS (WI-FI)",
+ "TUF GAMING X570-PRO (WI-FI)",
+ "TUF GAMING Z490-PLUS",
+ "TUF GAMING Z490-PLUS (WI-FI)",
+};
+
+static int __init sensors_nct6775_platform_init(void)
+{
+ int i, err;
+ bool found = false;
+ int address;
+ struct resource res;
+ struct nct6775_sio_data sio_data;
+ 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)
+ return err;
+
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name && board_vendor &&
+ !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");
+ }
+ }
+ }
+
+ /*
+ * initialize sio_data->kind and sio_data->sioreg.
+ *
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * driver will probe 0x2e and 0x4e and auto-detect the presence of a
+ * nct6775 hardware monitor, and call probe()
+ */
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ sio_data.sio_outb = superio_outb;
+ sio_data.sio_inb = superio_inb;
+ sio_data.sio_select = superio_select;
+ sio_data.sio_enter = superio_enter;
+ sio_data.sio_exit = superio_exit;
+
+ address = nct6775_find(sioaddr[i], &sio_data);
+ if (address <= 0)
+ continue;
+
+ found = true;
+
+ sio_data.access = access;
+
+ if (access == access_asuswmi) {
+ sio_data.sio_outb = superio_wmi_outb;
+ sio_data.sio_inb = superio_wmi_inb;
+ sio_data.sio_select = superio_wmi_select;
+ sio_data.sio_enter = superio_wmi_enter;
+ sio_data.sio_exit = superio_wmi_exit;
+ }
+
+ pdev[i] = platform_device_alloc(DRVNAME, address);
+ if (!pdev[i]) {
+ err = -ENOMEM;
+ goto exit_device_unregister;
+ }
+
+ err = platform_device_add_data(pdev[i], &sio_data,
+ sizeof(struct nct6775_sio_data));
+ if (err)
+ goto exit_device_put;
+
+ if (sio_data.access == access_direct) {
+ memset(&res, 0, sizeof(res));
+ res.name = DRVNAME;
+ res.start = address + IOREGION_OFFSET;
+ res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
+ res.flags = IORESOURCE_IO;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err) {
+ platform_device_put(pdev[i]);
+ pdev[i] = NULL;
+ continue;
+ }
+
+ err = platform_device_add_resources(pdev[i], &res, 1);
+ if (err)
+ goto exit_device_put;
+ }
+
+ /* platform_device_add calls probe() */
+ err = platform_device_add(pdev[i]);
+ if (err)
+ goto exit_device_put;
+ }
+ if (!found) {
+ err = -ENODEV;
+ goto exit_unregister;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev[i]);
+exit_device_unregister:
+ while (--i >= 0) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
+exit_unregister:
+ platform_driver_unregister(&nct6775_driver);
+ return err;
+}
+
+static void __exit sensors_nct6775_platform_exit(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
+ platform_driver_unregister(&nct6775_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("Platform driver for NCT6775F and compatible chips");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(HWMON_NCT6775);
+
+module_init(sensors_nct6775_platform_init);
+module_exit(sensors_nct6775_platform_exit);
diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
new file mode 100644
index 00000000000000..93f708148e6588
--- /dev/null
+++ b/drivers/hwmon/nct6775.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __HWMON_NCT6775_H__
+#define __HWMON_NCT6775_H__
+
+#include <linux/types.h>
+
+enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
+ nct6793, nct6795, nct6796, nct6797, nct6798 };
+enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+
+#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/
+#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */
+#define NUM_TSI_TEMP 8 /* Max number of TSI temp register pairs */
+
+#define NUM_REG_ALARM 7 /* Max number of alarm registers */
+#define NUM_REG_BEEP 5 /* Max number of beep registers */
+
+#define NUM_FAN 7
+
+struct nct6775_data {
+ int addr; /* IO base of hw monitor block */
+ int sioreg; /* SIO register address */
+ enum kinds kind;
+ const char *name;
+
+ const struct attribute_group *groups[7];
+ u8 num_groups;
+
+ u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit, 4=temp_lcrit
+ */
+ u8 temp_src[NUM_TEMP];
+ u16 reg_temp_config[NUM_TEMP];
+ const char * const *temp_label;
+ u32 temp_mask;
+ u32 virt_temp_mask;
+
+ u16 REG_CONFIG;
+ u16 REG_VBAT;
+ u16 REG_DIODE;
+ u8 DIODE_MASK;
+
+ const s8 *ALARM_BITS;
+ const s8 *BEEP_BITS;
+
+ const u16 *REG_VIN;
+ const u16 *REG_IN_MINMAX[2];
+
+ const u16 *REG_TARGET;
+ const u16 *REG_FAN;
+ const u16 *REG_FAN_MODE;
+ const u16 *REG_FAN_MIN;
+ const u16 *REG_FAN_PULSES;
+ const u16 *FAN_PULSE_SHIFT;
+ const u16 *REG_FAN_TIME[3];
+
+ const u16 *REG_TOLERANCE_H;
+
+ const u8 *REG_PWM_MODE;
+ const u8 *PWM_MODE_MASK;
+
+ const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
+ const u16 *REG_PWM_READ;
+
+ const u16 *REG_CRITICAL_PWM_ENABLE;
+ u8 CRITICAL_PWM_ENABLE_MASK;
+ const u16 *REG_CRITICAL_PWM;
+
+ const u16 *REG_AUTO_TEMP;
+ const u16 *REG_AUTO_PWM;
+
+ const u16 *REG_CRITICAL_TEMP;
+ const u16 *REG_CRITICAL_TEMP_TOLERANCE;
+
+ const u16 *REG_TEMP_SOURCE; /* temp register sources */
+ const u16 *REG_TEMP_SEL;
+ const u16 *REG_WEIGHT_TEMP_SEL;
+ const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */
+
+ const u16 *REG_TEMP_OFFSET;
+
+ const u16 *REG_ALARM;
+ const u16 *REG_BEEP;
+
+ const u16 *REG_TSI_TEMP;
+
+ unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
+ unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values */
+ u8 bank; /* current register bank */
+ u8 in_num; /* number of in inputs we have */
+ u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */
+ unsigned int rpm[NUM_FAN];
+ u16 fan_min[NUM_FAN];
+ u8 fan_pulses[NUM_FAN];
+ u8 fan_div[NUM_FAN];
+ u8 has_pwm;
+ u8 has_fan; /* some fan inputs can be disabled */
+ u8 has_fan_min; /* some fans don't have min register */
+ bool has_fan_div;
+
+ u8 num_temp_alarms; /* 2, 3, or 6 */
+ u8 num_temp_beeps; /* 2, 3, or 6 */
+ u8 temp_fixed_num; /* 3 or 6 */
+ u8 temp_type[NUM_TEMP_FIXED];
+ s8 temp_offset[NUM_TEMP_FIXED];
+ s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit, 4=temp_lcrit
+ */
+ s16 tsi_temp[NUM_TSI_TEMP];
+ u64 alarms;
+ u64 beeps;
+
+ u8 pwm_num; /* number of pwm */
+ u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage,
+ * 1->PWM variable duty cycle
+ */
+ enum pwm_enable pwm_enable[NUM_FAN];
+ /* 0->off
+ * 1->manual
+ * 2->thermal cruise mode (also called SmartFan I)
+ * 3->fan speed cruise mode
+ * 4->SmartFan III
+ * 5->enhanced variable thermal cruise (SmartFan IV)
+ */
+ u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
+
+ u8 target_temp[NUM_FAN];
+ u8 target_temp_mask;
+ u32 target_speed[NUM_FAN];
+ u32 target_speed_tolerance[NUM_FAN];
+ u8 speed_tolerance_limit;
+
+ u8 temp_tolerance[2][NUM_FAN];
+ u8 tolerance_mask;
+
+ u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
+
+ /* Automatic fan speed control registers */
+ int auto_pwm_num;
+ u8 auto_pwm[NUM_FAN][7];
+ u8 auto_temp[NUM_FAN][7];
+ u8 pwm_temp_sel[NUM_FAN];
+ u8 pwm_weight_temp_sel[NUM_FAN];
+ u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol,
+ * 2->temp_base
+ */
+
+ u8 vid;
+ u8 vrm;
+
+ bool have_vid;
+
+ u16 have_temp;
+ u16 have_temp_fixed;
+ u16 have_tsi_temp;
+ u16 have_in;
+
+ /* Remember extra register values over suspend/resume */
+ u8 vbat;
+ u8 fandiv1;
+ u8 fandiv2;
+ u8 sio_reg_enable;
+
+ struct regmap *regmap;
+ bool read_only;
+
+ /* driver-specific (platform, i2c) initialization hook and data */
+ int (*driver_init)(struct nct6775_data *data);
+ void *driver_data;
+};
+
+static inline int nct6775_read_value(struct nct6775_data *data, u16 reg, u16 *value)
+{
+ unsigned int tmp;
+ int ret = regmap_read(data->regmap, reg, &tmp);
+
+ if (!ret)
+ *value = tmp;
+ return ret;
+}
+
+static inline int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value)
+{
+ return regmap_write(data->regmap, reg, value);
+}
+
+bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg);
+int nct6775_probe(struct device *dev, struct nct6775_data *data,
+ const struct regmap_config *regmapcfg);
+
+ssize_t nct6775_show_alarm(struct device *dev, struct device_attribute *attr, char *buf);
+ssize_t nct6775_show_beep(struct device *dev, struct device_attribute *attr, char *buf);
+ssize_t nct6775_store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count);
+
+static inline int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
+{
+ if (!nct6775_reg_is_word_sized(data, reg))
+ value >>= 8;
+ return nct6775_write_value(data, reg, value);
+}
+
+static inline umode_t nct6775_attr_mode(struct nct6775_data *data, struct attribute *attr)
+{
+ return data->read_only ? (attr->mode & ~0222) : attr->mode;
+}
+
+static inline int
+nct6775_add_attr_group(struct nct6775_data *data, const struct attribute_group *group)
+{
+ /* Need to leave a NULL terminator at the end of data->groups */
+ if (data->num_groups == ARRAY_SIZE(data->groups) - 1)
+ return -ENOBUFS;
+
+ data->groups[data->num_groups++] = group;
+ return 0;
+}
+
+#define NCT6775_REG_BANK 0x4E
+#define NCT6775_REG_CONFIG 0x40
+
+#define NCT6775_REG_FANDIV1 0x506
+#define NCT6775_REG_FANDIV2 0x507
+
+#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28
+
+#define FAN_ALARM_BASE 16
+#define TEMP_ALARM_BASE 24
+#define INTRUSION_ALARM_BASE 30
+#define BEEP_ENABLE_BASE 15
+
+/*
+ * Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58
+ */
+
+#endif /* __HWMON_NCT6775_H__ */
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index f00cd59f1d19f7..d78f4bebc7189b 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1149,44 +1149,75 @@ static void occ_parse_poll_response(struct occ *occ)
sizeof(*header), size + sizeof(*header));
}
-int occ_setup(struct occ *occ, const char *name)
+int occ_active(struct occ *occ, bool active)
{
- int rc;
-
- mutex_init(&occ->lock);
- occ->groups[0] = &occ->group;
+ int rc = mutex_lock_interruptible(&occ->lock);
- /* no need to lock */
- rc = occ_poll(occ);
- if (rc == -ESHUTDOWN) {
- dev_info(occ->bus_dev, "host is not ready\n");
- return rc;
- } else if (rc < 0) {
- dev_err(occ->bus_dev,
- "failed to get OCC poll response=%02x: %d\n",
- occ->resp.return_status, rc);
+ if (rc)
return rc;
- }
- occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
- occ_parse_poll_response(occ);
+ if (active) {
+ if (occ->active) {
+ rc = -EALREADY;
+ goto unlock;
+ }
- rc = occ_setup_sensor_attrs(occ);
- if (rc) {
- dev_err(occ->bus_dev, "failed to setup sensor attrs: %d\n",
- rc);
- return rc;
- }
+ occ->error_count = 0;
+ occ->last_safe = 0;
- occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev, name,
- occ, occ->groups);
- if (IS_ERR(occ->hwmon)) {
- rc = PTR_ERR(occ->hwmon);
- dev_err(occ->bus_dev, "failed to register hwmon device: %d\n",
- rc);
- return rc;
+ rc = occ_poll(occ);
+ if (rc < 0) {
+ dev_err(occ->bus_dev,
+ "failed to get OCC poll response=%02x: %d\n",
+ occ->resp.return_status, rc);
+ goto unlock;
+ }
+
+ occ->active = true;
+ occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
+ occ_parse_poll_response(occ);
+
+ rc = occ_setup_sensor_attrs(occ);
+ if (rc) {
+ dev_err(occ->bus_dev,
+ "failed to setup sensor attrs: %d\n", rc);
+ goto unlock;
+ }
+
+ occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev,
+ "occ", occ,
+ occ->groups);
+ if (IS_ERR(occ->hwmon)) {
+ rc = PTR_ERR(occ->hwmon);
+ occ->hwmon = NULL;
+ dev_err(occ->bus_dev,
+ "failed to register hwmon device: %d\n", rc);
+ goto unlock;
+ }
+ } else {
+ if (!occ->active) {
+ rc = -EALREADY;
+ goto unlock;
+ }
+
+ if (occ->hwmon)
+ hwmon_device_unregister(occ->hwmon);
+ occ->active = false;
+ occ->hwmon = NULL;
}
+unlock:
+ mutex_unlock(&occ->lock);
+ return rc;
+}
+
+int occ_setup(struct occ *occ)
+{
+ int rc;
+
+ mutex_init(&occ->lock);
+ occ->groups[0] = &occ->group;
+
rc = occ_setup_sysfs(occ);
if (rc)
dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
@@ -1195,6 +1226,15 @@ int occ_setup(struct occ *occ, const char *name)
}
EXPORT_SYMBOL_GPL(occ_setup);
+void occ_shutdown(struct occ *occ)
+{
+ occ_shutdown_sysfs(occ);
+
+ if (occ->hwmon)
+ hwmon_device_unregister(occ->hwmon);
+}
+EXPORT_SYMBOL_GPL(occ_shutdown);
+
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
MODULE_DESCRIPTION("Common OCC hwmon code");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 2dd4a4d240c0f7..64d5ec7e169b04 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -106,6 +106,7 @@ struct occ {
struct attribute_group group;
const struct attribute_group *groups[2];
+ bool active;
int error; /* final transfer error after retry */
int last_error; /* latest transfer error */
unsigned int error_count; /* number of xfr errors observed */
@@ -123,9 +124,11 @@ struct occ {
u8 prev_mode;
};
-int occ_setup(struct occ *occ, const char *name);
+int occ_active(struct occ *occ, bool active);
+int occ_setup(struct occ *occ);
int occ_setup_sysfs(struct occ *occ);
void occ_shutdown(struct occ *occ);
+void occ_shutdown_sysfs(struct occ *occ);
void occ_sysfs_poll_done(struct occ *occ);
int occ_update_response(struct occ *occ);
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 9e61e1fb5142cf..da39ea28df3122 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -223,7 +223,7 @@ static int p8_i2c_occ_probe(struct i2c_client *client)
occ->poll_cmd_data = 0x10; /* P8 OCC poll data */
occ->send_cmd = p8_i2c_occ_send_cmd;
- return occ_setup(occ, "p8_occ");
+ return occ_setup(occ);
}
static int p8_i2c_occ_remove(struct i2c_client *client)
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index 49b13cc01073a5..42fc7b97bb34b5 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -145,7 +145,7 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
occ->poll_cmd_data = 0x20; /* P9 OCC poll data */
occ->send_cmd = p9_sbe_occ_send_cmd;
- rc = occ_setup(occ, "p9_occ");
+ rc = occ_setup(occ);
if (rc == -ESHUTDOWN)
rc = -ENODEV; /* Host is shutdown, don't spew errors */
diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index b2f788a7774693..2317301fc1e9f6 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -6,13 +6,13 @@
#include <linux/export.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/sysfs.h>
#include "common.h"
/* OCC status register */
#define OCC_STAT_MASTER BIT(7)
-#define OCC_STAT_ACTIVE BIT(0)
/* OCC extended status register */
#define OCC_EXT_STAT_DVFS_OT BIT(7)
@@ -22,6 +22,25 @@
#define OCC_EXT_STAT_DVFS_VDD BIT(3)
#define OCC_EXT_STAT_GPU_THROTTLE GENMASK(2, 0)
+static ssize_t occ_active_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ bool active;
+ struct occ *occ = dev_get_drvdata(dev);
+
+ rc = kstrtobool(buf, &active);
+ if (rc)
+ return rc;
+
+ rc = occ_active(occ, active);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
static ssize_t occ_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -31,54 +50,64 @@ static ssize_t occ_sysfs_show(struct device *dev,
struct occ_poll_response_header *header;
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- rc = occ_update_response(occ);
- if (rc)
- return rc;
+ if (occ->active) {
+ rc = occ_update_response(occ);
+ if (rc)
+ return rc;
- header = (struct occ_poll_response_header *)occ->resp.data;
-
- switch (sattr->index) {
- case 0:
- val = !!(header->status & OCC_STAT_MASTER);
- break;
- case 1:
- val = !!(header->status & OCC_STAT_ACTIVE);
- break;
- case 2:
- val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
- break;
- case 3:
- val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
- break;
- case 4:
- val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE);
- break;
- case 5:
- val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
- break;
- case 6:
- val = header->occ_state;
- break;
- case 7:
- if (header->status & OCC_STAT_MASTER)
- val = hweight8(header->occs_present);
- else
+ header = (struct occ_poll_response_header *)occ->resp.data;
+
+ switch (sattr->index) {
+ case 0:
+ val = !!(header->status & OCC_STAT_MASTER);
+ break;
+ case 1:
val = 1;
- break;
- case 8:
- val = header->ips_status;
- break;
- case 9:
- val = header->mode;
- break;
- case 10:
- val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD);
- break;
- case 11:
- val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE;
- break;
- default:
- return -EINVAL;
+ break;
+ case 2:
+ val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
+ break;
+ case 3:
+ val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
+ break;
+ case 4:
+ val = !!(header->ext_status &
+ OCC_EXT_STAT_MEM_THROTTLE);
+ break;
+ case 5:
+ val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
+ break;
+ case 6:
+ val = header->occ_state;
+ break;
+ case 7:
+ if (header->status & OCC_STAT_MASTER)
+ val = hweight8(header->occs_present);
+ else
+ val = 1;
+ break;
+ case 8:
+ val = header->ips_status;
+ break;
+ case 9:
+ val = header->mode;
+ break;
+ case 10:
+ val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD);
+ break;
+ case 11:
+ val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ if (sattr->index == 1)
+ val = 0;
+ else if (sattr->index <= 11)
+ val = -ENODATA;
+ else
+ return -EINVAL;
}
return sysfs_emit(buf, "%d\n", val);
@@ -95,7 +124,8 @@ static ssize_t occ_error_show(struct device *dev,
}
static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
-static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1);
+static SENSOR_DEVICE_ATTR(occ_active, 0644, occ_sysfs_show, occ_active_store,
+ 1);
static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);
static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3);
static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4);
@@ -139,7 +169,7 @@ void occ_sysfs_poll_done(struct occ *occ)
* On the first poll response, we haven't yet created the sysfs
* attributes, so don't make any notify calls.
*/
- if (!occ->hwmon)
+ if (!occ->active)
goto done;
if ((header->status & OCC_STAT_MASTER) !=
@@ -148,12 +178,6 @@ void occ_sysfs_poll_done(struct occ *occ)
sysfs_notify(&occ->bus_dev->kobj, NULL, name);
}
- if ((header->status & OCC_STAT_ACTIVE) !=
- (occ->prev_stat & OCC_STAT_ACTIVE)) {
- name = sensor_dev_attr_occ_active.dev_attr.attr.name;
- sysfs_notify(&occ->bus_dev->kobj, NULL, name);
- }
-
if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) !=
(occ->prev_ext_stat & OCC_EXT_STAT_DVFS_OT)) {
name = sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr.name;
@@ -227,8 +251,7 @@ int occ_setup_sysfs(struct occ *occ)
return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs);
}
-void occ_shutdown(struct occ *occ)
+void occ_shutdown_sysfs(struct occ *occ)
{
sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);
}
-EXPORT_SYMBOL_GPL(occ_shutdown);
diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c
index 12156328f5cf51..57470fda5f6c91 100644
--- a/drivers/hwmon/peci/cputemp.c
+++ b/drivers/hwmon/peci/cputemp.c
@@ -447,29 +447,23 @@ static const struct hwmon_ops peci_cputemp_ops = {
.read = cputemp_read,
};
-static const u32 peci_cputemp_temp_channel_config[] = {
- /* Die temperature */
- HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST,
- /* DTS margin */
- HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST,
- /* Tcontrol temperature */
- HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
- /* Tthrottle temperature */
- HWMON_T_LABEL | HWMON_T_INPUT,
- /* Tjmax temperature */
- HWMON_T_LABEL | HWMON_T_INPUT,
- /* Core temperature - for all core channels */
- [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | HWMON_T_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info peci_cputemp_temp_channel = {
- .type = hwmon_temp,
- .config = peci_cputemp_temp_channel_config,
-};
-
static const struct hwmon_channel_info *peci_cputemp_info[] = {
- &peci_cputemp_temp_channel,
+ HWMON_CHANNEL_INFO(temp,
+ /* Die temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST,
+ /* DTS margin */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST,
+ /* Tcontrol temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+ /* Tthrottle temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ /* Tjmax temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ /* Core temperature - for all core channels */
+ [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] =
+ HWMON_T_LABEL | HWMON_T_INPUT),
NULL
};
diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c
index c8222354c0056b..0a633bda36689b 100644
--- a/drivers/hwmon/peci/dimmtemp.c
+++ b/drivers/hwmon/peci/dimmtemp.c
@@ -4,6 +4,7 @@
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/devm-helpers.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
@@ -219,7 +220,7 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
int chan_rank_max = priv->gen_info->chan_rank_max;
int dimm_idx_max = priv->gen_info->dimm_idx_max;
u32 chan_rank_empty = 0;
- u64 dimm_mask = 0;
+ u32 dimm_mask = 0;
int chan_rank, dimm_idx, ret;
u32 pcs;
@@ -278,9 +279,9 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
return -EAGAIN;
}
- dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask);
+ dev_dbg(priv->dev, "Scanned populated DIMMs: %#x\n", dimm_mask);
- bitmap_from_u64(priv->dimm_mask, dimm_mask);
+ bitmap_from_arr32(priv->dimm_mask, &dimm_mask, DIMM_NUMS_MAX);
return 0;
}
@@ -299,18 +300,10 @@ static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan)
return 0;
}
-static const u32 peci_dimmtemp_temp_channel_config[] = {
- [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT,
- 0
-};
-
-static const struct hwmon_channel_info peci_dimmtemp_temp_channel = {
- .type = hwmon_temp,
- .config = peci_dimmtemp_temp_channel_config,
-};
-
static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = {
- &peci_dimmtemp_temp_channel,
+ HWMON_CHANNEL_INFO(temp,
+ [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL |
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT),
NULL
};
@@ -378,13 +371,6 @@ static void create_dimm_temp_info_delayed(struct work_struct *work)
dev_err(priv->dev, "Failed to populate DIMM temp info\n");
}
-static void remove_delayed_work(void *_priv)
-{
- struct peci_dimmtemp *priv = _priv;
-
- cancel_delayed_work_sync(&priv->detect_work);
-}
-
static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
{
struct device *dev = &adev->dev;
@@ -415,9 +401,8 @@ static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxil
"Unexpected PECI revision %#x, some features may be unavailable\n",
peci_dev->info.peci_revision);
- INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed);
-
- ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, priv);
+ ret = devm_delayed_work_autocancel(priv->dev, &priv->detect_work,
+ create_dimm_temp_info_delayed);
if (ret)
return ret;
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a2ea1d5a87650d..dfae76db65aef2 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -228,10 +228,10 @@ config SENSORS_MAX16064
be called max16064.
config SENSORS_MAX16601
- tristate "Maxim MAX16508, MAX16601"
+ tristate "Maxim MAX16508, MAX16601, MAX16602"
help
If you say yes here you get hardware monitoring support for Maxim
- MAX16508 and MAX16601.
+ MAX16508, MAX16601 and MAX16602.
This driver can also be built as a module. If so, the module will
be called max16601.
@@ -408,6 +408,15 @@ config SENSORS_UCD9200
This driver can also be built as a module. If so, the module will
be called ucd9200.
+config SENSORS_XDPE152
+ tristate "Infineon XDPE152 family"
+ help
+ If you say yes here you get hardware monitoring support for Infineon
+ XDPE15284, XDPE152C4, device.
+
+ This driver can also be built as a module. If so, the module will
+ be called xdpe152c4.
+
config SENSORS_XDPE122
tristate "Infineon XDPE122 family"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index a4a96ac71de799..4678fba5012c7f 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -43,5 +43,6 @@ obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
+obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 0127273883f042..531aa674a92833 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -196,6 +196,17 @@ static int ltc_read_byte_data(struct i2c_client *client, int page, int reg)
return pmbus_read_byte_data(client, page, reg);
}
+static int ltc_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
+{
+ int ret;
+
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_write_byte_data(client, page, reg, value);
+}
+
static int ltc_write_byte(struct i2c_client *client, int page, u8 byte)
{
int ret;
@@ -681,6 +692,7 @@ static int ltc2978_probe(struct i2c_client *client)
info = &data->info;
info->write_word_data = ltc2978_write_word_data;
info->write_byte = ltc_write_byte;
+ info->write_byte_data = ltc_write_byte_data;
info->read_word_data = ltc_read_word_data;
info->read_byte_data = ltc_read_byte_data;
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
index 5a226a564776c9..b628405e6586c3 100644
--- a/drivers/hwmon/pmbus/max16601.c
+++ b/drivers/hwmon/pmbus/max16601.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Hardware monitoring driver for Maxim MAX16508 and MAX16601.
+ * Hardware monitoring driver for Maxim MAX16508, MAX16601 and MAX16602.
*
* Implementation notes:
*
@@ -31,7 +31,7 @@
#include "pmbus.h"
-enum chips { max16508, max16601 };
+enum chips { max16508, max16601, max16602 };
#define REG_DEFAULT_NUM_POP 0xc4
#define REG_SETPT_DVID 0xd1
@@ -202,7 +202,7 @@ static int max16601_identify(struct i2c_client *client,
else
info->vrm_version[0] = vr12;
- if (data->id != max16601)
+ if (data->id != max16601 && data->id != max16602)
return 0;
reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP);
@@ -264,6 +264,7 @@ static void max16601_remove(void *_data)
static const struct i2c_device_id max16601_id[] = {
{"max16508", max16508},
{"max16601", max16601},
+ {"max16602", max16602},
{}
};
MODULE_DEVICE_TABLE(i2c, max16601_id);
@@ -280,13 +281,15 @@ static int max16601_get_id(struct i2c_client *client)
return -ENODEV;
/*
- * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx"
- * or "MAX16500y.xx".
+ * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" or
+ * MAX16602y.xx or "MAX16500y.xx".cdxxcccccccccc
*/
if (!strncmp(buf, "MAX16500", 8)) {
id = max16508;
} else if (!strncmp(buf, "MAX16601", 8)) {
id = max16601;
+ } else if (!strncmp(buf, "MAX16602", 8)) {
+ id = max16602;
} else {
buf[ret] = '\0';
dev_err(dev, "Unsupported chip '%s'\n", buf);
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index e74b6ef070f3ea..c031a9700ace9b 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -438,6 +438,8 @@ struct pmbus_driver_info {
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
int (*read_word_data)(struct i2c_client *client, int page, int phase,
int reg);
+ int (*write_byte_data)(struct i2c_client *client, int page, int reg,
+ u8 byte);
int (*write_word_data)(struct i2c_client *client, int page, int reg,
u16 word);
int (*write_byte)(struct i2c_client *client, int page, u8 value);
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index d93574d6a1fb63..02912022853d80 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -19,6 +19,8 @@
#include <linux/pmbus.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#include <linux/of.h>
+#include <linux/thermal.h>
#include "pmbus.h"
/*
@@ -276,6 +278,42 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
return pmbus_write_word_data(client, page, reg, word);
}
+/*
+ * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_byte_data) {
+ status = info->write_byte_data(client, page, reg, value);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_write_byte_data(client, page, reg, value);
+}
+
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->read_byte_data) {
+ status = info->read_byte_data(client, page, reg);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_read_byte_data(client, page, reg);
+}
+
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command)
{
@@ -283,14 +321,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
int rv;
u8 to;
- from = pmbus_read_byte_data(client, page,
+ from = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
if (from < 0)
return from;
to = (from & ~mask) | (config & mask);
if (to != from) {
- rv = pmbus_write_byte_data(client, page,
+ rv = _pmbus_write_byte_data(client, page,
pmbus_fan_config_registers[id], to);
if (rv < 0)
return rv;
@@ -390,37 +428,19 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
unsigned int tmp;
int rv;
- rv = pmbus_read_byte_data(client, page, reg);
+ rv = _pmbus_read_byte_data(client, page, reg);
if (rv < 0)
return rv;
tmp = (rv & ~mask) | (value & mask);
if (tmp != rv)
- rv = pmbus_write_byte_data(client, page, reg, tmp);
+ rv = _pmbus_write_byte_data(client, page, reg, tmp);
return rv;
}
EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
-/*
- * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
- * a device specific mapping function exists and calls it if necessary.
- */
-static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
-{
- struct pmbus_data *data = i2c_get_clientdata(client);
- const struct pmbus_driver_info *info = data->info;
- int status;
-
- if (info->read_byte_data) {
- status = info->read_byte_data(client, page, reg);
- if (status != -ENODATA)
- return status;
- }
- return pmbus_read_byte_data(client, page, reg);
-}
-
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
int reg)
{
@@ -455,7 +475,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
return s->data;
}
- config = pmbus_read_byte_data(client, page,
+ config = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
if (config < 0)
return config;
@@ -912,7 +932,7 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
regval = status & mask;
if (regval) {
- ret = pmbus_write_byte_data(client, page, reg, regval);
+ ret = _pmbus_write_byte_data(client, page, reg, regval);
if (ret)
goto unlock;
}
@@ -1083,6 +1103,68 @@ static int pmbus_add_boolean(struct pmbus_data *data,
return pmbus_add_attribute(data, &a->dev_attr.attr);
}
+/* of thermal for pmbus temperature sensors */
+struct pmbus_thermal_data {
+ struct pmbus_data *pmbus_data;
+ struct pmbus_sensor *sensor;
+};
+
+static int pmbus_thermal_get_temp(void *data, int *temp)
+{
+ struct pmbus_thermal_data *tdata = data;
+ struct pmbus_sensor *sensor = tdata->sensor;
+ struct pmbus_data *pmbus_data = tdata->pmbus_data;
+ struct i2c_client *client = to_i2c_client(pmbus_data->dev);
+ struct device *dev = pmbus_data->hwmon_dev;
+ int ret = 0;
+
+ if (!dev) {
+ /* May not even get to hwmon yet */
+ *temp = 0;
+ return 0;
+ }
+
+ mutex_lock(&pmbus_data->update_lock);
+ pmbus_update_sensor_data(client, sensor);
+ if (sensor->data < 0)
+ ret = sensor->data;
+ else
+ *temp = (int)pmbus_reg2data(pmbus_data, sensor);
+ mutex_unlock(&pmbus_data->update_lock);
+
+ return ret;
+}
+
+static const struct thermal_zone_of_device_ops pmbus_thermal_ops = {
+ .get_temp = pmbus_thermal_get_temp,
+};
+
+static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data,
+ struct pmbus_sensor *sensor, int index)
+{
+ struct device *dev = pmbus_data->dev;
+ struct pmbus_thermal_data *tdata;
+ struct thermal_zone_device *tzd;
+
+ tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
+ if (!tdata)
+ return -ENOMEM;
+
+ tdata->sensor = sensor;
+ tdata->pmbus_data = pmbus_data;
+
+ tzd = devm_thermal_zone_of_sensor_register(dev, index, tdata,
+ &pmbus_thermal_ops);
+ /*
+ * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV,
+ * so ignore that error but forward any other error.
+ */
+ if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
+ return PTR_ERR(tzd);
+
+ return 0;
+}
+
static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
const char *name, const char *type,
int seq, int page, int phase,
@@ -1126,6 +1208,10 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
sensor->next = data->sensors;
data->sensors = sensor;
+ /* temperature sensors with _input values are registered with thermal */
+ if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0)
+ pmbus_thermal_add_sensor(data, sensor, seq);
+
return sensor;
}
@@ -2309,6 +2395,21 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
int page, ret;
/*
+ * Figure out if PEC is enabled before accessing any other register.
+ * Make sure PEC is disabled, will be enabled later if needed.
+ */
+ client->flags &= ~I2C_CLIENT_PEC;
+
+ /* Enable PEC if the controller and bus supports it */
+ if (!(data->flags & PMBUS_NO_CAPABILITY)) {
+ ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
+ if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
+ client->flags |= I2C_CLIENT_PEC;
+ }
+ }
+
+ /*
* Some PMBus chips don't support PMBUS_STATUS_WORD, so try
* to use PMBUS_STATUS_BYTE instead if that is the case.
* Bail out if both registers are not supported.
@@ -2326,19 +2427,6 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
data->has_status_word = true;
}
- /* Make sure PEC is disabled, will be enabled later if needed */
- client->flags &= ~I2C_CLIENT_PEC;
-
- /* Enable PEC if the controller and bus supports it */
- if (!(data->flags & PMBUS_NO_CAPABILITY)) {
- ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
- if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
- if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) {
- client->flags |= I2C_CLIENT_PEC;
- }
- }
- }
-
/*
* Check if the chip is write protected. If it is, we can not clear
* faults, and we should not try it. Also, in that case, writes into
@@ -2399,7 +2487,7 @@ static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
int ret;
mutex_lock(&data->update_lock);
- ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
mutex_unlock(&data->update_lock);
if (ret < 0)
@@ -2498,7 +2586,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
if (!(func & cat->func))
continue;
- status = pmbus_read_byte_data(client, page, cat->reg);
+ status = _pmbus_read_byte_data(client, page, cat->reg);
if (status < 0) {
mutex_unlock(&data->update_lock);
return status;
@@ -2548,11 +2636,78 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
return 0;
}
+static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ struct pmbus_sensor s = {
+ .page = rdev_get_id(rdev),
+ .class = PSC_VOLTAGE_OUT,
+ .convert = true,
+ };
+
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
+ if (s.data < 0)
+ return s.data;
+
+ return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
+}
+
+static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
+ int max_uv, unsigned int *selector)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ struct pmbus_sensor s = {
+ .page = rdev_get_id(rdev),
+ .class = PSC_VOLTAGE_OUT,
+ .convert = true,
+ .data = -1,
+ };
+ int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
+ int low, high;
+
+ *selector = 0;
+
+ if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
+ if (s.data < 0) {
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
+ if (s.data < 0)
+ return s.data;
+ }
+ low = pmbus_reg2data(data, &s);
+
+ s.data = -1;
+ if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
+ if (s.data < 0) {
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
+ if (s.data < 0)
+ return s.data;
+ }
+ high = pmbus_reg2data(data, &s);
+
+ /* Make sure we are within margins */
+ if (low > val)
+ val = low;
+ if (high < val)
+ val = high;
+
+ val = pmbus_data2reg(data, &s, val);
+
+ return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
+}
+
const struct regulator_ops pmbus_regulator_ops = {
.enable = pmbus_regulator_enable,
.disable = pmbus_regulator_disable,
.is_enabled = pmbus_regulator_is_enabled,
.get_error_flags = pmbus_regulator_get_error_flags,
+ .get_voltage = pmbus_regulator_get_voltage,
+ .set_voltage = pmbus_regulator_set_voltage,
};
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c
new file mode 100644
index 00000000000000..b8a36ef73e4540
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdpe152c4.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers
+ *
+ * Copyright (c) 2022 Infineon Technologies. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define XDPE152_PAGE_NUM 2
+
+static struct pmbus_driver_info xdpe152_info = {
+ .pages = XDPE152_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+};
+
+static int xdpe152_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+
+ info = devm_kmemdup(&client->dev, &xdpe152_info, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id xdpe152_id[] = {
+ {"xdpe152c4", 0},
+ {"xdpe15284", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, xdpe152_id);
+
+static const struct of_device_id __maybe_unused xdpe152_of_match[] = {
+ {.compatible = "infineon,xdpe152c4"},
+ {.compatible = "infineon,xdpe15284"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, xdpe152_of_match);
+
+static struct i2c_driver xdpe152_driver = {
+ .driver = {
+ .name = "xdpe152c4",
+ .of_match_table = of_match_ptr(xdpe152_of_match),
+ },
+ .probe_new = xdpe152_probe,
+ .id_table = xdpe152_id,
+};
+
+module_i2c_driver(xdpe152_driver);
+
+MODULE_AUTHOR("Greg Schwendimann <greg.schwendimann@infineon.com>");
+MODULE_DESCRIPTION("PMBus driver for Infineon XDPE152 family");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index f12b9a28a232d4..6c08551d8d140e 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -49,16 +49,6 @@ struct pwm_fan_ctx {
struct hwmon_channel_info fan_channel;
};
-static const u32 pwm_fan_channel_config_pwm[] = {
- HWMON_PWM_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info pwm_fan_channel_pwm = {
- .type = hwmon_pwm,
- .config = pwm_fan_channel_config_pwm,
-};
-
/* This handler assumes self resetting edge triggered interrupt. */
static irqreturn_t pulse_handler(int irq, void *dev_id)
{
@@ -387,7 +377,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (!channels)
return -ENOMEM;
- channels[0] = &pwm_fan_channel_pwm;
+ channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT);
for (i = 0; i < ctx->tach_count; i++) {
struct pwm_fan_tach *tach = &ctx->tachs[i];
diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c
index e48f58ec5b9cf1..9ce4899a81a55c 100644
--- a/drivers/hwmon/sl28cpld-hwmon.c
+++ b/drivers/hwmon/sl28cpld-hwmon.c
@@ -54,7 +54,7 @@ static int sl28cpld_hwmon_read(struct device *dev,
/*
* The counter period is 1000ms and the sysfs specification
- * says we should asssume 2 pulses per revolution.
+ * says we should assume 2 pulses per revolution.
*/
value *= 60 / 2;
@@ -67,18 +67,8 @@ static int sl28cpld_hwmon_read(struct device *dev,
return 0;
}
-static const u32 sl28cpld_hwmon_fan_config[] = {
- HWMON_F_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info sl28cpld_hwmon_fan = {
- .type = hwmon_fan,
- .config = sl28cpld_hwmon_fan_config,
-};
-
static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = {
- &sl28cpld_hwmon_fan,
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
NULL
};
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 52c9e7d3f2ae78..cc0a1c219b1f96 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -41,6 +41,8 @@ enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
#define TMP401_STATUS 0x02
#define TMP401_CONFIG 0x03
#define TMP401_CONVERSION_RATE 0x04
+#define TMP4XX_N_FACTOR_REG 0x18
+#define TMP43X_BETA_RANGE 0x25
#define TMP401_TEMP_CRIT_HYST 0x21
#define TMP401_MANUFACTURER_ID_REG 0xFE
#define TMP401_DEVICE_ID_REG 0xFF
@@ -543,6 +545,8 @@ static int tmp401_init_client(struct tmp401_data *data)
struct regmap *regmap = data->regmap;
u32 config, config_orig;
int ret;
+ u32 val = 0;
+ s32 nfactor = 0;
/* Set conversion rate to 2 Hz */
ret = regmap_write(regmap, TMP401_CONVERSION_RATE, 5);
@@ -557,12 +561,50 @@ static int tmp401_init_client(struct tmp401_data *data)
config_orig = config;
config &= ~TMP401_CONFIG_SHUTDOWN;
+ if (of_property_read_bool(data->client->dev.of_node, "ti,extended-range-enable")) {
+ /* Enable measurement over extended temperature range */
+ config |= TMP401_CONFIG_RANGE;
+ }
+
data->extended_range = !!(config & TMP401_CONFIG_RANGE);
- if (config != config_orig)
+ if (config != config_orig) {
ret = regmap_write(regmap, TMP401_CONFIG, config);
+ if (ret < 0)
+ return ret;
+ }
- return ret;
+ ret = of_property_read_u32(data->client->dev.of_node, "ti,n-factor", &nfactor);
+ if (!ret) {
+ if (data->kind == tmp401) {
+ dev_err(&data->client->dev, "ti,tmp401 does not support n-factor correction\n");
+ return -EINVAL;
+ }
+ if (nfactor < -128 || nfactor > 127) {
+ dev_err(&data->client->dev, "n-factor is invalid (%d)\n", nfactor);
+ return -EINVAL;
+ }
+ ret = regmap_write(regmap, TMP4XX_N_FACTOR_REG, (unsigned int)nfactor);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = of_property_read_u32(data->client->dev.of_node, "ti,beta-compensation", &val);
+ if (!ret) {
+ if (data->kind == tmp401 || data->kind == tmp411) {
+ dev_err(&data->client->dev, "ti,tmp401 or ti,tmp411 does not support beta compensation\n");
+ return -EINVAL;
+ }
+ if (val > 15) {
+ dev_err(&data->client->dev, "beta-compensation is invalid (%u)\n", val);
+ return -EINVAL;
+ }
+ ret = regmap_write(regmap, TMP43X_BETA_RANGE, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
}
static int tmp401_detect(struct i2c_client *client,
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index ad03262cca5692..09e49ec8b6f489 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -149,8 +149,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
INIT_LIST_HEAD(&hwmon->tz_list);
strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
strreplace(hwmon->type, '-', '_');
- hwmon->device = hwmon_device_register_with_info(&tz->device, hwmon->type,
- hwmon, NULL, NULL);
+ hwmon->device = hwmon_device_register_for_thermal(&tz->device,
+ hwmon->type, hwmon);
if (IS_ERR(hwmon->device)) {
result = PTR_ERR(hwmon->device);
goto free_mem;
@@ -277,3 +277,5 @@ int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
return ret;
}
EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);
+
+MODULE_IMPORT_NS(HWMON_THERMAL);
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index eba380b76d1576..14325f93c6b2bb 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -450,6 +450,9 @@ hwmon_device_register_with_info(struct device *dev,
const struct hwmon_chip_info *info,
const struct attribute_group **extra_groups);
struct device *
+hwmon_device_register_for_thermal(struct device *dev, const char *name,
+ void *drvdata);
+struct device *
devm_hwmon_device_register_with_info(struct device *dev,
const char *name, void *drvdata,
const struct hwmon_chip_info *info,
@@ -461,6 +464,9 @@ void devm_hwmon_device_unregister(struct device *dev);
int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel);
+char *hwmon_sanitize_name(const char *name);
+char *devm_hwmon_sanitize_name(struct device *dev, const char *name);
+
/**
* hwmon_is_bad_char - Is the char invalid in a hwmon name
* @ch: the char to be considered
diff --git a/include/linux/polynomial.h b/include/linux/polynomial.h
new file mode 100644
index 00000000000000..9e074a0bb6fa71
--- /dev/null
+++ b/include/linux/polynomial.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ */
+
+#ifndef _POLYNOMIAL_H
+#define _POLYNOMIAL_H
+
+/*
+ * struct polynomial_term - one term descriptor of a polynomial
+ * @deg: degree of the term.
+ * @coef: multiplication factor of the term.
+ * @divider: distributed divider per each degree.
+ * @divider_leftover: divider leftover, which couldn't be redistributed.
+ */
+struct polynomial_term {
+ unsigned int deg;
+ long coef;
+ long divider;
+ long divider_leftover;
+};
+
+/*
+ * struct polynomial - a polynomial descriptor
+ * @total_divider: total data divider.
+ * @terms: polynomial terms, last term must have degree of 0
+ */
+struct polynomial {
+ long total_divider;
+ struct polynomial_term terms[];
+};
+
+long polynomial_calc(const struct polynomial *poly, long data);
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index 087e06b4cdfdeb..6a843639814fbf 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -737,3 +737,6 @@ config PLDMFW
config ASN1_ENCODER
tristate
+
+config POLYNOMIAL
+ tristate
diff --git a/lib/Makefile b/lib/Makefile
index 6b9ffc1bd1eed2..89fcae891361f0 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -263,6 +263,8 @@ obj-$(CONFIG_MEMREGION) += memregion.o
obj-$(CONFIG_STMP_DEVICE) += stmp_device.o
obj-$(CONFIG_IRQ_POLL) += irq_poll.o
+obj-$(CONFIG_POLYNOMIAL) += polynomial.o
+
# stackdepot.c should not be instrumented or call instrumented functions.
# Prevent the compiler from calling builtins like memcmp() or bcmp() from this
# file.
diff --git a/lib/polynomial.c b/lib/polynomial.c
new file mode 100644
index 00000000000000..66d383445fec9f
--- /dev/null
+++ b/lib/polynomial.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic polynomial calculation using integer coefficients.
+ *
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/polynomial.h>
+
+/*
+ * Originally this was part of drivers/hwmon/bt1-pvt.c.
+ * There the following conversion is used and should serve as an example here:
+ *
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * to PVT data and vice-versa are following:
+ *
+ * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
+ * 1.7204e2
+ * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
+ * 3.1020e-1*(N^1) - 4.838e1
+ *
+ * where T = [-48.380, 147.438]C and N = [0, 1023].
+ *
+ * They must be accordingly altered to be suitable for the integer arithmetics.
+ * The technique is called 'factor redistribution', which just makes sure the
+ * multiplications and divisions are made so to have a result of the operations
+ * within the integer numbers limit. In addition we need to translate the
+ * formulae to accept millidegrees of Celsius. Here what they look like after
+ * the alterations:
+ *
+ * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
+ * 17204e2) / 1e4
+ * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
+ * 48380
+ * where T = [-48380, 147438] mC and N = [0, 1023].
+ *
+ * static const struct polynomial poly_temp_to_N = {
+ * .total_divider = 10000,
+ * .terms = {
+ * {4, 18322, 10000, 10000},
+ * {3, 2343, 10000, 10},
+ * {2, 87018, 10000, 10},
+ * {1, 39269, 1000, 1},
+ * {0, 1720400, 1, 1}
+ * }
+ * };
+ *
+ * static const struct polynomial poly_N_to_temp = {
+ * .total_divider = 1,
+ * .terms = {
+ * {4, -16743, 1000, 1},
+ * {3, 81542, 1000, 1},
+ * {2, -182010, 1000, 1},
+ * {1, 310200, 1000, 1},
+ * {0, -48380, 1, 1}
+ * }
+ * };
+ */
+
+/**
+ * polynomial_calc - calculate a polynomial using integer arithmetic
+ *
+ * @poly: pointer to the descriptor of the polynomial
+ * @data: input value of the polynimal
+ *
+ * Calculate the result of a polynomial using only integer arithmetic. For
+ * this to work without too much loss of precision the coefficients has to
+ * be altered. This is called factor redistribution.
+ *
+ * Returns the result of the polynomial calculation.
+ */
+long polynomial_calc(const struct polynomial *poly, long data)
+{
+ const struct polynomial_term *term = poly->terms;
+ long total_divider = poly->total_divider ?: 1;
+ long tmp, ret = 0;
+ int deg;
+
+ /*
+ * Here is the polynomial calculation function, which performs the
+ * redistributed terms calculations. It's pretty straightforward.
+ * We walk over each degree term up to the free one, and perform
+ * the redistributed multiplication of the term coefficient, its
+ * divider (as for the rationale fraction representation), data
+ * power and the rational fraction divider leftover. Then all of
+ * this is collected in a total sum variable, which value is
+ * normalized by the total divider before being returned.
+ */
+ do {
+ tmp = term->coef;
+ for (deg = 0; deg < term->deg; ++deg)
+ tmp = mult_frac(tmp, data, term->divider);
+ ret += tmp / term->divider_leftover;
+ } while ((term++)->deg);
+
+ return ret / total_divider;
+}
+EXPORT_SYMBOL_GPL(polynomial_calc);
+
+MODULE_DESCRIPTION("Generic polynomial calculations");
+MODULE_LICENSE("GPL");