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 | |
39 | static 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 | |
66 | typedef 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 | |
80 | static 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 | |
88 | struct 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 | |
99 | enum 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 | , |
136 | /* "Extra_2" temperature sensor reading [℃] */ |
137 | , |
138 | /* "Extra_3" temperature sensor reading [℃] */ |
139 | , |
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 BIT(ec_sensor_temp_sensor_extra_1) |
160 | #define BIT(ec_sensor_temp_sensor_extra_2) |
161 | #define BIT(ec_sensor_temp_sensor_extra_3) |
162 | |
163 | enum 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 */ |
173 | static 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 | |
203 | static 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 | |
240 | static 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 | |
255 | static 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 | |
275 | static 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 | |
288 | struct 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 | |
301 | static 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 | |
310 | static 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 | |
317 | static 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 | |
325 | static 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 | |
333 | static 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 | |
341 | static 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 | |
349 | static 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 | |
357 | static 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 | |
367 | static 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 | |
378 | static 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 | |
387 | static 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 | |
396 | static 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 | |
404 | static 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 | |
413 | static 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 | |
422 | static 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 | |
430 | static 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 | |
437 | static 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 | |
446 | static 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 | |
454 | static 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 | |
460 | static 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 | |
482 | static 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 | |
534 | struct ec_sensor { |
535 | unsigned int info_index; |
536 | s32 cached_value; |
537 | }; |
538 | |
539 | struct 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 | */ |
553 | static 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 | |
563 | static bool unlock_acpi_mutex(struct lock_data *data) |
564 | { |
565 | return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL)); |
566 | } |
567 | |
568 | static 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 | |
574 | static bool unlock_global_acpi_lock(struct lock_data *data) |
575 | { |
576 | return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk)); |
577 | } |
578 | |
579 | struct 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 | |
602 | static u8 register_bank(u16 reg) |
603 | { |
604 | return reg >> 8; |
605 | } |
606 | |
607 | static u8 register_index(u16 reg) |
608 | { |
609 | return reg & 0x00ff; |
610 | } |
611 | |
612 | static 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 | |
621 | static const struct ec_sensor_info * |
622 | get_sensor_info(const struct ec_sensors_data *state, int index) |
623 | { |
624 | return state->sensors_info + state->sensors[index].info_index; |
625 | } |
626 | |
627 | static 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 | |
642 | static int bank_compare(const void *a, const void *b) |
643 | { |
644 | return *((const s8 *)a) - *((const s8 *)b); |
645 | } |
646 | |
647 | static 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 | |
679 | static 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 | |
694 | static 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 | |
726 | static 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 | |
738 | static 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 | |
781 | static 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 | |
808 | static 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 | |
821 | static 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 | |
843 | static 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 | |
854 | static 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 | |
875 | static 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 | |
897 | static 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 | |
908 | static 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 | |
917 | static int |
918 | asus_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 | |
936 | static 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 | |
942 | static struct hwmon_chip_info asus_ec_chip_info = { |
943 | .ops = &asus_ec_hwmon_ops, |
944 | }; |
945 | |
946 | static 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 | |
954 | static 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 | |
1068 | MODULE_DEVICE_TABLE(dmi, dmi_table); |
1069 | |
1070 | static struct platform_driver asus_ec_sensors_platform_driver = { |
1071 | .driver = { |
1072 | .name = "asus-ec-sensors" , |
1073 | }, |
1074 | .probe = asus_ec_probe, |
1075 | }; |
1076 | |
1077 | static struct platform_device *asus_ec_sensors_platform_device; |
1078 | |
1079 | static 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 | |
1091 | static 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 | |
1097 | module_init(asus_ec_init); |
1098 | module_exit(asus_ec_exit); |
1099 | |
1100 | module_param_named(mutex_path, mutex_path_override, charp, 0); |
1101 | MODULE_PARM_DESC(mutex_path, |
1102 | "Override ACPI mutex path used to guard access to hardware" ); |
1103 | |
1104 | MODULE_AUTHOR("Eugene Shalygin <eugene.shalygin@gmail.com>" ); |
1105 | MODULE_DESCRIPTION( |
1106 | "HWMON driver for sensors accessible via ACPI EC in ASUS motherboards" ); |
1107 | MODULE_LICENSE("GPL" ); |
1108 | |