1364 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3/*4 * msi-ec: MSI laptops' embedded controller driver.5 *6 * This driver allows various MSI laptops' functionalities to be7 * controlled from userspace.8 *9 * It contains EC memory configurations for different firmware versions10 * 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 ": " fmt18 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 NULL45};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_NULL76 },77 },78 .super_battery = {79 .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing80 },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_NULL89 },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 NULL125};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_NULL157 },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_NULL169 },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 NULL202};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_NULL233 },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_NULL247 },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 NULL280};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_NULL311 },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_NULL325 },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 NULL358};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 unknown376 .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_NULL389 },390 },391 .super_battery = { // may be supported, but address is unknown392 .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_NULL402 },403 },404 .cpu = {405 .rt_temp_address = 0x68, // needs testing406 .rt_fan_speed_address = 0x71, // needs testing407 .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 functional427 .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 NULL437};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: reverse454 .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_NULL468 },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_NULL481 },482 },483 .cpu = {484 .rt_temp_address = 0x68, // needs testing485 .rt_fan_speed_address = 0x71, // needs testing486 .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 functional506 .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 NULL515};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: reverse533 .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_NULL547 },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_NULL560 },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 functional585 .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 NULL595};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 testing613 .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_NULL627 },628 },629 .super_battery = {630 .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes631 .mask = 0x0f,632 },633 .fan_mode = {634 .address = 0xf4,635 .modes = {636 { FM_AUTO_NAME, 0x0d }, // d may not be relevant637 { FM_SILENT_NAME, 0x1d },638 { FM_ADVANCED_NAME, 0x8d },639 MSI_EC_MODE_NULL640 },641 },642 .cpu = {643 .rt_temp_address = 0x68,644 .rt_fan_speed_address = 0xc9, // needs testing645 .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 NULL673};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_NULL704 },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_NULL717 },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 functional742 .state_base_value = 0x80,743 .max_state = 3,744 },745};746 747static const char * const ALLOWED_FW_9[] __initconst = {748 "14JKEMS1.104",749 NULL750};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_NULL781 },782 },783 .super_battery = {784 .address = MSI_EC_ADDR_UNSUPP, // unsupported or enabled by ECO shift785 .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_NULL794 },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 app816 .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 11UC826 NULL827};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_NULL859 },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_NULL872 },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 a11scx904 "1552EMS1.115", // Modern 15 a11m905 NULL906};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_NULL937 },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_NULL950 },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 11UC978 NULL979};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_NULL1011 },1012 },1013 .super_battery = {1014 .address = MSI_EC_ADDR_UNSUPP, // 0xeb1015 .mask = 0x0f, // 00, 0f1016 },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_NULL1024 },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 A13VE1056 NULL1057};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-0x101076 },1077 .cooler_boost = {1078 .address = 0x98,1079 .bit = 7,1080 },1081 .shift_mode = {1082 .address = 0xd2,1083 .modes = {1084 { SM_ECO_NAME, 0xc2 }, // super battery1085 { SM_COMFORT_NAME, 0xc1 }, // balanced1086 { SM_TURBO_NAME, 0xc4 }, // extreme1087 MSI_EC_MODE_NULL1088 },1089 },1090 .super_battery = {1091 .address = MSI_EC_ADDR_UNSUPP,1092 .mask = 0x0f, // 00, 0f1093 },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_NULL1101 },1102 },1103 .cpu = {1104 .rt_temp_address = 0x68,1105 .rt_fan_speed_address = 0x71, // 0x0-0x961106 .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 off1123 .bl_modes = { 0x00, 0x08 }, // always on; off after 10 sec1124 .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 NULL1147};1148 1149static struct msi_ec_conf conf; // current configuration1150 1151/*1152 * Helper functions1153 */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 subsystem1184 */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 NULL1264};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/unload1289 */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);1364