Merge tag 'powerpc-6.6-6' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[platform/kernel/linux-starfive.git] / drivers / platform / x86 / msi-ec.c
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);