1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | /* |
4 | * msi-ec: MSI laptops' embedded controller driver. |
5 | * |
6 | * This driver allows various MSI laptops' functionalities to be |
7 | * controlled from userspace. |
8 | * |
9 | * It contains EC memory configurations for different firmware versions |
10 | * and exports battery charge thresholds to userspace. |
11 | * |
12 | * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es> |
13 | * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev> |
14 | * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com> |
15 | */ |
16 | |
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | |
19 | #include "msi-ec.h" |
20 | |
21 | #include <acpi/battery.h> |
22 | #include <linux/acpi.h> |
23 | #include <linux/init.h> |
24 | #include <linux/kernel.h> |
25 | #include <linux/module.h> |
26 | #include <linux/platform_device.h> |
27 | #include <linux/seq_file.h> |
28 | #include <linux/string.h> |
29 | |
30 | #define SM_ECO_NAME "eco" |
31 | #define SM_COMFORT_NAME "comfort" |
32 | #define SM_SPORT_NAME "sport" |
33 | #define SM_TURBO_NAME "turbo" |
34 | |
35 | #define FM_AUTO_NAME "auto" |
36 | #define FM_SILENT_NAME "silent" |
37 | #define FM_BASIC_NAME "basic" |
38 | #define FM_ADVANCED_NAME "advanced" |
39 | |
40 | static const char * const ALLOWED_FW_0[] __initconst = { |
41 | "14C1EMS1.012" , |
42 | "14C1EMS1.101" , |
43 | "14C1EMS1.102" , |
44 | NULL |
45 | }; |
46 | |
47 | static struct msi_ec_conf CONF0 __initdata = { |
48 | .allowed_fw = ALLOWED_FW_0, |
49 | .charge_control = { |
50 | .address = 0xef, |
51 | .offset_start = 0x8a, |
52 | .offset_end = 0x80, |
53 | .range_min = 0x8a, |
54 | .range_max = 0xe4, |
55 | }, |
56 | .webcam = { |
57 | .address = 0x2e, |
58 | .block_address = 0x2f, |
59 | .bit = 1, |
60 | }, |
61 | .fn_win_swap = { |
62 | .address = 0xbf, |
63 | .bit = 4, |
64 | }, |
65 | .cooler_boost = { |
66 | .address = 0x98, |
67 | .bit = 7, |
68 | }, |
69 | .shift_mode = { |
70 | .address = 0xf2, |
71 | .modes = { |
72 | { SM_ECO_NAME, 0xc2 }, |
73 | { SM_COMFORT_NAME, 0xc1 }, |
74 | { SM_SPORT_NAME, 0xc0 }, |
75 | MSI_EC_MODE_NULL |
76 | }, |
77 | }, |
78 | .super_battery = { |
79 | .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing |
80 | }, |
81 | .fan_mode = { |
82 | .address = 0xf4, |
83 | .modes = { |
84 | { FM_AUTO_NAME, 0x0d }, |
85 | { FM_SILENT_NAME, 0x1d }, |
86 | { FM_BASIC_NAME, 0x4d }, |
87 | { FM_ADVANCED_NAME, 0x8d }, |
88 | MSI_EC_MODE_NULL |
89 | }, |
90 | }, |
91 | .cpu = { |
92 | .rt_temp_address = 0x68, |
93 | .rt_fan_speed_address = 0x71, |
94 | .rt_fan_speed_base_min = 0x19, |
95 | .rt_fan_speed_base_max = 0x37, |
96 | .bs_fan_speed_address = 0x89, |
97 | .bs_fan_speed_base_min = 0x00, |
98 | .bs_fan_speed_base_max = 0x0f, |
99 | }, |
100 | .gpu = { |
101 | .rt_temp_address = 0x80, |
102 | .rt_fan_speed_address = 0x89, |
103 | }, |
104 | .leds = { |
105 | .micmute_led_address = 0x2b, |
106 | .mute_led_address = 0x2c, |
107 | .bit = 2, |
108 | }, |
109 | .kbd_bl = { |
110 | .bl_mode_address = 0x2c, // ? |
111 | .bl_modes = { 0x00, 0x08 }, // ? |
112 | .max_mode = 1, // ? |
113 | .bl_state_address = 0xf3, |
114 | .state_base_value = 0x80, |
115 | .max_state = 3, |
116 | }, |
117 | }; |
118 | |
119 | static const char * const ALLOWED_FW_1[] __initconst = { |
120 | "17F2EMS1.103" , |
121 | "17F2EMS1.104" , |
122 | "17F2EMS1.106" , |
123 | "17F2EMS1.107" , |
124 | NULL |
125 | }; |
126 | |
127 | static struct msi_ec_conf CONF1 __initdata = { |
128 | .allowed_fw = ALLOWED_FW_1, |
129 | .charge_control = { |
130 | .address = 0xef, |
131 | .offset_start = 0x8a, |
132 | .offset_end = 0x80, |
133 | .range_min = 0x8a, |
134 | .range_max = 0xe4, |
135 | }, |
136 | .webcam = { |
137 | .address = 0x2e, |
138 | .block_address = 0x2f, |
139 | .bit = 1, |
140 | }, |
141 | .fn_win_swap = { |
142 | .address = 0xbf, |
143 | .bit = 4, |
144 | }, |
145 | .cooler_boost = { |
146 | .address = 0x98, |
147 | .bit = 7, |
148 | }, |
149 | .shift_mode = { |
150 | .address = 0xf2, |
151 | .modes = { |
152 | { SM_ECO_NAME, 0xc2 }, |
153 | { SM_COMFORT_NAME, 0xc1 }, |
154 | { SM_SPORT_NAME, 0xc0 }, |
155 | { SM_TURBO_NAME, 0xc4 }, |
156 | MSI_EC_MODE_NULL |
157 | }, |
158 | }, |
159 | .super_battery = { |
160 | .address = MSI_EC_ADDR_UNKNOWN, |
161 | }, |
162 | .fan_mode = { |
163 | .address = 0xf4, |
164 | .modes = { |
165 | { FM_AUTO_NAME, 0x0d }, |
166 | { FM_BASIC_NAME, 0x4d }, |
167 | { FM_ADVANCED_NAME, 0x8d }, |
168 | MSI_EC_MODE_NULL |
169 | }, |
170 | }, |
171 | .cpu = { |
172 | .rt_temp_address = 0x68, |
173 | .rt_fan_speed_address = 0x71, |
174 | .rt_fan_speed_base_min = 0x19, |
175 | .rt_fan_speed_base_max = 0x37, |
176 | .bs_fan_speed_address = 0x89, |
177 | .bs_fan_speed_base_min = 0x00, |
178 | .bs_fan_speed_base_max = 0x0f, |
179 | }, |
180 | .gpu = { |
181 | .rt_temp_address = 0x80, |
182 | .rt_fan_speed_address = 0x89, |
183 | }, |
184 | .leds = { |
185 | .micmute_led_address = 0x2b, |
186 | .mute_led_address = 0x2c, |
187 | .bit = 2, |
188 | }, |
189 | .kbd_bl = { |
190 | .bl_mode_address = 0x2c, // ? |
191 | .bl_modes = { 0x00, 0x08 }, // ? |
192 | .max_mode = 1, // ? |
193 | .bl_state_address = 0xf3, |
194 | .state_base_value = 0x80, |
195 | .max_state = 3, |
196 | }, |
197 | }; |
198 | |
199 | static const char * const ALLOWED_FW_2[] __initconst = { |
200 | "1552EMS1.118" , |
201 | NULL |
202 | }; |
203 | |
204 | static struct msi_ec_conf CONF2 __initdata = { |
205 | .allowed_fw = ALLOWED_FW_2, |
206 | .charge_control = { |
207 | .address = 0xd7, |
208 | .offset_start = 0x8a, |
209 | .offset_end = 0x80, |
210 | .range_min = 0x8a, |
211 | .range_max = 0xe4, |
212 | }, |
213 | .webcam = { |
214 | .address = 0x2e, |
215 | .block_address = 0x2f, |
216 | .bit = 1, |
217 | }, |
218 | .fn_win_swap = { |
219 | .address = 0xe8, |
220 | .bit = 4, |
221 | }, |
222 | .cooler_boost = { |
223 | .address = 0x98, |
224 | .bit = 7, |
225 | }, |
226 | .shift_mode = { |
227 | .address = 0xf2, |
228 | .modes = { |
229 | { SM_ECO_NAME, 0xc2 }, |
230 | { SM_COMFORT_NAME, 0xc1 }, |
231 | { SM_SPORT_NAME, 0xc0 }, |
232 | MSI_EC_MODE_NULL |
233 | }, |
234 | }, |
235 | .super_battery = { |
236 | .address = 0xeb, |
237 | .mask = 0x0f, |
238 | }, |
239 | .fan_mode = { |
240 | .address = 0xd4, |
241 | .modes = { |
242 | { FM_AUTO_NAME, 0x0d }, |
243 | { FM_SILENT_NAME, 0x1d }, |
244 | { FM_BASIC_NAME, 0x4d }, |
245 | { FM_ADVANCED_NAME, 0x8d }, |
246 | MSI_EC_MODE_NULL |
247 | }, |
248 | }, |
249 | .cpu = { |
250 | .rt_temp_address = 0x68, |
251 | .rt_fan_speed_address = 0x71, |
252 | .rt_fan_speed_base_min = 0x19, |
253 | .rt_fan_speed_base_max = 0x37, |
254 | .bs_fan_speed_address = 0x89, |
255 | .bs_fan_speed_base_min = 0x00, |
256 | .bs_fan_speed_base_max = 0x0f, |
257 | }, |
258 | .gpu = { |
259 | .rt_temp_address = 0x80, |
260 | .rt_fan_speed_address = 0x89, |
261 | }, |
262 | .leds = { |
263 | .micmute_led_address = 0x2c, |
264 | .mute_led_address = 0x2d, |
265 | .bit = 1, |
266 | }, |
267 | .kbd_bl = { |
268 | .bl_mode_address = 0x2c, // ? |
269 | .bl_modes = { 0x00, 0x08 }, // ? |
270 | .max_mode = 1, // ? |
271 | .bl_state_address = 0xd3, |
272 | .state_base_value = 0x80, |
273 | .max_state = 3, |
274 | }, |
275 | }; |
276 | |
277 | static const char * const ALLOWED_FW_3[] __initconst = { |
278 | "1592EMS1.111" , |
279 | NULL |
280 | }; |
281 | |
282 | static struct msi_ec_conf CONF3 __initdata = { |
283 | .allowed_fw = ALLOWED_FW_3, |
284 | .charge_control = { |
285 | .address = 0xd7, |
286 | .offset_start = 0x8a, |
287 | .offset_end = 0x80, |
288 | .range_min = 0x8a, |
289 | .range_max = 0xe4, |
290 | }, |
291 | .webcam = { |
292 | .address = 0x2e, |
293 | .block_address = 0x2f, |
294 | .bit = 1, |
295 | }, |
296 | .fn_win_swap = { |
297 | .address = 0xe8, |
298 | .bit = 4, |
299 | }, |
300 | .cooler_boost = { |
301 | .address = 0x98, |
302 | .bit = 7, |
303 | }, |
304 | .shift_mode = { |
305 | .address = 0xd2, |
306 | .modes = { |
307 | { SM_ECO_NAME, 0xc2 }, |
308 | { SM_COMFORT_NAME, 0xc1 }, |
309 | { SM_SPORT_NAME, 0xc0 }, |
310 | MSI_EC_MODE_NULL |
311 | }, |
312 | }, |
313 | .super_battery = { |
314 | .address = 0xeb, |
315 | .mask = 0x0f, |
316 | }, |
317 | .fan_mode = { |
318 | .address = 0xd4, |
319 | .modes = { |
320 | { FM_AUTO_NAME, 0x0d }, |
321 | { FM_SILENT_NAME, 0x1d }, |
322 | { FM_BASIC_NAME, 0x4d }, |
323 | { FM_ADVANCED_NAME, 0x8d }, |
324 | MSI_EC_MODE_NULL |
325 | }, |
326 | }, |
327 | .cpu = { |
328 | .rt_temp_address = 0x68, |
329 | .rt_fan_speed_address = 0xc9, |
330 | .rt_fan_speed_base_min = 0x19, |
331 | .rt_fan_speed_base_max = 0x37, |
332 | .bs_fan_speed_address = 0x89, // ? |
333 | .bs_fan_speed_base_min = 0x00, |
334 | .bs_fan_speed_base_max = 0x0f, |
335 | }, |
336 | .gpu = { |
337 | .rt_temp_address = 0x80, |
338 | .rt_fan_speed_address = 0x89, |
339 | }, |
340 | .leds = { |
341 | .micmute_led_address = 0x2b, |
342 | .mute_led_address = 0x2c, |
343 | .bit = 1, |
344 | }, |
345 | .kbd_bl = { |
346 | .bl_mode_address = 0x2c, // ? |
347 | .bl_modes = { 0x00, 0x08 }, // ? |
348 | .max_mode = 1, // ? |
349 | .bl_state_address = 0xd3, |
350 | .state_base_value = 0x80, |
351 | .max_state = 3, |
352 | }, |
353 | }; |
354 | |
355 | static const char * const ALLOWED_FW_4[] __initconst = { |
356 | "16V4EMS1.114" , |
357 | NULL |
358 | }; |
359 | |
360 | static struct msi_ec_conf CONF4 __initdata = { |
361 | .allowed_fw = ALLOWED_FW_4, |
362 | .charge_control = { |
363 | .address = 0xd7, |
364 | .offset_start = 0x8a, |
365 | .offset_end = 0x80, |
366 | .range_min = 0x8a, |
367 | .range_max = 0xe4, |
368 | }, |
369 | .webcam = { |
370 | .address = 0x2e, |
371 | .block_address = 0x2f, |
372 | .bit = 1, |
373 | }, |
374 | .fn_win_swap = { |
375 | .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown |
376 | .bit = 4, |
377 | }, |
378 | .cooler_boost = { |
379 | .address = 0x98, |
380 | .bit = 7, |
381 | }, |
382 | .shift_mode = { |
383 | .address = 0xd2, |
384 | .modes = { |
385 | { SM_ECO_NAME, 0xc2 }, |
386 | { SM_COMFORT_NAME, 0xc1 }, |
387 | { SM_SPORT_NAME, 0xc0 }, |
388 | MSI_EC_MODE_NULL |
389 | }, |
390 | }, |
391 | .super_battery = { // may be supported, but address is unknown |
392 | .address = MSI_EC_ADDR_UNKNOWN, |
393 | .mask = 0x0f, |
394 | }, |
395 | .fan_mode = { |
396 | .address = 0xd4, |
397 | .modes = { |
398 | { FM_AUTO_NAME, 0x0d }, |
399 | { FM_SILENT_NAME, 0x1d }, |
400 | { FM_ADVANCED_NAME, 0x8d }, |
401 | MSI_EC_MODE_NULL |
402 | }, |
403 | }, |
404 | .cpu = { |
405 | .rt_temp_address = 0x68, // needs testing |
406 | .rt_fan_speed_address = 0x71, // needs testing |
407 | .rt_fan_speed_base_min = 0x19, |
408 | .rt_fan_speed_base_max = 0x37, |
409 | .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, |
410 | .bs_fan_speed_base_min = 0x00, |
411 | .bs_fan_speed_base_max = 0x0f, |
412 | }, |
413 | .gpu = { |
414 | .rt_temp_address = 0x80, |
415 | .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, |
416 | }, |
417 | .leds = { |
418 | .micmute_led_address = MSI_EC_ADDR_UNKNOWN, |
419 | .mute_led_address = MSI_EC_ADDR_UNKNOWN, |
420 | .bit = 1, |
421 | }, |
422 | .kbd_bl = { |
423 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? |
424 | .bl_modes = { 0x00, 0x08 }, // ? |
425 | .max_mode = 1, // ? |
426 | .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional |
427 | .state_base_value = 0x80, |
428 | .max_state = 3, |
429 | }, |
430 | }; |
431 | |
432 | static const char * const ALLOWED_FW_5[] __initconst = { |
433 | "158LEMS1.103" , |
434 | "158LEMS1.105" , |
435 | "158LEMS1.106" , |
436 | NULL |
437 | }; |
438 | |
439 | static struct msi_ec_conf CONF5 __initdata = { |
440 | .allowed_fw = ALLOWED_FW_5, |
441 | .charge_control = { |
442 | .address = 0xef, |
443 | .offset_start = 0x8a, |
444 | .offset_end = 0x80, |
445 | .range_min = 0x8a, |
446 | .range_max = 0xe4, |
447 | }, |
448 | .webcam = { |
449 | .address = 0x2e, |
450 | .block_address = 0x2f, |
451 | .bit = 1, |
452 | }, |
453 | .fn_win_swap = { // todo: reverse |
454 | .address = 0xbf, |
455 | .bit = 4, |
456 | }, |
457 | .cooler_boost = { |
458 | .address = 0x98, |
459 | .bit = 7, |
460 | }, |
461 | .shift_mode = { |
462 | .address = 0xf2, |
463 | .modes = { |
464 | { SM_ECO_NAME, 0xc2 }, |
465 | { SM_COMFORT_NAME, 0xc1 }, |
466 | { SM_TURBO_NAME, 0xc4 }, |
467 | MSI_EC_MODE_NULL |
468 | }, |
469 | }, |
470 | .super_battery = { // unsupported? |
471 | .address = MSI_EC_ADDR_UNKNOWN, |
472 | .mask = 0x0f, |
473 | }, |
474 | .fan_mode = { |
475 | .address = 0xf4, |
476 | .modes = { |
477 | { FM_AUTO_NAME, 0x0d }, |
478 | { FM_SILENT_NAME, 0x1d }, |
479 | { FM_ADVANCED_NAME, 0x8d }, |
480 | MSI_EC_MODE_NULL |
481 | }, |
482 | }, |
483 | .cpu = { |
484 | .rt_temp_address = 0x68, // needs testing |
485 | .rt_fan_speed_address = 0x71, // needs testing |
486 | .rt_fan_speed_base_min = 0x19, |
487 | .rt_fan_speed_base_max = 0x37, |
488 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
489 | .bs_fan_speed_base_min = 0x00, |
490 | .bs_fan_speed_base_max = 0x0f, |
491 | }, |
492 | .gpu = { |
493 | .rt_temp_address = MSI_EC_ADDR_UNKNOWN, |
494 | .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, |
495 | }, |
496 | .leds = { |
497 | .micmute_led_address = 0x2b, |
498 | .mute_led_address = 0x2c, |
499 | .bit = 2, |
500 | }, |
501 | .kbd_bl = { |
502 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? |
503 | .bl_modes = { 0x00, 0x08 }, // ? |
504 | .max_mode = 1, // ? |
505 | .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional |
506 | .state_base_value = 0x80, |
507 | .max_state = 3, |
508 | }, |
509 | }; |
510 | |
511 | static const char * const ALLOWED_FW_6[] __initconst = { |
512 | "1542EMS1.102" , |
513 | "1542EMS1.104" , |
514 | NULL |
515 | }; |
516 | |
517 | static struct msi_ec_conf CONF6 __initdata = { |
518 | .allowed_fw = ALLOWED_FW_6, |
519 | .charge_control = { |
520 | .address = 0xef, |
521 | .offset_start = 0x8a, |
522 | .offset_end = 0x80, |
523 | .range_min = 0x8a, |
524 | .range_max = 0xe4, |
525 | }, |
526 | .webcam = { |
527 | .address = 0x2e, |
528 | .block_address = MSI_EC_ADDR_UNSUPP, |
529 | .bit = 1, |
530 | }, |
531 | .fn_win_swap = { |
532 | .address = 0xbf, // todo: reverse |
533 | .bit = 4, |
534 | }, |
535 | .cooler_boost = { |
536 | .address = 0x98, |
537 | .bit = 7, |
538 | }, |
539 | .shift_mode = { |
540 | .address = 0xf2, |
541 | .modes = { |
542 | { SM_ECO_NAME, 0xc2 }, |
543 | { SM_COMFORT_NAME, 0xc1 }, |
544 | { SM_SPORT_NAME, 0xc0 }, |
545 | { SM_TURBO_NAME, 0xc4 }, |
546 | MSI_EC_MODE_NULL |
547 | }, |
548 | }, |
549 | .super_battery = { |
550 | .address = 0xd5, |
551 | .mask = 0x0f, |
552 | }, |
553 | .fan_mode = { |
554 | .address = 0xf4, |
555 | .modes = { |
556 | { FM_AUTO_NAME, 0x0d }, |
557 | { FM_SILENT_NAME, 0x1d }, |
558 | { FM_ADVANCED_NAME, 0x8d }, |
559 | MSI_EC_MODE_NULL |
560 | }, |
561 | }, |
562 | .cpu = { |
563 | .rt_temp_address = 0x68, |
564 | .rt_fan_speed_address = 0xc9, |
565 | .rt_fan_speed_base_min = 0x19, |
566 | .rt_fan_speed_base_max = 0x37, |
567 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
568 | .bs_fan_speed_base_min = 0x00, |
569 | .bs_fan_speed_base_max = 0x0f, |
570 | }, |
571 | .gpu = { |
572 | .rt_temp_address = 0x80, |
573 | .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, |
574 | }, |
575 | .leds = { |
576 | .micmute_led_address = MSI_EC_ADDR_UNSUPP, |
577 | .mute_led_address = MSI_EC_ADDR_UNSUPP, |
578 | .bit = 2, |
579 | }, |
580 | .kbd_bl = { |
581 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? |
582 | .bl_modes = { 0x00, 0x08 }, // ? |
583 | .max_mode = 1, // ? |
584 | .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional |
585 | .state_base_value = 0x80, |
586 | .max_state = 3, |
587 | }, |
588 | }; |
589 | |
590 | static const char * const ALLOWED_FW_7[] __initconst = { |
591 | "17FKEMS1.108" , |
592 | "17FKEMS1.109" , |
593 | "17FKEMS1.10A" , |
594 | NULL |
595 | }; |
596 | |
597 | static struct msi_ec_conf CONF7 __initdata = { |
598 | .allowed_fw = ALLOWED_FW_7, |
599 | .charge_control = { |
600 | .address = 0xef, |
601 | .offset_start = 0x8a, |
602 | .offset_end = 0x80, |
603 | .range_min = 0x8a, |
604 | .range_max = 0xe4, |
605 | }, |
606 | .webcam = { |
607 | .address = 0x2e, |
608 | .block_address = MSI_EC_ADDR_UNSUPP, |
609 | .bit = 1, |
610 | }, |
611 | .fn_win_swap = { |
612 | .address = 0xbf, // needs testing |
613 | .bit = 4, |
614 | }, |
615 | .cooler_boost = { |
616 | .address = 0x98, |
617 | .bit = 7, |
618 | }, |
619 | .shift_mode = { |
620 | .address = 0xf2, |
621 | .modes = { |
622 | { SM_ECO_NAME, 0xc2 }, |
623 | { SM_COMFORT_NAME, 0xc1 }, |
624 | { SM_SPORT_NAME, 0xc0 }, |
625 | { SM_TURBO_NAME, 0xc4 }, |
626 | MSI_EC_MODE_NULL |
627 | }, |
628 | }, |
629 | .super_battery = { |
630 | .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes |
631 | .mask = 0x0f, |
632 | }, |
633 | .fan_mode = { |
634 | .address = 0xf4, |
635 | .modes = { |
636 | { FM_AUTO_NAME, 0x0d }, // d may not be relevant |
637 | { FM_SILENT_NAME, 0x1d }, |
638 | { FM_ADVANCED_NAME, 0x8d }, |
639 | MSI_EC_MODE_NULL |
640 | }, |
641 | }, |
642 | .cpu = { |
643 | .rt_temp_address = 0x68, |
644 | .rt_fan_speed_address = 0xc9, // needs testing |
645 | .rt_fan_speed_base_min = 0x19, |
646 | .rt_fan_speed_base_max = 0x37, |
647 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
648 | .bs_fan_speed_base_min = 0x00, |
649 | .bs_fan_speed_base_max = 0x0f, |
650 | }, |
651 | .gpu = { |
652 | .rt_temp_address = MSI_EC_ADDR_UNKNOWN, |
653 | .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, |
654 | }, |
655 | .leds = { |
656 | .micmute_led_address = MSI_EC_ADDR_UNSUPP, |
657 | .mute_led_address = 0x2c, |
658 | .bit = 2, |
659 | }, |
660 | .kbd_bl = { |
661 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? |
662 | .bl_modes = { 0x00, 0x08 }, // ? |
663 | .max_mode = 1, // ? |
664 | .bl_state_address = 0xf3, |
665 | .state_base_value = 0x80, |
666 | .max_state = 3, |
667 | }, |
668 | }; |
669 | |
670 | static const char * const ALLOWED_FW_8[] __initconst = { |
671 | "14F1EMS1.115" , |
672 | NULL |
673 | }; |
674 | |
675 | static struct msi_ec_conf CONF8 __initdata = { |
676 | .allowed_fw = ALLOWED_FW_8, |
677 | .charge_control = { |
678 | .address = 0xd7, |
679 | .offset_start = 0x8a, |
680 | .offset_end = 0x80, |
681 | .range_min = 0x8a, |
682 | .range_max = 0xe4, |
683 | }, |
684 | .webcam = { |
685 | .address = 0x2e, |
686 | .block_address = MSI_EC_ADDR_UNSUPP, |
687 | .bit = 1, |
688 | }, |
689 | .fn_win_swap = { |
690 | .address = 0xe8, |
691 | .bit = 4, |
692 | }, |
693 | .cooler_boost = { |
694 | .address = 0x98, |
695 | .bit = 7, |
696 | }, |
697 | .shift_mode = { |
698 | .address = 0xd2, |
699 | .modes = { |
700 | { SM_ECO_NAME, 0xc2 }, |
701 | { SM_COMFORT_NAME, 0xc1 }, |
702 | { SM_SPORT_NAME, 0xc0 }, |
703 | MSI_EC_MODE_NULL |
704 | }, |
705 | }, |
706 | .super_battery = { |
707 | .address = 0xeb, |
708 | .mask = 0x0f, |
709 | }, |
710 | .fan_mode = { |
711 | .address = 0xd4, |
712 | .modes = { |
713 | { FM_AUTO_NAME, 0x0d }, |
714 | { FM_SILENT_NAME, 0x1d }, |
715 | { FM_BASIC_NAME, 0x4d }, |
716 | MSI_EC_MODE_NULL |
717 | }, |
718 | }, |
719 | .cpu = { |
720 | .rt_temp_address = 0x68, |
721 | .rt_fan_speed_address = 0x71, |
722 | .rt_fan_speed_base_min = 0x19, |
723 | .rt_fan_speed_base_max = 0x37, |
724 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
725 | .bs_fan_speed_base_min = 0x00, |
726 | .bs_fan_speed_base_max = 0x0f, |
727 | }, |
728 | .gpu = { |
729 | .rt_temp_address = MSI_EC_ADDR_UNKNOWN, |
730 | .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, |
731 | }, |
732 | .leds = { |
733 | .micmute_led_address = MSI_EC_ADDR_UNSUPP, |
734 | .mute_led_address = 0x2d, |
735 | .bit = 1, |
736 | }, |
737 | .kbd_bl = { |
738 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? |
739 | .bl_modes = { 0x00, 0x08 }, // ? |
740 | .max_mode = 1, // ? |
741 | .bl_state_address = MSI_EC_ADDR_UNSUPP, // not functional |
742 | .state_base_value = 0x80, |
743 | .max_state = 3, |
744 | }, |
745 | }; |
746 | |
747 | static const char * const ALLOWED_FW_9[] __initconst = { |
748 | "14JKEMS1.104" , |
749 | NULL |
750 | }; |
751 | |
752 | static struct msi_ec_conf CONF9 __initdata = { |
753 | .allowed_fw = ALLOWED_FW_9, |
754 | .charge_control = { |
755 | .address = 0xef, |
756 | .offset_start = 0x8a, |
757 | .offset_end = 0x80, |
758 | .range_min = 0x8a, |
759 | .range_max = 0xe4, |
760 | }, |
761 | .webcam = { |
762 | .address = 0x2e, |
763 | .block_address = 0x2f, |
764 | .bit = 1, |
765 | }, |
766 | .fn_win_swap = { |
767 | .address = 0xbf, |
768 | .bit = 4, |
769 | }, |
770 | .cooler_boost = { |
771 | .address = 0x98, |
772 | .bit = 7, |
773 | }, |
774 | .shift_mode = { |
775 | .address = 0xf2, |
776 | .modes = { |
777 | { SM_ECO_NAME, 0xc2 }, |
778 | { SM_COMFORT_NAME, 0xc1 }, |
779 | { SM_SPORT_NAME, 0xc0 }, |
780 | MSI_EC_MODE_NULL |
781 | }, |
782 | }, |
783 | .super_battery = { |
784 | .address = MSI_EC_ADDR_UNSUPP, // unsupported or enabled by ECO shift |
785 | .mask = 0x0f, |
786 | }, |
787 | .fan_mode = { |
788 | .address = 0xf4, |
789 | .modes = { |
790 | { FM_AUTO_NAME, 0x0d }, |
791 | { FM_SILENT_NAME, 0x1d }, |
792 | { FM_ADVANCED_NAME, 0x8d }, |
793 | MSI_EC_MODE_NULL |
794 | }, |
795 | }, |
796 | .cpu = { |
797 | .rt_temp_address = 0x68, |
798 | .rt_fan_speed_address = 0x71, |
799 | .rt_fan_speed_base_min = 0x00, |
800 | .rt_fan_speed_base_max = 0x96, |
801 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
802 | .bs_fan_speed_base_min = 0x00, |
803 | .bs_fan_speed_base_max = 0x0f, |
804 | }, |
805 | .gpu = { |
806 | .rt_temp_address = MSI_EC_ADDR_UNSUPP, |
807 | .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
808 | }, |
809 | .leds = { |
810 | .micmute_led_address = 0x2b, |
811 | .mute_led_address = 0x2c, |
812 | .bit = 2, |
813 | }, |
814 | .kbd_bl = { |
815 | .bl_mode_address = MSI_EC_ADDR_UNSUPP, // not presented in MSI app |
816 | .bl_modes = { 0x00, 0x08 }, |
817 | .max_mode = 1, |
818 | .bl_state_address = 0xf3, |
819 | .state_base_value = 0x80, |
820 | .max_state = 3, |
821 | }, |
822 | }; |
823 | |
824 | static const char * const ALLOWED_FW_10[] __initconst = { |
825 | "1582EMS1.107" , // GF66 11UC |
826 | NULL |
827 | }; |
828 | |
829 | static struct msi_ec_conf CONF10 __initdata = { |
830 | .allowed_fw = ALLOWED_FW_10, |
831 | .charge_control = { |
832 | .address = 0xd7, |
833 | .offset_start = 0x8a, |
834 | .offset_end = 0x80, |
835 | .range_min = 0x8a, |
836 | .range_max = 0xe4, |
837 | }, |
838 | .webcam = { |
839 | .address = 0x2e, |
840 | .block_address = 0x2f, |
841 | .bit = 1, |
842 | }, |
843 | .fn_win_swap = { |
844 | .address = MSI_EC_ADDR_UNSUPP, |
845 | .bit = 4, |
846 | }, |
847 | .cooler_boost = { |
848 | .address = 0x98, |
849 | .bit = 7, |
850 | }, |
851 | .shift_mode = { |
852 | .address = 0xd2, |
853 | .modes = { |
854 | { SM_ECO_NAME, 0xc2 }, |
855 | { SM_COMFORT_NAME, 0xc1 }, |
856 | { SM_SPORT_NAME, 0xc0 }, |
857 | { SM_TURBO_NAME, 0xc4 }, |
858 | MSI_EC_MODE_NULL |
859 | }, |
860 | }, |
861 | .super_battery = { |
862 | .address = 0xe5, |
863 | .mask = 0x0f, |
864 | }, |
865 | .fan_mode = { |
866 | .address = 0xd4, |
867 | .modes = { |
868 | { FM_AUTO_NAME, 0x0d }, |
869 | { FM_SILENT_NAME, 0x1d }, |
870 | { FM_ADVANCED_NAME, 0x8d }, |
871 | MSI_EC_MODE_NULL |
872 | }, |
873 | }, |
874 | .cpu = { |
875 | .rt_temp_address = 0x68, |
876 | .rt_fan_speed_address = 0x71, // ? |
877 | .rt_fan_speed_base_min = 0x19, |
878 | .rt_fan_speed_base_max = 0x37, |
879 | .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, // ? |
880 | .bs_fan_speed_base_min = 0x00, |
881 | .bs_fan_speed_base_max = 0x0f, |
882 | }, |
883 | .gpu = { |
884 | .rt_temp_address = 0x80, |
885 | .rt_fan_speed_address = 0x89, |
886 | }, |
887 | .leds = { |
888 | .micmute_led_address = 0x2c, |
889 | .mute_led_address = 0x2d, |
890 | .bit = 1, |
891 | }, |
892 | .kbd_bl = { |
893 | .bl_mode_address = 0x2c, |
894 | .bl_modes = { 0x00, 0x08 }, |
895 | .max_mode = 1, |
896 | .bl_state_address = 0xd3, |
897 | .state_base_value = 0x80, |
898 | .max_state = 3, |
899 | }, |
900 | }; |
901 | |
902 | static const char * const ALLOWED_FW_11[] __initconst = { |
903 | "16S6EMS1.111" , // Prestige 15 a11scx |
904 | "1552EMS1.115" , // Modern 15 a11m |
905 | NULL |
906 | }; |
907 | |
908 | static struct msi_ec_conf CONF11 __initdata = { |
909 | .allowed_fw = ALLOWED_FW_11, |
910 | .charge_control = { |
911 | .address = 0xd7, |
912 | .offset_start = 0x8a, |
913 | .offset_end = 0x80, |
914 | .range_min = 0x8a, |
915 | .range_max = 0xe4, |
916 | }, |
917 | .webcam = { |
918 | .address = 0x2e, |
919 | .block_address = MSI_EC_ADDR_UNKNOWN, |
920 | .bit = 1, |
921 | }, |
922 | .fn_win_swap = { |
923 | .address = 0xe8, |
924 | .bit = 4, |
925 | }, |
926 | .cooler_boost = { |
927 | .address = 0x98, |
928 | .bit = 7, |
929 | }, |
930 | .shift_mode = { |
931 | .address = 0xd2, |
932 | .modes = { |
933 | { SM_ECO_NAME, 0xc2 }, |
934 | { SM_COMFORT_NAME, 0xc1 }, |
935 | { SM_SPORT_NAME, 0xc0 }, |
936 | MSI_EC_MODE_NULL |
937 | }, |
938 | }, |
939 | .super_battery = { |
940 | .address = 0xeb, |
941 | .mask = 0x0f, |
942 | }, |
943 | .fan_mode = { |
944 | .address = 0xd4, |
945 | .modes = { |
946 | { FM_AUTO_NAME, 0x0d }, |
947 | { FM_SILENT_NAME, 0x1d }, |
948 | { FM_ADVANCED_NAME, 0x4d }, |
949 | MSI_EC_MODE_NULL |
950 | }, |
951 | }, |
952 | .cpu = { |
953 | .rt_temp_address = 0x68, |
954 | .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
955 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
956 | }, |
957 | .gpu = { |
958 | .rt_temp_address = MSI_EC_ADDR_UNSUPP, |
959 | .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
960 | }, |
961 | .leds = { |
962 | .micmute_led_address = 0x2c, |
963 | .mute_led_address = 0x2d, |
964 | .bit = 1, |
965 | }, |
966 | .kbd_bl = { |
967 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, |
968 | .bl_modes = {}, // ? |
969 | .max_mode = 1, // ? |
970 | .bl_state_address = 0xd3, |
971 | .state_base_value = 0x80, |
972 | .max_state = 3, |
973 | }, |
974 | }; |
975 | |
976 | static const char * const ALLOWED_FW_12[] __initconst = { |
977 | "16R6EMS1.104" , // GF63 Thin 11UC |
978 | NULL |
979 | }; |
980 | |
981 | static struct msi_ec_conf CONF12 __initdata = { |
982 | .allowed_fw = ALLOWED_FW_12, |
983 | .charge_control = { |
984 | .address = 0xd7, |
985 | .offset_start = 0x8a, |
986 | .offset_end = 0x80, |
987 | .range_min = 0x8a, |
988 | .range_max = 0xe4, |
989 | }, |
990 | .webcam = { |
991 | .address = 0x2e, |
992 | .block_address = 0x2f, |
993 | .bit = 1, |
994 | }, |
995 | .fn_win_swap = { |
996 | .address = 0xe8, |
997 | .bit = 4, |
998 | }, |
999 | .cooler_boost = { |
1000 | .address = 0x98, |
1001 | .bit = 7, |
1002 | }, |
1003 | .shift_mode = { |
1004 | .address = 0xd2, |
1005 | .modes = { |
1006 | { SM_ECO_NAME, 0xc2 }, |
1007 | { SM_COMFORT_NAME, 0xc1 }, |
1008 | { SM_SPORT_NAME, 0xc0 }, |
1009 | { SM_TURBO_NAME, 0xc4 }, |
1010 | MSI_EC_MODE_NULL |
1011 | }, |
1012 | }, |
1013 | .super_battery = { |
1014 | .address = MSI_EC_ADDR_UNSUPP, // 0xeb |
1015 | .mask = 0x0f, // 00, 0f |
1016 | }, |
1017 | .fan_mode = { |
1018 | .address = 0xd4, |
1019 | .modes = { |
1020 | { FM_AUTO_NAME, 0x0d }, |
1021 | { FM_SILENT_NAME, 0x1d }, |
1022 | { FM_ADVANCED_NAME, 0x8d }, |
1023 | MSI_EC_MODE_NULL |
1024 | }, |
1025 | }, |
1026 | .cpu = { |
1027 | .rt_temp_address = 0x68, |
1028 | .rt_fan_speed_address = 0x71, |
1029 | .rt_fan_speed_base_min = 0x19, |
1030 | .rt_fan_speed_base_max = 0x37, |
1031 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
1032 | .bs_fan_speed_base_min = 0x00, |
1033 | .bs_fan_speed_base_max = 0x0f, |
1034 | }, |
1035 | .gpu = { |
1036 | .rt_temp_address = MSI_EC_ADDR_UNSUPP, |
1037 | .rt_fan_speed_address = 0x89, |
1038 | }, |
1039 | .leds = { |
1040 | .micmute_led_address = MSI_EC_ADDR_UNSUPP, |
1041 | .mute_led_address = 0x2d, |
1042 | .bit = 1, |
1043 | }, |
1044 | .kbd_bl = { |
1045 | .bl_mode_address = MSI_EC_ADDR_UNKNOWN, |
1046 | .bl_modes = { 0x00, 0x08 }, |
1047 | .max_mode = 1, |
1048 | .bl_state_address = 0xd3, |
1049 | .state_base_value = 0x80, |
1050 | .max_state = 3, |
1051 | }, |
1052 | }; |
1053 | |
1054 | static const char * const ALLOWED_FW_13[] __initconst = { |
1055 | "1594EMS1.109" , // MSI Prestige 16 Studio A13VE |
1056 | NULL |
1057 | }; |
1058 | |
1059 | static struct msi_ec_conf CONF13 __initdata = { |
1060 | .allowed_fw = ALLOWED_FW_13, |
1061 | .charge_control = { |
1062 | .address = 0xd7, |
1063 | .offset_start = 0x8a, |
1064 | .offset_end = 0x80, |
1065 | .range_min = 0x8a, |
1066 | .range_max = 0xe4, |
1067 | }, |
1068 | .webcam = { |
1069 | .address = 0x2e, |
1070 | .block_address = 0x2f, |
1071 | .bit = 1, |
1072 | }, |
1073 | .fn_win_swap = { |
1074 | .address = 0xe8, |
1075 | .bit = 4, // 0x00-0x10 |
1076 | }, |
1077 | .cooler_boost = { |
1078 | .address = 0x98, |
1079 | .bit = 7, |
1080 | }, |
1081 | .shift_mode = { |
1082 | .address = 0xd2, |
1083 | .modes = { |
1084 | { SM_ECO_NAME, 0xc2 }, // super battery |
1085 | { SM_COMFORT_NAME, 0xc1 }, // balanced |
1086 | { SM_TURBO_NAME, 0xc4 }, // extreme |
1087 | MSI_EC_MODE_NULL |
1088 | }, |
1089 | }, |
1090 | .super_battery = { |
1091 | .address = MSI_EC_ADDR_UNSUPP, |
1092 | .mask = 0x0f, // 00, 0f |
1093 | }, |
1094 | .fan_mode = { |
1095 | .address = 0xd4, |
1096 | .modes = { |
1097 | { FM_AUTO_NAME, 0x0d }, |
1098 | { FM_SILENT_NAME, 0x1d }, |
1099 | { FM_ADVANCED_NAME, 0x8d }, |
1100 | MSI_EC_MODE_NULL |
1101 | }, |
1102 | }, |
1103 | .cpu = { |
1104 | .rt_temp_address = 0x68, |
1105 | .rt_fan_speed_address = 0x71, // 0x0-0x96 |
1106 | .rt_fan_speed_base_min = 0x00, |
1107 | .rt_fan_speed_base_max = 0x96, |
1108 | .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, |
1109 | .bs_fan_speed_base_min = 0x00, |
1110 | .bs_fan_speed_base_max = 0x0f, |
1111 | }, |
1112 | .gpu = { |
1113 | .rt_temp_address = 0x80, |
1114 | .rt_fan_speed_address = 0x89, |
1115 | }, |
1116 | .leds = { |
1117 | .micmute_led_address = 0x2c, |
1118 | .mute_led_address = 0x2d, |
1119 | .bit = 1, |
1120 | }, |
1121 | .kbd_bl = { |
1122 | .bl_mode_address = 0x2c, // KB auto turn off |
1123 | .bl_modes = { 0x00, 0x08 }, // always on; off after 10 sec |
1124 | .max_mode = 1, |
1125 | .bl_state_address = 0xd3, |
1126 | .state_base_value = 0x80, |
1127 | .max_state = 3, |
1128 | }, |
1129 | }; |
1130 | |
1131 | static struct msi_ec_conf *CONFIGS[] __initdata = { |
1132 | &CONF0, |
1133 | &CONF1, |
1134 | &CONF2, |
1135 | &CONF3, |
1136 | &CONF4, |
1137 | &CONF5, |
1138 | &CONF6, |
1139 | &CONF7, |
1140 | &CONF8, |
1141 | &CONF9, |
1142 | &CONF10, |
1143 | &CONF11, |
1144 | &CONF12, |
1145 | &CONF13, |
1146 | NULL |
1147 | }; |
1148 | |
1149 | static struct msi_ec_conf conf; // current configuration |
1150 | |
1151 | /* |
1152 | * Helper functions |
1153 | */ |
1154 | |
1155 | static int ec_read_seq(u8 addr, u8 *buf, u8 len) |
1156 | { |
1157 | int result; |
1158 | |
1159 | for (u8 i = 0; i < len; i++) { |
1160 | result = ec_read(addr: addr + i, val: buf + i); |
1161 | if (result < 0) |
1162 | return result; |
1163 | } |
1164 | |
1165 | return 0; |
1166 | } |
1167 | |
1168 | static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) |
1169 | { |
1170 | int result; |
1171 | |
1172 | memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); |
1173 | result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, |
1174 | buf, |
1175 | MSI_EC_FW_VERSION_LENGTH); |
1176 | if (result < 0) |
1177 | return result; |
1178 | |
1179 | return MSI_EC_FW_VERSION_LENGTH + 1; |
1180 | } |
1181 | |
1182 | /* |
1183 | * Sysfs power_supply subsystem |
1184 | */ |
1185 | |
1186 | static ssize_t charge_control_threshold_show(u8 offset, |
1187 | struct device *device, |
1188 | struct device_attribute *attr, |
1189 | char *buf) |
1190 | { |
1191 | u8 rdata; |
1192 | int result; |
1193 | |
1194 | result = ec_read(addr: conf.charge_control.address, val: &rdata); |
1195 | if (result < 0) |
1196 | return result; |
1197 | |
1198 | return sysfs_emit(buf, fmt: "%i\n" , rdata - offset); |
1199 | } |
1200 | |
1201 | static ssize_t charge_control_threshold_store(u8 offset, |
1202 | struct device *dev, |
1203 | struct device_attribute *attr, |
1204 | const char *buf, size_t count) |
1205 | { |
1206 | u8 wdata; |
1207 | int result; |
1208 | |
1209 | result = kstrtou8(s: buf, base: 10, res: &wdata); |
1210 | if (result < 0) |
1211 | return result; |
1212 | |
1213 | wdata += offset; |
1214 | if (wdata < conf.charge_control.range_min || |
1215 | wdata > conf.charge_control.range_max) |
1216 | return -EINVAL; |
1217 | |
1218 | result = ec_write(addr: conf.charge_control.address, val: wdata); |
1219 | if (result < 0) |
1220 | return result; |
1221 | |
1222 | return count; |
1223 | } |
1224 | |
1225 | static ssize_t charge_control_start_threshold_show(struct device *device, |
1226 | struct device_attribute *attr, |
1227 | char *buf) |
1228 | { |
1229 | return charge_control_threshold_show(offset: conf.charge_control.offset_start, |
1230 | device, attr, buf); |
1231 | } |
1232 | |
1233 | static ssize_t charge_control_start_threshold_store(struct device *dev, |
1234 | struct device_attribute *attr, |
1235 | const char *buf, size_t count) |
1236 | { |
1237 | return charge_control_threshold_store(offset: conf.charge_control.offset_start, |
1238 | dev, attr, buf, count); |
1239 | } |
1240 | |
1241 | static ssize_t charge_control_end_threshold_show(struct device *device, |
1242 | struct device_attribute *attr, |
1243 | char *buf) |
1244 | { |
1245 | return charge_control_threshold_show(offset: conf.charge_control.offset_end, |
1246 | device, attr, buf); |
1247 | } |
1248 | |
1249 | static ssize_t charge_control_end_threshold_store(struct device *dev, |
1250 | struct device_attribute *attr, |
1251 | const char *buf, size_t count) |
1252 | { |
1253 | return charge_control_threshold_store(offset: conf.charge_control.offset_end, |
1254 | dev, attr, buf, count); |
1255 | } |
1256 | |
1257 | static DEVICE_ATTR_RW(charge_control_start_threshold); |
1258 | static DEVICE_ATTR_RW(charge_control_end_threshold); |
1259 | |
1260 | static struct attribute *msi_battery_attrs[] = { |
1261 | &dev_attr_charge_control_start_threshold.attr, |
1262 | &dev_attr_charge_control_end_threshold.attr, |
1263 | NULL |
1264 | }; |
1265 | |
1266 | ATTRIBUTE_GROUPS(msi_battery); |
1267 | |
1268 | static int msi_battery_add(struct power_supply *battery, |
1269 | struct acpi_battery_hook *hook) |
1270 | { |
1271 | return device_add_groups(dev: &battery->dev, groups: msi_battery_groups); |
1272 | } |
1273 | |
1274 | static int msi_battery_remove(struct power_supply *battery, |
1275 | struct acpi_battery_hook *hook) |
1276 | { |
1277 | device_remove_groups(dev: &battery->dev, groups: msi_battery_groups); |
1278 | return 0; |
1279 | } |
1280 | |
1281 | static struct acpi_battery_hook battery_hook = { |
1282 | .add_battery = msi_battery_add, |
1283 | .remove_battery = msi_battery_remove, |
1284 | .name = MSI_EC_DRIVER_NAME, |
1285 | }; |
1286 | |
1287 | /* |
1288 | * Module load/unload |
1289 | */ |
1290 | |
1291 | static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { |
1292 | { |
1293 | .matches = { |
1294 | DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT" ), |
1295 | }, |
1296 | }, |
1297 | { |
1298 | .matches = { |
1299 | DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International" ), |
1300 | }, |
1301 | }, |
1302 | {} |
1303 | }; |
1304 | MODULE_DEVICE_TABLE(dmi, msi_dmi_table); |
1305 | |
1306 | static int __init load_configuration(void) |
1307 | { |
1308 | int result; |
1309 | |
1310 | u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; |
1311 | |
1312 | /* get firmware version */ |
1313 | result = ec_get_firmware_version(buf: fw_version); |
1314 | if (result < 0) |
1315 | return result; |
1316 | |
1317 | /* load the suitable configuration, if exists */ |
1318 | for (int i = 0; CONFIGS[i]; i++) { |
1319 | if (match_string(array: CONFIGS[i]->allowed_fw, n: -1, string: fw_version) != -EINVAL) { |
1320 | conf = *CONFIGS[i]; |
1321 | conf.allowed_fw = NULL; |
1322 | return 0; |
1323 | } |
1324 | } |
1325 | |
1326 | /* config not found */ |
1327 | |
1328 | for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { |
1329 | if (!isgraph(fw_version[i])) { |
1330 | pr_warn("Unable to find a valid firmware version!\n" ); |
1331 | return -EOPNOTSUPP; |
1332 | } |
1333 | } |
1334 | |
1335 | pr_warn("Firmware version is not supported: '%s'\n" , fw_version); |
1336 | return -EOPNOTSUPP; |
1337 | } |
1338 | |
1339 | static int __init msi_ec_init(void) |
1340 | { |
1341 | int result; |
1342 | |
1343 | result = load_configuration(); |
1344 | if (result < 0) |
1345 | return result; |
1346 | |
1347 | battery_hook_register(hook: &battery_hook); |
1348 | return 0; |
1349 | } |
1350 | |
1351 | static void __exit msi_ec_exit(void) |
1352 | { |
1353 | battery_hook_unregister(hook: &battery_hook); |
1354 | } |
1355 | |
1356 | MODULE_LICENSE("GPL" ); |
1357 | MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>" ); |
1358 | MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>" ); |
1359 | MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>" ); |
1360 | MODULE_DESCRIPTION("MSI Embedded Controller" ); |
1361 | |
1362 | module_init(msi_ec_init); |
1363 | module_exit(msi_ec_exit); |
1364 | |