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