2023-03-21 01:55:09 +03:00
// 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/platform_device.h>
# include <linux/seq_file.h>
# include <linux/string.h>
2023-08-05 10:10:10 +02:00
# define SM_ECO_NAME "eco"
# define SM_COMFORT_NAME "comfort"
# define SM_SPORT_NAME "sport"
# define SM_TURBO_NAME "turbo"
2023-03-21 01:55:09 +03:00
2023-08-05 10:10:10 +02:00
# define FM_AUTO_NAME "auto"
# define FM_SILENT_NAME "silent"
# define FM_BASIC_NAME "basic"
# define FM_ADVANCED_NAME "advanced"
2023-03-21 01:55:09 +03:00
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_super_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_super_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_super_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 " ,
" E1592IMS.10C " ,
NULL
} ;
static struct msi_ec_conf CONF3 __initdata = {
. allowed_fw = ALLOWED_FW_3 ,
. 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_super_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_super_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_super_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_super_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_super_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 struct msi_ec_conf * CONFIGS [ ] __initdata = {
& CONF0 ,
& CONF1 ,
& CONF2 ,
& CONF3 ,
& CONF4 ,
& CONF5 ,
& CONF6 ,
& CONF7 ,
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 ) ;