1392cacf2SNikita Kravets // SPDX-License-Identifier: GPL-2.0-or-later 2392cacf2SNikita Kravets 3392cacf2SNikita Kravets /* 4392cacf2SNikita Kravets * msi-ec: MSI laptops' embedded controller driver. 5392cacf2SNikita Kravets * 6392cacf2SNikita Kravets * This driver allows various MSI laptops' functionalities to be 7392cacf2SNikita Kravets * controlled from userspace. 8392cacf2SNikita Kravets * 9392cacf2SNikita Kravets * It contains EC memory configurations for different firmware versions 10392cacf2SNikita Kravets * and exports battery charge thresholds to userspace. 11392cacf2SNikita Kravets * 12392cacf2SNikita Kravets * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es> 13392cacf2SNikita Kravets * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev> 14392cacf2SNikita Kravets * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com> 15392cacf2SNikita Kravets */ 16392cacf2SNikita Kravets 17392cacf2SNikita Kravets #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18392cacf2SNikita Kravets 19392cacf2SNikita Kravets #include "msi-ec.h" 20392cacf2SNikita Kravets 21392cacf2SNikita Kravets #include <acpi/battery.h> 22392cacf2SNikita Kravets #include <linux/acpi.h> 23392cacf2SNikita Kravets #include <linux/init.h> 24392cacf2SNikita Kravets #include <linux/kernel.h> 25392cacf2SNikita Kravets #include <linux/module.h> 26392cacf2SNikita Kravets #include <linux/platform_device.h> 27392cacf2SNikita Kravets #include <linux/seq_file.h> 28392cacf2SNikita Kravets #include <linux/string.h> 29392cacf2SNikita Kravets 30*5a66d59bSJean Delvare #define SM_ECO_NAME "eco" 31*5a66d59bSJean Delvare #define SM_COMFORT_NAME "comfort" 32*5a66d59bSJean Delvare #define SM_SPORT_NAME "sport" 33*5a66d59bSJean Delvare #define SM_TURBO_NAME "turbo" 34392cacf2SNikita Kravets 35*5a66d59bSJean Delvare #define FM_AUTO_NAME "auto" 36*5a66d59bSJean Delvare #define FM_SILENT_NAME "silent" 37*5a66d59bSJean Delvare #define FM_BASIC_NAME "basic" 38*5a66d59bSJean Delvare #define FM_ADVANCED_NAME "advanced" 39392cacf2SNikita Kravets 40392cacf2SNikita Kravets static const char * const ALLOWED_FW_0[] __initconst = { 41392cacf2SNikita Kravets "14C1EMS1.012", 42392cacf2SNikita Kravets "14C1EMS1.101", 43392cacf2SNikita Kravets "14C1EMS1.102", 44392cacf2SNikita Kravets NULL 45392cacf2SNikita Kravets }; 46392cacf2SNikita Kravets 47392cacf2SNikita Kravets static struct msi_ec_conf CONF0 __initdata = { 48392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_0, 49392cacf2SNikita Kravets .charge_control = { 50392cacf2SNikita Kravets .address = 0xef, 51392cacf2SNikita Kravets .offset_start = 0x8a, 52392cacf2SNikita Kravets .offset_end = 0x80, 53392cacf2SNikita Kravets .range_min = 0x8a, 54392cacf2SNikita Kravets .range_max = 0xe4, 55392cacf2SNikita Kravets }, 56392cacf2SNikita Kravets .webcam = { 57392cacf2SNikita Kravets .address = 0x2e, 58392cacf2SNikita Kravets .block_address = 0x2f, 59392cacf2SNikita Kravets .bit = 1, 60392cacf2SNikita Kravets }, 61392cacf2SNikita Kravets .fn_super_swap = { 62392cacf2SNikita Kravets .address = 0xbf, 63392cacf2SNikita Kravets .bit = 4, 64392cacf2SNikita Kravets }, 65392cacf2SNikita Kravets .cooler_boost = { 66392cacf2SNikita Kravets .address = 0x98, 67392cacf2SNikita Kravets .bit = 7, 68392cacf2SNikita Kravets }, 69392cacf2SNikita Kravets .shift_mode = { 70392cacf2SNikita Kravets .address = 0xf2, 71392cacf2SNikita Kravets .modes = { 72392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 73392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 74392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 75392cacf2SNikita Kravets MSI_EC_MODE_NULL 76392cacf2SNikita Kravets }, 77392cacf2SNikita Kravets }, 78392cacf2SNikita Kravets .super_battery = { 79392cacf2SNikita Kravets .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing 80392cacf2SNikita Kravets }, 81392cacf2SNikita Kravets .fan_mode = { 82392cacf2SNikita Kravets .address = 0xf4, 83392cacf2SNikita Kravets .modes = { 84392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 85392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 86392cacf2SNikita Kravets { FM_BASIC_NAME, 0x4d }, 87392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 88392cacf2SNikita Kravets MSI_EC_MODE_NULL 89392cacf2SNikita Kravets }, 90392cacf2SNikita Kravets }, 91392cacf2SNikita Kravets .cpu = { 92392cacf2SNikita Kravets .rt_temp_address = 0x68, 93392cacf2SNikita Kravets .rt_fan_speed_address = 0x71, 94392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 95392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 96392cacf2SNikita Kravets .bs_fan_speed_address = 0x89, 97392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 98392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 99392cacf2SNikita Kravets }, 100392cacf2SNikita Kravets .gpu = { 101392cacf2SNikita Kravets .rt_temp_address = 0x80, 102392cacf2SNikita Kravets .rt_fan_speed_address = 0x89, 103392cacf2SNikita Kravets }, 104392cacf2SNikita Kravets .leds = { 105392cacf2SNikita Kravets .micmute_led_address = 0x2b, 106392cacf2SNikita Kravets .mute_led_address = 0x2c, 107392cacf2SNikita Kravets .bit = 2, 108392cacf2SNikita Kravets }, 109392cacf2SNikita Kravets .kbd_bl = { 110392cacf2SNikita Kravets .bl_mode_address = 0x2c, // ? 111392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 112392cacf2SNikita Kravets .max_mode = 1, // ? 113392cacf2SNikita Kravets .bl_state_address = 0xf3, 114392cacf2SNikita Kravets .state_base_value = 0x80, 115392cacf2SNikita Kravets .max_state = 3, 116392cacf2SNikita Kravets }, 117392cacf2SNikita Kravets }; 118392cacf2SNikita Kravets 119392cacf2SNikita Kravets static const char * const ALLOWED_FW_1[] __initconst = { 120392cacf2SNikita Kravets "17F2EMS1.103", 121392cacf2SNikita Kravets "17F2EMS1.104", 122392cacf2SNikita Kravets "17F2EMS1.106", 123392cacf2SNikita Kravets "17F2EMS1.107", 124392cacf2SNikita Kravets NULL 125392cacf2SNikita Kravets }; 126392cacf2SNikita Kravets 127392cacf2SNikita Kravets static struct msi_ec_conf CONF1 __initdata = { 128392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_1, 129392cacf2SNikita Kravets .charge_control = { 130392cacf2SNikita Kravets .address = 0xef, 131392cacf2SNikita Kravets .offset_start = 0x8a, 132392cacf2SNikita Kravets .offset_end = 0x80, 133392cacf2SNikita Kravets .range_min = 0x8a, 134392cacf2SNikita Kravets .range_max = 0xe4, 135392cacf2SNikita Kravets }, 136392cacf2SNikita Kravets .webcam = { 137392cacf2SNikita Kravets .address = 0x2e, 138392cacf2SNikita Kravets .block_address = 0x2f, 139392cacf2SNikita Kravets .bit = 1, 140392cacf2SNikita Kravets }, 141392cacf2SNikita Kravets .fn_super_swap = { 142392cacf2SNikita Kravets .address = 0xbf, 143392cacf2SNikita Kravets .bit = 4, 144392cacf2SNikita Kravets }, 145392cacf2SNikita Kravets .cooler_boost = { 146392cacf2SNikita Kravets .address = 0x98, 147392cacf2SNikita Kravets .bit = 7, 148392cacf2SNikita Kravets }, 149392cacf2SNikita Kravets .shift_mode = { 150392cacf2SNikita Kravets .address = 0xf2, 151392cacf2SNikita Kravets .modes = { 152392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 153392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 154392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 155392cacf2SNikita Kravets { SM_TURBO_NAME, 0xc4 }, 156392cacf2SNikita Kravets MSI_EC_MODE_NULL 157392cacf2SNikita Kravets }, 158392cacf2SNikita Kravets }, 159392cacf2SNikita Kravets .super_battery = { 160392cacf2SNikita Kravets .address = MSI_EC_ADDR_UNKNOWN, 161392cacf2SNikita Kravets }, 162392cacf2SNikita Kravets .fan_mode = { 163392cacf2SNikita Kravets .address = 0xf4, 164392cacf2SNikita Kravets .modes = { 165392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 166392cacf2SNikita Kravets { FM_BASIC_NAME, 0x4d }, 167392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 168392cacf2SNikita Kravets MSI_EC_MODE_NULL 169392cacf2SNikita Kravets }, 170392cacf2SNikita Kravets }, 171392cacf2SNikita Kravets .cpu = { 172392cacf2SNikita Kravets .rt_temp_address = 0x68, 173392cacf2SNikita Kravets .rt_fan_speed_address = 0x71, 174392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 175392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 176392cacf2SNikita Kravets .bs_fan_speed_address = 0x89, 177392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 178392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 179392cacf2SNikita Kravets }, 180392cacf2SNikita Kravets .gpu = { 181392cacf2SNikita Kravets .rt_temp_address = 0x80, 182392cacf2SNikita Kravets .rt_fan_speed_address = 0x89, 183392cacf2SNikita Kravets }, 184392cacf2SNikita Kravets .leds = { 185392cacf2SNikita Kravets .micmute_led_address = 0x2b, 186392cacf2SNikita Kravets .mute_led_address = 0x2c, 187392cacf2SNikita Kravets .bit = 2, 188392cacf2SNikita Kravets }, 189392cacf2SNikita Kravets .kbd_bl = { 190392cacf2SNikita Kravets .bl_mode_address = 0x2c, // ? 191392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 192392cacf2SNikita Kravets .max_mode = 1, // ? 193392cacf2SNikita Kravets .bl_state_address = 0xf3, 194392cacf2SNikita Kravets .state_base_value = 0x80, 195392cacf2SNikita Kravets .max_state = 3, 196392cacf2SNikita Kravets }, 197392cacf2SNikita Kravets }; 198392cacf2SNikita Kravets 199392cacf2SNikita Kravets static const char * const ALLOWED_FW_2[] __initconst = { 200392cacf2SNikita Kravets "1552EMS1.118", 201392cacf2SNikita Kravets NULL 202392cacf2SNikita Kravets }; 203392cacf2SNikita Kravets 204392cacf2SNikita Kravets static struct msi_ec_conf CONF2 __initdata = { 205392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_2, 206392cacf2SNikita Kravets .charge_control = { 207392cacf2SNikita Kravets .address = 0xd7, 208392cacf2SNikita Kravets .offset_start = 0x8a, 209392cacf2SNikita Kravets .offset_end = 0x80, 210392cacf2SNikita Kravets .range_min = 0x8a, 211392cacf2SNikita Kravets .range_max = 0xe4, 212392cacf2SNikita Kravets }, 213392cacf2SNikita Kravets .webcam = { 214392cacf2SNikita Kravets .address = 0x2e, 215392cacf2SNikita Kravets .block_address = 0x2f, 216392cacf2SNikita Kravets .bit = 1, 217392cacf2SNikita Kravets }, 218392cacf2SNikita Kravets .fn_super_swap = { 219392cacf2SNikita Kravets .address = 0xe8, 220392cacf2SNikita Kravets .bit = 4, 221392cacf2SNikita Kravets }, 222392cacf2SNikita Kravets .cooler_boost = { 223392cacf2SNikita Kravets .address = 0x98, 224392cacf2SNikita Kravets .bit = 7, 225392cacf2SNikita Kravets }, 226392cacf2SNikita Kravets .shift_mode = { 227392cacf2SNikita Kravets .address = 0xf2, 228392cacf2SNikita Kravets .modes = { 229392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 230392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 231392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 232392cacf2SNikita Kravets MSI_EC_MODE_NULL 233392cacf2SNikita Kravets }, 234392cacf2SNikita Kravets }, 235392cacf2SNikita Kravets .super_battery = { 236392cacf2SNikita Kravets .address = 0xeb, 237392cacf2SNikita Kravets .mask = 0x0f, 238392cacf2SNikita Kravets }, 239392cacf2SNikita Kravets .fan_mode = { 240392cacf2SNikita Kravets .address = 0xd4, 241392cacf2SNikita Kravets .modes = { 242392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 243392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 244392cacf2SNikita Kravets { FM_BASIC_NAME, 0x4d }, 245392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 246392cacf2SNikita Kravets MSI_EC_MODE_NULL 247392cacf2SNikita Kravets }, 248392cacf2SNikita Kravets }, 249392cacf2SNikita Kravets .cpu = { 250392cacf2SNikita Kravets .rt_temp_address = 0x68, 251392cacf2SNikita Kravets .rt_fan_speed_address = 0x71, 252392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 253392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 254392cacf2SNikita Kravets .bs_fan_speed_address = 0x89, 255392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 256392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 257392cacf2SNikita Kravets }, 258392cacf2SNikita Kravets .gpu = { 259392cacf2SNikita Kravets .rt_temp_address = 0x80, 260392cacf2SNikita Kravets .rt_fan_speed_address = 0x89, 261392cacf2SNikita Kravets }, 262392cacf2SNikita Kravets .leds = { 263392cacf2SNikita Kravets .micmute_led_address = 0x2c, 264392cacf2SNikita Kravets .mute_led_address = 0x2d, 265392cacf2SNikita Kravets .bit = 1, 266392cacf2SNikita Kravets }, 267392cacf2SNikita Kravets .kbd_bl = { 268392cacf2SNikita Kravets .bl_mode_address = 0x2c, // ? 269392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 270392cacf2SNikita Kravets .max_mode = 1, // ? 271392cacf2SNikita Kravets .bl_state_address = 0xd3, 272392cacf2SNikita Kravets .state_base_value = 0x80, 273392cacf2SNikita Kravets .max_state = 3, 274392cacf2SNikita Kravets }, 275392cacf2SNikita Kravets }; 276392cacf2SNikita Kravets 277392cacf2SNikita Kravets static const char * const ALLOWED_FW_3[] __initconst = { 278392cacf2SNikita Kravets "1592EMS1.111", 279392cacf2SNikita Kravets "E1592IMS.10C", 280392cacf2SNikita Kravets NULL 281392cacf2SNikita Kravets }; 282392cacf2SNikita Kravets 283392cacf2SNikita Kravets static struct msi_ec_conf CONF3 __initdata = { 284392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_3, 285392cacf2SNikita Kravets .charge_control = { 286392cacf2SNikita Kravets .address = 0xef, 287392cacf2SNikita Kravets .offset_start = 0x8a, 288392cacf2SNikita Kravets .offset_end = 0x80, 289392cacf2SNikita Kravets .range_min = 0x8a, 290392cacf2SNikita Kravets .range_max = 0xe4, 291392cacf2SNikita Kravets }, 292392cacf2SNikita Kravets .webcam = { 293392cacf2SNikita Kravets .address = 0x2e, 294392cacf2SNikita Kravets .block_address = 0x2f, 295392cacf2SNikita Kravets .bit = 1, 296392cacf2SNikita Kravets }, 297392cacf2SNikita Kravets .fn_super_swap = { 298392cacf2SNikita Kravets .address = 0xe8, 299392cacf2SNikita Kravets .bit = 4, 300392cacf2SNikita Kravets }, 301392cacf2SNikita Kravets .cooler_boost = { 302392cacf2SNikita Kravets .address = 0x98, 303392cacf2SNikita Kravets .bit = 7, 304392cacf2SNikita Kravets }, 305392cacf2SNikita Kravets .shift_mode = { 306392cacf2SNikita Kravets .address = 0xd2, 307392cacf2SNikita Kravets .modes = { 308392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 309392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 310392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 311392cacf2SNikita Kravets MSI_EC_MODE_NULL 312392cacf2SNikita Kravets }, 313392cacf2SNikita Kravets }, 314392cacf2SNikita Kravets .super_battery = { 315392cacf2SNikita Kravets .address = 0xeb, 316392cacf2SNikita Kravets .mask = 0x0f, 317392cacf2SNikita Kravets }, 318392cacf2SNikita Kravets .fan_mode = { 319392cacf2SNikita Kravets .address = 0xd4, 320392cacf2SNikita Kravets .modes = { 321392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 322392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 323392cacf2SNikita Kravets { FM_BASIC_NAME, 0x4d }, 324392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 325392cacf2SNikita Kravets MSI_EC_MODE_NULL 326392cacf2SNikita Kravets }, 327392cacf2SNikita Kravets }, 328392cacf2SNikita Kravets .cpu = { 329392cacf2SNikita Kravets .rt_temp_address = 0x68, 330392cacf2SNikita Kravets .rt_fan_speed_address = 0xc9, 331392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 332392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 333392cacf2SNikita Kravets .bs_fan_speed_address = 0x89, // ? 334392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 335392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 336392cacf2SNikita Kravets }, 337392cacf2SNikita Kravets .gpu = { 338392cacf2SNikita Kravets .rt_temp_address = 0x80, 339392cacf2SNikita Kravets .rt_fan_speed_address = 0x89, 340392cacf2SNikita Kravets }, 341392cacf2SNikita Kravets .leds = { 342392cacf2SNikita Kravets .micmute_led_address = 0x2b, 343392cacf2SNikita Kravets .mute_led_address = 0x2c, 344392cacf2SNikita Kravets .bit = 1, 345392cacf2SNikita Kravets }, 346392cacf2SNikita Kravets .kbd_bl = { 347392cacf2SNikita Kravets .bl_mode_address = 0x2c, // ? 348392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 349392cacf2SNikita Kravets .max_mode = 1, // ? 350392cacf2SNikita Kravets .bl_state_address = 0xd3, 351392cacf2SNikita Kravets .state_base_value = 0x80, 352392cacf2SNikita Kravets .max_state = 3, 353392cacf2SNikita Kravets }, 354392cacf2SNikita Kravets }; 355392cacf2SNikita Kravets 356392cacf2SNikita Kravets static const char * const ALLOWED_FW_4[] __initconst = { 357392cacf2SNikita Kravets "16V4EMS1.114", 358392cacf2SNikita Kravets NULL 359392cacf2SNikita Kravets }; 360392cacf2SNikita Kravets 361392cacf2SNikita Kravets static struct msi_ec_conf CONF4 __initdata = { 362392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_4, 363392cacf2SNikita Kravets .charge_control = { 364392cacf2SNikita Kravets .address = 0xd7, 365392cacf2SNikita Kravets .offset_start = 0x8a, 366392cacf2SNikita Kravets .offset_end = 0x80, 367392cacf2SNikita Kravets .range_min = 0x8a, 368392cacf2SNikita Kravets .range_max = 0xe4, 369392cacf2SNikita Kravets }, 370392cacf2SNikita Kravets .webcam = { 371392cacf2SNikita Kravets .address = 0x2e, 372392cacf2SNikita Kravets .block_address = 0x2f, 373392cacf2SNikita Kravets .bit = 1, 374392cacf2SNikita Kravets }, 375392cacf2SNikita Kravets .fn_super_swap = { 376392cacf2SNikita Kravets .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown 377392cacf2SNikita Kravets .bit = 4, 378392cacf2SNikita Kravets }, 379392cacf2SNikita Kravets .cooler_boost = { 380392cacf2SNikita Kravets .address = 0x98, 381392cacf2SNikita Kravets .bit = 7, 382392cacf2SNikita Kravets }, 383392cacf2SNikita Kravets .shift_mode = { 384392cacf2SNikita Kravets .address = 0xd2, 385392cacf2SNikita Kravets .modes = { 386392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 387392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 388392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 389392cacf2SNikita Kravets MSI_EC_MODE_NULL 390392cacf2SNikita Kravets }, 391392cacf2SNikita Kravets }, 392392cacf2SNikita Kravets .super_battery = { // may be supported, but address is unknown 393392cacf2SNikita Kravets .address = MSI_EC_ADDR_UNKNOWN, 394392cacf2SNikita Kravets .mask = 0x0f, 395392cacf2SNikita Kravets }, 396392cacf2SNikita Kravets .fan_mode = { 397392cacf2SNikita Kravets .address = 0xd4, 398392cacf2SNikita Kravets .modes = { 399392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 400392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 401392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 402392cacf2SNikita Kravets MSI_EC_MODE_NULL 403392cacf2SNikita Kravets }, 404392cacf2SNikita Kravets }, 405392cacf2SNikita Kravets .cpu = { 406392cacf2SNikita Kravets .rt_temp_address = 0x68, // needs testing 407392cacf2SNikita Kravets .rt_fan_speed_address = 0x71, // needs testing 408392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 409392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 410392cacf2SNikita Kravets .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 411392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 412392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 413392cacf2SNikita Kravets }, 414392cacf2SNikita Kravets .gpu = { 415392cacf2SNikita Kravets .rt_temp_address = 0x80, 416392cacf2SNikita Kravets .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 417392cacf2SNikita Kravets }, 418392cacf2SNikita Kravets .leds = { 419392cacf2SNikita Kravets .micmute_led_address = MSI_EC_ADDR_UNKNOWN, 420392cacf2SNikita Kravets .mute_led_address = MSI_EC_ADDR_UNKNOWN, 421392cacf2SNikita Kravets .bit = 1, 422392cacf2SNikita Kravets }, 423392cacf2SNikita Kravets .kbd_bl = { 424392cacf2SNikita Kravets .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 425392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 426392cacf2SNikita Kravets .max_mode = 1, // ? 427392cacf2SNikita Kravets .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional 428392cacf2SNikita Kravets .state_base_value = 0x80, 429392cacf2SNikita Kravets .max_state = 3, 430392cacf2SNikita Kravets }, 431392cacf2SNikita Kravets }; 432392cacf2SNikita Kravets 433392cacf2SNikita Kravets static const char * const ALLOWED_FW_5[] __initconst = { 434392cacf2SNikita Kravets "158LEMS1.103", 435392cacf2SNikita Kravets "158LEMS1.105", 436392cacf2SNikita Kravets "158LEMS1.106", 437392cacf2SNikita Kravets NULL 438392cacf2SNikita Kravets }; 439392cacf2SNikita Kravets 440392cacf2SNikita Kravets static struct msi_ec_conf CONF5 __initdata = { 441392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_5, 442392cacf2SNikita Kravets .charge_control = { 443392cacf2SNikita Kravets .address = 0xef, 444392cacf2SNikita Kravets .offset_start = 0x8a, 445392cacf2SNikita Kravets .offset_end = 0x80, 446392cacf2SNikita Kravets .range_min = 0x8a, 447392cacf2SNikita Kravets .range_max = 0xe4, 448392cacf2SNikita Kravets }, 449392cacf2SNikita Kravets .webcam = { 450392cacf2SNikita Kravets .address = 0x2e, 451392cacf2SNikita Kravets .block_address = 0x2f, 452392cacf2SNikita Kravets .bit = 1, 453392cacf2SNikita Kravets }, 454392cacf2SNikita Kravets .fn_super_swap = { // todo: reverse 455392cacf2SNikita Kravets .address = 0xbf, 456392cacf2SNikita Kravets .bit = 4, 457392cacf2SNikita Kravets }, 458392cacf2SNikita Kravets .cooler_boost = { 459392cacf2SNikita Kravets .address = 0x98, 460392cacf2SNikita Kravets .bit = 7, 461392cacf2SNikita Kravets }, 462392cacf2SNikita Kravets .shift_mode = { 463392cacf2SNikita Kravets .address = 0xf2, 464392cacf2SNikita Kravets .modes = { 465392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 466392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 467392cacf2SNikita Kravets { SM_TURBO_NAME, 0xc4 }, 468392cacf2SNikita Kravets MSI_EC_MODE_NULL 469392cacf2SNikita Kravets }, 470392cacf2SNikita Kravets }, 471392cacf2SNikita Kravets .super_battery = { // unsupported? 472392cacf2SNikita Kravets .address = MSI_EC_ADDR_UNKNOWN, 473392cacf2SNikita Kravets .mask = 0x0f, 474392cacf2SNikita Kravets }, 475392cacf2SNikita Kravets .fan_mode = { 476392cacf2SNikita Kravets .address = 0xf4, 477392cacf2SNikita Kravets .modes = { 478392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 479392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 480392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 481392cacf2SNikita Kravets MSI_EC_MODE_NULL 482392cacf2SNikita Kravets }, 483392cacf2SNikita Kravets }, 484392cacf2SNikita Kravets .cpu = { 485392cacf2SNikita Kravets .rt_temp_address = 0x68, // needs testing 486392cacf2SNikita Kravets .rt_fan_speed_address = 0x71, // needs testing 487392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 488392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 489392cacf2SNikita Kravets .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 490392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 491392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 492392cacf2SNikita Kravets }, 493392cacf2SNikita Kravets .gpu = { 494392cacf2SNikita Kravets .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 495392cacf2SNikita Kravets .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 496392cacf2SNikita Kravets }, 497392cacf2SNikita Kravets .leds = { 498392cacf2SNikita Kravets .micmute_led_address = 0x2b, 499392cacf2SNikita Kravets .mute_led_address = 0x2c, 500392cacf2SNikita Kravets .bit = 2, 501392cacf2SNikita Kravets }, 502392cacf2SNikita Kravets .kbd_bl = { 503392cacf2SNikita Kravets .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 504392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 505392cacf2SNikita Kravets .max_mode = 1, // ? 506392cacf2SNikita Kravets .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 507392cacf2SNikita Kravets .state_base_value = 0x80, 508392cacf2SNikita Kravets .max_state = 3, 509392cacf2SNikita Kravets }, 510392cacf2SNikita Kravets }; 511392cacf2SNikita Kravets 512392cacf2SNikita Kravets static const char * const ALLOWED_FW_6[] __initconst = { 513392cacf2SNikita Kravets "1542EMS1.102", 514392cacf2SNikita Kravets "1542EMS1.104", 515392cacf2SNikita Kravets NULL 516392cacf2SNikita Kravets }; 517392cacf2SNikita Kravets 518392cacf2SNikita Kravets static struct msi_ec_conf CONF6 __initdata = { 519392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_6, 520392cacf2SNikita Kravets .charge_control = { 521392cacf2SNikita Kravets .address = 0xef, 522392cacf2SNikita Kravets .offset_start = 0x8a, 523392cacf2SNikita Kravets .offset_end = 0x80, 524392cacf2SNikita Kravets .range_min = 0x8a, 525392cacf2SNikita Kravets .range_max = 0xe4, 526392cacf2SNikita Kravets }, 527392cacf2SNikita Kravets .webcam = { 528392cacf2SNikita Kravets .address = 0x2e, 529392cacf2SNikita Kravets .block_address = MSI_EC_ADDR_UNSUPP, 530392cacf2SNikita Kravets .bit = 1, 531392cacf2SNikita Kravets }, 532392cacf2SNikita Kravets .fn_super_swap = { 533392cacf2SNikita Kravets .address = 0xbf, // todo: reverse 534392cacf2SNikita Kravets .bit = 4, 535392cacf2SNikita Kravets }, 536392cacf2SNikita Kravets .cooler_boost = { 537392cacf2SNikita Kravets .address = 0x98, 538392cacf2SNikita Kravets .bit = 7, 539392cacf2SNikita Kravets }, 540392cacf2SNikita Kravets .shift_mode = { 541392cacf2SNikita Kravets .address = 0xf2, 542392cacf2SNikita Kravets .modes = { 543392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 544392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 545392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 546392cacf2SNikita Kravets { SM_TURBO_NAME, 0xc4 }, 547392cacf2SNikita Kravets MSI_EC_MODE_NULL 548392cacf2SNikita Kravets }, 549392cacf2SNikita Kravets }, 550392cacf2SNikita Kravets .super_battery = { 551392cacf2SNikita Kravets .address = 0xd5, 552392cacf2SNikita Kravets .mask = 0x0f, 553392cacf2SNikita Kravets }, 554392cacf2SNikita Kravets .fan_mode = { 555392cacf2SNikita Kravets .address = 0xf4, 556392cacf2SNikita Kravets .modes = { 557392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, 558392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 559392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 560392cacf2SNikita Kravets MSI_EC_MODE_NULL 561392cacf2SNikita Kravets }, 562392cacf2SNikita Kravets }, 563392cacf2SNikita Kravets .cpu = { 564392cacf2SNikita Kravets .rt_temp_address = 0x68, 565392cacf2SNikita Kravets .rt_fan_speed_address = 0xc9, 566392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 567392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 568392cacf2SNikita Kravets .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 569392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 570392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 571392cacf2SNikita Kravets }, 572392cacf2SNikita Kravets .gpu = { 573392cacf2SNikita Kravets .rt_temp_address = 0x80, 574392cacf2SNikita Kravets .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 575392cacf2SNikita Kravets }, 576392cacf2SNikita Kravets .leds = { 577392cacf2SNikita Kravets .micmute_led_address = MSI_EC_ADDR_UNSUPP, 578392cacf2SNikita Kravets .mute_led_address = MSI_EC_ADDR_UNSUPP, 579392cacf2SNikita Kravets .bit = 2, 580392cacf2SNikita Kravets }, 581392cacf2SNikita Kravets .kbd_bl = { 582392cacf2SNikita Kravets .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 583392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 584392cacf2SNikita Kravets .max_mode = 1, // ? 585392cacf2SNikita Kravets .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 586392cacf2SNikita Kravets .state_base_value = 0x80, 587392cacf2SNikita Kravets .max_state = 3, 588392cacf2SNikita Kravets }, 589392cacf2SNikita Kravets }; 590392cacf2SNikita Kravets 591392cacf2SNikita Kravets static const char * const ALLOWED_FW_7[] __initconst = { 592392cacf2SNikita Kravets "17FKEMS1.108", 593392cacf2SNikita Kravets "17FKEMS1.109", 594392cacf2SNikita Kravets "17FKEMS1.10A", 595392cacf2SNikita Kravets NULL 596392cacf2SNikita Kravets }; 597392cacf2SNikita Kravets 598392cacf2SNikita Kravets static struct msi_ec_conf CONF7 __initdata = { 599392cacf2SNikita Kravets .allowed_fw = ALLOWED_FW_7, 600392cacf2SNikita Kravets .charge_control = { 601392cacf2SNikita Kravets .address = 0xef, 602392cacf2SNikita Kravets .offset_start = 0x8a, 603392cacf2SNikita Kravets .offset_end = 0x80, 604392cacf2SNikita Kravets .range_min = 0x8a, 605392cacf2SNikita Kravets .range_max = 0xe4, 606392cacf2SNikita Kravets }, 607392cacf2SNikita Kravets .webcam = { 608392cacf2SNikita Kravets .address = 0x2e, 609392cacf2SNikita Kravets .block_address = MSI_EC_ADDR_UNSUPP, 610392cacf2SNikita Kravets .bit = 1, 611392cacf2SNikita Kravets }, 612392cacf2SNikita Kravets .fn_super_swap = { 613392cacf2SNikita Kravets .address = 0xbf, // needs testing 614392cacf2SNikita Kravets .bit = 4, 615392cacf2SNikita Kravets }, 616392cacf2SNikita Kravets .cooler_boost = { 617392cacf2SNikita Kravets .address = 0x98, 618392cacf2SNikita Kravets .bit = 7, 619392cacf2SNikita Kravets }, 620392cacf2SNikita Kravets .shift_mode = { 621392cacf2SNikita Kravets .address = 0xf2, 622392cacf2SNikita Kravets .modes = { 623392cacf2SNikita Kravets { SM_ECO_NAME, 0xc2 }, 624392cacf2SNikita Kravets { SM_COMFORT_NAME, 0xc1 }, 625392cacf2SNikita Kravets { SM_SPORT_NAME, 0xc0 }, 626392cacf2SNikita Kravets { SM_TURBO_NAME, 0xc4 }, 627392cacf2SNikita Kravets MSI_EC_MODE_NULL 628392cacf2SNikita Kravets }, 629392cacf2SNikita Kravets }, 630392cacf2SNikita Kravets .super_battery = { 631392cacf2SNikita Kravets .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes 632392cacf2SNikita Kravets .mask = 0x0f, 633392cacf2SNikita Kravets }, 634392cacf2SNikita Kravets .fan_mode = { 635392cacf2SNikita Kravets .address = 0xf4, 636392cacf2SNikita Kravets .modes = { 637392cacf2SNikita Kravets { FM_AUTO_NAME, 0x0d }, // d may not be relevant 638392cacf2SNikita Kravets { FM_SILENT_NAME, 0x1d }, 639392cacf2SNikita Kravets { FM_ADVANCED_NAME, 0x8d }, 640392cacf2SNikita Kravets MSI_EC_MODE_NULL 641392cacf2SNikita Kravets }, 642392cacf2SNikita Kravets }, 643392cacf2SNikita Kravets .cpu = { 644392cacf2SNikita Kravets .rt_temp_address = 0x68, 645392cacf2SNikita Kravets .rt_fan_speed_address = 0xc9, // needs testing 646392cacf2SNikita Kravets .rt_fan_speed_base_min = 0x19, 647392cacf2SNikita Kravets .rt_fan_speed_base_max = 0x37, 648392cacf2SNikita Kravets .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 649392cacf2SNikita Kravets .bs_fan_speed_base_min = 0x00, 650392cacf2SNikita Kravets .bs_fan_speed_base_max = 0x0f, 651392cacf2SNikita Kravets }, 652392cacf2SNikita Kravets .gpu = { 653392cacf2SNikita Kravets .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 654392cacf2SNikita Kravets .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 655392cacf2SNikita Kravets }, 656392cacf2SNikita Kravets .leds = { 657392cacf2SNikita Kravets .micmute_led_address = MSI_EC_ADDR_UNSUPP, 658392cacf2SNikita Kravets .mute_led_address = 0x2c, 659392cacf2SNikita Kravets .bit = 2, 660392cacf2SNikita Kravets }, 661392cacf2SNikita Kravets .kbd_bl = { 662392cacf2SNikita Kravets .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 663392cacf2SNikita Kravets .bl_modes = { 0x00, 0x08 }, // ? 664392cacf2SNikita Kravets .max_mode = 1, // ? 665392cacf2SNikita Kravets .bl_state_address = 0xf3, 666392cacf2SNikita Kravets .state_base_value = 0x80, 667392cacf2SNikita Kravets .max_state = 3, 668392cacf2SNikita Kravets }, 669392cacf2SNikita Kravets }; 670392cacf2SNikita Kravets 671392cacf2SNikita Kravets static struct msi_ec_conf *CONFIGS[] __initdata = { 672392cacf2SNikita Kravets &CONF0, 673392cacf2SNikita Kravets &CONF1, 674392cacf2SNikita Kravets &CONF2, 675392cacf2SNikita Kravets &CONF3, 676392cacf2SNikita Kravets &CONF4, 677392cacf2SNikita Kravets &CONF5, 678392cacf2SNikita Kravets &CONF6, 679392cacf2SNikita Kravets &CONF7, 680392cacf2SNikita Kravets NULL 681392cacf2SNikita Kravets }; 682392cacf2SNikita Kravets 683392cacf2SNikita Kravets static struct msi_ec_conf conf; // current configuration 684392cacf2SNikita Kravets 685392cacf2SNikita Kravets /* 686392cacf2SNikita Kravets * Helper functions 687392cacf2SNikita Kravets */ 688392cacf2SNikita Kravets 689392cacf2SNikita Kravets static int ec_read_seq(u8 addr, u8 *buf, u8 len) 690392cacf2SNikita Kravets { 691392cacf2SNikita Kravets int result; 692392cacf2SNikita Kravets 693392cacf2SNikita Kravets for (u8 i = 0; i < len; i++) { 694392cacf2SNikita Kravets result = ec_read(addr + i, buf + i); 695392cacf2SNikita Kravets if (result < 0) 696392cacf2SNikita Kravets return result; 697392cacf2SNikita Kravets } 698392cacf2SNikita Kravets 699392cacf2SNikita Kravets return 0; 700392cacf2SNikita Kravets } 701392cacf2SNikita Kravets 702392cacf2SNikita Kravets static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) 703392cacf2SNikita Kravets { 704392cacf2SNikita Kravets int result; 705392cacf2SNikita Kravets 706392cacf2SNikita Kravets memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); 707392cacf2SNikita Kravets result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, 708392cacf2SNikita Kravets buf, 709392cacf2SNikita Kravets MSI_EC_FW_VERSION_LENGTH); 710392cacf2SNikita Kravets if (result < 0) 711392cacf2SNikita Kravets return result; 712392cacf2SNikita Kravets 713392cacf2SNikita Kravets return MSI_EC_FW_VERSION_LENGTH + 1; 714392cacf2SNikita Kravets } 715392cacf2SNikita Kravets 716392cacf2SNikita Kravets /* 717392cacf2SNikita Kravets * Sysfs power_supply subsystem 718392cacf2SNikita Kravets */ 719392cacf2SNikita Kravets 720392cacf2SNikita Kravets static ssize_t charge_control_threshold_show(u8 offset, 721392cacf2SNikita Kravets struct device *device, 722392cacf2SNikita Kravets struct device_attribute *attr, 723392cacf2SNikita Kravets char *buf) 724392cacf2SNikita Kravets { 725392cacf2SNikita Kravets u8 rdata; 726392cacf2SNikita Kravets int result; 727392cacf2SNikita Kravets 728392cacf2SNikita Kravets result = ec_read(conf.charge_control.address, &rdata); 729392cacf2SNikita Kravets if (result < 0) 730392cacf2SNikita Kravets return result; 731392cacf2SNikita Kravets 732392cacf2SNikita Kravets return sysfs_emit(buf, "%i\n", rdata - offset); 733392cacf2SNikita Kravets } 734392cacf2SNikita Kravets 735392cacf2SNikita Kravets static ssize_t charge_control_threshold_store(u8 offset, 736392cacf2SNikita Kravets struct device *dev, 737392cacf2SNikita Kravets struct device_attribute *attr, 738392cacf2SNikita Kravets const char *buf, size_t count) 739392cacf2SNikita Kravets { 740392cacf2SNikita Kravets u8 wdata; 741392cacf2SNikita Kravets int result; 742392cacf2SNikita Kravets 743392cacf2SNikita Kravets result = kstrtou8(buf, 10, &wdata); 744392cacf2SNikita Kravets if (result < 0) 745392cacf2SNikita Kravets return result; 746392cacf2SNikita Kravets 747392cacf2SNikita Kravets wdata += offset; 748392cacf2SNikita Kravets if (wdata < conf.charge_control.range_min || 749392cacf2SNikita Kravets wdata > conf.charge_control.range_max) 750392cacf2SNikita Kravets return -EINVAL; 751392cacf2SNikita Kravets 752392cacf2SNikita Kravets result = ec_write(conf.charge_control.address, wdata); 753392cacf2SNikita Kravets if (result < 0) 754392cacf2SNikita Kravets return result; 755392cacf2SNikita Kravets 756392cacf2SNikita Kravets return count; 757392cacf2SNikita Kravets } 758392cacf2SNikita Kravets 759392cacf2SNikita Kravets static ssize_t charge_control_start_threshold_show(struct device *device, 760392cacf2SNikita Kravets struct device_attribute *attr, 761392cacf2SNikita Kravets char *buf) 762392cacf2SNikita Kravets { 763392cacf2SNikita Kravets return charge_control_threshold_show(conf.charge_control.offset_start, 764392cacf2SNikita Kravets device, attr, buf); 765392cacf2SNikita Kravets } 766392cacf2SNikita Kravets 767392cacf2SNikita Kravets static ssize_t charge_control_start_threshold_store(struct device *dev, 768392cacf2SNikita Kravets struct device_attribute *attr, 769392cacf2SNikita Kravets const char *buf, size_t count) 770392cacf2SNikita Kravets { 771392cacf2SNikita Kravets return charge_control_threshold_store(conf.charge_control.offset_start, 772392cacf2SNikita Kravets dev, attr, buf, count); 773392cacf2SNikita Kravets } 774392cacf2SNikita Kravets 775392cacf2SNikita Kravets static ssize_t charge_control_end_threshold_show(struct device *device, 776392cacf2SNikita Kravets struct device_attribute *attr, 777392cacf2SNikita Kravets char *buf) 778392cacf2SNikita Kravets { 779392cacf2SNikita Kravets return charge_control_threshold_show(conf.charge_control.offset_end, 780392cacf2SNikita Kravets device, attr, buf); 781392cacf2SNikita Kravets } 782392cacf2SNikita Kravets 783392cacf2SNikita Kravets static ssize_t charge_control_end_threshold_store(struct device *dev, 784392cacf2SNikita Kravets struct device_attribute *attr, 785392cacf2SNikita Kravets const char *buf, size_t count) 786392cacf2SNikita Kravets { 787392cacf2SNikita Kravets return charge_control_threshold_store(conf.charge_control.offset_end, 788392cacf2SNikita Kravets dev, attr, buf, count); 789392cacf2SNikita Kravets } 790392cacf2SNikita Kravets 791392cacf2SNikita Kravets static DEVICE_ATTR_RW(charge_control_start_threshold); 792392cacf2SNikita Kravets static DEVICE_ATTR_RW(charge_control_end_threshold); 793392cacf2SNikita Kravets 794392cacf2SNikita Kravets static struct attribute *msi_battery_attrs[] = { 795392cacf2SNikita Kravets &dev_attr_charge_control_start_threshold.attr, 796392cacf2SNikita Kravets &dev_attr_charge_control_end_threshold.attr, 797392cacf2SNikita Kravets NULL 798392cacf2SNikita Kravets }; 799392cacf2SNikita Kravets 800392cacf2SNikita Kravets ATTRIBUTE_GROUPS(msi_battery); 801392cacf2SNikita Kravets 802392cacf2SNikita Kravets static int msi_battery_add(struct power_supply *battery, 803392cacf2SNikita Kravets struct acpi_battery_hook *hook) 804392cacf2SNikita Kravets { 805392cacf2SNikita Kravets return device_add_groups(&battery->dev, msi_battery_groups); 806392cacf2SNikita Kravets } 807392cacf2SNikita Kravets 808392cacf2SNikita Kravets static int msi_battery_remove(struct power_supply *battery, 809392cacf2SNikita Kravets struct acpi_battery_hook *hook) 810392cacf2SNikita Kravets { 811392cacf2SNikita Kravets device_remove_groups(&battery->dev, msi_battery_groups); 812392cacf2SNikita Kravets return 0; 813392cacf2SNikita Kravets } 814392cacf2SNikita Kravets 815392cacf2SNikita Kravets static struct acpi_battery_hook battery_hook = { 816392cacf2SNikita Kravets .add_battery = msi_battery_add, 817392cacf2SNikita Kravets .remove_battery = msi_battery_remove, 818392cacf2SNikita Kravets .name = MSI_EC_DRIVER_NAME, 819392cacf2SNikita Kravets }; 820392cacf2SNikita Kravets 821392cacf2SNikita Kravets /* 822392cacf2SNikita Kravets * Module load/unload 823392cacf2SNikita Kravets */ 824392cacf2SNikita Kravets 825392cacf2SNikita Kravets static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { 826392cacf2SNikita Kravets { 827392cacf2SNikita Kravets .matches = { 828392cacf2SNikita Kravets DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), 829392cacf2SNikita Kravets }, 830392cacf2SNikita Kravets }, 831392cacf2SNikita Kravets { 832392cacf2SNikita Kravets .matches = { 833392cacf2SNikita Kravets DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 834392cacf2SNikita Kravets }, 835392cacf2SNikita Kravets }, 836392cacf2SNikita Kravets {} 837392cacf2SNikita Kravets }; 838392cacf2SNikita Kravets MODULE_DEVICE_TABLE(dmi, msi_dmi_table); 839392cacf2SNikita Kravets 840392cacf2SNikita Kravets static int __init load_configuration(void) 841392cacf2SNikita Kravets { 842392cacf2SNikita Kravets int result; 843392cacf2SNikita Kravets 844392cacf2SNikita Kravets u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; 845392cacf2SNikita Kravets 846392cacf2SNikita Kravets /* get firmware version */ 847392cacf2SNikita Kravets result = ec_get_firmware_version(fw_version); 848392cacf2SNikita Kravets if (result < 0) 849392cacf2SNikita Kravets return result; 850392cacf2SNikita Kravets 851392cacf2SNikita Kravets /* load the suitable configuration, if exists */ 852392cacf2SNikita Kravets for (int i = 0; CONFIGS[i]; i++) { 853392cacf2SNikita Kravets if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { 854392cacf2SNikita Kravets conf = *CONFIGS[i]; 855392cacf2SNikita Kravets conf.allowed_fw = NULL; 856392cacf2SNikita Kravets return 0; 857392cacf2SNikita Kravets } 858392cacf2SNikita Kravets } 859392cacf2SNikita Kravets 860392cacf2SNikita Kravets /* config not found */ 861392cacf2SNikita Kravets 862392cacf2SNikita Kravets for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { 863392cacf2SNikita Kravets if (!isgraph(fw_version[i])) { 864392cacf2SNikita Kravets pr_warn("Unable to find a valid firmware version!\n"); 865392cacf2SNikita Kravets return -EOPNOTSUPP; 866392cacf2SNikita Kravets } 867392cacf2SNikita Kravets } 868392cacf2SNikita Kravets 869392cacf2SNikita Kravets pr_warn("Firmware version is not supported: '%s'\n", fw_version); 870392cacf2SNikita Kravets return -EOPNOTSUPP; 871392cacf2SNikita Kravets } 872392cacf2SNikita Kravets 873392cacf2SNikita Kravets static int __init msi_ec_init(void) 874392cacf2SNikita Kravets { 875392cacf2SNikita Kravets int result; 876392cacf2SNikita Kravets 877392cacf2SNikita Kravets result = load_configuration(); 878392cacf2SNikita Kravets if (result < 0) 879392cacf2SNikita Kravets return result; 880392cacf2SNikita Kravets 881392cacf2SNikita Kravets battery_hook_register(&battery_hook); 882392cacf2SNikita Kravets return 0; 883392cacf2SNikita Kravets } 884392cacf2SNikita Kravets 885392cacf2SNikita Kravets static void __exit msi_ec_exit(void) 886392cacf2SNikita Kravets { 887392cacf2SNikita Kravets battery_hook_unregister(&battery_hook); 888392cacf2SNikita Kravets } 889392cacf2SNikita Kravets 890392cacf2SNikita Kravets MODULE_LICENSE("GPL"); 891392cacf2SNikita Kravets MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>"); 892392cacf2SNikita Kravets MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>"); 893392cacf2SNikita Kravets MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>"); 894392cacf2SNikita Kravets MODULE_DESCRIPTION("MSI Embedded Controller"); 895392cacf2SNikita Kravets 896392cacf2SNikita Kravets module_init(msi_ec_init); 897392cacf2SNikita Kravets module_exit(msi_ec_exit); 898