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
40static const char * const ALLOWED_FW_0[] __initconst = {
41 "14C1EMS1.012",
42 "14C1EMS1.101",
43 "14C1EMS1.102",
44 NULL
45};
46
47static 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
119static const char * const ALLOWED_FW_1[] __initconst = {
120 "17F2EMS1.103",
121 "17F2EMS1.104",
122 "17F2EMS1.106",
123 "17F2EMS1.107",
124 NULL
125};
126
127static 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
199static const char * const ALLOWED_FW_2[] __initconst = {
200 "1552EMS1.118",
201 NULL
202};
203
204static 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
277static const char * const ALLOWED_FW_3[] __initconst = {
278 "1592EMS1.111",
279 NULL
280};
281
282static 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
355static const char * const ALLOWED_FW_4[] __initconst = {
356 "16V4EMS1.114",
357 NULL
358};
359
360static 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
432static const char * const ALLOWED_FW_5[] __initconst = {
433 "158LEMS1.103",
434 "158LEMS1.105",
435 "158LEMS1.106",
436 NULL
437};
438
439static 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
511static const char * const ALLOWED_FW_6[] __initconst = {
512 "1542EMS1.102",
513 "1542EMS1.104",
514 NULL
515};
516
517static 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
590static const char * const ALLOWED_FW_7[] __initconst = {
591 "17FKEMS1.108",
592 "17FKEMS1.109",
593 "17FKEMS1.10A",
594 NULL
595};
596
597static 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
670static const char * const ALLOWED_FW_8[] __initconst = {
671 "14F1EMS1.115",
672 NULL
673};
674
675static 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
747static const char * const ALLOWED_FW_9[] __initconst = {
748 "14JKEMS1.104",
749 NULL
750};
751
752static 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
824static const char * const ALLOWED_FW_10[] __initconst = {
825 "1582EMS1.107", // GF66 11UC
826 NULL
827};
828
829static 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
902static const char * const ALLOWED_FW_11[] __initconst = {
903 "16S6EMS1.111", // Prestige 15 a11scx
904 "1552EMS1.115", // Modern 15 a11m
905 NULL
906};
907
908static 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
976static const char * const ALLOWED_FW_12[] __initconst = {
977 "16R6EMS1.104", // GF63 Thin 11UC
978 NULL
979};
980
981static 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
1054static const char * const ALLOWED_FW_13[] __initconst = {
1055 "1594EMS1.109", // MSI Prestige 16 Studio A13VE
1056 NULL
1057};
1058
1059static 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
1131static 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
1149static struct msi_ec_conf conf; // current configuration
1150
1151/*
1152 * Helper functions
1153 */
1154
1155static 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
1168static 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
1186static 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
1201static 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
1225static 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
1233static 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
1241static 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
1249static 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
1257static DEVICE_ATTR_RW(charge_control_start_threshold);
1258static DEVICE_ATTR_RW(charge_control_end_threshold);
1259
1260static 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
1266ATTRIBUTE_GROUPS(msi_battery);
1267
1268static 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
1274static 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
1281static 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
1291static 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};
1304MODULE_DEVICE_TABLE(dmi, msi_dmi_table);
1305
1306static 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
1339static 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
1351static void __exit msi_ec_exit(void)
1352{
1353 battery_hook_unregister(hook: &battery_hook);
1354}
1355
1356MODULE_LICENSE("GPL");
1357MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>");
1358MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>");
1359MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>");
1360MODULE_DESCRIPTION("MSI Embedded Controller");
1361
1362module_init(msi_ec_init);
1363module_exit(msi_ec_exit);
1364

source code of linux/drivers/platform/x86/msi-ec.c