162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * msi-ec: MSI laptops' embedded controller driver. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This driver allows various MSI laptops' functionalities to be 762306a36Sopenharmony_ci * controlled from userspace. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * It contains EC memory configurations for different firmware versions 1062306a36Sopenharmony_ci * and exports battery charge thresholds to userspace. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es> 1362306a36Sopenharmony_ci * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev> 1462306a36Sopenharmony_ci * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "msi-ec.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <acpi/battery.h> 2262306a36Sopenharmony_ci#include <linux/acpi.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/seq_file.h> 2862306a36Sopenharmony_ci#include <linux/string.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define SM_ECO_NAME "eco" 3162306a36Sopenharmony_ci#define SM_COMFORT_NAME "comfort" 3262306a36Sopenharmony_ci#define SM_SPORT_NAME "sport" 3362306a36Sopenharmony_ci#define SM_TURBO_NAME "turbo" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define FM_AUTO_NAME "auto" 3662306a36Sopenharmony_ci#define FM_SILENT_NAME "silent" 3762306a36Sopenharmony_ci#define FM_BASIC_NAME "basic" 3862306a36Sopenharmony_ci#define FM_ADVANCED_NAME "advanced" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const char * const ALLOWED_FW_0[] __initconst = { 4162306a36Sopenharmony_ci "14C1EMS1.012", 4262306a36Sopenharmony_ci "14C1EMS1.101", 4362306a36Sopenharmony_ci "14C1EMS1.102", 4462306a36Sopenharmony_ci NULL 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct msi_ec_conf CONF0 __initdata = { 4862306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_0, 4962306a36Sopenharmony_ci .charge_control = { 5062306a36Sopenharmony_ci .address = 0xef, 5162306a36Sopenharmony_ci .offset_start = 0x8a, 5262306a36Sopenharmony_ci .offset_end = 0x80, 5362306a36Sopenharmony_ci .range_min = 0x8a, 5462306a36Sopenharmony_ci .range_max = 0xe4, 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci .webcam = { 5762306a36Sopenharmony_ci .address = 0x2e, 5862306a36Sopenharmony_ci .block_address = 0x2f, 5962306a36Sopenharmony_ci .bit = 1, 6062306a36Sopenharmony_ci }, 6162306a36Sopenharmony_ci .fn_super_swap = { 6262306a36Sopenharmony_ci .address = 0xbf, 6362306a36Sopenharmony_ci .bit = 4, 6462306a36Sopenharmony_ci }, 6562306a36Sopenharmony_ci .cooler_boost = { 6662306a36Sopenharmony_ci .address = 0x98, 6762306a36Sopenharmony_ci .bit = 7, 6862306a36Sopenharmony_ci }, 6962306a36Sopenharmony_ci .shift_mode = { 7062306a36Sopenharmony_ci .address = 0xf2, 7162306a36Sopenharmony_ci .modes = { 7262306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 7362306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 7462306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 7562306a36Sopenharmony_ci MSI_EC_MODE_NULL 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci }, 7862306a36Sopenharmony_ci .super_battery = { 7962306a36Sopenharmony_ci .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing 8062306a36Sopenharmony_ci }, 8162306a36Sopenharmony_ci .fan_mode = { 8262306a36Sopenharmony_ci .address = 0xf4, 8362306a36Sopenharmony_ci .modes = { 8462306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 8562306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 8662306a36Sopenharmony_ci { FM_BASIC_NAME, 0x4d }, 8762306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 8862306a36Sopenharmony_ci MSI_EC_MODE_NULL 8962306a36Sopenharmony_ci }, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci .cpu = { 9262306a36Sopenharmony_ci .rt_temp_address = 0x68, 9362306a36Sopenharmony_ci .rt_fan_speed_address = 0x71, 9462306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 9562306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 9662306a36Sopenharmony_ci .bs_fan_speed_address = 0x89, 9762306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 9862306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 9962306a36Sopenharmony_ci }, 10062306a36Sopenharmony_ci .gpu = { 10162306a36Sopenharmony_ci .rt_temp_address = 0x80, 10262306a36Sopenharmony_ci .rt_fan_speed_address = 0x89, 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci .leds = { 10562306a36Sopenharmony_ci .micmute_led_address = 0x2b, 10662306a36Sopenharmony_ci .mute_led_address = 0x2c, 10762306a36Sopenharmony_ci .bit = 2, 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci .kbd_bl = { 11062306a36Sopenharmony_ci .bl_mode_address = 0x2c, // ? 11162306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 11262306a36Sopenharmony_ci .max_mode = 1, // ? 11362306a36Sopenharmony_ci .bl_state_address = 0xf3, 11462306a36Sopenharmony_ci .state_base_value = 0x80, 11562306a36Sopenharmony_ci .max_state = 3, 11662306a36Sopenharmony_ci }, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const char * const ALLOWED_FW_1[] __initconst = { 12062306a36Sopenharmony_ci "17F2EMS1.103", 12162306a36Sopenharmony_ci "17F2EMS1.104", 12262306a36Sopenharmony_ci "17F2EMS1.106", 12362306a36Sopenharmony_ci "17F2EMS1.107", 12462306a36Sopenharmony_ci NULL 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct msi_ec_conf CONF1 __initdata = { 12862306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_1, 12962306a36Sopenharmony_ci .charge_control = { 13062306a36Sopenharmony_ci .address = 0xef, 13162306a36Sopenharmony_ci .offset_start = 0x8a, 13262306a36Sopenharmony_ci .offset_end = 0x80, 13362306a36Sopenharmony_ci .range_min = 0x8a, 13462306a36Sopenharmony_ci .range_max = 0xe4, 13562306a36Sopenharmony_ci }, 13662306a36Sopenharmony_ci .webcam = { 13762306a36Sopenharmony_ci .address = 0x2e, 13862306a36Sopenharmony_ci .block_address = 0x2f, 13962306a36Sopenharmony_ci .bit = 1, 14062306a36Sopenharmony_ci }, 14162306a36Sopenharmony_ci .fn_super_swap = { 14262306a36Sopenharmony_ci .address = 0xbf, 14362306a36Sopenharmony_ci .bit = 4, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci .cooler_boost = { 14662306a36Sopenharmony_ci .address = 0x98, 14762306a36Sopenharmony_ci .bit = 7, 14862306a36Sopenharmony_ci }, 14962306a36Sopenharmony_ci .shift_mode = { 15062306a36Sopenharmony_ci .address = 0xf2, 15162306a36Sopenharmony_ci .modes = { 15262306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 15362306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 15462306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 15562306a36Sopenharmony_ci { SM_TURBO_NAME, 0xc4 }, 15662306a36Sopenharmony_ci MSI_EC_MODE_NULL 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci }, 15962306a36Sopenharmony_ci .super_battery = { 16062306a36Sopenharmony_ci .address = MSI_EC_ADDR_UNKNOWN, 16162306a36Sopenharmony_ci }, 16262306a36Sopenharmony_ci .fan_mode = { 16362306a36Sopenharmony_ci .address = 0xf4, 16462306a36Sopenharmony_ci .modes = { 16562306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 16662306a36Sopenharmony_ci { FM_BASIC_NAME, 0x4d }, 16762306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 16862306a36Sopenharmony_ci MSI_EC_MODE_NULL 16962306a36Sopenharmony_ci }, 17062306a36Sopenharmony_ci }, 17162306a36Sopenharmony_ci .cpu = { 17262306a36Sopenharmony_ci .rt_temp_address = 0x68, 17362306a36Sopenharmony_ci .rt_fan_speed_address = 0x71, 17462306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 17562306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 17662306a36Sopenharmony_ci .bs_fan_speed_address = 0x89, 17762306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 17862306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 17962306a36Sopenharmony_ci }, 18062306a36Sopenharmony_ci .gpu = { 18162306a36Sopenharmony_ci .rt_temp_address = 0x80, 18262306a36Sopenharmony_ci .rt_fan_speed_address = 0x89, 18362306a36Sopenharmony_ci }, 18462306a36Sopenharmony_ci .leds = { 18562306a36Sopenharmony_ci .micmute_led_address = 0x2b, 18662306a36Sopenharmony_ci .mute_led_address = 0x2c, 18762306a36Sopenharmony_ci .bit = 2, 18862306a36Sopenharmony_ci }, 18962306a36Sopenharmony_ci .kbd_bl = { 19062306a36Sopenharmony_ci .bl_mode_address = 0x2c, // ? 19162306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 19262306a36Sopenharmony_ci .max_mode = 1, // ? 19362306a36Sopenharmony_ci .bl_state_address = 0xf3, 19462306a36Sopenharmony_ci .state_base_value = 0x80, 19562306a36Sopenharmony_ci .max_state = 3, 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const char * const ALLOWED_FW_2[] __initconst = { 20062306a36Sopenharmony_ci "1552EMS1.118", 20162306a36Sopenharmony_ci NULL 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct msi_ec_conf CONF2 __initdata = { 20562306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_2, 20662306a36Sopenharmony_ci .charge_control = { 20762306a36Sopenharmony_ci .address = 0xd7, 20862306a36Sopenharmony_ci .offset_start = 0x8a, 20962306a36Sopenharmony_ci .offset_end = 0x80, 21062306a36Sopenharmony_ci .range_min = 0x8a, 21162306a36Sopenharmony_ci .range_max = 0xe4, 21262306a36Sopenharmony_ci }, 21362306a36Sopenharmony_ci .webcam = { 21462306a36Sopenharmony_ci .address = 0x2e, 21562306a36Sopenharmony_ci .block_address = 0x2f, 21662306a36Sopenharmony_ci .bit = 1, 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci .fn_super_swap = { 21962306a36Sopenharmony_ci .address = 0xe8, 22062306a36Sopenharmony_ci .bit = 4, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci .cooler_boost = { 22362306a36Sopenharmony_ci .address = 0x98, 22462306a36Sopenharmony_ci .bit = 7, 22562306a36Sopenharmony_ci }, 22662306a36Sopenharmony_ci .shift_mode = { 22762306a36Sopenharmony_ci .address = 0xf2, 22862306a36Sopenharmony_ci .modes = { 22962306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 23062306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 23162306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 23262306a36Sopenharmony_ci MSI_EC_MODE_NULL 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci }, 23562306a36Sopenharmony_ci .super_battery = { 23662306a36Sopenharmony_ci .address = 0xeb, 23762306a36Sopenharmony_ci .mask = 0x0f, 23862306a36Sopenharmony_ci }, 23962306a36Sopenharmony_ci .fan_mode = { 24062306a36Sopenharmony_ci .address = 0xd4, 24162306a36Sopenharmony_ci .modes = { 24262306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 24362306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 24462306a36Sopenharmony_ci { FM_BASIC_NAME, 0x4d }, 24562306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 24662306a36Sopenharmony_ci MSI_EC_MODE_NULL 24762306a36Sopenharmony_ci }, 24862306a36Sopenharmony_ci }, 24962306a36Sopenharmony_ci .cpu = { 25062306a36Sopenharmony_ci .rt_temp_address = 0x68, 25162306a36Sopenharmony_ci .rt_fan_speed_address = 0x71, 25262306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 25362306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 25462306a36Sopenharmony_ci .bs_fan_speed_address = 0x89, 25562306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 25662306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 25762306a36Sopenharmony_ci }, 25862306a36Sopenharmony_ci .gpu = { 25962306a36Sopenharmony_ci .rt_temp_address = 0x80, 26062306a36Sopenharmony_ci .rt_fan_speed_address = 0x89, 26162306a36Sopenharmony_ci }, 26262306a36Sopenharmony_ci .leds = { 26362306a36Sopenharmony_ci .micmute_led_address = 0x2c, 26462306a36Sopenharmony_ci .mute_led_address = 0x2d, 26562306a36Sopenharmony_ci .bit = 1, 26662306a36Sopenharmony_ci }, 26762306a36Sopenharmony_ci .kbd_bl = { 26862306a36Sopenharmony_ci .bl_mode_address = 0x2c, // ? 26962306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 27062306a36Sopenharmony_ci .max_mode = 1, // ? 27162306a36Sopenharmony_ci .bl_state_address = 0xd3, 27262306a36Sopenharmony_ci .state_base_value = 0x80, 27362306a36Sopenharmony_ci .max_state = 3, 27462306a36Sopenharmony_ci }, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const char * const ALLOWED_FW_3[] __initconst = { 27862306a36Sopenharmony_ci "1592EMS1.111", 27962306a36Sopenharmony_ci NULL 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic struct msi_ec_conf CONF3 __initdata = { 28362306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_3, 28462306a36Sopenharmony_ci .charge_control = { 28562306a36Sopenharmony_ci .address = 0xd7, 28662306a36Sopenharmony_ci .offset_start = 0x8a, 28762306a36Sopenharmony_ci .offset_end = 0x80, 28862306a36Sopenharmony_ci .range_min = 0x8a, 28962306a36Sopenharmony_ci .range_max = 0xe4, 29062306a36Sopenharmony_ci }, 29162306a36Sopenharmony_ci .webcam = { 29262306a36Sopenharmony_ci .address = 0x2e, 29362306a36Sopenharmony_ci .block_address = 0x2f, 29462306a36Sopenharmony_ci .bit = 1, 29562306a36Sopenharmony_ci }, 29662306a36Sopenharmony_ci .fn_super_swap = { 29762306a36Sopenharmony_ci .address = 0xe8, 29862306a36Sopenharmony_ci .bit = 4, 29962306a36Sopenharmony_ci }, 30062306a36Sopenharmony_ci .cooler_boost = { 30162306a36Sopenharmony_ci .address = 0x98, 30262306a36Sopenharmony_ci .bit = 7, 30362306a36Sopenharmony_ci }, 30462306a36Sopenharmony_ci .shift_mode = { 30562306a36Sopenharmony_ci .address = 0xd2, 30662306a36Sopenharmony_ci .modes = { 30762306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 30862306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 30962306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 31062306a36Sopenharmony_ci MSI_EC_MODE_NULL 31162306a36Sopenharmony_ci }, 31262306a36Sopenharmony_ci }, 31362306a36Sopenharmony_ci .super_battery = { 31462306a36Sopenharmony_ci .address = 0xeb, 31562306a36Sopenharmony_ci .mask = 0x0f, 31662306a36Sopenharmony_ci }, 31762306a36Sopenharmony_ci .fan_mode = { 31862306a36Sopenharmony_ci .address = 0xd4, 31962306a36Sopenharmony_ci .modes = { 32062306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 32162306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 32262306a36Sopenharmony_ci { FM_BASIC_NAME, 0x4d }, 32362306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 32462306a36Sopenharmony_ci MSI_EC_MODE_NULL 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci }, 32762306a36Sopenharmony_ci .cpu = { 32862306a36Sopenharmony_ci .rt_temp_address = 0x68, 32962306a36Sopenharmony_ci .rt_fan_speed_address = 0xc9, 33062306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 33162306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 33262306a36Sopenharmony_ci .bs_fan_speed_address = 0x89, // ? 33362306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 33462306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 33562306a36Sopenharmony_ci }, 33662306a36Sopenharmony_ci .gpu = { 33762306a36Sopenharmony_ci .rt_temp_address = 0x80, 33862306a36Sopenharmony_ci .rt_fan_speed_address = 0x89, 33962306a36Sopenharmony_ci }, 34062306a36Sopenharmony_ci .leds = { 34162306a36Sopenharmony_ci .micmute_led_address = 0x2b, 34262306a36Sopenharmony_ci .mute_led_address = 0x2c, 34362306a36Sopenharmony_ci .bit = 1, 34462306a36Sopenharmony_ci }, 34562306a36Sopenharmony_ci .kbd_bl = { 34662306a36Sopenharmony_ci .bl_mode_address = 0x2c, // ? 34762306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 34862306a36Sopenharmony_ci .max_mode = 1, // ? 34962306a36Sopenharmony_ci .bl_state_address = 0xd3, 35062306a36Sopenharmony_ci .state_base_value = 0x80, 35162306a36Sopenharmony_ci .max_state = 3, 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const char * const ALLOWED_FW_4[] __initconst = { 35662306a36Sopenharmony_ci "16V4EMS1.114", 35762306a36Sopenharmony_ci NULL 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic struct msi_ec_conf CONF4 __initdata = { 36162306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_4, 36262306a36Sopenharmony_ci .charge_control = { 36362306a36Sopenharmony_ci .address = 0xd7, 36462306a36Sopenharmony_ci .offset_start = 0x8a, 36562306a36Sopenharmony_ci .offset_end = 0x80, 36662306a36Sopenharmony_ci .range_min = 0x8a, 36762306a36Sopenharmony_ci .range_max = 0xe4, 36862306a36Sopenharmony_ci }, 36962306a36Sopenharmony_ci .webcam = { 37062306a36Sopenharmony_ci .address = 0x2e, 37162306a36Sopenharmony_ci .block_address = 0x2f, 37262306a36Sopenharmony_ci .bit = 1, 37362306a36Sopenharmony_ci }, 37462306a36Sopenharmony_ci .fn_super_swap = { 37562306a36Sopenharmony_ci .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown 37662306a36Sopenharmony_ci .bit = 4, 37762306a36Sopenharmony_ci }, 37862306a36Sopenharmony_ci .cooler_boost = { 37962306a36Sopenharmony_ci .address = 0x98, 38062306a36Sopenharmony_ci .bit = 7, 38162306a36Sopenharmony_ci }, 38262306a36Sopenharmony_ci .shift_mode = { 38362306a36Sopenharmony_ci .address = 0xd2, 38462306a36Sopenharmony_ci .modes = { 38562306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 38662306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 38762306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 38862306a36Sopenharmony_ci MSI_EC_MODE_NULL 38962306a36Sopenharmony_ci }, 39062306a36Sopenharmony_ci }, 39162306a36Sopenharmony_ci .super_battery = { // may be supported, but address is unknown 39262306a36Sopenharmony_ci .address = MSI_EC_ADDR_UNKNOWN, 39362306a36Sopenharmony_ci .mask = 0x0f, 39462306a36Sopenharmony_ci }, 39562306a36Sopenharmony_ci .fan_mode = { 39662306a36Sopenharmony_ci .address = 0xd4, 39762306a36Sopenharmony_ci .modes = { 39862306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 39962306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 40062306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 40162306a36Sopenharmony_ci MSI_EC_MODE_NULL 40262306a36Sopenharmony_ci }, 40362306a36Sopenharmony_ci }, 40462306a36Sopenharmony_ci .cpu = { 40562306a36Sopenharmony_ci .rt_temp_address = 0x68, // needs testing 40662306a36Sopenharmony_ci .rt_fan_speed_address = 0x71, // needs testing 40762306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 40862306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 40962306a36Sopenharmony_ci .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 41062306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 41162306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 41262306a36Sopenharmony_ci }, 41362306a36Sopenharmony_ci .gpu = { 41462306a36Sopenharmony_ci .rt_temp_address = 0x80, 41562306a36Sopenharmony_ci .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 41662306a36Sopenharmony_ci }, 41762306a36Sopenharmony_ci .leds = { 41862306a36Sopenharmony_ci .micmute_led_address = MSI_EC_ADDR_UNKNOWN, 41962306a36Sopenharmony_ci .mute_led_address = MSI_EC_ADDR_UNKNOWN, 42062306a36Sopenharmony_ci .bit = 1, 42162306a36Sopenharmony_ci }, 42262306a36Sopenharmony_ci .kbd_bl = { 42362306a36Sopenharmony_ci .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 42462306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 42562306a36Sopenharmony_ci .max_mode = 1, // ? 42662306a36Sopenharmony_ci .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional 42762306a36Sopenharmony_ci .state_base_value = 0x80, 42862306a36Sopenharmony_ci .max_state = 3, 42962306a36Sopenharmony_ci }, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const char * const ALLOWED_FW_5[] __initconst = { 43362306a36Sopenharmony_ci "158LEMS1.103", 43462306a36Sopenharmony_ci "158LEMS1.105", 43562306a36Sopenharmony_ci "158LEMS1.106", 43662306a36Sopenharmony_ci NULL 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic struct msi_ec_conf CONF5 __initdata = { 44062306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_5, 44162306a36Sopenharmony_ci .charge_control = { 44262306a36Sopenharmony_ci .address = 0xef, 44362306a36Sopenharmony_ci .offset_start = 0x8a, 44462306a36Sopenharmony_ci .offset_end = 0x80, 44562306a36Sopenharmony_ci .range_min = 0x8a, 44662306a36Sopenharmony_ci .range_max = 0xe4, 44762306a36Sopenharmony_ci }, 44862306a36Sopenharmony_ci .webcam = { 44962306a36Sopenharmony_ci .address = 0x2e, 45062306a36Sopenharmony_ci .block_address = 0x2f, 45162306a36Sopenharmony_ci .bit = 1, 45262306a36Sopenharmony_ci }, 45362306a36Sopenharmony_ci .fn_super_swap = { // todo: reverse 45462306a36Sopenharmony_ci .address = 0xbf, 45562306a36Sopenharmony_ci .bit = 4, 45662306a36Sopenharmony_ci }, 45762306a36Sopenharmony_ci .cooler_boost = { 45862306a36Sopenharmony_ci .address = 0x98, 45962306a36Sopenharmony_ci .bit = 7, 46062306a36Sopenharmony_ci }, 46162306a36Sopenharmony_ci .shift_mode = { 46262306a36Sopenharmony_ci .address = 0xf2, 46362306a36Sopenharmony_ci .modes = { 46462306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 46562306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 46662306a36Sopenharmony_ci { SM_TURBO_NAME, 0xc4 }, 46762306a36Sopenharmony_ci MSI_EC_MODE_NULL 46862306a36Sopenharmony_ci }, 46962306a36Sopenharmony_ci }, 47062306a36Sopenharmony_ci .super_battery = { // unsupported? 47162306a36Sopenharmony_ci .address = MSI_EC_ADDR_UNKNOWN, 47262306a36Sopenharmony_ci .mask = 0x0f, 47362306a36Sopenharmony_ci }, 47462306a36Sopenharmony_ci .fan_mode = { 47562306a36Sopenharmony_ci .address = 0xf4, 47662306a36Sopenharmony_ci .modes = { 47762306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 47862306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 47962306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 48062306a36Sopenharmony_ci MSI_EC_MODE_NULL 48162306a36Sopenharmony_ci }, 48262306a36Sopenharmony_ci }, 48362306a36Sopenharmony_ci .cpu = { 48462306a36Sopenharmony_ci .rt_temp_address = 0x68, // needs testing 48562306a36Sopenharmony_ci .rt_fan_speed_address = 0x71, // needs testing 48662306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 48762306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 48862306a36Sopenharmony_ci .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 48962306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 49062306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 49162306a36Sopenharmony_ci }, 49262306a36Sopenharmony_ci .gpu = { 49362306a36Sopenharmony_ci .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 49462306a36Sopenharmony_ci .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 49562306a36Sopenharmony_ci }, 49662306a36Sopenharmony_ci .leds = { 49762306a36Sopenharmony_ci .micmute_led_address = 0x2b, 49862306a36Sopenharmony_ci .mute_led_address = 0x2c, 49962306a36Sopenharmony_ci .bit = 2, 50062306a36Sopenharmony_ci }, 50162306a36Sopenharmony_ci .kbd_bl = { 50262306a36Sopenharmony_ci .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 50362306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 50462306a36Sopenharmony_ci .max_mode = 1, // ? 50562306a36Sopenharmony_ci .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 50662306a36Sopenharmony_ci .state_base_value = 0x80, 50762306a36Sopenharmony_ci .max_state = 3, 50862306a36Sopenharmony_ci }, 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic const char * const ALLOWED_FW_6[] __initconst = { 51262306a36Sopenharmony_ci "1542EMS1.102", 51362306a36Sopenharmony_ci "1542EMS1.104", 51462306a36Sopenharmony_ci NULL 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic struct msi_ec_conf CONF6 __initdata = { 51862306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_6, 51962306a36Sopenharmony_ci .charge_control = { 52062306a36Sopenharmony_ci .address = 0xef, 52162306a36Sopenharmony_ci .offset_start = 0x8a, 52262306a36Sopenharmony_ci .offset_end = 0x80, 52362306a36Sopenharmony_ci .range_min = 0x8a, 52462306a36Sopenharmony_ci .range_max = 0xe4, 52562306a36Sopenharmony_ci }, 52662306a36Sopenharmony_ci .webcam = { 52762306a36Sopenharmony_ci .address = 0x2e, 52862306a36Sopenharmony_ci .block_address = MSI_EC_ADDR_UNSUPP, 52962306a36Sopenharmony_ci .bit = 1, 53062306a36Sopenharmony_ci }, 53162306a36Sopenharmony_ci .fn_super_swap = { 53262306a36Sopenharmony_ci .address = 0xbf, // todo: reverse 53362306a36Sopenharmony_ci .bit = 4, 53462306a36Sopenharmony_ci }, 53562306a36Sopenharmony_ci .cooler_boost = { 53662306a36Sopenharmony_ci .address = 0x98, 53762306a36Sopenharmony_ci .bit = 7, 53862306a36Sopenharmony_ci }, 53962306a36Sopenharmony_ci .shift_mode = { 54062306a36Sopenharmony_ci .address = 0xf2, 54162306a36Sopenharmony_ci .modes = { 54262306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 54362306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 54462306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 54562306a36Sopenharmony_ci { SM_TURBO_NAME, 0xc4 }, 54662306a36Sopenharmony_ci MSI_EC_MODE_NULL 54762306a36Sopenharmony_ci }, 54862306a36Sopenharmony_ci }, 54962306a36Sopenharmony_ci .super_battery = { 55062306a36Sopenharmony_ci .address = 0xd5, 55162306a36Sopenharmony_ci .mask = 0x0f, 55262306a36Sopenharmony_ci }, 55362306a36Sopenharmony_ci .fan_mode = { 55462306a36Sopenharmony_ci .address = 0xf4, 55562306a36Sopenharmony_ci .modes = { 55662306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, 55762306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 55862306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 55962306a36Sopenharmony_ci MSI_EC_MODE_NULL 56062306a36Sopenharmony_ci }, 56162306a36Sopenharmony_ci }, 56262306a36Sopenharmony_ci .cpu = { 56362306a36Sopenharmony_ci .rt_temp_address = 0x68, 56462306a36Sopenharmony_ci .rt_fan_speed_address = 0xc9, 56562306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 56662306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 56762306a36Sopenharmony_ci .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 56862306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 56962306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 57062306a36Sopenharmony_ci }, 57162306a36Sopenharmony_ci .gpu = { 57262306a36Sopenharmony_ci .rt_temp_address = 0x80, 57362306a36Sopenharmony_ci .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 57462306a36Sopenharmony_ci }, 57562306a36Sopenharmony_ci .leds = { 57662306a36Sopenharmony_ci .micmute_led_address = MSI_EC_ADDR_UNSUPP, 57762306a36Sopenharmony_ci .mute_led_address = MSI_EC_ADDR_UNSUPP, 57862306a36Sopenharmony_ci .bit = 2, 57962306a36Sopenharmony_ci }, 58062306a36Sopenharmony_ci .kbd_bl = { 58162306a36Sopenharmony_ci .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 58262306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 58362306a36Sopenharmony_ci .max_mode = 1, // ? 58462306a36Sopenharmony_ci .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 58562306a36Sopenharmony_ci .state_base_value = 0x80, 58662306a36Sopenharmony_ci .max_state = 3, 58762306a36Sopenharmony_ci }, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic const char * const ALLOWED_FW_7[] __initconst = { 59162306a36Sopenharmony_ci "17FKEMS1.108", 59262306a36Sopenharmony_ci "17FKEMS1.109", 59362306a36Sopenharmony_ci "17FKEMS1.10A", 59462306a36Sopenharmony_ci NULL 59562306a36Sopenharmony_ci}; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic struct msi_ec_conf CONF7 __initdata = { 59862306a36Sopenharmony_ci .allowed_fw = ALLOWED_FW_7, 59962306a36Sopenharmony_ci .charge_control = { 60062306a36Sopenharmony_ci .address = 0xef, 60162306a36Sopenharmony_ci .offset_start = 0x8a, 60262306a36Sopenharmony_ci .offset_end = 0x80, 60362306a36Sopenharmony_ci .range_min = 0x8a, 60462306a36Sopenharmony_ci .range_max = 0xe4, 60562306a36Sopenharmony_ci }, 60662306a36Sopenharmony_ci .webcam = { 60762306a36Sopenharmony_ci .address = 0x2e, 60862306a36Sopenharmony_ci .block_address = MSI_EC_ADDR_UNSUPP, 60962306a36Sopenharmony_ci .bit = 1, 61062306a36Sopenharmony_ci }, 61162306a36Sopenharmony_ci .fn_super_swap = { 61262306a36Sopenharmony_ci .address = 0xbf, // needs testing 61362306a36Sopenharmony_ci .bit = 4, 61462306a36Sopenharmony_ci }, 61562306a36Sopenharmony_ci .cooler_boost = { 61662306a36Sopenharmony_ci .address = 0x98, 61762306a36Sopenharmony_ci .bit = 7, 61862306a36Sopenharmony_ci }, 61962306a36Sopenharmony_ci .shift_mode = { 62062306a36Sopenharmony_ci .address = 0xf2, 62162306a36Sopenharmony_ci .modes = { 62262306a36Sopenharmony_ci { SM_ECO_NAME, 0xc2 }, 62362306a36Sopenharmony_ci { SM_COMFORT_NAME, 0xc1 }, 62462306a36Sopenharmony_ci { SM_SPORT_NAME, 0xc0 }, 62562306a36Sopenharmony_ci { SM_TURBO_NAME, 0xc4 }, 62662306a36Sopenharmony_ci MSI_EC_MODE_NULL 62762306a36Sopenharmony_ci }, 62862306a36Sopenharmony_ci }, 62962306a36Sopenharmony_ci .super_battery = { 63062306a36Sopenharmony_ci .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes 63162306a36Sopenharmony_ci .mask = 0x0f, 63262306a36Sopenharmony_ci }, 63362306a36Sopenharmony_ci .fan_mode = { 63462306a36Sopenharmony_ci .address = 0xf4, 63562306a36Sopenharmony_ci .modes = { 63662306a36Sopenharmony_ci { FM_AUTO_NAME, 0x0d }, // d may not be relevant 63762306a36Sopenharmony_ci { FM_SILENT_NAME, 0x1d }, 63862306a36Sopenharmony_ci { FM_ADVANCED_NAME, 0x8d }, 63962306a36Sopenharmony_ci MSI_EC_MODE_NULL 64062306a36Sopenharmony_ci }, 64162306a36Sopenharmony_ci }, 64262306a36Sopenharmony_ci .cpu = { 64362306a36Sopenharmony_ci .rt_temp_address = 0x68, 64462306a36Sopenharmony_ci .rt_fan_speed_address = 0xc9, // needs testing 64562306a36Sopenharmony_ci .rt_fan_speed_base_min = 0x19, 64662306a36Sopenharmony_ci .rt_fan_speed_base_max = 0x37, 64762306a36Sopenharmony_ci .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 64862306a36Sopenharmony_ci .bs_fan_speed_base_min = 0x00, 64962306a36Sopenharmony_ci .bs_fan_speed_base_max = 0x0f, 65062306a36Sopenharmony_ci }, 65162306a36Sopenharmony_ci .gpu = { 65262306a36Sopenharmony_ci .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 65362306a36Sopenharmony_ci .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 65462306a36Sopenharmony_ci }, 65562306a36Sopenharmony_ci .leds = { 65662306a36Sopenharmony_ci .micmute_led_address = MSI_EC_ADDR_UNSUPP, 65762306a36Sopenharmony_ci .mute_led_address = 0x2c, 65862306a36Sopenharmony_ci .bit = 2, 65962306a36Sopenharmony_ci }, 66062306a36Sopenharmony_ci .kbd_bl = { 66162306a36Sopenharmony_ci .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 66262306a36Sopenharmony_ci .bl_modes = { 0x00, 0x08 }, // ? 66362306a36Sopenharmony_ci .max_mode = 1, // ? 66462306a36Sopenharmony_ci .bl_state_address = 0xf3, 66562306a36Sopenharmony_ci .state_base_value = 0x80, 66662306a36Sopenharmony_ci .max_state = 3, 66762306a36Sopenharmony_ci }, 66862306a36Sopenharmony_ci}; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic struct msi_ec_conf *CONFIGS[] __initdata = { 67162306a36Sopenharmony_ci &CONF0, 67262306a36Sopenharmony_ci &CONF1, 67362306a36Sopenharmony_ci &CONF2, 67462306a36Sopenharmony_ci &CONF3, 67562306a36Sopenharmony_ci &CONF4, 67662306a36Sopenharmony_ci &CONF5, 67762306a36Sopenharmony_ci &CONF6, 67862306a36Sopenharmony_ci &CONF7, 67962306a36Sopenharmony_ci NULL 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic struct msi_ec_conf conf; // current configuration 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* 68562306a36Sopenharmony_ci * Helper functions 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int ec_read_seq(u8 addr, u8 *buf, u8 len) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci int result; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci for (u8 i = 0; i < len; i++) { 69362306a36Sopenharmony_ci result = ec_read(addr + i, buf + i); 69462306a36Sopenharmony_ci if (result < 0) 69562306a36Sopenharmony_ci return result; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci int result; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); 70662306a36Sopenharmony_ci result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, 70762306a36Sopenharmony_ci buf, 70862306a36Sopenharmony_ci MSI_EC_FW_VERSION_LENGTH); 70962306a36Sopenharmony_ci if (result < 0) 71062306a36Sopenharmony_ci return result; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return MSI_EC_FW_VERSION_LENGTH + 1; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* 71662306a36Sopenharmony_ci * Sysfs power_supply subsystem 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic ssize_t charge_control_threshold_show(u8 offset, 72062306a36Sopenharmony_ci struct device *device, 72162306a36Sopenharmony_ci struct device_attribute *attr, 72262306a36Sopenharmony_ci char *buf) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci u8 rdata; 72562306a36Sopenharmony_ci int result; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci result = ec_read(conf.charge_control.address, &rdata); 72862306a36Sopenharmony_ci if (result < 0) 72962306a36Sopenharmony_ci return result; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return sysfs_emit(buf, "%i\n", rdata - offset); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic ssize_t charge_control_threshold_store(u8 offset, 73562306a36Sopenharmony_ci struct device *dev, 73662306a36Sopenharmony_ci struct device_attribute *attr, 73762306a36Sopenharmony_ci const char *buf, size_t count) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci u8 wdata; 74062306a36Sopenharmony_ci int result; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci result = kstrtou8(buf, 10, &wdata); 74362306a36Sopenharmony_ci if (result < 0) 74462306a36Sopenharmony_ci return result; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci wdata += offset; 74762306a36Sopenharmony_ci if (wdata < conf.charge_control.range_min || 74862306a36Sopenharmony_ci wdata > conf.charge_control.range_max) 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci result = ec_write(conf.charge_control.address, wdata); 75262306a36Sopenharmony_ci if (result < 0) 75362306a36Sopenharmony_ci return result; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return count; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic ssize_t charge_control_start_threshold_show(struct device *device, 75962306a36Sopenharmony_ci struct device_attribute *attr, 76062306a36Sopenharmony_ci char *buf) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci return charge_control_threshold_show(conf.charge_control.offset_start, 76362306a36Sopenharmony_ci device, attr, buf); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic ssize_t charge_control_start_threshold_store(struct device *dev, 76762306a36Sopenharmony_ci struct device_attribute *attr, 76862306a36Sopenharmony_ci const char *buf, size_t count) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci return charge_control_threshold_store(conf.charge_control.offset_start, 77162306a36Sopenharmony_ci dev, attr, buf, count); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic ssize_t charge_control_end_threshold_show(struct device *device, 77562306a36Sopenharmony_ci struct device_attribute *attr, 77662306a36Sopenharmony_ci char *buf) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci return charge_control_threshold_show(conf.charge_control.offset_end, 77962306a36Sopenharmony_ci device, attr, buf); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic ssize_t charge_control_end_threshold_store(struct device *dev, 78362306a36Sopenharmony_ci struct device_attribute *attr, 78462306a36Sopenharmony_ci const char *buf, size_t count) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci return charge_control_threshold_store(conf.charge_control.offset_end, 78762306a36Sopenharmony_ci dev, attr, buf, count); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(charge_control_start_threshold); 79162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(charge_control_end_threshold); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic struct attribute *msi_battery_attrs[] = { 79462306a36Sopenharmony_ci &dev_attr_charge_control_start_threshold.attr, 79562306a36Sopenharmony_ci &dev_attr_charge_control_end_threshold.attr, 79662306a36Sopenharmony_ci NULL 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ciATTRIBUTE_GROUPS(msi_battery); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic int msi_battery_add(struct power_supply *battery, 80262306a36Sopenharmony_ci struct acpi_battery_hook *hook) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci return device_add_groups(&battery->dev, msi_battery_groups); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic int msi_battery_remove(struct power_supply *battery, 80862306a36Sopenharmony_ci struct acpi_battery_hook *hook) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci device_remove_groups(&battery->dev, msi_battery_groups); 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic struct acpi_battery_hook battery_hook = { 81562306a36Sopenharmony_ci .add_battery = msi_battery_add, 81662306a36Sopenharmony_ci .remove_battery = msi_battery_remove, 81762306a36Sopenharmony_ci .name = MSI_EC_DRIVER_NAME, 81862306a36Sopenharmony_ci}; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/* 82162306a36Sopenharmony_ci * Module load/unload 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { 82562306a36Sopenharmony_ci { 82662306a36Sopenharmony_ci .matches = { 82762306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), 82862306a36Sopenharmony_ci }, 82962306a36Sopenharmony_ci }, 83062306a36Sopenharmony_ci { 83162306a36Sopenharmony_ci .matches = { 83262306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 83362306a36Sopenharmony_ci }, 83462306a36Sopenharmony_ci }, 83562306a36Sopenharmony_ci {} 83662306a36Sopenharmony_ci}; 83762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, msi_dmi_table); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int __init load_configuration(void) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci int result; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* get firmware version */ 84662306a36Sopenharmony_ci result = ec_get_firmware_version(fw_version); 84762306a36Sopenharmony_ci if (result < 0) 84862306a36Sopenharmony_ci return result; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* load the suitable configuration, if exists */ 85162306a36Sopenharmony_ci for (int i = 0; CONFIGS[i]; i++) { 85262306a36Sopenharmony_ci if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { 85362306a36Sopenharmony_ci conf = *CONFIGS[i]; 85462306a36Sopenharmony_ci conf.allowed_fw = NULL; 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* config not found */ 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { 86262306a36Sopenharmony_ci if (!isgraph(fw_version[i])) { 86362306a36Sopenharmony_ci pr_warn("Unable to find a valid firmware version!\n"); 86462306a36Sopenharmony_ci return -EOPNOTSUPP; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci pr_warn("Firmware version is not supported: '%s'\n", fw_version); 86962306a36Sopenharmony_ci return -EOPNOTSUPP; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic int __init msi_ec_init(void) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci int result; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci result = load_configuration(); 87762306a36Sopenharmony_ci if (result < 0) 87862306a36Sopenharmony_ci return result; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci battery_hook_register(&battery_hook); 88162306a36Sopenharmony_ci return 0; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic void __exit msi_ec_exit(void) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci battery_hook_unregister(&battery_hook); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 89062306a36Sopenharmony_ciMODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>"); 89162306a36Sopenharmony_ciMODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>"); 89262306a36Sopenharmony_ciMODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>"); 89362306a36Sopenharmony_ciMODULE_DESCRIPTION("MSI Embedded Controller"); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cimodule_init(msi_ec_init); 89662306a36Sopenharmony_cimodule_exit(msi_ec_exit); 897