xref: /openbmc/linux/drivers/platform/x86/msi-ec.c (revision 18afb028)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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