xref: /linux/drivers/hwmon/asus-ec-sensors.c (revision f7ac3020)
1d0ddfd24SEugene Shalygin // SPDX-License-Identifier: GPL-2.0+
2d0ddfd24SEugene Shalygin /*
3d0ddfd24SEugene Shalygin  * HWMON driver for ASUS motherboards that publish some sensor values
4d0ddfd24SEugene Shalygin  * via the embedded controller registers.
5d0ddfd24SEugene Shalygin  *
6d0ddfd24SEugene Shalygin  * Copyright (C) 2021 Eugene Shalygin <eugene.shalygin@gmail.com>
7d0ddfd24SEugene Shalygin 
8d0ddfd24SEugene Shalygin  * EC provides:
9d0ddfd24SEugene Shalygin  * - Chipset temperature
10d0ddfd24SEugene Shalygin  * - CPU temperature
11d0ddfd24SEugene Shalygin  * - Motherboard temperature
12d0ddfd24SEugene Shalygin  * - T_Sensor temperature
13d0ddfd24SEugene Shalygin  * - VRM temperature
14d0ddfd24SEugene Shalygin  * - Water In temperature
15d0ddfd24SEugene Shalygin  * - Water Out temperature
16d0ddfd24SEugene Shalygin  * - CPU Optional fan RPM
17d0ddfd24SEugene Shalygin  * - Chipset fan RPM
18d0ddfd24SEugene Shalygin  * - VRM Heat Sink fan RPM
19d0ddfd24SEugene Shalygin  * - Water Flow fan RPM
20d0ddfd24SEugene Shalygin  * - CPU current
21f545a2fdSEugene Shalygin  * - CPU core voltage
22d0ddfd24SEugene Shalygin  */
23d0ddfd24SEugene Shalygin 
24d0ddfd24SEugene Shalygin #include <linux/acpi.h>
25d0ddfd24SEugene Shalygin #include <linux/bitops.h>
26d0ddfd24SEugene Shalygin #include <linux/dev_printk.h>
27d0ddfd24SEugene Shalygin #include <linux/dmi.h>
28d0ddfd24SEugene Shalygin #include <linux/hwmon.h>
29d0ddfd24SEugene Shalygin #include <linux/init.h>
30d0ddfd24SEugene Shalygin #include <linux/jiffies.h>
31d0ddfd24SEugene Shalygin #include <linux/kernel.h>
32d0ddfd24SEugene Shalygin #include <linux/module.h>
33d0ddfd24SEugene Shalygin #include <linux/platform_device.h>
34d0ddfd24SEugene Shalygin #include <linux/sort.h>
35d0ddfd24SEugene Shalygin #include <linux/units.h>
36d0ddfd24SEugene Shalygin 
37d0ddfd24SEugene Shalygin #include <asm/unaligned.h>
38d0ddfd24SEugene Shalygin 
39d0ddfd24SEugene Shalygin static char *mutex_path_override;
40d0ddfd24SEugene Shalygin 
41d0ddfd24SEugene Shalygin /* Writing to this EC register switches EC bank */
42d0ddfd24SEugene Shalygin #define ASUS_EC_BANK_REGISTER	0xff
43d0ddfd24SEugene Shalygin #define SENSOR_LABEL_LEN	16
44d0ddfd24SEugene Shalygin 
45d0ddfd24SEugene Shalygin /*
46d0ddfd24SEugene Shalygin  * Arbitrary set max. allowed bank number. Required for sorting banks and
47d0ddfd24SEugene Shalygin  * currently is overkill with just 2 banks used at max, but for the sake
48d0ddfd24SEugene Shalygin  * of alignment let's set it to a higher value.
49d0ddfd24SEugene Shalygin  */
50d0ddfd24SEugene Shalygin #define ASUS_EC_MAX_BANK	3
51d0ddfd24SEugene Shalygin 
52d0ddfd24SEugene Shalygin #define ACPI_LOCK_DELAY_MS	500
53d0ddfd24SEugene Shalygin 
54d0ddfd24SEugene Shalygin /* ACPI mutex for locking access to the EC for the firmware */
55d0ddfd24SEugene Shalygin #define ASUS_HW_ACCESS_MUTEX_ASMX	"\\AMW0.ASMX"
56d0ddfd24SEugene Shalygin 
57bae26b80SShady Nawara #define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX	"\\RMTW.ASMX"
58bae26b80SShady Nawara 
599992b19dSUrs Schroffenegger #define ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0 "\\_SB_.PCI0.SBRG.SIO1.MUT0"
609992b19dSUrs Schroffenegger 
615b4285c5SEugene Shalygin #define MAX_IDENTICAL_BOARD_VARIATIONS	3
62d0ddfd24SEugene Shalygin 
63de8fbac5SEugene Shalygin /* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
64de8fbac5SEugene Shalygin #define ACPI_GLOBAL_LOCK_PSEUDO_PATH	":GLOBAL_LOCK"
65de8fbac5SEugene Shalygin 
66d0ddfd24SEugene Shalygin typedef union {
67d0ddfd24SEugene Shalygin 	u32 value;
68d0ddfd24SEugene Shalygin 	struct {
69d0ddfd24SEugene Shalygin 		u8 index;
70d0ddfd24SEugene Shalygin 		u8 bank;
71d0ddfd24SEugene Shalygin 		u8 size;
72d0ddfd24SEugene Shalygin 		u8 dummy;
73d0ddfd24SEugene Shalygin 	} components;
74d0ddfd24SEugene Shalygin } sensor_address;
75d0ddfd24SEugene Shalygin 
76d0ddfd24SEugene Shalygin #define MAKE_SENSOR_ADDRESS(size, bank, index) {                               \
77d0ddfd24SEugene Shalygin 		.value = (size << 16) + (bank << 8) + index                    \
78d0ddfd24SEugene Shalygin 	}
79d0ddfd24SEugene Shalygin 
80d0ddfd24SEugene Shalygin static u32 hwmon_attributes[hwmon_max] = {
81d0ddfd24SEugene Shalygin 	[hwmon_chip] = HWMON_C_REGISTER_TZ,
82d0ddfd24SEugene Shalygin 	[hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
83d0ddfd24SEugene Shalygin 	[hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
84d0ddfd24SEugene Shalygin 	[hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
85d0ddfd24SEugene Shalygin 	[hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL,
86d0ddfd24SEugene Shalygin };
87d0ddfd24SEugene Shalygin 
88d0ddfd24SEugene Shalygin struct ec_sensor_info {
89d0ddfd24SEugene Shalygin 	char label[SENSOR_LABEL_LEN];
90d0ddfd24SEugene Shalygin 	enum hwmon_sensor_types type;
91d0ddfd24SEugene Shalygin 	sensor_address addr;
92d0ddfd24SEugene Shalygin };
93d0ddfd24SEugene Shalygin 
94d0ddfd24SEugene Shalygin #define EC_SENSOR(sensor_label, sensor_type, size, bank, index) {              \
95d0ddfd24SEugene Shalygin 		.label = sensor_label, .type = sensor_type,                    \
96d0ddfd24SEugene Shalygin 		.addr = MAKE_SENSOR_ADDRESS(size, bank, index),                \
97d0ddfd24SEugene Shalygin 	}
98d0ddfd24SEugene Shalygin 
99d0ddfd24SEugene Shalygin enum ec_sensors {
100d0ddfd24SEugene Shalygin 	/* chipset temperature [℃] */
101d0ddfd24SEugene Shalygin 	ec_sensor_temp_chipset,
102d0ddfd24SEugene Shalygin 	/* CPU temperature [℃] */
103d0ddfd24SEugene Shalygin 	ec_sensor_temp_cpu,
104790dec13SMichael Carns 	/* CPU package temperature [℃] */
105790dec13SMichael Carns 	ec_sensor_temp_cpu_package,
106d0ddfd24SEugene Shalygin 	/* motherboard temperature [℃] */
107d0ddfd24SEugene Shalygin 	ec_sensor_temp_mb,
108d0ddfd24SEugene Shalygin 	/* "T_Sensor" temperature sensor reading [℃] */
109d0ddfd24SEugene Shalygin 	ec_sensor_temp_t_sensor,
110d0ddfd24SEugene Shalygin 	/* VRM temperature [℃] */
111d0ddfd24SEugene Shalygin 	ec_sensor_temp_vrm,
112f545a2fdSEugene Shalygin 	/* CPU Core voltage [mV] */
113f545a2fdSEugene Shalygin 	ec_sensor_in_cpu_core,
114d0ddfd24SEugene Shalygin 	/* CPU_Opt fan [RPM] */
115d0ddfd24SEugene Shalygin 	ec_sensor_fan_cpu_opt,
116d0ddfd24SEugene Shalygin 	/* VRM heat sink fan [RPM] */
117d0ddfd24SEugene Shalygin 	ec_sensor_fan_vrm_hs,
118d0ddfd24SEugene Shalygin 	/* Chipset fan [RPM] */
119d0ddfd24SEugene Shalygin 	ec_sensor_fan_chipset,
120d0ddfd24SEugene Shalygin 	/* Water flow sensor reading [RPM] */
121d0ddfd24SEugene Shalygin 	ec_sensor_fan_water_flow,
122d0ddfd24SEugene Shalygin 	/* CPU current [A] */
123d0ddfd24SEugene Shalygin 	ec_sensor_curr_cpu,
124d0ddfd24SEugene Shalygin 	/* "Water_In" temperature sensor reading [℃] */
125d0ddfd24SEugene Shalygin 	ec_sensor_temp_water_in,
126d0ddfd24SEugene Shalygin 	/* "Water_Out" temperature sensor reading [℃] */
127d0ddfd24SEugene Shalygin 	ec_sensor_temp_water_out,
1289992b19dSUrs Schroffenegger 	/* "Water_Block_In" temperature sensor reading [℃] */
1299992b19dSUrs Schroffenegger 	ec_sensor_temp_water_block_in,
1309992b19dSUrs Schroffenegger 	/* "Water_Block_Out" temperature sensor reading [℃] */
1319992b19dSUrs Schroffenegger 	ec_sensor_temp_water_block_out,
1329992b19dSUrs Schroffenegger 	/* "T_sensor_2" temperature sensor reading [℃] */
1339992b19dSUrs Schroffenegger 	ec_sensor_temp_t_sensor_2,
1349992b19dSUrs Schroffenegger 	/* "Extra_1" temperature sensor reading [℃] */
1359992b19dSUrs Schroffenegger 	ec_sensor_temp_sensor_extra_1,
1369992b19dSUrs Schroffenegger 	/* "Extra_2" temperature sensor reading [℃] */
1379992b19dSUrs Schroffenegger 	ec_sensor_temp_sensor_extra_2,
1389992b19dSUrs Schroffenegger 	/* "Extra_3" temperature sensor reading [℃] */
1399992b19dSUrs Schroffenegger 	ec_sensor_temp_sensor_extra_3,
140d0ddfd24SEugene Shalygin };
141d0ddfd24SEugene Shalygin 
142d0ddfd24SEugene Shalygin #define SENSOR_TEMP_CHIPSET BIT(ec_sensor_temp_chipset)
143d0ddfd24SEugene Shalygin #define SENSOR_TEMP_CPU BIT(ec_sensor_temp_cpu)
144790dec13SMichael Carns #define SENSOR_TEMP_CPU_PACKAGE BIT(ec_sensor_temp_cpu_package)
145d0ddfd24SEugene Shalygin #define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb)
146d0ddfd24SEugene Shalygin #define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor)
147d0ddfd24SEugene Shalygin #define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm)
148f545a2fdSEugene Shalygin #define SENSOR_IN_CPU_CORE BIT(ec_sensor_in_cpu_core)
149d0ddfd24SEugene Shalygin #define SENSOR_FAN_CPU_OPT BIT(ec_sensor_fan_cpu_opt)
150d0ddfd24SEugene Shalygin #define SENSOR_FAN_VRM_HS BIT(ec_sensor_fan_vrm_hs)
151d0ddfd24SEugene Shalygin #define SENSOR_FAN_CHIPSET BIT(ec_sensor_fan_chipset)
152d0ddfd24SEugene Shalygin #define SENSOR_FAN_WATER_FLOW BIT(ec_sensor_fan_water_flow)
153d0ddfd24SEugene Shalygin #define SENSOR_CURR_CPU BIT(ec_sensor_curr_cpu)
154d0ddfd24SEugene Shalygin #define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
155d0ddfd24SEugene Shalygin #define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out)
1569992b19dSUrs Schroffenegger #define SENSOR_TEMP_WATER_BLOCK_IN BIT(ec_sensor_temp_water_block_in)
1579992b19dSUrs Schroffenegger #define SENSOR_TEMP_WATER_BLOCK_OUT BIT(ec_sensor_temp_water_block_out)
1589992b19dSUrs Schroffenegger #define SENSOR_TEMP_T_SENSOR_2 BIT(ec_sensor_temp_t_sensor_2)
1599992b19dSUrs Schroffenegger #define SENSOR_TEMP_SENSOR_EXTRA_1 BIT(ec_sensor_temp_sensor_extra_1)
1609992b19dSUrs Schroffenegger #define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2)
1619992b19dSUrs Schroffenegger #define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3)
162d0ddfd24SEugene Shalygin 
16345934e4aSEugene Shalygin enum board_family {
16445934e4aSEugene Shalygin 	family_unknown,
1657cc44e5aSEugene Shalygin 	family_amd_400_series,
16645934e4aSEugene Shalygin 	family_amd_500_series,
167790dec13SMichael Carns 	family_amd_600_series,
1688f9eb10fSMichael Carns 	family_intel_300_series,
169bae26b80SShady Nawara 	family_intel_600_series
17045934e4aSEugene Shalygin };
17145934e4aSEugene Shalygin 
172d0ddfd24SEugene Shalygin /* All the known sensors for ASUS EC controllers */
1737cc44e5aSEugene Shalygin static const struct ec_sensor_info sensors_family_amd_400[] = {
1747cc44e5aSEugene Shalygin 	[ec_sensor_temp_chipset] =
1757cc44e5aSEugene Shalygin 		EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
1767cc44e5aSEugene Shalygin 	[ec_sensor_temp_cpu] =
1777cc44e5aSEugene Shalygin 		EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
1787cc44e5aSEugene Shalygin 	[ec_sensor_temp_mb] =
1797cc44e5aSEugene Shalygin 		EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
1807cc44e5aSEugene Shalygin 	[ec_sensor_temp_t_sensor] =
1817cc44e5aSEugene Shalygin 		EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
1827cc44e5aSEugene Shalygin 	[ec_sensor_temp_vrm] =
1837cc44e5aSEugene Shalygin 		EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
1847cc44e5aSEugene Shalygin 	[ec_sensor_in_cpu_core] =
1857cc44e5aSEugene Shalygin 		EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
1867cc44e5aSEugene Shalygin 	[ec_sensor_fan_cpu_opt] =
1877cc44e5aSEugene Shalygin 		EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
1887cc44e5aSEugene Shalygin 	[ec_sensor_fan_vrm_hs] =
1897cc44e5aSEugene Shalygin 		EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
1907cc44e5aSEugene Shalygin 	[ec_sensor_fan_chipset] =
1917cc44e5aSEugene Shalygin 		/* no chipset fans in this generation */
1927cc44e5aSEugene Shalygin 		EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00),
1937cc44e5aSEugene Shalygin 	[ec_sensor_fan_water_flow] =
1947cc44e5aSEugene Shalygin 		EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4),
1957cc44e5aSEugene Shalygin 	[ec_sensor_curr_cpu] =
1967cc44e5aSEugene Shalygin 		EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
1977cc44e5aSEugene Shalygin 	[ec_sensor_temp_water_in] =
1987cc44e5aSEugene Shalygin 		EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
1997cc44e5aSEugene Shalygin 	[ec_sensor_temp_water_out] =
2007cc44e5aSEugene Shalygin 		EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b),
2017cc44e5aSEugene Shalygin };
2027cc44e5aSEugene Shalygin 
20345934e4aSEugene Shalygin static const struct ec_sensor_info sensors_family_amd_500[] = {
204d0ddfd24SEugene Shalygin 	[ec_sensor_temp_chipset] =
205d0ddfd24SEugene Shalygin 		EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
206d0ddfd24SEugene Shalygin 	[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
207d0ddfd24SEugene Shalygin 	[ec_sensor_temp_mb] =
208d0ddfd24SEugene Shalygin 		EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
209d0ddfd24SEugene Shalygin 	[ec_sensor_temp_t_sensor] =
210d0ddfd24SEugene Shalygin 		EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
211d0ddfd24SEugene Shalygin 	[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
212f545a2fdSEugene Shalygin 	[ec_sensor_in_cpu_core] =
213f545a2fdSEugene Shalygin 		EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
214d0ddfd24SEugene Shalygin 	[ec_sensor_fan_cpu_opt] =
215d0ddfd24SEugene Shalygin 		EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
216d0ddfd24SEugene Shalygin 	[ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
217d0ddfd24SEugene Shalygin 	[ec_sensor_fan_chipset] =
218d0ddfd24SEugene Shalygin 		EC_SENSOR("Chipset", hwmon_fan, 2, 0x00, 0xb4),
219d0ddfd24SEugene Shalygin 	[ec_sensor_fan_water_flow] =
220d0ddfd24SEugene Shalygin 		EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
221d0ddfd24SEugene Shalygin 	[ec_sensor_curr_cpu] = EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
222d0ddfd24SEugene Shalygin 	[ec_sensor_temp_water_in] =
223d0ddfd24SEugene Shalygin 		EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
224d0ddfd24SEugene Shalygin 	[ec_sensor_temp_water_out] =
225d0ddfd24SEugene Shalygin 		EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
2269992b19dSUrs Schroffenegger 	[ec_sensor_temp_water_block_in] =
2279992b19dSUrs Schroffenegger 		EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02),
2289992b19dSUrs Schroffenegger 	[ec_sensor_temp_water_block_out] =
2299992b19dSUrs Schroffenegger 		EC_SENSOR("Water_Block_Out", hwmon_temp, 1, 0x01, 0x03),
2309992b19dSUrs Schroffenegger 	[ec_sensor_temp_sensor_extra_1] =
2319992b19dSUrs Schroffenegger 		EC_SENSOR("Extra_1", hwmon_temp, 1, 0x01, 0x09),
2329992b19dSUrs Schroffenegger 	[ec_sensor_temp_t_sensor_2] =
2339992b19dSUrs Schroffenegger 		EC_SENSOR("T_sensor_2", hwmon_temp, 1, 0x01, 0x0a),
2349992b19dSUrs Schroffenegger 	[ec_sensor_temp_sensor_extra_2] =
2359992b19dSUrs Schroffenegger 		EC_SENSOR("Extra_2", hwmon_temp, 1, 0x01, 0x0b),
2369992b19dSUrs Schroffenegger 	[ec_sensor_temp_sensor_extra_3] =
2379992b19dSUrs Schroffenegger 		EC_SENSOR("Extra_3", hwmon_temp, 1, 0x01, 0x0c),
238d0ddfd24SEugene Shalygin };
239d0ddfd24SEugene Shalygin 
240790dec13SMichael Carns static const struct ec_sensor_info sensors_family_amd_600[] = {
241790dec13SMichael Carns 	[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30),
242790dec13SMichael Carns 	[ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
243790dec13SMichael Carns 	[ec_sensor_temp_mb] =
244790dec13SMichael Carns 	EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
245790dec13SMichael Carns 	[ec_sensor_temp_vrm] =
246790dec13SMichael Carns 		EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
247*f7ac3020SEllie Hermaszewska 	[ec_sensor_temp_t_sensor] =
248*f7ac3020SEllie Hermaszewska 		EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36),
249790dec13SMichael Carns 	[ec_sensor_temp_water_in] =
250790dec13SMichael Carns 		EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
251790dec13SMichael Carns 	[ec_sensor_temp_water_out] =
252790dec13SMichael Carns 		EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
253790dec13SMichael Carns };
254790dec13SMichael Carns 
2558f9eb10fSMichael Carns static const struct ec_sensor_info sensors_family_intel_300[] = {
2568f9eb10fSMichael Carns 	[ec_sensor_temp_chipset] =
2578f9eb10fSMichael Carns 		EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
2588f9eb10fSMichael Carns 	[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
2598f9eb10fSMichael Carns 	[ec_sensor_temp_mb] =
2608f9eb10fSMichael Carns 		EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
2618f9eb10fSMichael Carns 	[ec_sensor_temp_t_sensor] =
2628f9eb10fSMichael Carns 		EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
2638f9eb10fSMichael Carns 	[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
2648f9eb10fSMichael Carns 	[ec_sensor_fan_cpu_opt] =
2658f9eb10fSMichael Carns 		EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
2668f9eb10fSMichael Carns 	[ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
2678f9eb10fSMichael Carns 	[ec_sensor_fan_water_flow] =
2688f9eb10fSMichael Carns 		EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
2698f9eb10fSMichael Carns 	[ec_sensor_temp_water_in] =
2708f9eb10fSMichael Carns 		EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
2718f9eb10fSMichael Carns 	[ec_sensor_temp_water_out] =
2728f9eb10fSMichael Carns 		EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
2738f9eb10fSMichael Carns };
2748f9eb10fSMichael Carns 
275bae26b80SShady Nawara static const struct ec_sensor_info sensors_family_intel_600[] = {
276bae26b80SShady Nawara 	[ec_sensor_temp_t_sensor] =
277bae26b80SShady Nawara 		EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
278bae26b80SShady Nawara 	[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
279bae26b80SShady Nawara };
280bae26b80SShady Nawara 
281d0ddfd24SEugene Shalygin /* Shortcuts for common combinations */
282d0ddfd24SEugene Shalygin #define SENSOR_SET_TEMP_CHIPSET_CPU_MB                                         \
283d0ddfd24SEugene Shalygin 	(SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB)
284d0ddfd24SEugene Shalygin #define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT)
2859992b19dSUrs Schroffenegger #define SENSOR_SET_WATER_BLOCK                                                 \
2869992b19dSUrs Schroffenegger 	(SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT)
2879992b19dSUrs Schroffenegger 
2885cd29012SEugene Shalygin struct ec_board_info {
2895cd29012SEugene Shalygin 	unsigned long sensors;
290de8fbac5SEugene Shalygin 	/*
291de8fbac5SEugene Shalygin 	 * Defines which mutex to use for guarding access to the state and the
292de8fbac5SEugene Shalygin 	 * hardware. Can be either a full path to an AML mutex or the
293de8fbac5SEugene Shalygin 	 * pseudo-path ACPI_GLOBAL_LOCK_PSEUDO_PATH to use the global ACPI lock,
294de8fbac5SEugene Shalygin 	 * or left empty to use a regular mutex object, in which case access to
295de8fbac5SEugene Shalygin 	 * the hardware is not guarded.
296de8fbac5SEugene Shalygin 	 */
297de8fbac5SEugene Shalygin 	const char *mutex_path;
29845934e4aSEugene Shalygin 	enum board_family family;
2995cd29012SEugene Shalygin };
300d0ddfd24SEugene Shalygin 
30188700d13SEugene Shalygin static const struct ec_board_info board_info_prime_x470_pro = {
3027cc44e5aSEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
3037cc44e5aSEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
3047cc44e5aSEugene Shalygin 		SENSOR_FAN_CPU_OPT |
3057cc44e5aSEugene Shalygin 		SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
3067cc44e5aSEugene Shalygin 	.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
3077cc44e5aSEugene Shalygin 	.family = family_amd_400_series,
30888700d13SEugene Shalygin };
30988700d13SEugene Shalygin 
31088700d13SEugene Shalygin static const struct ec_board_info board_info_prime_x570_pro = {
3115cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
3125cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
313de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
31445934e4aSEugene Shalygin 	.family = family_amd_500_series,
31588700d13SEugene Shalygin };
31688700d13SEugene Shalygin 
31788700d13SEugene Shalygin static const struct ec_board_info board_info_pro_art_x570_creator_wifi = {
3185cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
319d7cc063fSEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
3205cd29012SEugene Shalygin 		SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
321e2de0e6aSEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
32288700d13SEugene Shalygin 	.family = family_amd_500_series,
32388700d13SEugene Shalygin };
32488700d13SEugene Shalygin 
325c7ba3e26Sfireflame90051 static const struct ec_board_info board_info_pro_art_b550_creator = {
326c7ba3e26Sfireflame90051 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
327c7ba3e26Sfireflame90051 		SENSOR_TEMP_T_SENSOR |
328c7ba3e26Sfireflame90051 		SENSOR_FAN_CPU_OPT,
329c7ba3e26Sfireflame90051 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
330c7ba3e26Sfireflame90051 	.family = family_amd_500_series,
331c7ba3e26Sfireflame90051 };
332c7ba3e26Sfireflame90051 
33388700d13SEugene Shalygin static const struct ec_board_info board_info_pro_ws_x570_ace = {
3345cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
335ab9ac6dfSWei Shuyu 		SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET |
3365cd29012SEugene Shalygin 		SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
337de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
33845934e4aSEugene Shalygin 	.family = family_amd_500_series,
33988700d13SEugene Shalygin };
34088700d13SEugene Shalygin 
341790dec13SMichael Carns static const struct ec_board_info board_info_crosshair_x670e_hero = {
342790dec13SMichael Carns 	.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
343790dec13SMichael Carns 		SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
344790dec13SMichael Carns 		SENSOR_SET_TEMP_WATER,
3459c53fb0aSEugene Shalygin 	.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
346790dec13SMichael Carns 	.family = family_amd_600_series,
347790dec13SMichael Carns };
348790dec13SMichael Carns 
349*f7ac3020SEllie Hermaszewska static const struct ec_board_info board_info_crosshair_x670e_gene = {
350*f7ac3020SEllie Hermaszewska 	.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
351*f7ac3020SEllie Hermaszewska 		SENSOR_TEMP_T_SENSOR |
352*f7ac3020SEllie Hermaszewska 		SENSOR_TEMP_MB | SENSOR_TEMP_VRM,
353*f7ac3020SEllie Hermaszewska 	.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
354*f7ac3020SEllie Hermaszewska 	.family = family_amd_600_series,
355*f7ac3020SEllie Hermaszewska };
356*f7ac3020SEllie Hermaszewska 
35788700d13SEugene Shalygin static const struct ec_board_info board_info_crosshair_viii_dark_hero = {
3585cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
3595cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR |
360d0ddfd24SEugene Shalygin 		SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
361f545a2fdSEugene Shalygin 		SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
3625cd29012SEugene Shalygin 		SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
363de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
36445934e4aSEugene Shalygin 	.family = family_amd_500_series,
36588700d13SEugene Shalygin };
36688700d13SEugene Shalygin 
36788700d13SEugene Shalygin static const struct ec_board_info board_info_crosshair_viii_hero = {
3685cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
3695cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR |
3702f66cb5bSEugene Shalygin 		SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
3712f66cb5bSEugene Shalygin 		SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
3725cd29012SEugene Shalygin 		SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU |
3735cd29012SEugene Shalygin 		SENSOR_IN_CPU_CORE,
374de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
37545934e4aSEugene Shalygin 	.family = family_amd_500_series,
37688700d13SEugene Shalygin };
37788700d13SEugene Shalygin 
37888700d13SEugene Shalygin static const struct ec_board_info board_info_maximus_xi_hero = {
3798f9eb10fSMichael Carns 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
3808f9eb10fSMichael Carns 		SENSOR_TEMP_T_SENSOR |
3818f9eb10fSMichael Carns 		SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
3828f9eb10fSMichael Carns 		SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW,
3838f9eb10fSMichael Carns 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
3848f9eb10fSMichael Carns 	.family = family_intel_300_series,
38588700d13SEugene Shalygin };
38688700d13SEugene Shalygin 
38788700d13SEugene Shalygin static const struct ec_board_info board_info_crosshair_viii_impact = {
3885cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
3895cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
3905cd29012SEugene Shalygin 		SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
3915cd29012SEugene Shalygin 		SENSOR_IN_CPU_CORE,
392de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
39345934e4aSEugene Shalygin 	.family = family_amd_500_series,
39488700d13SEugene Shalygin };
39588700d13SEugene Shalygin 
39688700d13SEugene Shalygin static const struct ec_board_info board_info_strix_b550_e_gaming = {
3975cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
3985cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
3995cd29012SEugene Shalygin 		SENSOR_FAN_CPU_OPT,
400de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
40145934e4aSEugene Shalygin 	.family = family_amd_500_series,
40288700d13SEugene Shalygin };
40388700d13SEugene Shalygin 
40488700d13SEugene Shalygin static const struct ec_board_info board_info_strix_b550_i_gaming = {
4055cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
4065cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
4075cd29012SEugene Shalygin 		SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU |
4085cd29012SEugene Shalygin 		SENSOR_IN_CPU_CORE,
409de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
41045934e4aSEugene Shalygin 	.family = family_amd_500_series,
41188700d13SEugene Shalygin };
41288700d13SEugene Shalygin 
41388700d13SEugene Shalygin static const struct ec_board_info board_info_strix_x570_e_gaming = {
4145cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
4155cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
4165cd29012SEugene Shalygin 		SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
4175cd29012SEugene Shalygin 		SENSOR_IN_CPU_CORE,
418de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
41945934e4aSEugene Shalygin 	.family = family_amd_500_series,
42088700d13SEugene Shalygin };
42188700d13SEugene Shalygin 
42288700d13SEugene Shalygin static const struct ec_board_info board_info_strix_x570_e_gaming_wifi_ii = {
4239ccafe46SDebabrata Banerjee 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
4249ccafe46SDebabrata Banerjee 		SENSOR_TEMP_T_SENSOR | SENSOR_CURR_CPU |
4259ccafe46SDebabrata Banerjee 		SENSOR_IN_CPU_CORE,
4269ccafe46SDebabrata Banerjee 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
4279ccafe46SDebabrata Banerjee 	.family = family_amd_500_series,
42888700d13SEugene Shalygin };
42988700d13SEugene Shalygin 
43088700d13SEugene Shalygin static const struct ec_board_info board_info_strix_x570_f_gaming = {
4315cd29012SEugene Shalygin 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
4325cd29012SEugene Shalygin 		SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
433de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
43445934e4aSEugene Shalygin 	.family = family_amd_500_series,
43588700d13SEugene Shalygin };
43688700d13SEugene Shalygin 
43788700d13SEugene Shalygin static const struct ec_board_info board_info_strix_x570_i_gaming = {
4381c4e4f4aSEugene Shalygin 	.sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
4391c4e4f4aSEugene Shalygin 		SENSOR_TEMP_T_SENSOR |
4401c4e4f4aSEugene Shalygin 		SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET |
4411c4e4f4aSEugene Shalygin 		SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
442de8fbac5SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
44345934e4aSEugene Shalygin 	.family = family_amd_500_series,
44488700d13SEugene Shalygin };
44588700d13SEugene Shalygin 
4463a31e092SEugene Shalygin static const struct ec_board_info board_info_strix_z390_f_gaming = {
4473a31e092SEugene Shalygin 	.sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
4483a31e092SEugene Shalygin 		SENSOR_TEMP_T_SENSOR |
4493a31e092SEugene Shalygin 		SENSOR_FAN_CPU_OPT,
4503a31e092SEugene Shalygin 	.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
4513a31e092SEugene Shalygin 	.family = family_intel_300_series,
4523a31e092SEugene Shalygin };
4533a31e092SEugene Shalygin 
45488700d13SEugene Shalygin static const struct ec_board_info board_info_strix_z690_a_gaming_wifi_d4 = {
455bae26b80SShady Nawara 	.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM,
456bae26b80SShady Nawara 	.mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
457bae26b80SShady Nawara 	.family = family_intel_600_series,
45888700d13SEugene Shalygin };
45988700d13SEugene Shalygin 
46088700d13SEugene Shalygin static const struct ec_board_info board_info_zenith_ii_extreme = {
4619992b19dSUrs Schroffenegger 	.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
4629992b19dSUrs Schroffenegger 		SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
4639992b19dSUrs Schroffenegger 		SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_VRM_HS |
4649992b19dSUrs Schroffenegger 		SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE |
4659992b19dSUrs Schroffenegger 		SENSOR_SET_WATER_BLOCK |
4669992b19dSUrs Schroffenegger 		SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 |
4679992b19dSUrs Schroffenegger 		SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3,
4689992b19dSUrs Schroffenegger 	.mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
4699992b19dSUrs Schroffenegger 	.family = family_amd_500_series,
47088700d13SEugene Shalygin };
47188700d13SEugene Shalygin 
47288700d13SEugene Shalygin #define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, board_info)                      \
47388700d13SEugene Shalygin 	{                                                                      \
47488700d13SEugene Shalygin 		.matches = {                                                   \
47588700d13SEugene Shalygin 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR,                      \
47688700d13SEugene Shalygin 					"ASUSTeK COMPUTER INC."),              \
47788700d13SEugene Shalygin 			DMI_EXACT_MATCH(DMI_BOARD_NAME, name),                 \
47888700d13SEugene Shalygin 		},                                                             \
47988700d13SEugene Shalygin 		.driver_data = (void *)board_info,                              \
48088700d13SEugene Shalygin 	}
48188700d13SEugene Shalygin 
48288700d13SEugene Shalygin static const struct dmi_system_id dmi_table[] = {
48388700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO",
48488700d13SEugene Shalygin 					&board_info_prime_x470_pro),
48588700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO",
48688700d13SEugene Shalygin 					&board_info_prime_x570_pro),
48788700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI",
48888700d13SEugene Shalygin 					&board_info_pro_art_x570_creator_wifi),
489c7ba3e26Sfireflame90051 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt B550-CREATOR",
490c7ba3e26Sfireflame90051 					&board_info_pro_art_b550_creator),
49188700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE",
49288700d13SEugene Shalygin 					&board_info_pro_ws_x570_ace),
49388700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII DARK HERO",
49488700d13SEugene Shalygin 					&board_info_crosshair_viii_dark_hero),
49588700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII FORMULA",
49688700d13SEugene Shalygin 					&board_info_crosshair_viii_hero),
49788700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO",
49888700d13SEugene Shalygin 					&board_info_crosshair_viii_hero),
49988700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO (WI-FI)",
50088700d13SEugene Shalygin 					&board_info_crosshair_viii_hero),
501790dec13SMichael Carns 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO",
502790dec13SMichael Carns 					&board_info_crosshair_x670e_hero),
503*f7ac3020SEllie Hermaszewska 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E GENE",
504*f7ac3020SEllie Hermaszewska 					&board_info_crosshair_x670e_gene),
50588700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO",
50688700d13SEugene Shalygin 					&board_info_maximus_xi_hero),
50788700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)",
50888700d13SEugene Shalygin 					&board_info_maximus_xi_hero),
50988700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII IMPACT",
51088700d13SEugene Shalygin 					&board_info_crosshair_viii_impact),
51188700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING",
51288700d13SEugene Shalygin 					&board_info_strix_b550_e_gaming),
51388700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING",
51488700d13SEugene Shalygin 					&board_info_strix_b550_i_gaming),
51588700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING",
51688700d13SEugene Shalygin 					&board_info_strix_x570_e_gaming),
51788700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING WIFI II",
51888700d13SEugene Shalygin 					&board_info_strix_x570_e_gaming_wifi_ii),
51988700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-F GAMING",
52088700d13SEugene Shalygin 					&board_info_strix_x570_f_gaming),
52188700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-I GAMING",
52288700d13SEugene Shalygin 					&board_info_strix_x570_i_gaming),
5233a31e092SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING",
5243a31e092SEugene Shalygin 					&board_info_strix_z390_f_gaming),
52588700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z690-A GAMING WIFI D4",
52688700d13SEugene Shalygin 					&board_info_strix_z690_a_gaming_wifi_d4),
52788700d13SEugene Shalygin 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME",
52888700d13SEugene Shalygin 					&board_info_zenith_ii_extreme),
529195f46e5SEric Nguyen 	DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME ALPHA",
530195f46e5SEric Nguyen 					&board_info_zenith_ii_extreme),
53188700d13SEugene Shalygin 	{},
532d0ddfd24SEugene Shalygin };
533d0ddfd24SEugene Shalygin 
534d0ddfd24SEugene Shalygin struct ec_sensor {
535d0ddfd24SEugene Shalygin 	unsigned int info_index;
536339f8a99SEugene Shalygin 	s32 cached_value;
537d0ddfd24SEugene Shalygin };
538d0ddfd24SEugene Shalygin 
539de8fbac5SEugene Shalygin struct lock_data {
540de8fbac5SEugene Shalygin 	union {
541de8fbac5SEugene Shalygin 		acpi_handle aml;
542de8fbac5SEugene Shalygin 		/* global lock handle */
543de8fbac5SEugene Shalygin 		u32 glk;
544de8fbac5SEugene Shalygin 	} mutex;
545de8fbac5SEugene Shalygin 	bool (*lock)(struct lock_data *data);
546de8fbac5SEugene Shalygin 	bool (*unlock)(struct lock_data *data);
547de8fbac5SEugene Shalygin };
548de8fbac5SEugene Shalygin 
549de8fbac5SEugene Shalygin /*
550de8fbac5SEugene Shalygin  * The next function pairs implement options for locking access to the
551de8fbac5SEugene Shalygin  * state and the EC
552de8fbac5SEugene Shalygin  */
lock_via_acpi_mutex(struct lock_data * data)553de8fbac5SEugene Shalygin static bool lock_via_acpi_mutex(struct lock_data *data)
554de8fbac5SEugene Shalygin {
555de8fbac5SEugene Shalygin 	/*
556de8fbac5SEugene Shalygin 	 * ASUS DSDT does not specify that access to the EC has to be guarded,
557de8fbac5SEugene Shalygin 	 * but firmware does access it via ACPI
558de8fbac5SEugene Shalygin 	 */
559de8fbac5SEugene Shalygin 	return ACPI_SUCCESS(acpi_acquire_mutex(data->mutex.aml,
560de8fbac5SEugene Shalygin 					       NULL, ACPI_LOCK_DELAY_MS));
561de8fbac5SEugene Shalygin }
562de8fbac5SEugene Shalygin 
unlock_acpi_mutex(struct lock_data * data)563de8fbac5SEugene Shalygin static bool unlock_acpi_mutex(struct lock_data *data)
564de8fbac5SEugene Shalygin {
565de8fbac5SEugene Shalygin 	return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL));
566de8fbac5SEugene Shalygin }
567de8fbac5SEugene Shalygin 
lock_via_global_acpi_lock(struct lock_data * data)568de8fbac5SEugene Shalygin static bool lock_via_global_acpi_lock(struct lock_data *data)
569de8fbac5SEugene Shalygin {
570de8fbac5SEugene Shalygin 	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS,
571de8fbac5SEugene Shalygin 						     &data->mutex.glk));
572de8fbac5SEugene Shalygin }
573de8fbac5SEugene Shalygin 
unlock_global_acpi_lock(struct lock_data * data)574de8fbac5SEugene Shalygin static bool unlock_global_acpi_lock(struct lock_data *data)
575de8fbac5SEugene Shalygin {
576de8fbac5SEugene Shalygin 	return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk));
577de8fbac5SEugene Shalygin }
578de8fbac5SEugene Shalygin 
579d0ddfd24SEugene Shalygin struct ec_sensors_data {
5805cd29012SEugene Shalygin 	const struct ec_board_info *board_info;
58145934e4aSEugene Shalygin 	const struct ec_sensor_info *sensors_info;
582d0ddfd24SEugene Shalygin 	struct ec_sensor *sensors;
583d0ddfd24SEugene Shalygin 	/* EC registers to read from */
584d0ddfd24SEugene Shalygin 	u16 *registers;
585d0ddfd24SEugene Shalygin 	u8 *read_buffer;
586d0ddfd24SEugene Shalygin 	/* sorted list of unique register banks */
587d0ddfd24SEugene Shalygin 	u8 banks[ASUS_EC_MAX_BANK + 1];
588d0ddfd24SEugene Shalygin 	/* in jiffies */
589d0ddfd24SEugene Shalygin 	unsigned long last_updated;
590de8fbac5SEugene Shalygin 	struct lock_data lock_data;
591d0ddfd24SEugene Shalygin 	/* number of board EC sensors */
592d0ddfd24SEugene Shalygin 	u8 nr_sensors;
593d0ddfd24SEugene Shalygin 	/*
594d0ddfd24SEugene Shalygin 	 * number of EC registers to read
595d0ddfd24SEugene Shalygin 	 * (sensor might span more than 1 register)
596d0ddfd24SEugene Shalygin 	 */
597d0ddfd24SEugene Shalygin 	u8 nr_registers;
598d0ddfd24SEugene Shalygin 	/* number of unique register banks */
599d0ddfd24SEugene Shalygin 	u8 nr_banks;
600d0ddfd24SEugene Shalygin };
601d0ddfd24SEugene Shalygin 
register_bank(u16 reg)602d0ddfd24SEugene Shalygin static u8 register_bank(u16 reg)
603d0ddfd24SEugene Shalygin {
604d0ddfd24SEugene Shalygin 	return reg >> 8;
605d0ddfd24SEugene Shalygin }
606d0ddfd24SEugene Shalygin 
register_index(u16 reg)607d0ddfd24SEugene Shalygin static u8 register_index(u16 reg)
608d0ddfd24SEugene Shalygin {
609d0ddfd24SEugene Shalygin 	return reg & 0x00ff;
610d0ddfd24SEugene Shalygin }
611d0ddfd24SEugene Shalygin 
is_sensor_data_signed(const struct ec_sensor_info * si)6128aba9ca6SEugene Shalygin static bool is_sensor_data_signed(const struct ec_sensor_info *si)
6138aba9ca6SEugene Shalygin {
6148aba9ca6SEugene Shalygin 	/*
6158aba9ca6SEugene Shalygin 	 * guessed from WMI functions in DSDT code for boards
6168aba9ca6SEugene Shalygin 	 * of the X470 generation
6178aba9ca6SEugene Shalygin 	 */
6188aba9ca6SEugene Shalygin 	return si->type == hwmon_temp;
6198aba9ca6SEugene Shalygin }
6208aba9ca6SEugene Shalygin 
621d0ddfd24SEugene Shalygin static const struct ec_sensor_info *
get_sensor_info(const struct ec_sensors_data * state,int index)622d0ddfd24SEugene Shalygin get_sensor_info(const struct ec_sensors_data *state, int index)
623d0ddfd24SEugene Shalygin {
62445934e4aSEugene Shalygin 	return state->sensors_info + state->sensors[index].info_index;
625d0ddfd24SEugene Shalygin }
626d0ddfd24SEugene Shalygin 
find_ec_sensor_index(const struct ec_sensors_data * ec,enum hwmon_sensor_types type,int channel)627d0ddfd24SEugene Shalygin static int find_ec_sensor_index(const struct ec_sensors_data *ec,
628d0ddfd24SEugene Shalygin 				enum hwmon_sensor_types type, int channel)
629d0ddfd24SEugene Shalygin {
630d0ddfd24SEugene Shalygin 	unsigned int i;
631d0ddfd24SEugene Shalygin 
632d0ddfd24SEugene Shalygin 	for (i = 0; i < ec->nr_sensors; i++) {
633d0ddfd24SEugene Shalygin 		if (get_sensor_info(ec, i)->type == type) {
634d0ddfd24SEugene Shalygin 			if (channel == 0)
635d0ddfd24SEugene Shalygin 				return i;
636d0ddfd24SEugene Shalygin 			channel--;
637d0ddfd24SEugene Shalygin 		}
638d0ddfd24SEugene Shalygin 	}
639d0ddfd24SEugene Shalygin 	return -ENOENT;
640d0ddfd24SEugene Shalygin }
641d0ddfd24SEugene Shalygin 
bank_compare(const void * a,const void * b)64288700d13SEugene Shalygin static int bank_compare(const void *a, const void *b)
643d0ddfd24SEugene Shalygin {
644d0ddfd24SEugene Shalygin 	return *((const s8 *)a) - *((const s8 *)b);
645d0ddfd24SEugene Shalygin }
646d0ddfd24SEugene Shalygin 
setup_sensor_data(struct ec_sensors_data * ec)64788700d13SEugene Shalygin static void setup_sensor_data(struct ec_sensors_data *ec)
648d0ddfd24SEugene Shalygin {
649d0ddfd24SEugene Shalygin 	struct ec_sensor *s = ec->sensors;
650d0ddfd24SEugene Shalygin 	bool bank_found;
651d0ddfd24SEugene Shalygin 	int i, j;
652d0ddfd24SEugene Shalygin 	u8 bank;
653d0ddfd24SEugene Shalygin 
654d0ddfd24SEugene Shalygin 	ec->nr_banks = 0;
655d0ddfd24SEugene Shalygin 	ec->nr_registers = 0;
656d0ddfd24SEugene Shalygin 
6575cd29012SEugene Shalygin 	for_each_set_bit(i, &ec->board_info->sensors,
6585cd29012SEugene Shalygin 			 BITS_PER_TYPE(ec->board_info->sensors)) {
659d0ddfd24SEugene Shalygin 		s->info_index = i;
660d0ddfd24SEugene Shalygin 		s->cached_value = 0;
661d0ddfd24SEugene Shalygin 		ec->nr_registers +=
66245934e4aSEugene Shalygin 			ec->sensors_info[s->info_index].addr.components.size;
663d0ddfd24SEugene Shalygin 		bank_found = false;
66445934e4aSEugene Shalygin 		bank = ec->sensors_info[s->info_index].addr.components.bank;
665d0ddfd24SEugene Shalygin 		for (j = 0; j < ec->nr_banks; j++) {
666d0ddfd24SEugene Shalygin 			if (ec->banks[j] == bank) {
667d0ddfd24SEugene Shalygin 				bank_found = true;
668d0ddfd24SEugene Shalygin 				break;
669d0ddfd24SEugene Shalygin 			}
670d0ddfd24SEugene Shalygin 		}
671d0ddfd24SEugene Shalygin 		if (!bank_found) {
672d0ddfd24SEugene Shalygin 			ec->banks[ec->nr_banks++] = bank;
673d0ddfd24SEugene Shalygin 		}
674d0ddfd24SEugene Shalygin 		s++;
675d0ddfd24SEugene Shalygin 	}
676d0ddfd24SEugene Shalygin 	sort(ec->banks, ec->nr_banks, 1, bank_compare, NULL);
677d0ddfd24SEugene Shalygin }
678d0ddfd24SEugene Shalygin 
fill_ec_registers(struct ec_sensors_data * ec)67988700d13SEugene Shalygin static void fill_ec_registers(struct ec_sensors_data *ec)
680d0ddfd24SEugene Shalygin {
681d0ddfd24SEugene Shalygin 	const struct ec_sensor_info *si;
682d0ddfd24SEugene Shalygin 	unsigned int i, j, register_idx = 0;
683d0ddfd24SEugene Shalygin 
684d0ddfd24SEugene Shalygin 	for (i = 0; i < ec->nr_sensors; ++i) {
685d0ddfd24SEugene Shalygin 		si = get_sensor_info(ec, i);
686d0ddfd24SEugene Shalygin 		for (j = 0; j < si->addr.components.size; ++j, ++register_idx) {
687d0ddfd24SEugene Shalygin 			ec->registers[register_idx] =
688d0ddfd24SEugene Shalygin 				(si->addr.components.bank << 8) +
689d0ddfd24SEugene Shalygin 				si->addr.components.index + j;
690d0ddfd24SEugene Shalygin 		}
691d0ddfd24SEugene Shalygin 	}
692d0ddfd24SEugene Shalygin }
693d0ddfd24SEugene Shalygin 
setup_lock_data(struct device * dev)69488700d13SEugene Shalygin static int setup_lock_data(struct device *dev)
695d0ddfd24SEugene Shalygin {
696d0ddfd24SEugene Shalygin 	const char *mutex_path;
697d0ddfd24SEugene Shalygin 	int status;
698de8fbac5SEugene Shalygin 	struct ec_sensors_data *state = dev_get_drvdata(dev);
699d0ddfd24SEugene Shalygin 
700d0ddfd24SEugene Shalygin 	mutex_path = mutex_path_override ?
701de8fbac5SEugene Shalygin 		mutex_path_override : state->board_info->mutex_path;
702d0ddfd24SEugene Shalygin 
703de8fbac5SEugene Shalygin 	if (!mutex_path || !strlen(mutex_path)) {
704de8fbac5SEugene Shalygin 		dev_err(dev, "Hardware access guard mutex name is empty");
705de8fbac5SEugene Shalygin 		return -EINVAL;
706de8fbac5SEugene Shalygin 	}
707de8fbac5SEugene Shalygin 	if (!strcmp(mutex_path, ACPI_GLOBAL_LOCK_PSEUDO_PATH)) {
708de8fbac5SEugene Shalygin 		state->lock_data.mutex.glk = 0;
709de8fbac5SEugene Shalygin 		state->lock_data.lock = lock_via_global_acpi_lock;
710de8fbac5SEugene Shalygin 		state->lock_data.unlock = unlock_global_acpi_lock;
711de8fbac5SEugene Shalygin 	} else {
712de8fbac5SEugene Shalygin 		status = acpi_get_handle(NULL, (acpi_string)mutex_path,
713de8fbac5SEugene Shalygin 					 &state->lock_data.mutex.aml);
714d0ddfd24SEugene Shalygin 		if (ACPI_FAILURE(status)) {
715d0ddfd24SEugene Shalygin 			dev_err(dev,
716de8fbac5SEugene Shalygin 				"Failed to get hardware access guard AML mutex '%s': error %d",
717d0ddfd24SEugene Shalygin 				mutex_path, status);
718de8fbac5SEugene Shalygin 			return -ENOENT;
719d0ddfd24SEugene Shalygin 		}
720de8fbac5SEugene Shalygin 		state->lock_data.lock = lock_via_acpi_mutex;
721de8fbac5SEugene Shalygin 		state->lock_data.unlock = unlock_acpi_mutex;
722de8fbac5SEugene Shalygin 	}
723de8fbac5SEugene Shalygin 	return 0;
724d0ddfd24SEugene Shalygin }
725d0ddfd24SEugene Shalygin 
asus_ec_bank_switch(u8 bank,u8 * old)726d0ddfd24SEugene Shalygin static int asus_ec_bank_switch(u8 bank, u8 *old)
727d0ddfd24SEugene Shalygin {
728d0ddfd24SEugene Shalygin 	int status = 0;
729d0ddfd24SEugene Shalygin 
730d0ddfd24SEugene Shalygin 	if (old) {
731d0ddfd24SEugene Shalygin 		status = ec_read(ASUS_EC_BANK_REGISTER, old);
732d0ddfd24SEugene Shalygin 	}
733d0ddfd24SEugene Shalygin 	if (status || (old && (*old == bank)))
734d0ddfd24SEugene Shalygin 		return status;
735d0ddfd24SEugene Shalygin 	return ec_write(ASUS_EC_BANK_REGISTER, bank);
736d0ddfd24SEugene Shalygin }
737d0ddfd24SEugene Shalygin 
asus_ec_block_read(const struct device * dev,struct ec_sensors_data * ec)738d0ddfd24SEugene Shalygin static int asus_ec_block_read(const struct device *dev,
739d0ddfd24SEugene Shalygin 			      struct ec_sensors_data *ec)
740d0ddfd24SEugene Shalygin {
741d0ddfd24SEugene Shalygin 	int ireg, ibank, status;
742d0ddfd24SEugene Shalygin 	u8 bank, reg_bank, prev_bank;
743d0ddfd24SEugene Shalygin 
744d0ddfd24SEugene Shalygin 	bank = 0;
745d0ddfd24SEugene Shalygin 	status = asus_ec_bank_switch(bank, &prev_bank);
746d0ddfd24SEugene Shalygin 	if (status) {
747d0ddfd24SEugene Shalygin 		dev_warn(dev, "EC bank switch failed");
748d0ddfd24SEugene Shalygin 		return status;
749d0ddfd24SEugene Shalygin 	}
750d0ddfd24SEugene Shalygin 
751d0ddfd24SEugene Shalygin 	if (prev_bank) {
752d0ddfd24SEugene Shalygin 		/* oops... somebody else is working with the EC too */
753d0ddfd24SEugene Shalygin 		dev_warn(dev,
754d0ddfd24SEugene Shalygin 			"Concurrent access to the ACPI EC detected.\nRace condition possible.");
755d0ddfd24SEugene Shalygin 	}
756d0ddfd24SEugene Shalygin 
757d0ddfd24SEugene Shalygin 	/* read registers minimizing bank switches. */
758d0ddfd24SEugene Shalygin 	for (ibank = 0; ibank < ec->nr_banks; ibank++) {
759d0ddfd24SEugene Shalygin 		if (bank != ec->banks[ibank]) {
760d0ddfd24SEugene Shalygin 			bank = ec->banks[ibank];
761d0ddfd24SEugene Shalygin 			if (asus_ec_bank_switch(bank, NULL)) {
762d0ddfd24SEugene Shalygin 				dev_warn(dev, "EC bank switch to %d failed",
763d0ddfd24SEugene Shalygin 					 bank);
764d0ddfd24SEugene Shalygin 				break;
765d0ddfd24SEugene Shalygin 			}
766d0ddfd24SEugene Shalygin 		}
767d0ddfd24SEugene Shalygin 		for (ireg = 0; ireg < ec->nr_registers; ireg++) {
768d0ddfd24SEugene Shalygin 			reg_bank = register_bank(ec->registers[ireg]);
769d0ddfd24SEugene Shalygin 			if (reg_bank < bank) {
770d0ddfd24SEugene Shalygin 				continue;
771d0ddfd24SEugene Shalygin 			}
772d0ddfd24SEugene Shalygin 			ec_read(register_index(ec->registers[ireg]),
773d0ddfd24SEugene Shalygin 				ec->read_buffer + ireg);
774d0ddfd24SEugene Shalygin 		}
775d0ddfd24SEugene Shalygin 	}
776d0ddfd24SEugene Shalygin 
777d0ddfd24SEugene Shalygin 	status = asus_ec_bank_switch(prev_bank, NULL);
778d0ddfd24SEugene Shalygin 	return status;
779d0ddfd24SEugene Shalygin }
780d0ddfd24SEugene Shalygin 
get_sensor_value(const struct ec_sensor_info * si,u8 * data)781339f8a99SEugene Shalygin static inline s32 get_sensor_value(const struct ec_sensor_info *si, u8 *data)
782d0ddfd24SEugene Shalygin {
7838aba9ca6SEugene Shalygin 	if (is_sensor_data_signed(si)) {
784d0ddfd24SEugene Shalygin 		switch (si->addr.components.size) {
785d0ddfd24SEugene Shalygin 		case 1:
786339f8a99SEugene Shalygin 			return (s8)*data;
787d0ddfd24SEugene Shalygin 		case 2:
788339f8a99SEugene Shalygin 			return (s16)get_unaligned_be16(data);
789d0ddfd24SEugene Shalygin 		case 4:
790339f8a99SEugene Shalygin 			return (s32)get_unaligned_be32(data);
791d0ddfd24SEugene Shalygin 		default:
792d0ddfd24SEugene Shalygin 			return 0;
793d0ddfd24SEugene Shalygin 		}
7948aba9ca6SEugene Shalygin 	} else {
7958aba9ca6SEugene Shalygin 		switch (si->addr.components.size) {
7968aba9ca6SEugene Shalygin 		case 1:
7978aba9ca6SEugene Shalygin 			return *data;
7988aba9ca6SEugene Shalygin 		case 2:
7998aba9ca6SEugene Shalygin 			return get_unaligned_be16(data);
8008aba9ca6SEugene Shalygin 		case 4:
8018aba9ca6SEugene Shalygin 			return get_unaligned_be32(data);
8028aba9ca6SEugene Shalygin 		default:
8038aba9ca6SEugene Shalygin 			return 0;
8048aba9ca6SEugene Shalygin 		}
8058aba9ca6SEugene Shalygin 	}
806d0ddfd24SEugene Shalygin }
807d0ddfd24SEugene Shalygin 
update_sensor_values(struct ec_sensors_data * ec,u8 * data)808d0ddfd24SEugene Shalygin static void update_sensor_values(struct ec_sensors_data *ec, u8 *data)
809d0ddfd24SEugene Shalygin {
810d0ddfd24SEugene Shalygin 	const struct ec_sensor_info *si;
8115cd29012SEugene Shalygin 	struct ec_sensor *s, *sensor_end;
812d0ddfd24SEugene Shalygin 
8135cd29012SEugene Shalygin 	sensor_end = ec->sensors + ec->nr_sensors;
8145cd29012SEugene Shalygin 	for (s = ec->sensors; s != sensor_end; s++) {
81545934e4aSEugene Shalygin 		si = ec->sensors_info + s->info_index;
816d0ddfd24SEugene Shalygin 		s->cached_value = get_sensor_value(si, data);
817d0ddfd24SEugene Shalygin 		data += si->addr.components.size;
818d0ddfd24SEugene Shalygin 	}
819d0ddfd24SEugene Shalygin }
820d0ddfd24SEugene Shalygin 
update_ec_sensors(const struct device * dev,struct ec_sensors_data * ec)821d0ddfd24SEugene Shalygin static int update_ec_sensors(const struct device *dev,
822d0ddfd24SEugene Shalygin 			     struct ec_sensors_data *ec)
823d0ddfd24SEugene Shalygin {
824d0ddfd24SEugene Shalygin 	int status;
825d0ddfd24SEugene Shalygin 
826de8fbac5SEugene Shalygin 	if (!ec->lock_data.lock(&ec->lock_data)) {
827de8fbac5SEugene Shalygin 		dev_warn(dev, "Failed to acquire mutex");
828de8fbac5SEugene Shalygin 		return -EBUSY;
829d0ddfd24SEugene Shalygin 	}
830d0ddfd24SEugene Shalygin 
831d0ddfd24SEugene Shalygin 	status = asus_ec_block_read(dev, ec);
832d0ddfd24SEugene Shalygin 
833d0ddfd24SEugene Shalygin 	if (!status) {
834d0ddfd24SEugene Shalygin 		update_sensor_values(ec, ec->read_buffer);
835d0ddfd24SEugene Shalygin 	}
836de8fbac5SEugene Shalygin 
837de8fbac5SEugene Shalygin 	if (!ec->lock_data.unlock(&ec->lock_data))
838de8fbac5SEugene Shalygin 		dev_err(dev, "Failed to release mutex");
839de8fbac5SEugene Shalygin 
840d0ddfd24SEugene Shalygin 	return status;
841d0ddfd24SEugene Shalygin }
842d0ddfd24SEugene Shalygin 
scale_sensor_value(s32 value,int data_type)843339f8a99SEugene Shalygin static long scale_sensor_value(s32 value, int data_type)
844d0ddfd24SEugene Shalygin {
845d0ddfd24SEugene Shalygin 	switch (data_type) {
846d0ddfd24SEugene Shalygin 	case hwmon_curr:
847d0ddfd24SEugene Shalygin 	case hwmon_temp:
848d0ddfd24SEugene Shalygin 		return value * MILLI;
849d0ddfd24SEugene Shalygin 	default:
850d0ddfd24SEugene Shalygin 		return value;
851d0ddfd24SEugene Shalygin 	}
852d0ddfd24SEugene Shalygin }
853d0ddfd24SEugene Shalygin 
get_cached_value_or_update(const struct device * dev,int sensor_index,struct ec_sensors_data * state,s32 * value)854d0ddfd24SEugene Shalygin static int get_cached_value_or_update(const struct device *dev,
855d0ddfd24SEugene Shalygin 				      int sensor_index,
856339f8a99SEugene Shalygin 				      struct ec_sensors_data *state, s32 *value)
857d0ddfd24SEugene Shalygin {
858d0ddfd24SEugene Shalygin 	if (time_after(jiffies, state->last_updated + HZ)) {
859d0ddfd24SEugene Shalygin 		if (update_ec_sensors(dev, state)) {
860d0ddfd24SEugene Shalygin 			dev_err(dev, "update_ec_sensors() failure\n");
861d0ddfd24SEugene Shalygin 			return -EIO;
862d0ddfd24SEugene Shalygin 		}
863d0ddfd24SEugene Shalygin 
864d0ddfd24SEugene Shalygin 		state->last_updated = jiffies;
865d0ddfd24SEugene Shalygin 	}
866d0ddfd24SEugene Shalygin 
867d0ddfd24SEugene Shalygin 	*value = state->sensors[sensor_index].cached_value;
868d0ddfd24SEugene Shalygin 	return 0;
869d0ddfd24SEugene Shalygin }
870d0ddfd24SEugene Shalygin 
871d0ddfd24SEugene Shalygin /*
872d0ddfd24SEugene Shalygin  * Now follow the functions that implement the hwmon interface
873d0ddfd24SEugene Shalygin  */
874d0ddfd24SEugene Shalygin 
asus_ec_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)875d0ddfd24SEugene Shalygin static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
876d0ddfd24SEugene Shalygin 			      u32 attr, int channel, long *val)
877d0ddfd24SEugene Shalygin {
878d0ddfd24SEugene Shalygin 	int ret;
879339f8a99SEugene Shalygin 	s32 value = 0;
880d0ddfd24SEugene Shalygin 
881d0ddfd24SEugene Shalygin 	struct ec_sensors_data *state = dev_get_drvdata(dev);
882d0ddfd24SEugene Shalygin 	int sidx = find_ec_sensor_index(state, type, channel);
883d0ddfd24SEugene Shalygin 
884d0ddfd24SEugene Shalygin 	if (sidx < 0) {
885d0ddfd24SEugene Shalygin 		return sidx;
886d0ddfd24SEugene Shalygin 	}
887d0ddfd24SEugene Shalygin 
888d0ddfd24SEugene Shalygin 	ret = get_cached_value_or_update(dev, sidx, state, &value);
889d0ddfd24SEugene Shalygin 	if (!ret) {
890d0ddfd24SEugene Shalygin 		*val = scale_sensor_value(value,
891d0ddfd24SEugene Shalygin 					  get_sensor_info(state, sidx)->type);
892d0ddfd24SEugene Shalygin 	}
893d0ddfd24SEugene Shalygin 
894d0ddfd24SEugene Shalygin 	return ret;
895d0ddfd24SEugene Shalygin }
896d0ddfd24SEugene Shalygin 
asus_ec_hwmon_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)897d0ddfd24SEugene Shalygin static int asus_ec_hwmon_read_string(struct device *dev,
898d0ddfd24SEugene Shalygin 				     enum hwmon_sensor_types type, u32 attr,
899d0ddfd24SEugene Shalygin 				     int channel, const char **str)
900d0ddfd24SEugene Shalygin {
901d0ddfd24SEugene Shalygin 	struct ec_sensors_data *state = dev_get_drvdata(dev);
902d0ddfd24SEugene Shalygin 	int sensor_index = find_ec_sensor_index(state, type, channel);
903d0ddfd24SEugene Shalygin 	*str = get_sensor_info(state, sensor_index)->label;
904d0ddfd24SEugene Shalygin 
905d0ddfd24SEugene Shalygin 	return 0;
906d0ddfd24SEugene Shalygin }
907d0ddfd24SEugene Shalygin 
asus_ec_hwmon_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int channel)908d0ddfd24SEugene Shalygin static umode_t asus_ec_hwmon_is_visible(const void *drvdata,
909d0ddfd24SEugene Shalygin 					enum hwmon_sensor_types type, u32 attr,
910d0ddfd24SEugene Shalygin 					int channel)
911d0ddfd24SEugene Shalygin {
912d0ddfd24SEugene Shalygin 	const struct ec_sensors_data *state = drvdata;
913d0ddfd24SEugene Shalygin 
914d0ddfd24SEugene Shalygin 	return find_ec_sensor_index(state, type, channel) >= 0 ? S_IRUGO : 0;
915d0ddfd24SEugene Shalygin }
916d0ddfd24SEugene Shalygin 
91788700d13SEugene Shalygin static int
asus_ec_hwmon_add_chan_info(struct hwmon_channel_info * asus_ec_hwmon_chan,struct device * dev,int num,enum hwmon_sensor_types type,u32 config)918d0ddfd24SEugene Shalygin asus_ec_hwmon_add_chan_info(struct hwmon_channel_info *asus_ec_hwmon_chan,
919d0ddfd24SEugene Shalygin 			     struct device *dev, int num,
920d0ddfd24SEugene Shalygin 			     enum hwmon_sensor_types type, u32 config)
921d0ddfd24SEugene Shalygin {
922d0ddfd24SEugene Shalygin 	int i;
923d0ddfd24SEugene Shalygin 	u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
924d0ddfd24SEugene Shalygin 
925d0ddfd24SEugene Shalygin 	if (!cfg)
926d0ddfd24SEugene Shalygin 		return -ENOMEM;
927d0ddfd24SEugene Shalygin 
928d0ddfd24SEugene Shalygin 	asus_ec_hwmon_chan->type = type;
929d0ddfd24SEugene Shalygin 	asus_ec_hwmon_chan->config = cfg;
930d0ddfd24SEugene Shalygin 	for (i = 0; i < num; i++, cfg++)
931d0ddfd24SEugene Shalygin 		*cfg = config;
932d0ddfd24SEugene Shalygin 
933d0ddfd24SEugene Shalygin 	return 0;
934d0ddfd24SEugene Shalygin }
935d0ddfd24SEugene Shalygin 
936d0ddfd24SEugene Shalygin static const struct hwmon_ops asus_ec_hwmon_ops = {
937d0ddfd24SEugene Shalygin 	.is_visible = asus_ec_hwmon_is_visible,
938d0ddfd24SEugene Shalygin 	.read = asus_ec_hwmon_read,
939d0ddfd24SEugene Shalygin 	.read_string = asus_ec_hwmon_read_string,
940d0ddfd24SEugene Shalygin };
941d0ddfd24SEugene Shalygin 
942d0ddfd24SEugene Shalygin static struct hwmon_chip_info asus_ec_chip_info = {
943d0ddfd24SEugene Shalygin 	.ops = &asus_ec_hwmon_ops,
944d0ddfd24SEugene Shalygin };
945d0ddfd24SEugene Shalygin 
get_board_info(void)94688700d13SEugene Shalygin static const struct ec_board_info *get_board_info(void)
947d0ddfd24SEugene Shalygin {
94888700d13SEugene Shalygin 	const struct dmi_system_id *dmi_entry;
949d0ddfd24SEugene Shalygin 
95088700d13SEugene Shalygin 	dmi_entry = dmi_first_match(dmi_table);
95188700d13SEugene Shalygin 	return dmi_entry ? dmi_entry->driver_data : NULL;
9525cd29012SEugene Shalygin }
9535cd29012SEugene Shalygin 
asus_ec_probe(struct platform_device * pdev)95488700d13SEugene Shalygin static int asus_ec_probe(struct platform_device *pdev)
955d0ddfd24SEugene Shalygin {
956d0ddfd24SEugene Shalygin 	const struct hwmon_channel_info **ptr_asus_ec_ci;
9571298184bSEugene Shalygin 	int nr_count[hwmon_max] = { 0 }, nr_types = 0;
9581298184bSEugene Shalygin 	struct hwmon_channel_info *asus_ec_hwmon_chan;
9595cd29012SEugene Shalygin 	const struct ec_board_info *pboard_info;
960d0ddfd24SEugene Shalygin 	const struct hwmon_chip_info *chip_info;
9611298184bSEugene Shalygin 	struct device *dev = &pdev->dev;
9621298184bSEugene Shalygin 	struct ec_sensors_data *ec_data;
963d0ddfd24SEugene Shalygin 	const struct ec_sensor_info *si;
964d0ddfd24SEugene Shalygin 	enum hwmon_sensor_types type;
9651298184bSEugene Shalygin 	struct device *hwdev;
966d0ddfd24SEugene Shalygin 	unsigned int i;
967de8fbac5SEugene Shalygin 	int status;
968d0ddfd24SEugene Shalygin 
9695cd29012SEugene Shalygin 	pboard_info = get_board_info();
9705cd29012SEugene Shalygin 	if (!pboard_info)
971d0ddfd24SEugene Shalygin 		return -ENODEV;
972d0ddfd24SEugene Shalygin 
9731298184bSEugene Shalygin 	ec_data = devm_kzalloc(dev, sizeof(struct ec_sensors_data),
9741298184bSEugene Shalygin 			       GFP_KERNEL);
9751298184bSEugene Shalygin 	if (!ec_data)
9761298184bSEugene Shalygin 		return -ENOMEM;
9771298184bSEugene Shalygin 
9781298184bSEugene Shalygin 	dev_set_drvdata(dev, ec_data);
9795cd29012SEugene Shalygin 	ec_data->board_info = pboard_info;
98045934e4aSEugene Shalygin 
98145934e4aSEugene Shalygin 	switch (ec_data->board_info->family) {
9827cc44e5aSEugene Shalygin 	case family_amd_400_series:
9837cc44e5aSEugene Shalygin 		ec_data->sensors_info = sensors_family_amd_400;
9847cc44e5aSEugene Shalygin 		break;
98545934e4aSEugene Shalygin 	case family_amd_500_series:
98645934e4aSEugene Shalygin 		ec_data->sensors_info = sensors_family_amd_500;
98745934e4aSEugene Shalygin 		break;
988790dec13SMichael Carns 	case family_amd_600_series:
989790dec13SMichael Carns 		ec_data->sensors_info = sensors_family_amd_600;
990790dec13SMichael Carns 		break;
9918f9eb10fSMichael Carns 	case family_intel_300_series:
9928f9eb10fSMichael Carns 		ec_data->sensors_info = sensors_family_intel_300;
9938f9eb10fSMichael Carns 		break;
994bae26b80SShady Nawara 	case family_intel_600_series:
995bae26b80SShady Nawara 		ec_data->sensors_info = sensors_family_intel_600;
996bae26b80SShady Nawara 		break;
99745934e4aSEugene Shalygin 	default:
99845934e4aSEugene Shalygin 		dev_err(dev, "Unknown board family: %d",
99945934e4aSEugene Shalygin 			ec_data->board_info->family);
100045934e4aSEugene Shalygin 		return -EINVAL;
100145934e4aSEugene Shalygin 	}
100245934e4aSEugene Shalygin 
10035cd29012SEugene Shalygin 	ec_data->nr_sensors = hweight_long(ec_data->board_info->sensors);
1004d0ddfd24SEugene Shalygin 	ec_data->sensors = devm_kcalloc(dev, ec_data->nr_sensors,
1005d0ddfd24SEugene Shalygin 					sizeof(struct ec_sensor), GFP_KERNEL);
10069bdc112bSYuan Can 	if (!ec_data->sensors)
10079bdc112bSYuan Can 		return -ENOMEM;
1008d0ddfd24SEugene Shalygin 
1009de8fbac5SEugene Shalygin 	status = setup_lock_data(dev);
1010de8fbac5SEugene Shalygin 	if (status) {
1011de8fbac5SEugene Shalygin 		dev_err(dev, "Failed to setup state/EC locking: %d", status);
1012de8fbac5SEugene Shalygin 		return status;
1013de8fbac5SEugene Shalygin 	}
101445934e4aSEugene Shalygin 
1015d0ddfd24SEugene Shalygin 	setup_sensor_data(ec_data);
1016d0ddfd24SEugene Shalygin 	ec_data->registers = devm_kcalloc(dev, ec_data->nr_registers,
1017d0ddfd24SEugene Shalygin 					  sizeof(u16), GFP_KERNEL);
1018d0ddfd24SEugene Shalygin 	ec_data->read_buffer = devm_kcalloc(dev, ec_data->nr_registers,
1019d0ddfd24SEugene Shalygin 					    sizeof(u8), GFP_KERNEL);
1020d0ddfd24SEugene Shalygin 
10211298184bSEugene Shalygin 	if (!ec_data->registers || !ec_data->read_buffer)
1022d0ddfd24SEugene Shalygin 		return -ENOMEM;
1023d0ddfd24SEugene Shalygin 
1024d0ddfd24SEugene Shalygin 	fill_ec_registers(ec_data);
1025d0ddfd24SEugene Shalygin 
1026d0ddfd24SEugene Shalygin 	for (i = 0; i < ec_data->nr_sensors; ++i) {
1027d0ddfd24SEugene Shalygin 		si = get_sensor_info(ec_data, i);
1028d0ddfd24SEugene Shalygin 		if (!nr_count[si->type])
1029d0ddfd24SEugene Shalygin 			++nr_types;
1030d0ddfd24SEugene Shalygin 		++nr_count[si->type];
1031d0ddfd24SEugene Shalygin 	}
1032d0ddfd24SEugene Shalygin 
1033d0ddfd24SEugene Shalygin 	if (nr_count[hwmon_temp])
1034d0ddfd24SEugene Shalygin 		nr_count[hwmon_chip]++, nr_types++;
1035d0ddfd24SEugene Shalygin 
1036d0ddfd24SEugene Shalygin 	asus_ec_hwmon_chan = devm_kcalloc(
1037d0ddfd24SEugene Shalygin 		dev, nr_types, sizeof(*asus_ec_hwmon_chan), GFP_KERNEL);
1038d0ddfd24SEugene Shalygin 	if (!asus_ec_hwmon_chan)
1039d0ddfd24SEugene Shalygin 		return -ENOMEM;
1040d0ddfd24SEugene Shalygin 
1041d0ddfd24SEugene Shalygin 	ptr_asus_ec_ci = devm_kcalloc(dev, nr_types + 1,
1042d0ddfd24SEugene Shalygin 				       sizeof(*ptr_asus_ec_ci), GFP_KERNEL);
1043d0ddfd24SEugene Shalygin 	if (!ptr_asus_ec_ci)
1044d0ddfd24SEugene Shalygin 		return -ENOMEM;
1045d0ddfd24SEugene Shalygin 
1046d0ddfd24SEugene Shalygin 	asus_ec_chip_info.info = ptr_asus_ec_ci;
1047d0ddfd24SEugene Shalygin 	chip_info = &asus_ec_chip_info;
1048d0ddfd24SEugene Shalygin 
1049d0ddfd24SEugene Shalygin 	for (type = 0; type < hwmon_max; ++type) {
1050d0ddfd24SEugene Shalygin 		if (!nr_count[type])
1051d0ddfd24SEugene Shalygin 			continue;
1052d0ddfd24SEugene Shalygin 
1053d0ddfd24SEugene Shalygin 		asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev,
1054d0ddfd24SEugene Shalygin 					     nr_count[type], type,
1055d0ddfd24SEugene Shalygin 					     hwmon_attributes[type]);
1056d0ddfd24SEugene Shalygin 		*ptr_asus_ec_ci++ = asus_ec_hwmon_chan++;
1057d0ddfd24SEugene Shalygin 	}
1058d0ddfd24SEugene Shalygin 
1059d0ddfd24SEugene Shalygin 	dev_info(dev, "board has %d EC sensors that span %d registers",
1060d0ddfd24SEugene Shalygin 		 ec_data->nr_sensors, ec_data->nr_registers);
1061d0ddfd24SEugene Shalygin 
1062d0ddfd24SEugene Shalygin 	hwdev = devm_hwmon_device_register_with_info(dev, "asusec",
1063d0ddfd24SEugene Shalygin 						     ec_data, chip_info, NULL);
1064d0ddfd24SEugene Shalygin 
1065d0ddfd24SEugene Shalygin 	return PTR_ERR_OR_ZERO(hwdev);
1066d0ddfd24SEugene Shalygin }
1067d0ddfd24SEugene Shalygin 
106888700d13SEugene Shalygin MODULE_DEVICE_TABLE(dmi, dmi_table);
1069d0ddfd24SEugene Shalygin 
1070d0ddfd24SEugene Shalygin static struct platform_driver asus_ec_sensors_platform_driver = {
1071d0ddfd24SEugene Shalygin 	.driver = {
1072d0ddfd24SEugene Shalygin 		.name	= "asus-ec-sensors",
1073d0ddfd24SEugene Shalygin 	},
107488700d13SEugene Shalygin 	.probe = asus_ec_probe,
1075d0ddfd24SEugene Shalygin };
1076d0ddfd24SEugene Shalygin 
107788700d13SEugene Shalygin static struct platform_device *asus_ec_sensors_platform_device;
107888700d13SEugene Shalygin 
asus_ec_init(void)107988700d13SEugene Shalygin static int __init asus_ec_init(void)
108088700d13SEugene Shalygin {
108188700d13SEugene Shalygin 	asus_ec_sensors_platform_device =
108288700d13SEugene Shalygin 		platform_create_bundle(&asus_ec_sensors_platform_driver,
108388700d13SEugene Shalygin 				       asus_ec_probe, NULL, 0, NULL, 0);
108488700d13SEugene Shalygin 
108588700d13SEugene Shalygin 	if (IS_ERR(asus_ec_sensors_platform_device))
108688700d13SEugene Shalygin 		return PTR_ERR(asus_ec_sensors_platform_device);
108788700d13SEugene Shalygin 
108888700d13SEugene Shalygin 	return 0;
108988700d13SEugene Shalygin }
109088700d13SEugene Shalygin 
asus_ec_exit(void)109188700d13SEugene Shalygin static void __exit asus_ec_exit(void)
109288700d13SEugene Shalygin {
109388700d13SEugene Shalygin 	platform_device_unregister(asus_ec_sensors_platform_device);
109488700d13SEugene Shalygin 	platform_driver_unregister(&asus_ec_sensors_platform_driver);
109588700d13SEugene Shalygin }
109688700d13SEugene Shalygin 
109788700d13SEugene Shalygin module_init(asus_ec_init);
109888700d13SEugene Shalygin module_exit(asus_ec_exit);
1099d0ddfd24SEugene Shalygin 
1100d0ddfd24SEugene Shalygin module_param_named(mutex_path, mutex_path_override, charp, 0);
1101d0ddfd24SEugene Shalygin MODULE_PARM_DESC(mutex_path,
1102d0ddfd24SEugene Shalygin 		 "Override ACPI mutex path used to guard access to hardware");
1103d0ddfd24SEugene Shalygin 
1104d0ddfd24SEugene Shalygin MODULE_AUTHOR("Eugene Shalygin <eugene.shalygin@gmail.com>");
1105d0ddfd24SEugene Shalygin MODULE_DESCRIPTION(
1106d0ddfd24SEugene Shalygin 	"HWMON driver for sensors accessible via ACPI EC in ASUS motherboards");
1107d0ddfd24SEugene Shalygin MODULE_LICENSE("GPL");
1108