| // SPDX-License-Identifier: GPL-2.0-or-later | |
| /* | |
| * msi-ec: MSI laptops' embedded controller driver. | |
| * | |
| * This driver allows various MSI laptops' functionalities to be | |
| * controlled from userspace. | |
| * | |
| * It contains EC memory configurations for different firmware versions | |
| * and exports battery charge thresholds to userspace. | |
| * | |
| * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es> | |
| * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev> | |
| * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com> | |
| */ | |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| #include "msi-ec.h" | |
| #include <acpi/battery.h> | |
| #include <linux/acpi.h> | |
| #include <linux/init.h> | |
| #include <linux/kernel.h> | |
| #include <linux/module.h> | |
| #include <linux/device-id/dmi.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/seq_file.h> | |
| #include <linux/string.h> | |
| #define SM_ECO_NAME "eco" | |
| #define SM_COMFORT_NAME "comfort" | |
| #define SM_SPORT_NAME "sport" | |
| #define SM_TURBO_NAME "turbo" | |
| #define FM_AUTO_NAME "auto" | |
| #define FM_SILENT_NAME "silent" | |
| #define FM_BASIC_NAME "basic" | |
| #define FM_ADVANCED_NAME "advanced" | |
| static const char * const ALLOWED_FW_0[] __initconst = { | |
| "14C1EMS1.012", | |
| "14C1EMS1.101", | |
| "14C1EMS1.102", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF0 __initdata = { | |
| .allowed_fw = ALLOWED_FW_0, | |
| .charge_control = { | |
| .address = 0xef, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xbf, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing | |
| }, | |
| .fan_mode = { | |
| .address = 0xf4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_BASIC_NAME, 0x4d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = 0x89, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2b, | |
| .mute_led_address = 0x2c, | |
| .bit = 2, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = 0x2c, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = 0xf3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_1[] __initconst = { | |
| "17F2EMS1.103", | |
| "17F2EMS1.104", | |
| "17F2EMS1.106", | |
| "17F2EMS1.107", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF1 __initdata = { | |
| .allowed_fw = ALLOWED_FW_1, | |
| .charge_control = { | |
| .address = 0xef, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xbf, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = MSI_EC_ADDR_UNKNOWN, | |
| }, | |
| .fan_mode = { | |
| .address = 0xf4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_BASIC_NAME, 0x4d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = 0x89, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2b, | |
| .mute_led_address = 0x2c, | |
| .bit = 2, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = 0x2c, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = 0xf3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_2[] __initconst = { | |
| "1552EMS1.118", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF2 __initdata = { | |
| .allowed_fw = ALLOWED_FW_2, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xeb, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_BASIC_NAME, 0x4d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = 0x89, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2c, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = 0x2c, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = 0xd3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_3[] __initconst = { | |
| "1592EMS1.111", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF3 __initdata = { | |
| .allowed_fw = ALLOWED_FW_3, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xeb, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_BASIC_NAME, 0x4d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0xc9, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = 0x89, // ? | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2b, | |
| .mute_led_address = 0x2c, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = 0x2c, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = 0xd3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_4[] __initconst = { | |
| "16V4EMS1.114", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF4 __initdata = { | |
| .allowed_fw = ALLOWED_FW_4, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { // may be supported, but address is unknown | |
| .address = MSI_EC_ADDR_UNKNOWN, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, // needs testing | |
| .rt_fan_speed_address = 0x71, // needs testing | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, | |
| }, | |
| .leds = { | |
| .micmute_led_address = MSI_EC_ADDR_UNKNOWN, | |
| .mute_led_address = MSI_EC_ADDR_UNKNOWN, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_5[] __initconst = { | |
| "158LEMS1.103", | |
| "158LEMS1.105", | |
| "158LEMS1.106", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF5 __initdata = { | |
| .allowed_fw = ALLOWED_FW_5, | |
| .charge_control = { | |
| .address = 0xef, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { // todo: reverse | |
| .address = 0xbf, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { // unsupported? | |
| .address = MSI_EC_ADDR_UNKNOWN, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xf4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, // needs testing | |
| .rt_fan_speed_address = 0x71, // needs testing | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = MSI_EC_ADDR_UNKNOWN, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2b, | |
| .mute_led_address = 0x2c, | |
| .bit = 2, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_6[] __initconst = { | |
| "1542EMS1.102", | |
| "1542EMS1.104", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF6 __initdata = { | |
| .allowed_fw = ALLOWED_FW_6, | |
| .charge_control = { | |
| .address = 0xef, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = MSI_EC_ADDR_UNSUPP, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xbf, // todo: reverse | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xd5, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xf4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0xc9, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, | |
| }, | |
| .leds = { | |
| .micmute_led_address = MSI_EC_ADDR_UNSUPP, | |
| .mute_led_address = MSI_EC_ADDR_UNSUPP, | |
| .bit = 2, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_7[] __initconst = { | |
| "17FKEMS1.108", | |
| "17FKEMS1.109", | |
| "17FKEMS1.10A", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF7 __initdata = { | |
| .allowed_fw = ALLOWED_FW_7, | |
| .charge_control = { | |
| .address = 0xef, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = MSI_EC_ADDR_UNSUPP, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xbf, // needs testing | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xf4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, // d may not be relevant | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0xc9, // needs testing | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = MSI_EC_ADDR_UNKNOWN, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, | |
| }, | |
| .leds = { | |
| .micmute_led_address = MSI_EC_ADDR_UNSUPP, | |
| .mute_led_address = 0x2c, | |
| .bit = 2, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = 0xf3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_8[] __initconst = { | |
| "14F1EMS1.115", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF8 __initdata = { | |
| .allowed_fw = ALLOWED_FW_8, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = MSI_EC_ADDR_UNSUPP, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xeb, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_BASIC_NAME, 0x4d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = MSI_EC_ADDR_UNKNOWN, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, | |
| }, | |
| .leds = { | |
| .micmute_led_address = MSI_EC_ADDR_UNSUPP, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? | |
| .bl_modes = { 0x00, 0x08 }, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = MSI_EC_ADDR_UNSUPP, // not functional | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_9[] __initconst = { | |
| "14JKEMS1.104", | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF9 __initdata = { | |
| .allowed_fw = ALLOWED_FW_9, | |
| .charge_control = { | |
| .address = 0xef, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xbf, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xf2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = MSI_EC_ADDR_UNSUPP, // unsupported or enabled by ECO shift | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xf4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x00, | |
| .rt_fan_speed_base_max = 0x96, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = MSI_EC_ADDR_UNSUPP, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2b, | |
| .mute_led_address = 0x2c, | |
| .bit = 2, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNSUPP, // not presented in MSI app | |
| .bl_modes = { 0x00, 0x08 }, | |
| .max_mode = 1, | |
| .bl_state_address = 0xf3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_10[] __initconst = { | |
| "1582EMS1.107", // GF66 11UC | |
| "1583EMS1.109", // Pulse GL66 12UEK | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF10 __initdata = { | |
| .allowed_fw = ALLOWED_FW_10, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = MSI_EC_ADDR_UNSUPP, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xe5, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, // ? | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, // ? | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2c, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = 0x2c, | |
| .bl_modes = { 0x00, 0x08 }, | |
| .max_mode = 1, | |
| .bl_state_address = 0xd3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_11[] __initconst = { | |
| "16S6EMS1.111", // Prestige 15 a11scx | |
| "1552EMS1.115", // Modern 15 a11m | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF11 __initdata = { | |
| .allowed_fw = ALLOWED_FW_11, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = MSI_EC_ADDR_UNKNOWN, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xeb, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x4d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = MSI_EC_ADDR_UNSUPP, | |
| .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2c, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, | |
| .bl_modes = {}, // ? | |
| .max_mode = 1, // ? | |
| .bl_state_address = 0xd3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_12[] __initconst = { | |
| "16R6EMS1.104", // GF63 Thin 11UC | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF12 __initdata = { | |
| .allowed_fw = ALLOWED_FW_12, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_SPORT_NAME, 0xc0 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = MSI_EC_ADDR_UNSUPP, // 0xeb | |
| .mask = 0x0f, // 00, 0f | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x19, | |
| .rt_fan_speed_base_max = 0x37, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = MSI_EC_ADDR_UNSUPP, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = MSI_EC_ADDR_UNSUPP, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNKNOWN, | |
| .bl_modes = { 0x00, 0x08 }, | |
| .max_mode = 1, | |
| .bl_state_address = 0xd3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_13[] __initconst = { | |
| "1594EMS1.109", // MSI Prestige 16 Studio A13VE | |
| "17L1EMS1.107", // MSI Katana GF76 11UEK | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF13 __initdata = { | |
| .allowed_fw = ALLOWED_FW_13, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, // 0x00-0x10 | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, // super battery | |
| { SM_COMFORT_NAME, 0xc1 }, // balanced | |
| { SM_TURBO_NAME, 0xc4 }, // extreme | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = MSI_EC_ADDR_UNSUPP, | |
| .mask = 0x0f, // 00, 0f | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, // 0x0-0x96 | |
| .rt_fan_speed_base_min = 0x00, | |
| .rt_fan_speed_base_max = 0x96, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2c, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = 0x2c, // KB auto turn off | |
| .bl_modes = { 0x00, 0x08 }, // always on; off after 10 sec | |
| .max_mode = 1, | |
| .bl_state_address = 0xd3, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static const char * const ALLOWED_FW_14[] __initconst = { | |
| "1824EMS1.108", // Raider 18 HX AI A2XWJG (MS-1824) | |
| "182LIMS1.108", // Vector A18 HX A9WHG | |
| "182LIMS1.111", // MSI Raider A18 HX A9WJG / Vector A18 HX A9WHG | |
| "182KIMS1.113", // Raider A18 HX A7VIG | |
| NULL | |
| }; | |
| static struct msi_ec_conf CONF14 __initdata = { | |
| .allowed_fw = ALLOWED_FW_14, | |
| .charge_control = { | |
| .address = 0xd7, | |
| .offset_start = 0x8a, | |
| .offset_end = 0x80, | |
| .range_min = 0x8a, | |
| .range_max = 0xe4, | |
| }, | |
| .webcam = { | |
| .address = 0x2e, | |
| .block_address = 0x2f, | |
| .bit = 1, | |
| }, | |
| .fn_win_swap = { | |
| .address = 0xe8, | |
| .bit = 4, | |
| }, | |
| .cooler_boost = { | |
| .address = 0x98, | |
| .bit = 7, | |
| }, | |
| .shift_mode = { | |
| .address = 0xd2, | |
| .modes = { | |
| { SM_ECO_NAME, 0xc2 }, | |
| { SM_COMFORT_NAME, 0xc1 }, | |
| { SM_TURBO_NAME, 0xc4 }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .super_battery = { | |
| .address = 0xeb, | |
| .mask = 0x0f, | |
| }, | |
| .fan_mode = { | |
| .address = 0xd4, | |
| .modes = { | |
| { FM_AUTO_NAME, 0x0d }, | |
| { FM_SILENT_NAME, 0x1d }, | |
| { FM_ADVANCED_NAME, 0x8d }, | |
| MSI_EC_MODE_NULL | |
| }, | |
| }, | |
| .cpu = { | |
| .rt_temp_address = 0x68, | |
| .rt_fan_speed_address = 0x71, | |
| .rt_fan_speed_base_min = 0x00, | |
| .rt_fan_speed_base_max = 0x96, | |
| .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, | |
| .bs_fan_speed_base_min = 0x00, | |
| .bs_fan_speed_base_max = 0x0f, | |
| }, | |
| .gpu = { | |
| .rt_temp_address = 0x80, | |
| .rt_fan_speed_address = 0x89, | |
| }, | |
| .leds = { | |
| .micmute_led_address = 0x2c, | |
| .mute_led_address = 0x2d, | |
| .bit = 1, | |
| }, | |
| .kbd_bl = { | |
| .bl_mode_address = MSI_EC_ADDR_UNSUPP, | |
| .bl_modes = { 0x00, 0x08 }, | |
| .max_mode = 1, | |
| .bl_state_address = MSI_EC_ADDR_UNSUPP, | |
| .state_base_value = 0x80, | |
| .max_state = 3, | |
| }, | |
| }; | |
| static struct msi_ec_conf *CONFIGS[] __initdata = { | |
| &CONF0, | |
| &CONF1, | |
| &CONF2, | |
| &CONF3, | |
| &CONF4, | |
| &CONF5, | |
| &CONF6, | |
| &CONF7, | |
| &CONF8, | |
| &CONF9, | |
| &CONF10, | |
| &CONF11, | |
| &CONF12, | |
| &CONF13, | |
| &CONF14, | |
| NULL | |
| }; | |
| static struct msi_ec_conf conf; // current configuration | |
| /* | |
| * Helper functions | |
| */ | |
| static int ec_read_seq(u8 addr, u8 *buf, u8 len) | |
| { | |
| int result; | |
| for (u8 i = 0; i < len; i++) { | |
| result = ec_read(addr + i, buf + i); | |
| if (result < 0) | |
| return result; | |
| } | |
| return 0; | |
| } | |
| static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) | |
| { | |
| int result; | |
| memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); | |
| result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, | |
| buf, | |
| MSI_EC_FW_VERSION_LENGTH); | |
| if (result < 0) | |
| return result; | |
| return MSI_EC_FW_VERSION_LENGTH + 1; | |
| } | |
| /* | |
| * Sysfs power_supply subsystem | |
| */ | |
| static ssize_t charge_control_threshold_show(u8 offset, | |
| struct device *device, | |
| struct device_attribute *attr, | |
| char *buf) | |
| { | |
| u8 rdata; | |
| int result; | |
| result = ec_read(conf.charge_control.address, &rdata); | |
| if (result < 0) | |
| return result; | |
| return sysfs_emit(buf, "%i\n", rdata - offset); | |
| } | |
| static ssize_t charge_control_threshold_store(u8 offset, | |
| struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| u8 wdata; | |
| int result; | |
| result = kstrtou8(buf, 10, &wdata); | |
| if (result < 0) | |
| return result; | |
| wdata += offset; | |
| if (wdata < conf.charge_control.range_min || | |
| wdata > conf.charge_control.range_max) | |
| return -EINVAL; | |
| result = ec_write(conf.charge_control.address, wdata); | |
| if (result < 0) | |
| return result; | |
| return count; | |
| } | |
| static ssize_t charge_control_start_threshold_show(struct device *device, | |
| struct device_attribute *attr, | |
| char *buf) | |
| { | |
| return charge_control_threshold_show(conf.charge_control.offset_start, | |
| device, attr, buf); | |
| } | |
| static ssize_t charge_control_start_threshold_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| return charge_control_threshold_store(conf.charge_control.offset_start, | |
| dev, attr, buf, count); | |
| } | |
| static ssize_t charge_control_end_threshold_show(struct device *device, | |
| struct device_attribute *attr, | |
| char *buf) | |
| { | |
| return charge_control_threshold_show(conf.charge_control.offset_end, | |
| device, attr, buf); | |
| } | |
| static ssize_t charge_control_end_threshold_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| return charge_control_threshold_store(conf.charge_control.offset_end, | |
| dev, attr, buf, count); | |
| } | |
| static DEVICE_ATTR_RW(charge_control_start_threshold); | |
| static DEVICE_ATTR_RW(charge_control_end_threshold); | |
| static struct attribute *msi_battery_attrs[] = { | |
| &dev_attr_charge_control_start_threshold.attr, | |
| &dev_attr_charge_control_end_threshold.attr, | |
| NULL | |
| }; | |
| ATTRIBUTE_GROUPS(msi_battery); | |
| static int msi_battery_add(struct power_supply *battery, | |
| struct acpi_battery_hook *hook) | |
| { | |
| return device_add_groups(&battery->dev, msi_battery_groups); | |
| } | |
| static int msi_battery_remove(struct power_supply *battery, | |
| struct acpi_battery_hook *hook) | |
| { | |
| device_remove_groups(&battery->dev, msi_battery_groups); | |
| return 0; | |
| } | |
| static struct acpi_battery_hook battery_hook = { | |
| .add_battery = msi_battery_add, | |
| .remove_battery = msi_battery_remove, | |
| .name = MSI_EC_DRIVER_NAME, | |
| }; | |
| /* | |
| * Module load/unload | |
| */ | |
| static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { | |
| { | |
| .matches = { | |
| DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), | |
| }, | |
| }, | |
| { | |
| .matches = { | |
| DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), | |
| }, | |
| }, | |
| {} | |
| }; | |
| MODULE_DEVICE_TABLE(dmi, msi_dmi_table); | |
| static int __init load_configuration(void) | |
| { | |
| int result; | |
| u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; | |
| /* get firmware version */ | |
| result = ec_get_firmware_version(fw_version); | |
| if (result < 0) | |
| return result; | |
| /* load the suitable configuration, if exists */ | |
| for (int i = 0; CONFIGS[i]; i++) { | |
| if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { | |
| conf = *CONFIGS[i]; | |
| conf.allowed_fw = NULL; | |
| return 0; | |
| } | |
| } | |
| /* config not found */ | |
| for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { | |
| if (!isgraph(fw_version[i])) { | |
| pr_warn("Unable to find a valid firmware version!\n"); | |
| return -EOPNOTSUPP; | |
| } | |
| } | |
| pr_warn("Firmware version is not supported: '%s'\n", fw_version); | |
| return -EOPNOTSUPP; | |
| } | |
| static int __init msi_ec_init(void) | |
| { | |
| int result; | |
| result = load_configuration(); | |
| if (result < 0) | |
| return result; | |
| battery_hook_register(&battery_hook); | |
| return 0; | |
| } | |
| static void __exit msi_ec_exit(void) | |
| { | |
| battery_hook_unregister(&battery_hook); | |
| } | |
| MODULE_LICENSE("GPL"); | |
| MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>"); | |
| MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>"); | |
| MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>"); | |
| MODULE_DESCRIPTION("MSI Embedded Controller"); | |
| module_init(msi_ec_init); | |
| module_exit(msi_ec_exit); |