mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 00:34:20 +08:00
5a66d59b5f
The msi-ec driver fails to build for me (gcc 7.5):
CC [M] drivers/platform/x86/msi-ec.o
drivers/platform/x86/msi-ec.c:72:6: error: initializer element is not constant
{ SM_ECO_NAME, 0xc2 },
^~~~~~~~~~~
drivers/platform/x86/msi-ec.c:72:6: note: (near initialization for ‘CONF0.shift_mode.modes[0].name’)
drivers/platform/x86/msi-ec.c:73:6: error: initializer element is not constant
{ SM_COMFORT_NAME, 0xc1 },
^~~~~~~~~~~~~~~
drivers/platform/x86/msi-ec.c:73:6: note: (near initialization for ‘CONF0.shift_mode.modes[1].name’)
drivers/platform/x86/msi-ec.c:74:6: error: initializer element is not constant
{ SM_SPORT_NAME, 0xc0 },
^~~~~~~~~~~~~
drivers/platform/x86/msi-ec.c:74:6: note: (near initialization for ‘CONF0.shift_mode.modes[2].name’)
(...)
Don't try to be smart, just use defines for the constant strings. The
compiler will recognize it's the same string and will store it only
once in the data section anyway.
Signed-off-by: Jean Delvare <jdelvare@suse.de>
Fixes: 392cacf2aa
("platform/x86: Add new msi-ec driver")
Cc: stable@vger.kernel.org
Cc: Nikita Kravets <teackot@gmail.com>
Cc: Hans de Goede <hdegoede@redhat.com>
Cc: Mark Gross <markgross@kernel.org>
Link: https://lore.kernel.org/r/20230805101010.54d49e91@endymion.delvare
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
898 lines
19 KiB
C
898 lines
19 KiB
C
// 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>
|
|
|
|
#define SM_ECO_NAME "eco"
|
|
#define SM_COMFORT_NAME "comfort"
|
|
#define SM_SPORT_NAME "sport"
|
|
#define SM_TURBO_NAME "turbo"
|
|
|
|
#define FM_AUTO_NAME "auto"
|
|
#define FM_SILENT_NAME "silent"
|
|
#define FM_BASIC_NAME "basic"
|
|
#define FM_ADVANCED_NAME "advanced"
|
|
|
|
static const char * const ALLOWED_FW_0[] __initconst = {
|
|
"14C1EMS1.012",
|
|
"14C1EMS1.101",
|
|
"14C1EMS1.102",
|
|
NULL
|
|
};
|
|
|
|
static struct msi_ec_conf CONF0 __initdata = {
|
|
.allowed_fw = ALLOWED_FW_0,
|
|
.charge_control = {
|
|
.address = 0xef,
|
|
.offset_start = 0x8a,
|
|
.offset_end = 0x80,
|
|
.range_min = 0x8a,
|
|
.range_max = 0xe4,
|
|
},
|
|
.webcam = {
|
|
.address = 0x2e,
|
|
.block_address = 0x2f,
|
|
.bit = 1,
|
|
},
|
|
.fn_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);
|