1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * HWMON driver for ASUS motherboards that publish some sensor values
4 * via the embedded controller registers.
5 *
6 * Copyright (C) 2021 Eugene Shalygin <eugene.shalygin@gmail.com>
7
8 * EC provides:
9 * - Chipset temperature
10 * - CPU temperature
11 * - Motherboard temperature
12 * - T_Sensor temperature
13 * - VRM temperature
14 * - Water In temperature
15 * - Water Out temperature
16 * - CPU Optional fan RPM
17 * - Chipset fan RPM
18 * - VRM Heat Sink fan RPM
19 * - Water Flow fan RPM
20 * - CPU current
21 * - CPU core voltage
22 */
23
24#include <linux/acpi.h>
25#include <linux/bitops.h>
26#include <linux/dev_printk.h>
27#include <linux/dmi.h>
28#include <linux/hwmon.h>
29#include <linux/init.h>
30#include <linux/jiffies.h>
31#include <linux/kernel.h>
32#include <linux/module.h>
33#include <linux/platform_device.h>
34#include <linux/sort.h>
35#include <linux/units.h>
36
37#include <asm/unaligned.h>
38
39static char *mutex_path_override;
40
41/* Writing to this EC register switches EC bank */
42#define ASUS_EC_BANK_REGISTER 0xff
43#define SENSOR_LABEL_LEN 16
44
45/*
46 * Arbitrary set max. allowed bank number. Required for sorting banks and
47 * currently is overkill with just 2 banks used at max, but for the sake
48 * of alignment let's set it to a higher value.
49 */
50#define ASUS_EC_MAX_BANK 3
51
52#define ACPI_LOCK_DELAY_MS 500
53
54/* ACPI mutex for locking access to the EC for the firmware */
55#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX"
56
57#define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX "\\RMTW.ASMX"
58
59#define ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0 "\\_SB_.PCI0.SBRG.SIO1.MUT0"
60
61#define MAX_IDENTICAL_BOARD_VARIATIONS 3
62
63/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
64#define ACPI_GLOBAL_LOCK_PSEUDO_PATH ":GLOBAL_LOCK"
65
66typedef union {
67 u32 value;
68 struct {
69 u8 index;
70 u8 bank;
71 u8 size;
72 u8 dummy;
73 } components;
74} sensor_address;
75
76#define MAKE_SENSOR_ADDRESS(size, bank, index) { \
77 .value = (size << 16) + (bank << 8) + index \
78 }
79
80static u32 hwmon_attributes[hwmon_max] = {
81 [hwmon_chip] = HWMON_C_REGISTER_TZ,
82 [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
83 [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
84 [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
85 [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL,
86};
87
88struct ec_sensor_info {
89 char label[SENSOR_LABEL_LEN];
90 enum hwmon_sensor_types type;
91 sensor_address addr;
92};
93
94#define EC_SENSOR(sensor_label, sensor_type, size, bank, index) { \
95 .label = sensor_label, .type = sensor_type, \
96 .addr = MAKE_SENSOR_ADDRESS(size, bank, index), \
97 }
98
99enum ec_sensors {
100 /* chipset temperature [℃] */
101 ec_sensor_temp_chipset,
102 /* CPU temperature [℃] */
103 ec_sensor_temp_cpu,
104 /* CPU package temperature [℃] */
105 ec_sensor_temp_cpu_package,
106 /* motherboard temperature [℃] */
107 ec_sensor_temp_mb,
108 /* "T_Sensor" temperature sensor reading [℃] */
109 ec_sensor_temp_t_sensor,
110 /* VRM temperature [℃] */
111 ec_sensor_temp_vrm,
112 /* CPU Core voltage [mV] */
113 ec_sensor_in_cpu_core,
114 /* CPU_Opt fan [RPM] */
115 ec_sensor_fan_cpu_opt,
116 /* VRM heat sink fan [RPM] */
117 ec_sensor_fan_vrm_hs,
118 /* Chipset fan [RPM] */
119 ec_sensor_fan_chipset,
120 /* Water flow sensor reading [RPM] */
121 ec_sensor_fan_water_flow,
122 /* CPU current [A] */
123 ec_sensor_curr_cpu,
124 /* "Water_In" temperature sensor reading [℃] */
125 ec_sensor_temp_water_in,
126 /* "Water_Out" temperature sensor reading [℃] */
127 ec_sensor_temp_water_out,
128 /* "Water_Block_In" temperature sensor reading [℃] */
129 ec_sensor_temp_water_block_in,
130 /* "Water_Block_Out" temperature sensor reading [℃] */
131 ec_sensor_temp_water_block_out,
132 /* "T_sensor_2" temperature sensor reading [℃] */
133 ec_sensor_temp_t_sensor_2,
134 /* "Extra_1" temperature sensor reading [℃] */
135 ec_sensor_temp_sensor_extra_1,
136 /* "Extra_2" temperature sensor reading [℃] */
137 ec_sensor_temp_sensor_extra_2,
138 /* "Extra_3" temperature sensor reading [℃] */
139 ec_sensor_temp_sensor_extra_3,
140};
141
142#define SENSOR_TEMP_CHIPSET BIT(ec_sensor_temp_chipset)
143#define SENSOR_TEMP_CPU BIT(ec_sensor_temp_cpu)
144#define SENSOR_TEMP_CPU_PACKAGE BIT(ec_sensor_temp_cpu_package)
145#define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb)
146#define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor)
147#define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm)
148#define SENSOR_IN_CPU_CORE BIT(ec_sensor_in_cpu_core)
149#define SENSOR_FAN_CPU_OPT BIT(ec_sensor_fan_cpu_opt)
150#define SENSOR_FAN_VRM_HS BIT(ec_sensor_fan_vrm_hs)
151#define SENSOR_FAN_CHIPSET BIT(ec_sensor_fan_chipset)
152#define SENSOR_FAN_WATER_FLOW BIT(ec_sensor_fan_water_flow)
153#define SENSOR_CURR_CPU BIT(ec_sensor_curr_cpu)
154#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
155#define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out)
156#define SENSOR_TEMP_WATER_BLOCK_IN BIT(ec_sensor_temp_water_block_in)
157#define SENSOR_TEMP_WATER_BLOCK_OUT BIT(ec_sensor_temp_water_block_out)
158#define SENSOR_TEMP_T_SENSOR_2 BIT(ec_sensor_temp_t_sensor_2)
159#define SENSOR_TEMP_SENSOR_EXTRA_1 BIT(ec_sensor_temp_sensor_extra_1)
160#define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2)
161#define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3)
162
163enum board_family {
164 family_unknown,
165 family_amd_400_series,
166 family_amd_500_series,
167 family_amd_600_series,
168 family_intel_300_series,
169 family_intel_600_series
170};
171
172/* All the known sensors for ASUS EC controllers */
173static const struct ec_sensor_info sensors_family_amd_400[] = {
174 [ec_sensor_temp_chipset] =
175 EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
176 [ec_sensor_temp_cpu] =
177 EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
178 [ec_sensor_temp_mb] =
179 EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
180 [ec_sensor_temp_t_sensor] =
181 EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
182 [ec_sensor_temp_vrm] =
183 EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
184 [ec_sensor_in_cpu_core] =
185 EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
186 [ec_sensor_fan_cpu_opt] =
187 EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
188 [ec_sensor_fan_vrm_hs] =
189 EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
190 [ec_sensor_fan_chipset] =
191 /* no chipset fans in this generation */
192 EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00),
193 [ec_sensor_fan_water_flow] =
194 EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4),
195 [ec_sensor_curr_cpu] =
196 EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
197 [ec_sensor_temp_water_in] =
198 EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
199 [ec_sensor_temp_water_out] =
200 EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b),
201};
202
203static const struct ec_sensor_info sensors_family_amd_500[] = {
204 [ec_sensor_temp_chipset] =
205 EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
206 [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
207 [ec_sensor_temp_mb] =
208 EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
209 [ec_sensor_temp_t_sensor] =
210 EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
211 [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
212 [ec_sensor_in_cpu_core] =
213 EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
214 [ec_sensor_fan_cpu_opt] =
215 EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
216 [ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
217 [ec_sensor_fan_chipset] =
218 EC_SENSOR("Chipset", hwmon_fan, 2, 0x00, 0xb4),
219 [ec_sensor_fan_water_flow] =
220 EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
221 [ec_sensor_curr_cpu] = EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
222 [ec_sensor_temp_water_in] =
223 EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
224 [ec_sensor_temp_water_out] =
225 EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
226 [ec_sensor_temp_water_block_in] =
227 EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02),
228 [ec_sensor_temp_water_block_out] =
229 EC_SENSOR("Water_Block_Out", hwmon_temp, 1, 0x01, 0x03),
230 [ec_sensor_temp_sensor_extra_1] =
231 EC_SENSOR("Extra_1", hwmon_temp, 1, 0x01, 0x09),
232 [ec_sensor_temp_t_sensor_2] =
233 EC_SENSOR("T_sensor_2", hwmon_temp, 1, 0x01, 0x0a),
234 [ec_sensor_temp_sensor_extra_2] =
235 EC_SENSOR("Extra_2", hwmon_temp, 1, 0x01, 0x0b),
236 [ec_sensor_temp_sensor_extra_3] =
237 EC_SENSOR("Extra_3", hwmon_temp, 1, 0x01, 0x0c),
238};
239
240static const struct ec_sensor_info sensors_family_amd_600[] = {
241 [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30),
242 [ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
243 [ec_sensor_temp_mb] =
244 EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
245 [ec_sensor_temp_vrm] =
246 EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
247 [ec_sensor_temp_t_sensor] =
248 EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36),
249 [ec_sensor_temp_water_in] =
250 EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
251 [ec_sensor_temp_water_out] =
252 EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
253};
254
255static const struct ec_sensor_info sensors_family_intel_300[] = {
256 [ec_sensor_temp_chipset] =
257 EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
258 [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
259 [ec_sensor_temp_mb] =
260 EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
261 [ec_sensor_temp_t_sensor] =
262 EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
263 [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
264 [ec_sensor_fan_cpu_opt] =
265 EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
266 [ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
267 [ec_sensor_fan_water_flow] =
268 EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
269 [ec_sensor_temp_water_in] =
270 EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
271 [ec_sensor_temp_water_out] =
272 EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
273};
274
275static const struct ec_sensor_info sensors_family_intel_600[] = {
276 [ec_sensor_temp_t_sensor] =
277 EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
278 [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
279};
280
281/* Shortcuts for common combinations */
282#define SENSOR_SET_TEMP_CHIPSET_CPU_MB \
283 (SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB)
284#define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT)
285#define SENSOR_SET_WATER_BLOCK \
286 (SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT)
287
288struct ec_board_info {
289 unsigned long sensors;
290 /*
291 * Defines which mutex to use for guarding access to the state and the
292 * hardware. Can be either a full path to an AML mutex or the
293 * pseudo-path ACPI_GLOBAL_LOCK_PSEUDO_PATH to use the global ACPI lock,
294 * or left empty to use a regular mutex object, in which case access to
295 * the hardware is not guarded.
296 */
297 const char *mutex_path;
298 enum board_family family;
299};
300
301static const struct ec_board_info board_info_prime_x470_pro = {
302 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
303 SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
304 SENSOR_FAN_CPU_OPT |
305 SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
306 .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
307 .family = family_amd_400_series,
308};
309
310static const struct ec_board_info board_info_prime_x570_pro = {
311 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
312 SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
313 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
314 .family = family_amd_500_series,
315};
316
317static const struct ec_board_info board_info_pro_art_x570_creator_wifi = {
318 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
319 SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
320 SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
321 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
322 .family = family_amd_500_series,
323};
324
325static const struct ec_board_info board_info_pro_art_b550_creator = {
326 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
327 SENSOR_TEMP_T_SENSOR |
328 SENSOR_FAN_CPU_OPT,
329 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
330 .family = family_amd_500_series,
331};
332
333static const struct ec_board_info board_info_pro_ws_x570_ace = {
334 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
335 SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET |
336 SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
337 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
338 .family = family_amd_500_series,
339};
340
341static const struct ec_board_info board_info_crosshair_x670e_hero = {
342 .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
343 SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
344 SENSOR_SET_TEMP_WATER,
345 .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
346 .family = family_amd_600_series,
347};
348
349static const struct ec_board_info board_info_crosshair_x670e_gene = {
350 .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
351 SENSOR_TEMP_T_SENSOR |
352 SENSOR_TEMP_MB | SENSOR_TEMP_VRM,
353 .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
354 .family = family_amd_600_series,
355};
356
357static const struct ec_board_info board_info_crosshair_viii_dark_hero = {
358 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
359 SENSOR_TEMP_T_SENSOR |
360 SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
361 SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
362 SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
363 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
364 .family = family_amd_500_series,
365};
366
367static const struct ec_board_info board_info_crosshair_viii_hero = {
368 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
369 SENSOR_TEMP_T_SENSOR |
370 SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
371 SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
372 SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU |
373 SENSOR_IN_CPU_CORE,
374 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
375 .family = family_amd_500_series,
376};
377
378static const struct ec_board_info board_info_maximus_xi_hero = {
379 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
380 SENSOR_TEMP_T_SENSOR |
381 SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
382 SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW,
383 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
384 .family = family_intel_300_series,
385};
386
387static const struct ec_board_info board_info_crosshair_viii_impact = {
388 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
389 SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
390 SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
391 SENSOR_IN_CPU_CORE,
392 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
393 .family = family_amd_500_series,
394};
395
396static const struct ec_board_info board_info_strix_b550_e_gaming = {
397 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
398 SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
399 SENSOR_FAN_CPU_OPT,
400 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
401 .family = family_amd_500_series,
402};
403
404static const struct ec_board_info board_info_strix_b550_i_gaming = {
405 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
406 SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
407 SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU |
408 SENSOR_IN_CPU_CORE,
409 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
410 .family = family_amd_500_series,
411};
412
413static const struct ec_board_info board_info_strix_x570_e_gaming = {
414 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
415 SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
416 SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
417 SENSOR_IN_CPU_CORE,
418 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
419 .family = family_amd_500_series,
420};
421
422static const struct ec_board_info board_info_strix_x570_e_gaming_wifi_ii = {
423 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
424 SENSOR_TEMP_T_SENSOR | SENSOR_CURR_CPU |
425 SENSOR_IN_CPU_CORE,
426 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
427 .family = family_amd_500_series,
428};
429
430static const struct ec_board_info board_info_strix_x570_f_gaming = {
431 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
432 SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
433 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
434 .family = family_amd_500_series,
435};
436
437static const struct ec_board_info board_info_strix_x570_i_gaming = {
438 .sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
439 SENSOR_TEMP_T_SENSOR |
440 SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET |
441 SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
442 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
443 .family = family_amd_500_series,
444};
445
446static const struct ec_board_info board_info_strix_z390_f_gaming = {
447 .sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
448 SENSOR_TEMP_T_SENSOR |
449 SENSOR_FAN_CPU_OPT,
450 .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
451 .family = family_intel_300_series,
452};
453
454static const struct ec_board_info board_info_strix_z690_a_gaming_wifi_d4 = {
455 .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM,
456 .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
457 .family = family_intel_600_series,
458};
459
460static const struct ec_board_info board_info_zenith_ii_extreme = {
461 .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
462 SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
463 SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_VRM_HS |
464 SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE |
465 SENSOR_SET_WATER_BLOCK |
466 SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 |
467 SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3,
468 .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
469 .family = family_amd_500_series,
470};
471
472#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, board_info) \
473 { \
474 .matches = { \
475 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \
476 "ASUSTeK COMPUTER INC."), \
477 DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
478 }, \
479 .driver_data = (void *)board_info, \
480 }
481
482static const struct dmi_system_id dmi_table[] = {
483 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO",
484 &board_info_prime_x470_pro),
485 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO",
486 &board_info_prime_x570_pro),
487 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI",
488 &board_info_pro_art_x570_creator_wifi),
489 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt B550-CREATOR",
490 &board_info_pro_art_b550_creator),
491 DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE",
492 &board_info_pro_ws_x570_ace),
493 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII DARK HERO",
494 &board_info_crosshair_viii_dark_hero),
495 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII FORMULA",
496 &board_info_crosshair_viii_hero),
497 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO",
498 &board_info_crosshair_viii_hero),
499 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO (WI-FI)",
500 &board_info_crosshair_viii_hero),
501 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO",
502 &board_info_crosshair_x670e_hero),
503 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E GENE",
504 &board_info_crosshair_x670e_gene),
505 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO",
506 &board_info_maximus_xi_hero),
507 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)",
508 &board_info_maximus_xi_hero),
509 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII IMPACT",
510 &board_info_crosshair_viii_impact),
511 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING",
512 &board_info_strix_b550_e_gaming),
513 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING",
514 &board_info_strix_b550_i_gaming),
515 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING",
516 &board_info_strix_x570_e_gaming),
517 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING WIFI II",
518 &board_info_strix_x570_e_gaming_wifi_ii),
519 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-F GAMING",
520 &board_info_strix_x570_f_gaming),
521 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-I GAMING",
522 &board_info_strix_x570_i_gaming),
523 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING",
524 &board_info_strix_z390_f_gaming),
525 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z690-A GAMING WIFI D4",
526 &board_info_strix_z690_a_gaming_wifi_d4),
527 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME",
528 &board_info_zenith_ii_extreme),
529 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME ALPHA",
530 &board_info_zenith_ii_extreme),
531 {},
532};
533
534struct ec_sensor {
535 unsigned int info_index;
536 s32 cached_value;
537};
538
539struct lock_data {
540 union {
541 acpi_handle aml;
542 /* global lock handle */
543 u32 glk;
544 } mutex;
545 bool (*lock)(struct lock_data *data);
546 bool (*unlock)(struct lock_data *data);
547};
548
549/*
550 * The next function pairs implement options for locking access to the
551 * state and the EC
552 */
553static bool lock_via_acpi_mutex(struct lock_data *data)
554{
555 /*
556 * ASUS DSDT does not specify that access to the EC has to be guarded,
557 * but firmware does access it via ACPI
558 */
559 return ACPI_SUCCESS(acpi_acquire_mutex(data->mutex.aml,
560 NULL, ACPI_LOCK_DELAY_MS));
561}
562
563static bool unlock_acpi_mutex(struct lock_data *data)
564{
565 return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL));
566}
567
568static bool lock_via_global_acpi_lock(struct lock_data *data)
569{
570 return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS,
571 &data->mutex.glk));
572}
573
574static bool unlock_global_acpi_lock(struct lock_data *data)
575{
576 return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk));
577}
578
579struct ec_sensors_data {
580 const struct ec_board_info *board_info;
581 const struct ec_sensor_info *sensors_info;
582 struct ec_sensor *sensors;
583 /* EC registers to read from */
584 u16 *registers;
585 u8 *read_buffer;
586 /* sorted list of unique register banks */
587 u8 banks[ASUS_EC_MAX_BANK + 1];
588 /* in jiffies */
589 unsigned long last_updated;
590 struct lock_data lock_data;
591 /* number of board EC sensors */
592 u8 nr_sensors;
593 /*
594 * number of EC registers to read
595 * (sensor might span more than 1 register)
596 */
597 u8 nr_registers;
598 /* number of unique register banks */
599 u8 nr_banks;
600};
601
602static u8 register_bank(u16 reg)
603{
604 return reg >> 8;
605}
606
607static u8 register_index(u16 reg)
608{
609 return reg & 0x00ff;
610}
611
612static bool is_sensor_data_signed(const struct ec_sensor_info *si)
613{
614 /*
615 * guessed from WMI functions in DSDT code for boards
616 * of the X470 generation
617 */
618 return si->type == hwmon_temp;
619}
620
621static const struct ec_sensor_info *
622get_sensor_info(const struct ec_sensors_data *state, int index)
623{
624 return state->sensors_info + state->sensors[index].info_index;
625}
626
627static int find_ec_sensor_index(const struct ec_sensors_data *ec,
628 enum hwmon_sensor_types type, int channel)
629{
630 unsigned int i;
631
632 for (i = 0; i < ec->nr_sensors; i++) {
633 if (get_sensor_info(state: ec, index: i)->type == type) {
634 if (channel == 0)
635 return i;
636 channel--;
637 }
638 }
639 return -ENOENT;
640}
641
642static int bank_compare(const void *a, const void *b)
643{
644 return *((const s8 *)a) - *((const s8 *)b);
645}
646
647static void setup_sensor_data(struct ec_sensors_data *ec)
648{
649 struct ec_sensor *s = ec->sensors;
650 bool bank_found;
651 int i, j;
652 u8 bank;
653
654 ec->nr_banks = 0;
655 ec->nr_registers = 0;
656
657 for_each_set_bit(i, &ec->board_info->sensors,
658 BITS_PER_TYPE(ec->board_info->sensors)) {
659 s->info_index = i;
660 s->cached_value = 0;
661 ec->nr_registers +=
662 ec->sensors_info[s->info_index].addr.components.size;
663 bank_found = false;
664 bank = ec->sensors_info[s->info_index].addr.components.bank;
665 for (j = 0; j < ec->nr_banks; j++) {
666 if (ec->banks[j] == bank) {
667 bank_found = true;
668 break;
669 }
670 }
671 if (!bank_found) {
672 ec->banks[ec->nr_banks++] = bank;
673 }
674 s++;
675 }
676 sort(base: ec->banks, num: ec->nr_banks, size: 1, cmp_func: bank_compare, NULL);
677}
678
679static void fill_ec_registers(struct ec_sensors_data *ec)
680{
681 const struct ec_sensor_info *si;
682 unsigned int i, j, register_idx = 0;
683
684 for (i = 0; i < ec->nr_sensors; ++i) {
685 si = get_sensor_info(state: ec, index: i);
686 for (j = 0; j < si->addr.components.size; ++j, ++register_idx) {
687 ec->registers[register_idx] =
688 (si->addr.components.bank << 8) +
689 si->addr.components.index + j;
690 }
691 }
692}
693
694static int setup_lock_data(struct device *dev)
695{
696 const char *mutex_path;
697 int status;
698 struct ec_sensors_data *state = dev_get_drvdata(dev);
699
700 mutex_path = mutex_path_override ?
701 mutex_path_override : state->board_info->mutex_path;
702
703 if (!mutex_path || !strlen(mutex_path)) {
704 dev_err(dev, "Hardware access guard mutex name is empty");
705 return -EINVAL;
706 }
707 if (!strcmp(mutex_path, ACPI_GLOBAL_LOCK_PSEUDO_PATH)) {
708 state->lock_data.mutex.glk = 0;
709 state->lock_data.lock = lock_via_global_acpi_lock;
710 state->lock_data.unlock = unlock_global_acpi_lock;
711 } else {
712 status = acpi_get_handle(NULL, pathname: (acpi_string)mutex_path,
713 ret_handle: &state->lock_data.mutex.aml);
714 if (ACPI_FAILURE(status)) {
715 dev_err(dev,
716 "Failed to get hardware access guard AML mutex '%s': error %d",
717 mutex_path, status);
718 return -ENOENT;
719 }
720 state->lock_data.lock = lock_via_acpi_mutex;
721 state->lock_data.unlock = unlock_acpi_mutex;
722 }
723 return 0;
724}
725
726static int asus_ec_bank_switch(u8 bank, u8 *old)
727{
728 int status = 0;
729
730 if (old) {
731 status = ec_read(ASUS_EC_BANK_REGISTER, val: old);
732 }
733 if (status || (old && (*old == bank)))
734 return status;
735 return ec_write(ASUS_EC_BANK_REGISTER, val: bank);
736}
737
738static int asus_ec_block_read(const struct device *dev,
739 struct ec_sensors_data *ec)
740{
741 int ireg, ibank, status;
742 u8 bank, reg_bank, prev_bank;
743
744 bank = 0;
745 status = asus_ec_bank_switch(bank, old: &prev_bank);
746 if (status) {
747 dev_warn(dev, "EC bank switch failed");
748 return status;
749 }
750
751 if (prev_bank) {
752 /* oops... somebody else is working with the EC too */
753 dev_warn(dev,
754 "Concurrent access to the ACPI EC detected.\nRace condition possible.");
755 }
756
757 /* read registers minimizing bank switches. */
758 for (ibank = 0; ibank < ec->nr_banks; ibank++) {
759 if (bank != ec->banks[ibank]) {
760 bank = ec->banks[ibank];
761 if (asus_ec_bank_switch(bank, NULL)) {
762 dev_warn(dev, "EC bank switch to %d failed",
763 bank);
764 break;
765 }
766 }
767 for (ireg = 0; ireg < ec->nr_registers; ireg++) {
768 reg_bank = register_bank(reg: ec->registers[ireg]);
769 if (reg_bank < bank) {
770 continue;
771 }
772 ec_read(addr: register_index(reg: ec->registers[ireg]),
773 val: ec->read_buffer + ireg);
774 }
775 }
776
777 status = asus_ec_bank_switch(bank: prev_bank, NULL);
778 return status;
779}
780
781static inline s32 get_sensor_value(const struct ec_sensor_info *si, u8 *data)
782{
783 if (is_sensor_data_signed(si)) {
784 switch (si->addr.components.size) {
785 case 1:
786 return (s8)*data;
787 case 2:
788 return (s16)get_unaligned_be16(p: data);
789 case 4:
790 return (s32)get_unaligned_be32(p: data);
791 default:
792 return 0;
793 }
794 } else {
795 switch (si->addr.components.size) {
796 case 1:
797 return *data;
798 case 2:
799 return get_unaligned_be16(p: data);
800 case 4:
801 return get_unaligned_be32(p: data);
802 default:
803 return 0;
804 }
805 }
806}
807
808static void update_sensor_values(struct ec_sensors_data *ec, u8 *data)
809{
810 const struct ec_sensor_info *si;
811 struct ec_sensor *s, *sensor_end;
812
813 sensor_end = ec->sensors + ec->nr_sensors;
814 for (s = ec->sensors; s != sensor_end; s++) {
815 si = ec->sensors_info + s->info_index;
816 s->cached_value = get_sensor_value(si, data);
817 data += si->addr.components.size;
818 }
819}
820
821static int update_ec_sensors(const struct device *dev,
822 struct ec_sensors_data *ec)
823{
824 int status;
825
826 if (!ec->lock_data.lock(&ec->lock_data)) {
827 dev_warn(dev, "Failed to acquire mutex");
828 return -EBUSY;
829 }
830
831 status = asus_ec_block_read(dev, ec);
832
833 if (!status) {
834 update_sensor_values(ec, data: ec->read_buffer);
835 }
836
837 if (!ec->lock_data.unlock(&ec->lock_data))
838 dev_err(dev, "Failed to release mutex");
839
840 return status;
841}
842
843static long scale_sensor_value(s32 value, int data_type)
844{
845 switch (data_type) {
846 case hwmon_curr:
847 case hwmon_temp:
848 return value * MILLI;
849 default:
850 return value;
851 }
852}
853
854static int get_cached_value_or_update(const struct device *dev,
855 int sensor_index,
856 struct ec_sensors_data *state, s32 *value)
857{
858 if (time_after(jiffies, state->last_updated + HZ)) {
859 if (update_ec_sensors(dev, ec: state)) {
860 dev_err(dev, "update_ec_sensors() failure\n");
861 return -EIO;
862 }
863
864 state->last_updated = jiffies;
865 }
866
867 *value = state->sensors[sensor_index].cached_value;
868 return 0;
869}
870
871/*
872 * Now follow the functions that implement the hwmon interface
873 */
874
875static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
876 u32 attr, int channel, long *val)
877{
878 int ret;
879 s32 value = 0;
880
881 struct ec_sensors_data *state = dev_get_drvdata(dev);
882 int sidx = find_ec_sensor_index(ec: state, type, channel);
883
884 if (sidx < 0) {
885 return sidx;
886 }
887
888 ret = get_cached_value_or_update(dev, sensor_index: sidx, state, value: &value);
889 if (!ret) {
890 *val = scale_sensor_value(value,
891 data_type: get_sensor_info(state, index: sidx)->type);
892 }
893
894 return ret;
895}
896
897static int asus_ec_hwmon_read_string(struct device *dev,
898 enum hwmon_sensor_types type, u32 attr,
899 int channel, const char **str)
900{
901 struct ec_sensors_data *state = dev_get_drvdata(dev);
902 int sensor_index = find_ec_sensor_index(ec: state, type, channel);
903 *str = get_sensor_info(state, index: sensor_index)->label;
904
905 return 0;
906}
907
908static umode_t asus_ec_hwmon_is_visible(const void *drvdata,
909 enum hwmon_sensor_types type, u32 attr,
910 int channel)
911{
912 const struct ec_sensors_data *state = drvdata;
913
914 return find_ec_sensor_index(ec: state, type, channel) >= 0 ? S_IRUGO : 0;
915}
916
917static int
918asus_ec_hwmon_add_chan_info(struct hwmon_channel_info *asus_ec_hwmon_chan,
919 struct device *dev, int num,
920 enum hwmon_sensor_types type, u32 config)
921{
922 int i;
923 u32 *cfg = devm_kcalloc(dev, n: num + 1, size: sizeof(*cfg), GFP_KERNEL);
924
925 if (!cfg)
926 return -ENOMEM;
927
928 asus_ec_hwmon_chan->type = type;
929 asus_ec_hwmon_chan->config = cfg;
930 for (i = 0; i < num; i++, cfg++)
931 *cfg = config;
932
933 return 0;
934}
935
936static const struct hwmon_ops asus_ec_hwmon_ops = {
937 .is_visible = asus_ec_hwmon_is_visible,
938 .read = asus_ec_hwmon_read,
939 .read_string = asus_ec_hwmon_read_string,
940};
941
942static struct hwmon_chip_info asus_ec_chip_info = {
943 .ops = &asus_ec_hwmon_ops,
944};
945
946static const struct ec_board_info *get_board_info(void)
947{
948 const struct dmi_system_id *dmi_entry;
949
950 dmi_entry = dmi_first_match(list: dmi_table);
951 return dmi_entry ? dmi_entry->driver_data : NULL;
952}
953
954static int asus_ec_probe(struct platform_device *pdev)
955{
956 const struct hwmon_channel_info **ptr_asus_ec_ci;
957 int nr_count[hwmon_max] = { 0 }, nr_types = 0;
958 struct hwmon_channel_info *asus_ec_hwmon_chan;
959 const struct ec_board_info *pboard_info;
960 const struct hwmon_chip_info *chip_info;
961 struct device *dev = &pdev->dev;
962 struct ec_sensors_data *ec_data;
963 const struct ec_sensor_info *si;
964 enum hwmon_sensor_types type;
965 struct device *hwdev;
966 unsigned int i;
967 int status;
968
969 pboard_info = get_board_info();
970 if (!pboard_info)
971 return -ENODEV;
972
973 ec_data = devm_kzalloc(dev, size: sizeof(struct ec_sensors_data),
974 GFP_KERNEL);
975 if (!ec_data)
976 return -ENOMEM;
977
978 dev_set_drvdata(dev, data: ec_data);
979 ec_data->board_info = pboard_info;
980
981 switch (ec_data->board_info->family) {
982 case family_amd_400_series:
983 ec_data->sensors_info = sensors_family_amd_400;
984 break;
985 case family_amd_500_series:
986 ec_data->sensors_info = sensors_family_amd_500;
987 break;
988 case family_amd_600_series:
989 ec_data->sensors_info = sensors_family_amd_600;
990 break;
991 case family_intel_300_series:
992 ec_data->sensors_info = sensors_family_intel_300;
993 break;
994 case family_intel_600_series:
995 ec_data->sensors_info = sensors_family_intel_600;
996 break;
997 default:
998 dev_err(dev, "Unknown board family: %d",
999 ec_data->board_info->family);
1000 return -EINVAL;
1001 }
1002
1003 ec_data->nr_sensors = hweight_long(w: ec_data->board_info->sensors);
1004 ec_data->sensors = devm_kcalloc(dev, n: ec_data->nr_sensors,
1005 size: sizeof(struct ec_sensor), GFP_KERNEL);
1006 if (!ec_data->sensors)
1007 return -ENOMEM;
1008
1009 status = setup_lock_data(dev);
1010 if (status) {
1011 dev_err(dev, "Failed to setup state/EC locking: %d", status);
1012 return status;
1013 }
1014
1015 setup_sensor_data(ec_data);
1016 ec_data->registers = devm_kcalloc(dev, n: ec_data->nr_registers,
1017 size: sizeof(u16), GFP_KERNEL);
1018 ec_data->read_buffer = devm_kcalloc(dev, n: ec_data->nr_registers,
1019 size: sizeof(u8), GFP_KERNEL);
1020
1021 if (!ec_data->registers || !ec_data->read_buffer)
1022 return -ENOMEM;
1023
1024 fill_ec_registers(ec: ec_data);
1025
1026 for (i = 0; i < ec_data->nr_sensors; ++i) {
1027 si = get_sensor_info(state: ec_data, index: i);
1028 if (!nr_count[si->type])
1029 ++nr_types;
1030 ++nr_count[si->type];
1031 }
1032
1033 if (nr_count[hwmon_temp])
1034 nr_count[hwmon_chip]++, nr_types++;
1035
1036 asus_ec_hwmon_chan = devm_kcalloc(
1037 dev, n: nr_types, size: sizeof(*asus_ec_hwmon_chan), GFP_KERNEL);
1038 if (!asus_ec_hwmon_chan)
1039 return -ENOMEM;
1040
1041 ptr_asus_ec_ci = devm_kcalloc(dev, n: nr_types + 1,
1042 size: sizeof(*ptr_asus_ec_ci), GFP_KERNEL);
1043 if (!ptr_asus_ec_ci)
1044 return -ENOMEM;
1045
1046 asus_ec_chip_info.info = ptr_asus_ec_ci;
1047 chip_info = &asus_ec_chip_info;
1048
1049 for (type = 0; type < hwmon_max; ++type) {
1050 if (!nr_count[type])
1051 continue;
1052
1053 asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev,
1054 num: nr_count[type], type,
1055 config: hwmon_attributes[type]);
1056 *ptr_asus_ec_ci++ = asus_ec_hwmon_chan++;
1057 }
1058
1059 dev_info(dev, "board has %d EC sensors that span %d registers",
1060 ec_data->nr_sensors, ec_data->nr_registers);
1061
1062 hwdev = devm_hwmon_device_register_with_info(dev, name: "asusec",
1063 drvdata: ec_data, info: chip_info, NULL);
1064
1065 return PTR_ERR_OR_ZERO(ptr: hwdev);
1066}
1067
1068MODULE_DEVICE_TABLE(dmi, dmi_table);
1069
1070static struct platform_driver asus_ec_sensors_platform_driver = {
1071 .driver = {
1072 .name = "asus-ec-sensors",
1073 },
1074 .probe = asus_ec_probe,
1075};
1076
1077static struct platform_device *asus_ec_sensors_platform_device;
1078
1079static int __init asus_ec_init(void)
1080{
1081 asus_ec_sensors_platform_device =
1082 platform_create_bundle(&asus_ec_sensors_platform_driver,
1083 asus_ec_probe, NULL, 0, NULL, 0);
1084
1085 if (IS_ERR(ptr: asus_ec_sensors_platform_device))
1086 return PTR_ERR(ptr: asus_ec_sensors_platform_device);
1087
1088 return 0;
1089}
1090
1091static void __exit asus_ec_exit(void)
1092{
1093 platform_device_unregister(asus_ec_sensors_platform_device);
1094 platform_driver_unregister(&asus_ec_sensors_platform_driver);
1095}
1096
1097module_init(asus_ec_init);
1098module_exit(asus_ec_exit);
1099
1100module_param_named(mutex_path, mutex_path_override, charp, 0);
1101MODULE_PARM_DESC(mutex_path,
1102 "Override ACPI mutex path used to guard access to hardware");
1103
1104MODULE_AUTHOR("Eugene Shalygin <eugene.shalygin@gmail.com>");
1105MODULE_DESCRIPTION(
1106 "HWMON driver for sensors accessible via ACPI EC in ASUS motherboards");
1107MODULE_LICENSE("GPL");
1108

source code of linux/drivers/hwmon/asus-ec-sensors.c