Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2
   3/*
   4 * msi-ec: MSI laptops' embedded controller driver.
   5 *
   6 * This driver allows various MSI laptops' functionalities to be
   7 * controlled from userspace.
   8 *
   9 * It contains EC memory configurations for different firmware versions
  10 * and exports battery charge thresholds to userspace.
  11 *
  12 * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es>
  13 * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev>
  14 * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com>
  15 */
  16
  17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  18
  19#include "msi-ec.h"
  20
  21#include <acpi/battery.h>
  22#include <linux/acpi.h>
  23#include <linux/init.h>
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/platform_device.h>
  27#include <linux/seq_file.h>
  28#include <linux/string.h>
  29
  30#define SM_ECO_NAME		"eco"
  31#define SM_COMFORT_NAME		"comfort"
  32#define SM_SPORT_NAME		"sport"
  33#define SM_TURBO_NAME		"turbo"
  34
  35#define FM_AUTO_NAME		"auto"
  36#define FM_SILENT_NAME		"silent"
  37#define FM_BASIC_NAME		"basic"
  38#define FM_ADVANCED_NAME	"advanced"
  39
  40static const char * const ALLOWED_FW_0[] __initconst = {
  41	"14C1EMS1.012",
  42	"14C1EMS1.101",
  43	"14C1EMS1.102",
  44	NULL
  45};
  46
  47static struct msi_ec_conf CONF0 __initdata = {
  48	.allowed_fw = ALLOWED_FW_0,
  49	.charge_control = {
  50		.address      = 0xef,
  51		.offset_start = 0x8a,
  52		.offset_end   = 0x80,
  53		.range_min    = 0x8a,
  54		.range_max    = 0xe4,
  55	},
  56	.webcam = {
  57		.address       = 0x2e,
  58		.block_address = 0x2f,
  59		.bit           = 1,
  60	},
  61	.fn_win_swap = {
  62		.address = 0xbf,
  63		.bit     = 4,
  64	},
  65	.cooler_boost = {
  66		.address = 0x98,
  67		.bit     = 7,
  68	},
  69	.shift_mode = {
  70		.address = 0xf2,
  71		.modes = {
  72			{ SM_ECO_NAME,     0xc2 },
  73			{ SM_COMFORT_NAME, 0xc1 },
  74			{ SM_SPORT_NAME,   0xc0 },
  75			MSI_EC_MODE_NULL
  76		},
  77	},
  78	.super_battery = {
  79		.address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing
  80	},
  81	.fan_mode = {
  82		.address = 0xf4,
  83		.modes = {
  84			{ FM_AUTO_NAME,     0x0d },
  85			{ FM_SILENT_NAME,   0x1d },
  86			{ FM_BASIC_NAME,    0x4d },
  87			{ FM_ADVANCED_NAME, 0x8d },
  88			MSI_EC_MODE_NULL
  89		},
  90	},
  91	.cpu = {
  92		.rt_temp_address       = 0x68,
  93		.rt_fan_speed_address  = 0x71,
  94		.rt_fan_speed_base_min = 0x19,
  95		.rt_fan_speed_base_max = 0x37,
  96		.bs_fan_speed_address  = 0x89,
  97		.bs_fan_speed_base_min = 0x00,
  98		.bs_fan_speed_base_max = 0x0f,
  99	},
 100	.gpu = {
 101		.rt_temp_address      = 0x80,
 102		.rt_fan_speed_address = 0x89,
 103	},
 104	.leds = {
 105		.micmute_led_address = 0x2b,
 106		.mute_led_address    = 0x2c,
 107		.bit                 = 2,
 108	},
 109	.kbd_bl = {
 110		.bl_mode_address  = 0x2c, // ?
 111		.bl_modes         = { 0x00, 0x08 }, // ?
 112		.max_mode         = 1, // ?
 113		.bl_state_address = 0xf3,
 114		.state_base_value = 0x80,
 115		.max_state        = 3,
 116	},
 117};
 118
 119static const char * const ALLOWED_FW_1[] __initconst = {
 120	"17F2EMS1.103",
 121	"17F2EMS1.104",
 122	"17F2EMS1.106",
 123	"17F2EMS1.107",
 124	NULL
 125};
 126
 127static struct msi_ec_conf CONF1 __initdata = {
 128	.allowed_fw = ALLOWED_FW_1,
 129	.charge_control = {
 130		.address      = 0xef,
 131		.offset_start = 0x8a,
 132		.offset_end   = 0x80,
 133		.range_min    = 0x8a,
 134		.range_max    = 0xe4,
 135	},
 136	.webcam = {
 137		.address       = 0x2e,
 138		.block_address = 0x2f,
 139		.bit           = 1,
 140	},
 141	.fn_win_swap = {
 142		.address = 0xbf,
 143		.bit     = 4,
 144	},
 145	.cooler_boost = {
 146		.address = 0x98,
 147		.bit     = 7,
 148	},
 149	.shift_mode = {
 150		.address = 0xf2,
 151		.modes = {
 152			{ SM_ECO_NAME,     0xc2 },
 153			{ SM_COMFORT_NAME, 0xc1 },
 154			{ SM_SPORT_NAME,   0xc0 },
 155			{ SM_TURBO_NAME,   0xc4 },
 156			MSI_EC_MODE_NULL
 157		},
 158	},
 159	.super_battery = {
 160		.address = MSI_EC_ADDR_UNKNOWN,
 161	},
 162	.fan_mode = {
 163		.address = 0xf4,
 164		.modes = {
 165			{ FM_AUTO_NAME,     0x0d },
 166			{ FM_BASIC_NAME,    0x4d },
 167			{ FM_ADVANCED_NAME, 0x8d },
 168			MSI_EC_MODE_NULL
 169		},
 170	},
 171	.cpu = {
 172		.rt_temp_address       = 0x68,
 173		.rt_fan_speed_address  = 0x71,
 174		.rt_fan_speed_base_min = 0x19,
 175		.rt_fan_speed_base_max = 0x37,
 176		.bs_fan_speed_address  = 0x89,
 177		.bs_fan_speed_base_min = 0x00,
 178		.bs_fan_speed_base_max = 0x0f,
 179	},
 180	.gpu = {
 181		.rt_temp_address      = 0x80,
 182		.rt_fan_speed_address = 0x89,
 183	},
 184	.leds = {
 185		.micmute_led_address = 0x2b,
 186		.mute_led_address    = 0x2c,
 187		.bit                 = 2,
 188	},
 189	.kbd_bl = {
 190		.bl_mode_address  = 0x2c, // ?
 191		.bl_modes         = { 0x00, 0x08 }, // ?
 192		.max_mode         = 1, // ?
 193		.bl_state_address = 0xf3,
 194		.state_base_value = 0x80,
 195		.max_state        = 3,
 196	},
 197};
 198
 199static const char * const ALLOWED_FW_2[] __initconst = {
 200	"1552EMS1.118",
 201	NULL
 202};
 203
 204static struct msi_ec_conf CONF2 __initdata = {
 205	.allowed_fw = ALLOWED_FW_2,
 206	.charge_control = {
 207		.address      = 0xd7,
 208		.offset_start = 0x8a,
 209		.offset_end   = 0x80,
 210		.range_min    = 0x8a,
 211		.range_max    = 0xe4,
 212	},
 213	.webcam = {
 214		.address       = 0x2e,
 215		.block_address = 0x2f,
 216		.bit           = 1,
 217	},
 218	.fn_win_swap = {
 219		.address = 0xe8,
 220		.bit     = 4,
 221	},
 222	.cooler_boost = {
 223		.address = 0x98,
 224		.bit     = 7,
 225	},
 226	.shift_mode = {
 227		.address = 0xf2,
 228		.modes = {
 229			{ SM_ECO_NAME,     0xc2 },
 230			{ SM_COMFORT_NAME, 0xc1 },
 231			{ SM_SPORT_NAME,   0xc0 },
 232			MSI_EC_MODE_NULL
 233		},
 234	},
 235	.super_battery = {
 236		.address = 0xeb,
 237		.mask    = 0x0f,
 238	},
 239	.fan_mode = {
 240		.address = 0xd4,
 241		.modes = {
 242			{ FM_AUTO_NAME,     0x0d },
 243			{ FM_SILENT_NAME,   0x1d },
 244			{ FM_BASIC_NAME,    0x4d },
 245			{ FM_ADVANCED_NAME, 0x8d },
 246			MSI_EC_MODE_NULL
 247		},
 248	},
 249	.cpu = {
 250		.rt_temp_address       = 0x68,
 251		.rt_fan_speed_address  = 0x71,
 252		.rt_fan_speed_base_min = 0x19,
 253		.rt_fan_speed_base_max = 0x37,
 254		.bs_fan_speed_address  = 0x89,
 255		.bs_fan_speed_base_min = 0x00,
 256		.bs_fan_speed_base_max = 0x0f,
 257	},
 258	.gpu = {
 259		.rt_temp_address      = 0x80,
 260		.rt_fan_speed_address = 0x89,
 261	},
 262	.leds = {
 263		.micmute_led_address = 0x2c,
 264		.mute_led_address    = 0x2d,
 265		.bit                 = 1,
 266	},
 267	.kbd_bl = {
 268		.bl_mode_address  = 0x2c, // ?
 269		.bl_modes         = { 0x00, 0x08 }, // ?
 270		.max_mode         = 1, // ?
 271		.bl_state_address = 0xd3,
 272		.state_base_value = 0x80,
 273		.max_state        = 3,
 274	},
 275};
 276
 277static const char * const ALLOWED_FW_3[] __initconst = {
 278	"1592EMS1.111",
 279	NULL
 280};
 281
 282static struct msi_ec_conf CONF3 __initdata = {
 283	.allowed_fw = ALLOWED_FW_3,
 284	.charge_control = {
 285		.address      = 0xd7,
 286		.offset_start = 0x8a,
 287		.offset_end   = 0x80,
 288		.range_min    = 0x8a,
 289		.range_max    = 0xe4,
 290	},
 291	.webcam = {
 292		.address       = 0x2e,
 293		.block_address = 0x2f,
 294		.bit           = 1,
 295	},
 296	.fn_win_swap = {
 297		.address = 0xe8,
 298		.bit     = 4,
 299	},
 300	.cooler_boost = {
 301		.address = 0x98,
 302		.bit     = 7,
 303	},
 304	.shift_mode = {
 305		.address = 0xd2,
 306		.modes = {
 307			{ SM_ECO_NAME,     0xc2 },
 308			{ SM_COMFORT_NAME, 0xc1 },
 309			{ SM_SPORT_NAME,   0xc0 },
 310			MSI_EC_MODE_NULL
 311		},
 312	},
 313	.super_battery = {
 314		.address = 0xeb,
 315		.mask    = 0x0f,
 316	},
 317	.fan_mode = {
 318		.address = 0xd4,
 319		.modes = {
 320			{ FM_AUTO_NAME,     0x0d },
 321			{ FM_SILENT_NAME,   0x1d },
 322			{ FM_BASIC_NAME,    0x4d },
 323			{ FM_ADVANCED_NAME, 0x8d },
 324			MSI_EC_MODE_NULL
 325		},
 326	},
 327	.cpu = {
 328		.rt_temp_address       = 0x68,
 329		.rt_fan_speed_address  = 0xc9,
 330		.rt_fan_speed_base_min = 0x19,
 331		.rt_fan_speed_base_max = 0x37,
 332		.bs_fan_speed_address  = 0x89, // ?
 333		.bs_fan_speed_base_min = 0x00,
 334		.bs_fan_speed_base_max = 0x0f,
 335	},
 336	.gpu = {
 337		.rt_temp_address      = 0x80,
 338		.rt_fan_speed_address = 0x89,
 339	},
 340	.leds = {
 341		.micmute_led_address = 0x2b,
 342		.mute_led_address    = 0x2c,
 343		.bit                 = 1,
 344	},
 345	.kbd_bl = {
 346		.bl_mode_address  = 0x2c, // ?
 347		.bl_modes         = { 0x00, 0x08 }, // ?
 348		.max_mode         = 1, // ?
 349		.bl_state_address = 0xd3,
 350		.state_base_value = 0x80,
 351		.max_state        = 3,
 352	},
 353};
 354
 355static const char * const ALLOWED_FW_4[] __initconst = {
 356	"16V4EMS1.114",
 357	NULL
 358};
 359
 360static struct msi_ec_conf CONF4 __initdata = {
 361	.allowed_fw = ALLOWED_FW_4,
 362	.charge_control = {
 363		.address      = 0xd7,
 364		.offset_start = 0x8a,
 365		.offset_end   = 0x80,
 366		.range_min    = 0x8a,
 367		.range_max    = 0xe4,
 368	},
 369	.webcam = {
 370		.address       = 0x2e,
 371		.block_address = 0x2f,
 372		.bit           = 1,
 373	},
 374	.fn_win_swap = {
 375		.address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown
 376		.bit     = 4,
 377	},
 378	.cooler_boost = {
 379		.address = 0x98,
 380		.bit     = 7,
 381	},
 382	.shift_mode = {
 383		.address = 0xd2,
 384		.modes = {
 385			{ SM_ECO_NAME,     0xc2 },
 386			{ SM_COMFORT_NAME, 0xc1 },
 387			{ SM_SPORT_NAME,   0xc0 },
 388			MSI_EC_MODE_NULL
 389		},
 390	},
 391	.super_battery = { // may be supported, but address is unknown
 392		.address = MSI_EC_ADDR_UNKNOWN,
 393		.mask    = 0x0f,
 394	},
 395	.fan_mode = {
 396		.address = 0xd4,
 397		.modes = {
 398			{ FM_AUTO_NAME,     0x0d },
 399			{ FM_SILENT_NAME,   0x1d },
 400			{ FM_ADVANCED_NAME, 0x8d },
 401			MSI_EC_MODE_NULL
 402		},
 403	},
 404	.cpu = {
 405		.rt_temp_address       = 0x68, // needs testing
 406		.rt_fan_speed_address  = 0x71, // needs testing
 407		.rt_fan_speed_base_min = 0x19,
 408		.rt_fan_speed_base_max = 0x37,
 409		.bs_fan_speed_address  = MSI_EC_ADDR_UNKNOWN,
 410		.bs_fan_speed_base_min = 0x00,
 411		.bs_fan_speed_base_max = 0x0f,
 412	},
 413	.gpu = {
 414		.rt_temp_address      = 0x80,
 415		.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
 416	},
 417	.leds = {
 418		.micmute_led_address = MSI_EC_ADDR_UNKNOWN,
 419		.mute_led_address    = MSI_EC_ADDR_UNKNOWN,
 420		.bit                 = 1,
 421	},
 422	.kbd_bl = {
 423		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN, // ?
 424		.bl_modes         = { 0x00, 0x08 }, // ?
 425		.max_mode         = 1, // ?
 426		.bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional
 427		.state_base_value = 0x80,
 428		.max_state        = 3,
 429	},
 430};
 431
 432static const char * const ALLOWED_FW_5[] __initconst = {
 433	"158LEMS1.103",
 434	"158LEMS1.105",
 435	"158LEMS1.106",
 436	NULL
 437};
 438
 439static struct msi_ec_conf CONF5 __initdata = {
 440	.allowed_fw = ALLOWED_FW_5,
 441	.charge_control = {
 442		.address      = 0xef,
 443		.offset_start = 0x8a,
 444		.offset_end   = 0x80,
 445		.range_min    = 0x8a,
 446		.range_max    = 0xe4,
 447	},
 448	.webcam = {
 449		.address       = 0x2e,
 450		.block_address = 0x2f,
 451		.bit           = 1,
 452	},
 453	.fn_win_swap = { // todo: reverse
 454		.address = 0xbf,
 455		.bit     = 4,
 456	},
 457	.cooler_boost = {
 458		.address = 0x98,
 459		.bit     = 7,
 460	},
 461	.shift_mode = {
 462		.address = 0xf2,
 463		.modes = {
 464			{ SM_ECO_NAME,     0xc2 },
 465			{ SM_COMFORT_NAME, 0xc1 },
 466			{ SM_TURBO_NAME,   0xc4 },
 467			MSI_EC_MODE_NULL
 468		},
 469	},
 470	.super_battery = { // unsupported?
 471		.address = MSI_EC_ADDR_UNKNOWN,
 472		.mask    = 0x0f,
 473	},
 474	.fan_mode = {
 475		.address = 0xf4,
 476		.modes = {
 477			{ FM_AUTO_NAME,     0x0d },
 478			{ FM_SILENT_NAME,   0x1d },
 479			{ FM_ADVANCED_NAME, 0x8d },
 480			MSI_EC_MODE_NULL
 481		},
 482	},
 483	.cpu = {
 484		.rt_temp_address       = 0x68, // needs testing
 485		.rt_fan_speed_address  = 0x71, // needs testing
 486		.rt_fan_speed_base_min = 0x19,
 487		.rt_fan_speed_base_max = 0x37,
 488		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 489		.bs_fan_speed_base_min = 0x00,
 490		.bs_fan_speed_base_max = 0x0f,
 491	},
 492	.gpu = {
 493		.rt_temp_address      = MSI_EC_ADDR_UNKNOWN,
 494		.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
 495	},
 496	.leds = {
 497		.micmute_led_address = 0x2b,
 498		.mute_led_address    = 0x2c,
 499		.bit                 = 2,
 500	},
 501	.kbd_bl = {
 502		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN, // ?
 503		.bl_modes         = { 0x00, 0x08 }, // ?
 504		.max_mode         = 1, // ?
 505		.bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional
 506		.state_base_value = 0x80,
 507		.max_state        = 3,
 508	},
 509};
 510
 511static const char * const ALLOWED_FW_6[] __initconst = {
 512	"1542EMS1.102",
 513	"1542EMS1.104",
 514	NULL
 515};
 516
 517static struct msi_ec_conf CONF6 __initdata = {
 518	.allowed_fw = ALLOWED_FW_6,
 519	.charge_control = {
 520		.address      = 0xef,
 521		.offset_start = 0x8a,
 522		.offset_end   = 0x80,
 523		.range_min    = 0x8a,
 524		.range_max    = 0xe4,
 525	},
 526	.webcam = {
 527		.address       = 0x2e,
 528		.block_address = MSI_EC_ADDR_UNSUPP,
 529		.bit           = 1,
 530	},
 531	.fn_win_swap = {
 532		.address = 0xbf, // todo: reverse
 533		.bit     = 4,
 534	},
 535	.cooler_boost = {
 536		.address = 0x98,
 537		.bit     = 7,
 538	},
 539	.shift_mode = {
 540		.address = 0xf2,
 541		.modes = {
 542			{ SM_ECO_NAME,     0xc2 },
 543			{ SM_COMFORT_NAME, 0xc1 },
 544			{ SM_SPORT_NAME,   0xc0 },
 545			{ SM_TURBO_NAME,   0xc4 },
 546			MSI_EC_MODE_NULL
 547		},
 548	},
 549	.super_battery = {
 550		.address = 0xd5,
 551		.mask    = 0x0f,
 552	},
 553	.fan_mode = {
 554		.address = 0xf4,
 555		.modes = {
 556			{ FM_AUTO_NAME,     0x0d },
 557			{ FM_SILENT_NAME,   0x1d },
 558			{ FM_ADVANCED_NAME, 0x8d },
 559			MSI_EC_MODE_NULL
 560		},
 561	},
 562	.cpu = {
 563		.rt_temp_address       = 0x68,
 564		.rt_fan_speed_address  = 0xc9,
 565		.rt_fan_speed_base_min = 0x19,
 566		.rt_fan_speed_base_max = 0x37,
 567		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 568		.bs_fan_speed_base_min = 0x00,
 569		.bs_fan_speed_base_max = 0x0f,
 570	},
 571	.gpu = {
 572		.rt_temp_address      = 0x80,
 573		.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
 574	},
 575	.leds = {
 576		.micmute_led_address = MSI_EC_ADDR_UNSUPP,
 577		.mute_led_address    = MSI_EC_ADDR_UNSUPP,
 578		.bit                 = 2,
 579	},
 580	.kbd_bl = {
 581		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN, // ?
 582		.bl_modes         = { 0x00, 0x08 }, // ?
 583		.max_mode         = 1, // ?
 584		.bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional
 585		.state_base_value = 0x80,
 586		.max_state        = 3,
 587	},
 588};
 589
 590static const char * const ALLOWED_FW_7[] __initconst = {
 591	"17FKEMS1.108",
 592	"17FKEMS1.109",
 593	"17FKEMS1.10A",
 594	NULL
 595};
 596
 597static struct msi_ec_conf CONF7 __initdata = {
 598	.allowed_fw = ALLOWED_FW_7,
 599	.charge_control = {
 600		.address      = 0xef,
 601		.offset_start = 0x8a,
 602		.offset_end   = 0x80,
 603		.range_min    = 0x8a,
 604		.range_max    = 0xe4,
 605	},
 606	.webcam = {
 607		.address       = 0x2e,
 608		.block_address = MSI_EC_ADDR_UNSUPP,
 609		.bit           = 1,
 610	},
 611	.fn_win_swap = {
 612		.address = 0xbf, // needs testing
 613		.bit     = 4,
 614	},
 615	.cooler_boost = {
 616		.address = 0x98,
 617		.bit     = 7,
 618	},
 619	.shift_mode = {
 620		.address = 0xf2,
 621		.modes = {
 622			{ SM_ECO_NAME,     0xc2 },
 623			{ SM_COMFORT_NAME, 0xc1 },
 624			{ SM_SPORT_NAME,   0xc0 },
 625			{ SM_TURBO_NAME,   0xc4 },
 626			MSI_EC_MODE_NULL
 627		},
 628	},
 629	.super_battery = {
 630		.address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes
 631		.mask    = 0x0f,
 632	},
 633	.fan_mode = {
 634		.address = 0xf4,
 635		.modes = {
 636			{ FM_AUTO_NAME,     0x0d }, // d may not be relevant
 637			{ FM_SILENT_NAME,   0x1d },
 638			{ FM_ADVANCED_NAME, 0x8d },
 639			MSI_EC_MODE_NULL
 640		},
 641	},
 642	.cpu = {
 643		.rt_temp_address       = 0x68,
 644		.rt_fan_speed_address  = 0xc9, // needs testing
 645		.rt_fan_speed_base_min = 0x19,
 646		.rt_fan_speed_base_max = 0x37,
 647		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 648		.bs_fan_speed_base_min = 0x00,
 649		.bs_fan_speed_base_max = 0x0f,
 650	},
 651	.gpu = {
 652		.rt_temp_address      = MSI_EC_ADDR_UNKNOWN,
 653		.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
 654	},
 655	.leds = {
 656		.micmute_led_address = MSI_EC_ADDR_UNSUPP,
 657		.mute_led_address    = 0x2c,
 658		.bit                 = 2,
 659	},
 660	.kbd_bl = {
 661		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN, // ?
 662		.bl_modes         = { 0x00, 0x08 }, // ?
 663		.max_mode         = 1, // ?
 664		.bl_state_address = 0xf3,
 665		.state_base_value = 0x80,
 666		.max_state        = 3,
 667	},
 668};
 669
 670static const char * const ALLOWED_FW_8[] __initconst = {
 671	"14F1EMS1.115",
 672	NULL
 673};
 674
 675static struct msi_ec_conf CONF8 __initdata = {
 676	.allowed_fw = ALLOWED_FW_8,
 677	.charge_control = {
 678		.address      = 0xd7,
 679		.offset_start = 0x8a,
 680		.offset_end   = 0x80,
 681		.range_min    = 0x8a,
 682		.range_max    = 0xe4,
 683	},
 684	.webcam = {
 685		.address       = 0x2e,
 686		.block_address = MSI_EC_ADDR_UNSUPP,
 687		.bit           = 1,
 688	},
 689	.fn_win_swap = {
 690		.address = 0xe8,
 691		.bit     = 4,
 692	},
 693	.cooler_boost = {
 694		.address = 0x98,
 695		.bit     = 7,
 696	},
 697	.shift_mode = {
 698		.address = 0xd2,
 699		.modes = {
 700			{ SM_ECO_NAME,     0xc2 },
 701			{ SM_COMFORT_NAME, 0xc1 },
 702			{ SM_SPORT_NAME,   0xc0 },
 703			MSI_EC_MODE_NULL
 704		},
 705	},
 706	.super_battery = {
 707		.address = 0xeb,
 708		.mask    = 0x0f,
 709	},
 710	.fan_mode = {
 711		.address = 0xd4,
 712		.modes = {
 713			{ FM_AUTO_NAME,     0x0d },
 714			{ FM_SILENT_NAME,   0x1d },
 715			{ FM_BASIC_NAME,    0x4d },
 716			MSI_EC_MODE_NULL
 717		},
 718	},
 719	.cpu = {
 720		.rt_temp_address       = 0x68,
 721		.rt_fan_speed_address  = 0x71,
 722		.rt_fan_speed_base_min = 0x19,
 723		.rt_fan_speed_base_max = 0x37,
 724		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 725		.bs_fan_speed_base_min = 0x00,
 726		.bs_fan_speed_base_max = 0x0f,
 727	},
 728	.gpu = {
 729		.rt_temp_address      = MSI_EC_ADDR_UNKNOWN,
 730		.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
 731	},
 732	.leds = {
 733		.micmute_led_address = MSI_EC_ADDR_UNSUPP,
 734		.mute_led_address    = 0x2d,
 735		.bit                 = 1,
 736	},
 737	.kbd_bl = {
 738		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN, // ?
 739		.bl_modes         = { 0x00, 0x08 }, // ?
 740		.max_mode         = 1, // ?
 741		.bl_state_address = MSI_EC_ADDR_UNSUPP, // not functional
 742		.state_base_value = 0x80,
 743		.max_state        = 3,
 744	},
 745};
 746
 747static const char * const ALLOWED_FW_9[] __initconst = {
 748	"14JKEMS1.104",
 749	NULL
 750};
 751
 752static struct msi_ec_conf CONF9 __initdata = {
 753	.allowed_fw = ALLOWED_FW_9,
 754	.charge_control = {
 755		.address      = 0xef,
 756		.offset_start = 0x8a,
 757		.offset_end   = 0x80,
 758		.range_min    = 0x8a,
 759		.range_max    = 0xe4,
 760	},
 761	.webcam = {
 762		.address       = 0x2e,
 763		.block_address = 0x2f,
 764		.bit           = 1,
 765	},
 766	.fn_win_swap = {
 767		.address = 0xbf,
 768		.bit     = 4,
 769	},
 770	.cooler_boost = {
 771		.address = 0x98,
 772		.bit     = 7,
 773	},
 774	.shift_mode = {
 775		.address = 0xf2,
 776		.modes = {
 777			{ SM_ECO_NAME,     0xc2 },
 778			{ SM_COMFORT_NAME, 0xc1 },
 779			{ SM_SPORT_NAME,   0xc0 },
 780			MSI_EC_MODE_NULL
 781		},
 782	},
 783	.super_battery = {
 784		.address = MSI_EC_ADDR_UNSUPP, // unsupported or enabled by ECO shift
 785		.mask    = 0x0f,
 786	},
 787	.fan_mode = {
 788		.address = 0xf4,
 789		.modes = {
 790			{ FM_AUTO_NAME,     0x0d },
 791			{ FM_SILENT_NAME,   0x1d },
 792			{ FM_ADVANCED_NAME, 0x8d },
 793			MSI_EC_MODE_NULL
 794		},
 795	},
 796	.cpu = {
 797		.rt_temp_address       = 0x68,
 798		.rt_fan_speed_address  = 0x71,
 799		.rt_fan_speed_base_min = 0x00,
 800		.rt_fan_speed_base_max = 0x96,
 801		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 802		.bs_fan_speed_base_min = 0x00,
 803		.bs_fan_speed_base_max = 0x0f,
 804	},
 805	.gpu = {
 806		.rt_temp_address      = MSI_EC_ADDR_UNSUPP,
 807		.rt_fan_speed_address = MSI_EC_ADDR_UNSUPP,
 808	},
 809	.leds = {
 810		.micmute_led_address = 0x2b,
 811		.mute_led_address    = 0x2c,
 812		.bit                 = 2,
 813	},
 814	.kbd_bl = {
 815		.bl_mode_address  = MSI_EC_ADDR_UNSUPP, // not presented in MSI app
 816		.bl_modes         = { 0x00, 0x08 },
 817		.max_mode         = 1,
 818		.bl_state_address = 0xf3,
 819		.state_base_value = 0x80,
 820		.max_state        = 3,
 821	},
 822};
 823
 824static const char * const ALLOWED_FW_10[] __initconst = {
 825	"1582EMS1.107", // GF66 11UC
 826	NULL
 827};
 828
 829static struct msi_ec_conf CONF10 __initdata = {
 830	.allowed_fw = ALLOWED_FW_10,
 831	.charge_control = {
 832		.address      = 0xd7,
 833		.offset_start = 0x8a,
 834		.offset_end   = 0x80,
 835		.range_min    = 0x8a,
 836		.range_max    = 0xe4,
 837	},
 838	.webcam = {
 839		.address       = 0x2e,
 840		.block_address = 0x2f,
 841		.bit           = 1,
 842	},
 843	.fn_win_swap = {
 844		.address = MSI_EC_ADDR_UNSUPP,
 845		.bit     = 4,
 846	},
 847	.cooler_boost = {
 848		.address = 0x98,
 849		.bit     = 7,
 850	},
 851	.shift_mode = {
 852		.address = 0xd2,
 853		.modes = {
 854			{ SM_ECO_NAME,     0xc2 },
 855			{ SM_COMFORT_NAME, 0xc1 },
 856			{ SM_SPORT_NAME,   0xc0 },
 857			{ SM_TURBO_NAME,   0xc4 },
 858			MSI_EC_MODE_NULL
 859		},
 860	},
 861	.super_battery = {
 862		.address = 0xe5,
 863		.mask    = 0x0f,
 864	},
 865	.fan_mode = {
 866		.address = 0xd4,
 867		.modes = {
 868			{ FM_AUTO_NAME,     0x0d },
 869			{ FM_SILENT_NAME,   0x1d },
 870			{ FM_ADVANCED_NAME, 0x8d },
 871			MSI_EC_MODE_NULL
 872		},
 873	},
 874	.cpu = {
 875		.rt_temp_address       = 0x68,
 876		.rt_fan_speed_address  = 0x71, // ?
 877		.rt_fan_speed_base_min = 0x19,
 878		.rt_fan_speed_base_max = 0x37,
 879		.bs_fan_speed_address  = MSI_EC_ADDR_UNKNOWN, // ?
 880		.bs_fan_speed_base_min = 0x00,
 881		.bs_fan_speed_base_max = 0x0f,
 882	},
 883	.gpu = {
 884		.rt_temp_address      = 0x80,
 885		.rt_fan_speed_address = 0x89,
 886	},
 887	.leds = {
 888		.micmute_led_address = 0x2c,
 889		.mute_led_address    = 0x2d,
 890		.bit                 = 1,
 891	},
 892	.kbd_bl = {
 893		.bl_mode_address  = 0x2c,
 894		.bl_modes         = { 0x00, 0x08 },
 895		.max_mode         = 1,
 896		.bl_state_address = 0xd3,
 897		.state_base_value = 0x80,
 898		.max_state        = 3,
 899	},
 900};
 901
 902static const char * const ALLOWED_FW_11[] __initconst = {
 903	"16S6EMS1.111", // Prestige 15 a11scx
 904	"1552EMS1.115", // Modern 15 a11m
 905	NULL
 906};
 907
 908static struct msi_ec_conf CONF11 __initdata = {
 909	.allowed_fw = ALLOWED_FW_11,
 910	.charge_control = {
 911		.address      = 0xd7,
 912		.offset_start = 0x8a,
 913		.offset_end   = 0x80,
 914		.range_min    = 0x8a,
 915		.range_max    = 0xe4,
 916	},
 917	.webcam = {
 918		.address       = 0x2e,
 919		.block_address = MSI_EC_ADDR_UNKNOWN,
 920		.bit           = 1,
 921	},
 922	.fn_win_swap = {
 923		.address = 0xe8,
 924		.bit     = 4,
 925	},
 926	.cooler_boost = {
 927		.address = 0x98,
 928		.bit     = 7,
 929	},
 930	.shift_mode = {
 931		.address = 0xd2,
 932		.modes = {
 933			{ SM_ECO_NAME,     0xc2 },
 934			{ SM_COMFORT_NAME, 0xc1 },
 935			{ SM_SPORT_NAME,   0xc0 },
 936			MSI_EC_MODE_NULL
 937		},
 938	},
 939	.super_battery = {
 940		.address = 0xeb,
 941		.mask = 0x0f,
 942	},
 943	.fan_mode = {
 944		.address = 0xd4,
 945		.modes = {
 946			{ FM_AUTO_NAME,     0x0d },
 947			{ FM_SILENT_NAME,   0x1d },
 948			{ FM_ADVANCED_NAME, 0x4d },
 949			MSI_EC_MODE_NULL
 950		},
 951	},
 952	.cpu = {
 953		.rt_temp_address       = 0x68,
 954		.rt_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 955		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
 956	},
 957	.gpu = {
 958		.rt_temp_address      = MSI_EC_ADDR_UNSUPP,
 959		.rt_fan_speed_address = MSI_EC_ADDR_UNSUPP,
 960	},
 961	.leds = {
 962		.micmute_led_address = 0x2c,
 963		.mute_led_address    = 0x2d,
 964		.bit                 = 1,
 965	},
 966	.kbd_bl = {
 967		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN,
 968		.bl_modes         = {}, // ?
 969		.max_mode         = 1, // ?
 970		.bl_state_address = 0xd3,
 971		.state_base_value = 0x80,
 972		.max_state        = 3,
 973	},
 974};
 975
 976static const char * const ALLOWED_FW_12[] __initconst = {
 977	"16R6EMS1.104", // GF63 Thin 11UC
 978	NULL
 979};
 980
 981static struct msi_ec_conf CONF12 __initdata = {
 982	.allowed_fw = ALLOWED_FW_12,
 983	.charge_control = {
 984		.address      = 0xd7,
 985		.offset_start = 0x8a,
 986		.offset_end   = 0x80,
 987		.range_min    = 0x8a,
 988		.range_max    = 0xe4,
 989	},
 990	.webcam = {
 991		.address       = 0x2e,
 992		.block_address = 0x2f,
 993		.bit           = 1,
 994	},
 995	.fn_win_swap = {
 996		.address = 0xe8,
 997		.bit     = 4,
 998	},
 999	.cooler_boost = {
1000		.address = 0x98,
1001		.bit     = 7,
1002	},
1003	.shift_mode = {
1004		.address = 0xd2,
1005		.modes = {
1006			{ SM_ECO_NAME,     0xc2 },
1007			{ SM_COMFORT_NAME, 0xc1 },
1008			{ SM_SPORT_NAME,   0xc0 },
1009			{ SM_TURBO_NAME,   0xc4 },
1010			MSI_EC_MODE_NULL
1011		},
1012	},
1013	.super_battery = {
1014		.address = MSI_EC_ADDR_UNSUPP, // 0xeb
1015		.mask    = 0x0f, // 00, 0f
1016	},
1017	.fan_mode = {
1018		.address = 0xd4,
1019		.modes = {
1020			{ FM_AUTO_NAME,     0x0d },
1021			{ FM_SILENT_NAME,   0x1d },
1022			{ FM_ADVANCED_NAME, 0x8d },
1023			MSI_EC_MODE_NULL
1024		},
1025	},
1026	.cpu = {
1027		.rt_temp_address       = 0x68,
1028		.rt_fan_speed_address  = 0x71,
1029		.rt_fan_speed_base_min = 0x19,
1030		.rt_fan_speed_base_max = 0x37,
1031		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
1032		.bs_fan_speed_base_min = 0x00,
1033		.bs_fan_speed_base_max = 0x0f,
1034	},
1035	.gpu = {
1036		.rt_temp_address      = MSI_EC_ADDR_UNSUPP,
1037		.rt_fan_speed_address = 0x89,
1038	},
1039	.leds = {
1040		.micmute_led_address = MSI_EC_ADDR_UNSUPP,
1041		.mute_led_address    = 0x2d,
1042		.bit                 = 1,
1043	},
1044	.kbd_bl = {
1045		.bl_mode_address  = MSI_EC_ADDR_UNKNOWN,
1046		.bl_modes         = { 0x00, 0x08 },
1047		.max_mode         = 1,
1048		.bl_state_address = 0xd3,
1049		.state_base_value = 0x80,
1050		.max_state        = 3,
1051	},
1052};
1053
1054static const char * const ALLOWED_FW_13[] __initconst = {
1055	"1594EMS1.109", // MSI Prestige 16 Studio A13VE
1056	NULL
1057};
1058
1059static struct msi_ec_conf CONF13 __initdata = {
1060	.allowed_fw = ALLOWED_FW_13,
1061	.charge_control = {
1062		.address      = 0xd7,
1063		.offset_start = 0x8a,
1064		.offset_end   = 0x80,
1065		.range_min    = 0x8a,
1066		.range_max    = 0xe4,
1067	},
1068	.webcam = {
1069		.address       = 0x2e,
1070		.block_address = 0x2f,
1071		.bit           = 1,
1072	},
1073	.fn_win_swap = {
1074		.address = 0xe8,
1075		.bit     = 4, // 0x00-0x10
1076	},
1077	.cooler_boost = {
1078		.address = 0x98,
1079		.bit     = 7,
1080	},
1081	.shift_mode = {
1082		.address = 0xd2,
1083		.modes = {
1084			{ SM_ECO_NAME,     0xc2 }, // super battery
1085			{ SM_COMFORT_NAME, 0xc1 }, // balanced
1086			{ SM_TURBO_NAME,   0xc4 }, // extreme
1087			MSI_EC_MODE_NULL
1088		},
1089	},
1090	.super_battery = {
1091		.address = MSI_EC_ADDR_UNSUPP,
1092		.mask    = 0x0f, // 00, 0f
1093	},
1094	.fan_mode = {
1095		.address = 0xd4,
1096		.modes = {
1097			{ FM_AUTO_NAME,     0x0d },
1098			{ FM_SILENT_NAME,   0x1d },
1099			{ FM_ADVANCED_NAME, 0x8d },
1100			MSI_EC_MODE_NULL
1101		},
1102	},
1103	.cpu = {
1104		.rt_temp_address       = 0x68,
1105		.rt_fan_speed_address  = 0x71, // 0x0-0x96
1106		.rt_fan_speed_base_min = 0x00,
1107		.rt_fan_speed_base_max = 0x96,
1108		.bs_fan_speed_address  = MSI_EC_ADDR_UNSUPP,
1109		.bs_fan_speed_base_min = 0x00,
1110		.bs_fan_speed_base_max = 0x0f,
1111	},
1112	.gpu = {
1113		.rt_temp_address      = 0x80,
1114		.rt_fan_speed_address = 0x89,
1115	},
1116	.leds = {
1117		.micmute_led_address = 0x2c,
1118		.mute_led_address    = 0x2d,
1119		.bit                 = 1,
1120	},
1121	.kbd_bl = {
1122		.bl_mode_address  = 0x2c, // KB auto turn off
1123		.bl_modes         = { 0x00, 0x08 }, // always on; off after 10 sec
1124		.max_mode         = 1,
1125		.bl_state_address = 0xd3,
1126		.state_base_value = 0x80,
1127		.max_state        = 3,
1128	},
1129};
1130
1131static struct msi_ec_conf *CONFIGS[] __initdata = {
1132	&CONF0,
1133	&CONF1,
1134	&CONF2,
1135	&CONF3,
1136	&CONF4,
1137	&CONF5,
1138	&CONF6,
1139	&CONF7,
1140	&CONF8,
1141	&CONF9,
1142	&CONF10,
1143	&CONF11,
1144	&CONF12,
1145	&CONF13,
1146	NULL
1147};
1148
1149static struct msi_ec_conf conf; // current configuration
1150
1151/*
1152 * Helper functions
1153 */
1154
1155static int ec_read_seq(u8 addr, u8 *buf, u8 len)
1156{
1157	int result;
1158
1159	for (u8 i = 0; i < len; i++) {
1160		result = ec_read(addr + i, buf + i);
1161		if (result < 0)
1162			return result;
1163	}
1164
1165	return 0;
1166}
1167
1168static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1])
1169{
1170	int result;
1171
1172	memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1);
1173	result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS,
1174			     buf,
1175			     MSI_EC_FW_VERSION_LENGTH);
1176	if (result < 0)
1177		return result;
1178
1179	return MSI_EC_FW_VERSION_LENGTH + 1;
1180}
1181
1182/*
1183 * Sysfs power_supply subsystem
1184 */
1185
1186static ssize_t charge_control_threshold_show(u8 offset,
1187					     struct device *device,
1188					     struct device_attribute *attr,
1189					     char *buf)
1190{
1191	u8 rdata;
1192	int result;
1193
1194	result = ec_read(conf.charge_control.address, &rdata);
1195	if (result < 0)
1196		return result;
1197
1198	return sysfs_emit(buf, "%i\n", rdata - offset);
1199}
1200
1201static ssize_t charge_control_threshold_store(u8 offset,
1202					      struct device *dev,
1203					      struct device_attribute *attr,
1204					      const char *buf, size_t count)
1205{
1206	u8 wdata;
1207	int result;
1208
1209	result = kstrtou8(buf, 10, &wdata);
1210	if (result < 0)
1211		return result;
1212
1213	wdata += offset;
1214	if (wdata < conf.charge_control.range_min ||
1215	    wdata > conf.charge_control.range_max)
1216		return -EINVAL;
1217
1218	result = ec_write(conf.charge_control.address, wdata);
1219	if (result < 0)
1220		return result;
1221
1222	return count;
1223}
1224
1225static ssize_t charge_control_start_threshold_show(struct device *device,
1226						   struct device_attribute *attr,
1227						   char *buf)
1228{
1229	return charge_control_threshold_show(conf.charge_control.offset_start,
1230					     device, attr, buf);
1231}
1232
1233static ssize_t charge_control_start_threshold_store(struct device *dev,
1234						    struct device_attribute *attr,
1235						    const char *buf, size_t count)
1236{
1237	return charge_control_threshold_store(conf.charge_control.offset_start,
1238					      dev, attr, buf, count);
1239}
1240
1241static ssize_t charge_control_end_threshold_show(struct device *device,
1242						 struct device_attribute *attr,
1243						 char *buf)
1244{
1245	return charge_control_threshold_show(conf.charge_control.offset_end,
1246					     device, attr, buf);
1247}
1248
1249static ssize_t charge_control_end_threshold_store(struct device *dev,
1250						  struct device_attribute *attr,
1251						  const char *buf, size_t count)
1252{
1253	return charge_control_threshold_store(conf.charge_control.offset_end,
1254					      dev, attr, buf, count);
1255}
1256
1257static DEVICE_ATTR_RW(charge_control_start_threshold);
1258static DEVICE_ATTR_RW(charge_control_end_threshold);
1259
1260static struct attribute *msi_battery_attrs[] = {
1261	&dev_attr_charge_control_start_threshold.attr,
1262	&dev_attr_charge_control_end_threshold.attr,
1263	NULL
1264};
1265
1266ATTRIBUTE_GROUPS(msi_battery);
1267
1268static int msi_battery_add(struct power_supply *battery,
1269			   struct acpi_battery_hook *hook)
1270{
1271	return device_add_groups(&battery->dev, msi_battery_groups);
1272}
1273
1274static int msi_battery_remove(struct power_supply *battery,
1275			      struct acpi_battery_hook *hook)
1276{
1277	device_remove_groups(&battery->dev, msi_battery_groups);
1278	return 0;
1279}
1280
1281static struct acpi_battery_hook battery_hook = {
1282	.add_battery = msi_battery_add,
1283	.remove_battery = msi_battery_remove,
1284	.name = MSI_EC_DRIVER_NAME,
1285};
1286
1287/*
1288 * Module load/unload
1289 */
1290
1291static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = {
1292	{
1293		.matches = {
1294			DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"),
1295		},
1296	},
1297	{
1298		.matches = {
1299			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
1300		},
1301	},
1302	{}
1303};
1304MODULE_DEVICE_TABLE(dmi, msi_dmi_table);
1305
1306static int __init load_configuration(void)
1307{
1308	int result;
1309
1310	u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1];
1311
1312	/* get firmware version */
1313	result = ec_get_firmware_version(fw_version);
1314	if (result < 0)
1315		return result;
1316
1317	/* load the suitable configuration, if exists */
1318	for (int i = 0; CONFIGS[i]; i++) {
1319		if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) {
1320			conf = *CONFIGS[i];
1321			conf.allowed_fw = NULL;
1322			return 0;
1323		}
1324	}
1325
1326	/* config not found */
1327
1328	for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) {
1329		if (!isgraph(fw_version[i])) {
1330			pr_warn("Unable to find a valid firmware version!\n");
1331			return -EOPNOTSUPP;
1332		}
1333	}
1334
1335	pr_warn("Firmware version is not supported: '%s'\n", fw_version);
1336	return -EOPNOTSUPP;
1337}
1338
1339static int __init msi_ec_init(void)
1340{
1341	int result;
1342
1343	result = load_configuration();
1344	if (result < 0)
1345		return result;
1346
1347	battery_hook_register(&battery_hook);
1348	return 0;
1349}
1350
1351static void __exit msi_ec_exit(void)
1352{
1353	battery_hook_unregister(&battery_hook);
1354}
1355
1356MODULE_LICENSE("GPL");
1357MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>");
1358MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>");
1359MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>");
1360MODULE_DESCRIPTION("MSI Embedded Controller");
1361
1362module_init(msi_ec_init);
1363module_exit(msi_ec_exit);