1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * msi-ec: MSI laptops' embedded controller driver.
6 * This driver allows various MSI laptops' functionalities to be
7 * controlled from userspace.
9 * It contains EC memory configurations for different firmware versions
10 * and exports battery charge thresholds to userspace.
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
30 #define SM_ECO_NAME "eco"
31 #define SM_COMFORT_NAME "comfort"
32 #define SM_SPORT_NAME "sport"
33 #define SM_TURBO_NAME "turbo"
35 #define FM_AUTO_NAME "auto"
36 #define FM_SILENT_NAME "silent"
37 #define FM_BASIC_NAME "basic"
38 #define FM_ADVANCED_NAME "advanced"
40 static const char * const ALLOWED_FW_0[] __initconst = {
47 static struct msi_ec_conf CONF0 __initdata = {
48 .allowed_fw = ALLOWED_FW_0,
58 .block_address = 0x2f,
72 { SM_ECO_NAME, 0xc2 },
73 { SM_COMFORT_NAME, 0xc1 },
74 { SM_SPORT_NAME, 0xc0 },
79 .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing
84 { FM_AUTO_NAME, 0x0d },
85 { FM_SILENT_NAME, 0x1d },
86 { FM_BASIC_NAME, 0x4d },
87 { FM_ADVANCED_NAME, 0x8d },
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,
101 .rt_temp_address = 0x80,
102 .rt_fan_speed_address = 0x89,
105 .micmute_led_address = 0x2b,
106 .mute_led_address = 0x2c,
110 .bl_mode_address = 0x2c, // ?
111 .bl_modes = { 0x00, 0x08 }, // ?
113 .bl_state_address = 0xf3,
114 .state_base_value = 0x80,
119 static const char * const ALLOWED_FW_1[] __initconst = {
127 static struct msi_ec_conf CONF1 __initdata = {
128 .allowed_fw = ALLOWED_FW_1,
131 .offset_start = 0x8a,
138 .block_address = 0x2f,
152 { SM_ECO_NAME, 0xc2 },
153 { SM_COMFORT_NAME, 0xc1 },
154 { SM_SPORT_NAME, 0xc0 },
155 { SM_TURBO_NAME, 0xc4 },
160 .address = MSI_EC_ADDR_UNKNOWN,
165 { FM_AUTO_NAME, 0x0d },
166 { FM_BASIC_NAME, 0x4d },
167 { FM_ADVANCED_NAME, 0x8d },
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,
181 .rt_temp_address = 0x80,
182 .rt_fan_speed_address = 0x89,
185 .micmute_led_address = 0x2b,
186 .mute_led_address = 0x2c,
190 .bl_mode_address = 0x2c, // ?
191 .bl_modes = { 0x00, 0x08 }, // ?
193 .bl_state_address = 0xf3,
194 .state_base_value = 0x80,
199 static const char * const ALLOWED_FW_2[] __initconst = {
204 static struct msi_ec_conf CONF2 __initdata = {
205 .allowed_fw = ALLOWED_FW_2,
208 .offset_start = 0x8a,
215 .block_address = 0x2f,
229 { SM_ECO_NAME, 0xc2 },
230 { SM_COMFORT_NAME, 0xc1 },
231 { SM_SPORT_NAME, 0xc0 },
242 { FM_AUTO_NAME, 0x0d },
243 { FM_SILENT_NAME, 0x1d },
244 { FM_BASIC_NAME, 0x4d },
245 { FM_ADVANCED_NAME, 0x8d },
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,
259 .rt_temp_address = 0x80,
260 .rt_fan_speed_address = 0x89,
263 .micmute_led_address = 0x2c,
264 .mute_led_address = 0x2d,
268 .bl_mode_address = 0x2c, // ?
269 .bl_modes = { 0x00, 0x08 }, // ?
271 .bl_state_address = 0xd3,
272 .state_base_value = 0x80,
277 static const char * const ALLOWED_FW_3[] __initconst = {
282 static struct msi_ec_conf CONF3 __initdata = {
283 .allowed_fw = ALLOWED_FW_3,
286 .offset_start = 0x8a,
293 .block_address = 0x2f,
307 { SM_ECO_NAME, 0xc2 },
308 { SM_COMFORT_NAME, 0xc1 },
309 { SM_SPORT_NAME, 0xc0 },
320 { FM_AUTO_NAME, 0x0d },
321 { FM_SILENT_NAME, 0x1d },
322 { FM_BASIC_NAME, 0x4d },
323 { FM_ADVANCED_NAME, 0x8d },
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,
337 .rt_temp_address = 0x80,
338 .rt_fan_speed_address = 0x89,
341 .micmute_led_address = 0x2b,
342 .mute_led_address = 0x2c,
346 .bl_mode_address = 0x2c, // ?
347 .bl_modes = { 0x00, 0x08 }, // ?
349 .bl_state_address = 0xd3,
350 .state_base_value = 0x80,
355 static const char * const ALLOWED_FW_4[] __initconst = {
360 static struct msi_ec_conf CONF4 __initdata = {
361 .allowed_fw = ALLOWED_FW_4,
364 .offset_start = 0x8a,
371 .block_address = 0x2f,
375 .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown
385 { SM_ECO_NAME, 0xc2 },
386 { SM_COMFORT_NAME, 0xc1 },
387 { SM_SPORT_NAME, 0xc0 },
391 .super_battery = { // may be supported, but address is unknown
392 .address = MSI_EC_ADDR_UNKNOWN,
398 { FM_AUTO_NAME, 0x0d },
399 { FM_SILENT_NAME, 0x1d },
400 { FM_ADVANCED_NAME, 0x8d },
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,
414 .rt_temp_address = 0x80,
415 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
418 .micmute_led_address = MSI_EC_ADDR_UNKNOWN,
419 .mute_led_address = MSI_EC_ADDR_UNKNOWN,
423 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
424 .bl_modes = { 0x00, 0x08 }, // ?
426 .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional
427 .state_base_value = 0x80,
432 static const char * const ALLOWED_FW_5[] __initconst = {
439 static struct msi_ec_conf CONF5 __initdata = {
440 .allowed_fw = ALLOWED_FW_5,
443 .offset_start = 0x8a,
450 .block_address = 0x2f,
453 .fn_win_swap = { // todo: reverse
464 { SM_ECO_NAME, 0xc2 },
465 { SM_COMFORT_NAME, 0xc1 },
466 { SM_TURBO_NAME, 0xc4 },
470 .super_battery = { // unsupported?
471 .address = MSI_EC_ADDR_UNKNOWN,
477 { FM_AUTO_NAME, 0x0d },
478 { FM_SILENT_NAME, 0x1d },
479 { FM_ADVANCED_NAME, 0x8d },
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,
493 .rt_temp_address = MSI_EC_ADDR_UNKNOWN,
494 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
497 .micmute_led_address = 0x2b,
498 .mute_led_address = 0x2c,
502 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
503 .bl_modes = { 0x00, 0x08 }, // ?
505 .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional
506 .state_base_value = 0x80,
511 static const char * const ALLOWED_FW_6[] __initconst = {
517 static struct msi_ec_conf CONF6 __initdata = {
518 .allowed_fw = ALLOWED_FW_6,
521 .offset_start = 0x8a,
528 .block_address = MSI_EC_ADDR_UNSUPP,
532 .address = 0xbf, // todo: reverse
542 { SM_ECO_NAME, 0xc2 },
543 { SM_COMFORT_NAME, 0xc1 },
544 { SM_SPORT_NAME, 0xc0 },
545 { SM_TURBO_NAME, 0xc4 },
556 { FM_AUTO_NAME, 0x0d },
557 { FM_SILENT_NAME, 0x1d },
558 { FM_ADVANCED_NAME, 0x8d },
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,
572 .rt_temp_address = 0x80,
573 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
576 .micmute_led_address = MSI_EC_ADDR_UNSUPP,
577 .mute_led_address = MSI_EC_ADDR_UNSUPP,
581 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
582 .bl_modes = { 0x00, 0x08 }, // ?
584 .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional
585 .state_base_value = 0x80,
590 static const char * const ALLOWED_FW_7[] __initconst = {
597 static struct msi_ec_conf CONF7 __initdata = {
598 .allowed_fw = ALLOWED_FW_7,
601 .offset_start = 0x8a,
608 .block_address = MSI_EC_ADDR_UNSUPP,
612 .address = 0xbf, // needs testing
622 { SM_ECO_NAME, 0xc2 },
623 { SM_COMFORT_NAME, 0xc1 },
624 { SM_SPORT_NAME, 0xc0 },
625 { SM_TURBO_NAME, 0xc4 },
630 .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes
636 { FM_AUTO_NAME, 0x0d }, // d may not be relevant
637 { FM_SILENT_NAME, 0x1d },
638 { FM_ADVANCED_NAME, 0x8d },
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,
652 .rt_temp_address = MSI_EC_ADDR_UNKNOWN,
653 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
656 .micmute_led_address = MSI_EC_ADDR_UNSUPP,
657 .mute_led_address = 0x2c,
661 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
662 .bl_modes = { 0x00, 0x08 }, // ?
664 .bl_state_address = 0xf3,
665 .state_base_value = 0x80,
670 static const char * const ALLOWED_FW_8[] __initconst = {
675 static struct msi_ec_conf CONF8 __initdata = {
676 .allowed_fw = ALLOWED_FW_8,
679 .offset_start = 0x8a,
686 .block_address = MSI_EC_ADDR_UNSUPP,
700 { SM_ECO_NAME, 0xc2 },
701 { SM_COMFORT_NAME, 0xc1 },
702 { SM_SPORT_NAME, 0xc0 },
713 { FM_AUTO_NAME, 0x0d },
714 { FM_SILENT_NAME, 0x1d },
715 { FM_BASIC_NAME, 0x4d },
720 .rt_temp_address = 0x68,
721 .rt_fan_speed_address = 0x71,
722 .rt_fan_speed_base_min = 0x19,
723 .rt_fan_speed_base_max = 0x37,
724 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
725 .bs_fan_speed_base_min = 0x00,
726 .bs_fan_speed_base_max = 0x0f,
729 .rt_temp_address = MSI_EC_ADDR_UNKNOWN,
730 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
733 .micmute_led_address = MSI_EC_ADDR_UNSUPP,
734 .mute_led_address = 0x2d,
738 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
739 .bl_modes = { 0x00, 0x08 }, // ?
741 .bl_state_address = MSI_EC_ADDR_UNSUPP, // not functional
742 .state_base_value = 0x80,
747 static const char * const ALLOWED_FW_9[] __initconst = {
752 static struct msi_ec_conf CONF9 __initdata = {
753 .allowed_fw = ALLOWED_FW_9,
756 .offset_start = 0x8a,
763 .block_address = 0x2f,
777 { SM_ECO_NAME, 0xc2 },
778 { SM_COMFORT_NAME, 0xc1 },
779 { SM_SPORT_NAME, 0xc0 },
784 .address = MSI_EC_ADDR_UNSUPP, // unsupported or enabled by ECO shift
790 { FM_AUTO_NAME, 0x0d },
791 { FM_SILENT_NAME, 0x1d },
792 { FM_ADVANCED_NAME, 0x8d },
797 .rt_temp_address = 0x68,
798 .rt_fan_speed_address = 0x71,
799 .rt_fan_speed_base_min = 0x00,
800 .rt_fan_speed_base_max = 0x96,
801 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
802 .bs_fan_speed_base_min = 0x00,
803 .bs_fan_speed_base_max = 0x0f,
806 .rt_temp_address = MSI_EC_ADDR_UNSUPP,
807 .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP,
810 .micmute_led_address = 0x2b,
811 .mute_led_address = 0x2c,
815 .bl_mode_address = MSI_EC_ADDR_UNSUPP, // not presented in MSI app
816 .bl_modes = { 0x00, 0x08 },
818 .bl_state_address = 0xf3,
819 .state_base_value = 0x80,
824 static const char * const ALLOWED_FW_10[] __initconst = {
825 "1582EMS1.107", // GF66 11UC
829 static struct msi_ec_conf CONF10 __initdata = {
830 .allowed_fw = ALLOWED_FW_10,
833 .offset_start = 0x8a,
840 .block_address = 0x2f,
844 .address = MSI_EC_ADDR_UNSUPP,
854 { SM_ECO_NAME, 0xc2 },
855 { SM_COMFORT_NAME, 0xc1 },
856 { SM_SPORT_NAME, 0xc0 },
857 { SM_TURBO_NAME, 0xc4 },
868 { FM_AUTO_NAME, 0x0d },
869 { FM_SILENT_NAME, 0x1d },
870 { FM_ADVANCED_NAME, 0x8d },
875 .rt_temp_address = 0x68,
876 .rt_fan_speed_address = 0x71, // ?
877 .rt_fan_speed_base_min = 0x19,
878 .rt_fan_speed_base_max = 0x37,
879 .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, // ?
880 .bs_fan_speed_base_min = 0x00,
881 .bs_fan_speed_base_max = 0x0f,
884 .rt_temp_address = 0x80,
885 .rt_fan_speed_address = 0x89,
888 .micmute_led_address = 0x2c,
889 .mute_led_address = 0x2d,
893 .bl_mode_address = 0x2c,
894 .bl_modes = { 0x00, 0x08 },
896 .bl_state_address = 0xd3,
897 .state_base_value = 0x80,
902 static const char * const ALLOWED_FW_11[] __initconst = {
903 "16S6EMS1.111", // Prestige 15 a11scx
904 "1552EMS1.115", // Modern 15 a11m
908 static struct msi_ec_conf CONF11 __initdata = {
909 .allowed_fw = ALLOWED_FW_11,
912 .offset_start = 0x8a,
919 .block_address = MSI_EC_ADDR_UNKNOWN,
933 { SM_ECO_NAME, 0xc2 },
934 { SM_COMFORT_NAME, 0xc1 },
935 { SM_SPORT_NAME, 0xc0 },
946 { FM_AUTO_NAME, 0x0d },
947 { FM_SILENT_NAME, 0x1d },
948 { FM_ADVANCED_NAME, 0x4d },
953 .rt_temp_address = 0x68,
954 .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP,
955 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
958 .rt_temp_address = MSI_EC_ADDR_UNSUPP,
959 .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP,
962 .micmute_led_address = 0x2c,
963 .mute_led_address = 0x2d,
967 .bl_mode_address = MSI_EC_ADDR_UNKNOWN,
970 .bl_state_address = 0xd3,
971 .state_base_value = 0x80,
976 static const char * const ALLOWED_FW_12[] __initconst = {
977 "16R6EMS1.104", // GF63 Thin 11UC
981 static struct msi_ec_conf CONF12 __initdata = {
982 .allowed_fw = ALLOWED_FW_12,
985 .offset_start = 0x8a,
992 .block_address = 0x2f,
1006 { SM_ECO_NAME, 0xc2 },
1007 { SM_COMFORT_NAME, 0xc1 },
1008 { SM_SPORT_NAME, 0xc0 },
1009 { SM_TURBO_NAME, 0xc4 },
1014 .address = MSI_EC_ADDR_UNSUPP, // 0xeb
1015 .mask = 0x0f, // 00, 0f
1020 { FM_AUTO_NAME, 0x0d },
1021 { FM_SILENT_NAME, 0x1d },
1022 { FM_ADVANCED_NAME, 0x8d },
1027 .rt_temp_address = 0x68,
1028 .rt_fan_speed_address = 0x71,
1029 .rt_fan_speed_base_min = 0x19,
1030 .rt_fan_speed_base_max = 0x37,
1031 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
1032 .bs_fan_speed_base_min = 0x00,
1033 .bs_fan_speed_base_max = 0x0f,
1036 .rt_temp_address = MSI_EC_ADDR_UNSUPP,
1037 .rt_fan_speed_address = 0x89,
1040 .micmute_led_address = MSI_EC_ADDR_UNSUPP,
1041 .mute_led_address = 0x2d,
1045 .bl_mode_address = MSI_EC_ADDR_UNKNOWN,
1046 .bl_modes = { 0x00, 0x08 },
1048 .bl_state_address = 0xd3,
1049 .state_base_value = 0x80,
1054 static const char * const ALLOWED_FW_13[] __initconst = {
1055 "1594EMS1.109", // MSI Prestige 16 Studio A13VE
1059 static struct msi_ec_conf CONF13 __initdata = {
1060 .allowed_fw = ALLOWED_FW_13,
1063 .offset_start = 0x8a,
1070 .block_address = 0x2f,
1075 .bit = 4, // 0x00-0x10
1084 { SM_ECO_NAME, 0xc2 }, // super battery
1085 { SM_COMFORT_NAME, 0xc1 }, // balanced
1086 { SM_TURBO_NAME, 0xc4 }, // extreme
1091 .address = MSI_EC_ADDR_UNSUPP,
1092 .mask = 0x0f, // 00, 0f
1097 { FM_AUTO_NAME, 0x0d },
1098 { FM_SILENT_NAME, 0x1d },
1099 { FM_ADVANCED_NAME, 0x8d },
1104 .rt_temp_address = 0x68,
1105 .rt_fan_speed_address = 0x71, // 0x0-0x96
1106 .rt_fan_speed_base_min = 0x00,
1107 .rt_fan_speed_base_max = 0x96,
1108 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
1109 .bs_fan_speed_base_min = 0x00,
1110 .bs_fan_speed_base_max = 0x0f,
1113 .rt_temp_address = 0x80,
1114 .rt_fan_speed_address = 0x89,
1117 .micmute_led_address = 0x2c,
1118 .mute_led_address = 0x2d,
1122 .bl_mode_address = 0x2c, // KB auto turn off
1123 .bl_modes = { 0x00, 0x08 }, // always on; off after 10 sec
1125 .bl_state_address = 0xd3,
1126 .state_base_value = 0x80,
1131 static struct msi_ec_conf *CONFIGS[] __initdata = {
1149 static struct msi_ec_conf conf; // current configuration
1155 static int ec_read_seq(u8 addr, u8 *buf, u8 len)
1159 for (u8 i = 0; i < len; i++) {
1160 result = ec_read(addr + i, buf + i);
1168 static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1])
1172 memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1);
1173 result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS,
1175 MSI_EC_FW_VERSION_LENGTH);
1179 return MSI_EC_FW_VERSION_LENGTH + 1;
1183 * Sysfs power_supply subsystem
1186 static ssize_t charge_control_threshold_show(u8 offset,
1187 struct device *device,
1188 struct device_attribute *attr,
1194 result = ec_read(conf.charge_control.address, &rdata);
1198 return sysfs_emit(buf, "%i\n", rdata - offset);
1201 static ssize_t charge_control_threshold_store(u8 offset,
1203 struct device_attribute *attr,
1204 const char *buf, size_t count)
1209 result = kstrtou8(buf, 10, &wdata);
1214 if (wdata < conf.charge_control.range_min ||
1215 wdata > conf.charge_control.range_max)
1218 result = ec_write(conf.charge_control.address, wdata);
1225 static ssize_t charge_control_start_threshold_show(struct device *device,
1226 struct device_attribute *attr,
1229 return charge_control_threshold_show(conf.charge_control.offset_start,
1233 static ssize_t charge_control_start_threshold_store(struct device *dev,
1234 struct device_attribute *attr,
1235 const char *buf, size_t count)
1237 return charge_control_threshold_store(conf.charge_control.offset_start,
1238 dev, attr, buf, count);
1241 static ssize_t charge_control_end_threshold_show(struct device *device,
1242 struct device_attribute *attr,
1245 return charge_control_threshold_show(conf.charge_control.offset_end,
1249 static ssize_t charge_control_end_threshold_store(struct device *dev,
1250 struct device_attribute *attr,
1251 const char *buf, size_t count)
1253 return charge_control_threshold_store(conf.charge_control.offset_end,
1254 dev, attr, buf, count);
1257 static DEVICE_ATTR_RW(charge_control_start_threshold);
1258 static DEVICE_ATTR_RW(charge_control_end_threshold);
1260 static struct attribute *msi_battery_attrs[] = {
1261 &dev_attr_charge_control_start_threshold.attr,
1262 &dev_attr_charge_control_end_threshold.attr,
1266 ATTRIBUTE_GROUPS(msi_battery);
1268 static int msi_battery_add(struct power_supply *battery,
1269 struct acpi_battery_hook *hook)
1271 return device_add_groups(&battery->dev, msi_battery_groups);
1274 static int msi_battery_remove(struct power_supply *battery,
1275 struct acpi_battery_hook *hook)
1277 device_remove_groups(&battery->dev, msi_battery_groups);
1281 static struct acpi_battery_hook battery_hook = {
1282 .add_battery = msi_battery_add,
1283 .remove_battery = msi_battery_remove,
1284 .name = MSI_EC_DRIVER_NAME,
1288 * Module load/unload
1291 static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = {
1294 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"),
1299 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
1304 MODULE_DEVICE_TABLE(dmi, msi_dmi_table);
1306 static int __init load_configuration(void)
1310 u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1];
1312 /* get firmware version */
1313 result = ec_get_firmware_version(fw_version);
1317 /* load the suitable configuration, if exists */
1318 for (int i = 0; CONFIGS[i]; i++) {
1319 if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) {
1321 conf.allowed_fw = NULL;
1326 /* config not found */
1328 for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) {
1329 if (!isgraph(fw_version[i])) {
1330 pr_warn("Unable to find a valid firmware version!\n");
1335 pr_warn("Firmware version is not supported: '%s'\n", fw_version);
1339 static int __init msi_ec_init(void)
1343 result = load_configuration();
1347 battery_hook_register(&battery_hook);
1351 static void __exit msi_ec_exit(void)
1353 battery_hook_unregister(&battery_hook);
1356 MODULE_LICENSE("GPL");
1360 MODULE_DESCRIPTION("MSI Embedded Controller");
1362 module_init(msi_ec_init);
1363 module_exit(msi_ec_exit);