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_super_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_super_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_super_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_super_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_super_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_super_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_super_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_super_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 struct msi_ec_conf *CONFIGS[] __initdata = {
671 &CONF0,
672 &CONF1,
673 &CONF2,
674 &CONF3,
675 &CONF4,
676 &CONF5,
677 &CONF6,
678 &CONF7,
679 NULL
680 };
681
682 static struct msi_ec_conf conf; // current configuration
683
684 /*
685 * Helper functions
686 */
687
ec_read_seq(u8 addr,u8 * buf,u8 len)688 static int ec_read_seq(u8 addr, u8 *buf, u8 len)
689 {
690 int result;
691
692 for (u8 i = 0; i < len; i++) {
693 result = ec_read(addr + i, buf + i);
694 if (result < 0)
695 return result;
696 }
697
698 return 0;
699 }
700
ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH+1])701 static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1])
702 {
703 int result;
704
705 memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1);
706 result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS,
707 buf,
708 MSI_EC_FW_VERSION_LENGTH);
709 if (result < 0)
710 return result;
711
712 return MSI_EC_FW_VERSION_LENGTH + 1;
713 }
714
715 /*
716 * Sysfs power_supply subsystem
717 */
718
charge_control_threshold_show(u8 offset,struct device * device,struct device_attribute * attr,char * buf)719 static ssize_t charge_control_threshold_show(u8 offset,
720 struct device *device,
721 struct device_attribute *attr,
722 char *buf)
723 {
724 u8 rdata;
725 int result;
726
727 result = ec_read(conf.charge_control.address, &rdata);
728 if (result < 0)
729 return result;
730
731 return sysfs_emit(buf, "%i\n", rdata - offset);
732 }
733
charge_control_threshold_store(u8 offset,struct device * dev,struct device_attribute * attr,const char * buf,size_t count)734 static ssize_t charge_control_threshold_store(u8 offset,
735 struct device *dev,
736 struct device_attribute *attr,
737 const char *buf, size_t count)
738 {
739 u8 wdata;
740 int result;
741
742 result = kstrtou8(buf, 10, &wdata);
743 if (result < 0)
744 return result;
745
746 wdata += offset;
747 if (wdata < conf.charge_control.range_min ||
748 wdata > conf.charge_control.range_max)
749 return -EINVAL;
750
751 result = ec_write(conf.charge_control.address, wdata);
752 if (result < 0)
753 return result;
754
755 return count;
756 }
757
charge_control_start_threshold_show(struct device * device,struct device_attribute * attr,char * buf)758 static ssize_t charge_control_start_threshold_show(struct device *device,
759 struct device_attribute *attr,
760 char *buf)
761 {
762 return charge_control_threshold_show(conf.charge_control.offset_start,
763 device, attr, buf);
764 }
765
charge_control_start_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)766 static ssize_t charge_control_start_threshold_store(struct device *dev,
767 struct device_attribute *attr,
768 const char *buf, size_t count)
769 {
770 return charge_control_threshold_store(conf.charge_control.offset_start,
771 dev, attr, buf, count);
772 }
773
charge_control_end_threshold_show(struct device * device,struct device_attribute * attr,char * buf)774 static ssize_t charge_control_end_threshold_show(struct device *device,
775 struct device_attribute *attr,
776 char *buf)
777 {
778 return charge_control_threshold_show(conf.charge_control.offset_end,
779 device, attr, buf);
780 }
781
charge_control_end_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)782 static ssize_t charge_control_end_threshold_store(struct device *dev,
783 struct device_attribute *attr,
784 const char *buf, size_t count)
785 {
786 return charge_control_threshold_store(conf.charge_control.offset_end,
787 dev, attr, buf, count);
788 }
789
790 static DEVICE_ATTR_RW(charge_control_start_threshold);
791 static DEVICE_ATTR_RW(charge_control_end_threshold);
792
793 static struct attribute *msi_battery_attrs[] = {
794 &dev_attr_charge_control_start_threshold.attr,
795 &dev_attr_charge_control_end_threshold.attr,
796 NULL
797 };
798
799 ATTRIBUTE_GROUPS(msi_battery);
800
msi_battery_add(struct power_supply * battery,struct acpi_battery_hook * hook)801 static int msi_battery_add(struct power_supply *battery,
802 struct acpi_battery_hook *hook)
803 {
804 return device_add_groups(&battery->dev, msi_battery_groups);
805 }
806
msi_battery_remove(struct power_supply * battery,struct acpi_battery_hook * hook)807 static int msi_battery_remove(struct power_supply *battery,
808 struct acpi_battery_hook *hook)
809 {
810 device_remove_groups(&battery->dev, msi_battery_groups);
811 return 0;
812 }
813
814 static struct acpi_battery_hook battery_hook = {
815 .add_battery = msi_battery_add,
816 .remove_battery = msi_battery_remove,
817 .name = MSI_EC_DRIVER_NAME,
818 };
819
820 /*
821 * Module load/unload
822 */
823
824 static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = {
825 {
826 .matches = {
827 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"),
828 },
829 },
830 {
831 .matches = {
832 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
833 },
834 },
835 {}
836 };
837 MODULE_DEVICE_TABLE(dmi, msi_dmi_table);
838
load_configuration(void)839 static int __init load_configuration(void)
840 {
841 int result;
842
843 u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1];
844
845 /* get firmware version */
846 result = ec_get_firmware_version(fw_version);
847 if (result < 0)
848 return result;
849
850 /* load the suitable configuration, if exists */
851 for (int i = 0; CONFIGS[i]; i++) {
852 if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) {
853 conf = *CONFIGS[i];
854 conf.allowed_fw = NULL;
855 return 0;
856 }
857 }
858
859 /* config not found */
860
861 for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) {
862 if (!isgraph(fw_version[i])) {
863 pr_warn("Unable to find a valid firmware version!\n");
864 return -EOPNOTSUPP;
865 }
866 }
867
868 pr_warn("Firmware version is not supported: '%s'\n", fw_version);
869 return -EOPNOTSUPP;
870 }
871
msi_ec_init(void)872 static int __init msi_ec_init(void)
873 {
874 int result;
875
876 result = load_configuration();
877 if (result < 0)
878 return result;
879
880 battery_hook_register(&battery_hook);
881 return 0;
882 }
883
msi_ec_exit(void)884 static void __exit msi_ec_exit(void)
885 {
886 battery_hook_unregister(&battery_hook);
887 }
888
889 MODULE_LICENSE("GPL");
890 MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>");
891 MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>");
892 MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>");
893 MODULE_DESCRIPTION("MSI Embedded Controller");
894
895 module_init(msi_ec_init);
896 module_exit(msi_ec_exit);
897