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 static const char *const SM_ECO_NAME = "eco"; 31 static const char *const SM_COMFORT_NAME = "comfort"; 32 static const char *const SM_SPORT_NAME = "sport"; 33 static const char *const SM_TURBO_NAME = "turbo"; 34 35 static const char *const FM_AUTO_NAME = "auto"; 36 static const char *const FM_SILENT_NAME = "silent"; 37 static const char *const FM_BASIC_NAME = "basic"; 38 static const char *const FM_ADVANCED_NAME = "advanced"; 39 40 static const char * const ALLOWED_FW_0[] __initconst = { 41 "14C1EMS1.012", 42 "14C1EMS1.101", 43 "14C1EMS1.102", 44 NULL 45 }; 46 47 static 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_super_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 119 static const char * const ALLOWED_FW_1[] __initconst = { 120 "17F2EMS1.103", 121 "17F2EMS1.104", 122 "17F2EMS1.106", 123 "17F2EMS1.107", 124 NULL 125 }; 126 127 static 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_super_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 199 static const char * const ALLOWED_FW_2[] __initconst = { 200 "1552EMS1.118", 201 NULL 202 }; 203 204 static 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_super_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 277 static const char * const ALLOWED_FW_3[] __initconst = { 278 "1592EMS1.111", 279 "E1592IMS.10C", 280 NULL 281 }; 282 283 static struct msi_ec_conf CONF3 __initdata = { 284 .allowed_fw = ALLOWED_FW_3, 285 .charge_control = { 286 .address = 0xef, 287 .offset_start = 0x8a, 288 .offset_end = 0x80, 289 .range_min = 0x8a, 290 .range_max = 0xe4, 291 }, 292 .webcam = { 293 .address = 0x2e, 294 .block_address = 0x2f, 295 .bit = 1, 296 }, 297 .fn_super_swap = { 298 .address = 0xe8, 299 .bit = 4, 300 }, 301 .cooler_boost = { 302 .address = 0x98, 303 .bit = 7, 304 }, 305 .shift_mode = { 306 .address = 0xd2, 307 .modes = { 308 { SM_ECO_NAME, 0xc2 }, 309 { SM_COMFORT_NAME, 0xc1 }, 310 { SM_SPORT_NAME, 0xc0 }, 311 MSI_EC_MODE_NULL 312 }, 313 }, 314 .super_battery = { 315 .address = 0xeb, 316 .mask = 0x0f, 317 }, 318 .fan_mode = { 319 .address = 0xd4, 320 .modes = { 321 { FM_AUTO_NAME, 0x0d }, 322 { FM_SILENT_NAME, 0x1d }, 323 { FM_BASIC_NAME, 0x4d }, 324 { FM_ADVANCED_NAME, 0x8d }, 325 MSI_EC_MODE_NULL 326 }, 327 }, 328 .cpu = { 329 .rt_temp_address = 0x68, 330 .rt_fan_speed_address = 0xc9, 331 .rt_fan_speed_base_min = 0x19, 332 .rt_fan_speed_base_max = 0x37, 333 .bs_fan_speed_address = 0x89, // ? 334 .bs_fan_speed_base_min = 0x00, 335 .bs_fan_speed_base_max = 0x0f, 336 }, 337 .gpu = { 338 .rt_temp_address = 0x80, 339 .rt_fan_speed_address = 0x89, 340 }, 341 .leds = { 342 .micmute_led_address = 0x2b, 343 .mute_led_address = 0x2c, 344 .bit = 1, 345 }, 346 .kbd_bl = { 347 .bl_mode_address = 0x2c, // ? 348 .bl_modes = { 0x00, 0x08 }, // ? 349 .max_mode = 1, // ? 350 .bl_state_address = 0xd3, 351 .state_base_value = 0x80, 352 .max_state = 3, 353 }, 354 }; 355 356 static const char * const ALLOWED_FW_4[] __initconst = { 357 "16V4EMS1.114", 358 NULL 359 }; 360 361 static struct msi_ec_conf CONF4 __initdata = { 362 .allowed_fw = ALLOWED_FW_4, 363 .charge_control = { 364 .address = 0xd7, 365 .offset_start = 0x8a, 366 .offset_end = 0x80, 367 .range_min = 0x8a, 368 .range_max = 0xe4, 369 }, 370 .webcam = { 371 .address = 0x2e, 372 .block_address = 0x2f, 373 .bit = 1, 374 }, 375 .fn_super_swap = { 376 .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown 377 .bit = 4, 378 }, 379 .cooler_boost = { 380 .address = 0x98, 381 .bit = 7, 382 }, 383 .shift_mode = { 384 .address = 0xd2, 385 .modes = { 386 { SM_ECO_NAME, 0xc2 }, 387 { SM_COMFORT_NAME, 0xc1 }, 388 { SM_SPORT_NAME, 0xc0 }, 389 MSI_EC_MODE_NULL 390 }, 391 }, 392 .super_battery = { // may be supported, but address is unknown 393 .address = MSI_EC_ADDR_UNKNOWN, 394 .mask = 0x0f, 395 }, 396 .fan_mode = { 397 .address = 0xd4, 398 .modes = { 399 { FM_AUTO_NAME, 0x0d }, 400 { FM_SILENT_NAME, 0x1d }, 401 { FM_ADVANCED_NAME, 0x8d }, 402 MSI_EC_MODE_NULL 403 }, 404 }, 405 .cpu = { 406 .rt_temp_address = 0x68, // needs testing 407 .rt_fan_speed_address = 0x71, // needs testing 408 .rt_fan_speed_base_min = 0x19, 409 .rt_fan_speed_base_max = 0x37, 410 .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 411 .bs_fan_speed_base_min = 0x00, 412 .bs_fan_speed_base_max = 0x0f, 413 }, 414 .gpu = { 415 .rt_temp_address = 0x80, 416 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 417 }, 418 .leds = { 419 .micmute_led_address = MSI_EC_ADDR_UNKNOWN, 420 .mute_led_address = MSI_EC_ADDR_UNKNOWN, 421 .bit = 1, 422 }, 423 .kbd_bl = { 424 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 425 .bl_modes = { 0x00, 0x08 }, // ? 426 .max_mode = 1, // ? 427 .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional 428 .state_base_value = 0x80, 429 .max_state = 3, 430 }, 431 }; 432 433 static const char * const ALLOWED_FW_5[] __initconst = { 434 "158LEMS1.103", 435 "158LEMS1.105", 436 "158LEMS1.106", 437 NULL 438 }; 439 440 static struct msi_ec_conf CONF5 __initdata = { 441 .allowed_fw = ALLOWED_FW_5, 442 .charge_control = { 443 .address = 0xef, 444 .offset_start = 0x8a, 445 .offset_end = 0x80, 446 .range_min = 0x8a, 447 .range_max = 0xe4, 448 }, 449 .webcam = { 450 .address = 0x2e, 451 .block_address = 0x2f, 452 .bit = 1, 453 }, 454 .fn_super_swap = { // todo: reverse 455 .address = 0xbf, 456 .bit = 4, 457 }, 458 .cooler_boost = { 459 .address = 0x98, 460 .bit = 7, 461 }, 462 .shift_mode = { 463 .address = 0xf2, 464 .modes = { 465 { SM_ECO_NAME, 0xc2 }, 466 { SM_COMFORT_NAME, 0xc1 }, 467 { SM_TURBO_NAME, 0xc4 }, 468 MSI_EC_MODE_NULL 469 }, 470 }, 471 .super_battery = { // unsupported? 472 .address = MSI_EC_ADDR_UNKNOWN, 473 .mask = 0x0f, 474 }, 475 .fan_mode = { 476 .address = 0xf4, 477 .modes = { 478 { FM_AUTO_NAME, 0x0d }, 479 { FM_SILENT_NAME, 0x1d }, 480 { FM_ADVANCED_NAME, 0x8d }, 481 MSI_EC_MODE_NULL 482 }, 483 }, 484 .cpu = { 485 .rt_temp_address = 0x68, // needs testing 486 .rt_fan_speed_address = 0x71, // needs testing 487 .rt_fan_speed_base_min = 0x19, 488 .rt_fan_speed_base_max = 0x37, 489 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 490 .bs_fan_speed_base_min = 0x00, 491 .bs_fan_speed_base_max = 0x0f, 492 }, 493 .gpu = { 494 .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 495 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 496 }, 497 .leds = { 498 .micmute_led_address = 0x2b, 499 .mute_led_address = 0x2c, 500 .bit = 2, 501 }, 502 .kbd_bl = { 503 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 504 .bl_modes = { 0x00, 0x08 }, // ? 505 .max_mode = 1, // ? 506 .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 507 .state_base_value = 0x80, 508 .max_state = 3, 509 }, 510 }; 511 512 static const char * const ALLOWED_FW_6[] __initconst = { 513 "1542EMS1.102", 514 "1542EMS1.104", 515 NULL 516 }; 517 518 static struct msi_ec_conf CONF6 __initdata = { 519 .allowed_fw = ALLOWED_FW_6, 520 .charge_control = { 521 .address = 0xef, 522 .offset_start = 0x8a, 523 .offset_end = 0x80, 524 .range_min = 0x8a, 525 .range_max = 0xe4, 526 }, 527 .webcam = { 528 .address = 0x2e, 529 .block_address = MSI_EC_ADDR_UNSUPP, 530 .bit = 1, 531 }, 532 .fn_super_swap = { 533 .address = 0xbf, // todo: reverse 534 .bit = 4, 535 }, 536 .cooler_boost = { 537 .address = 0x98, 538 .bit = 7, 539 }, 540 .shift_mode = { 541 .address = 0xf2, 542 .modes = { 543 { SM_ECO_NAME, 0xc2 }, 544 { SM_COMFORT_NAME, 0xc1 }, 545 { SM_SPORT_NAME, 0xc0 }, 546 { SM_TURBO_NAME, 0xc4 }, 547 MSI_EC_MODE_NULL 548 }, 549 }, 550 .super_battery = { 551 .address = 0xd5, 552 .mask = 0x0f, 553 }, 554 .fan_mode = { 555 .address = 0xf4, 556 .modes = { 557 { FM_AUTO_NAME, 0x0d }, 558 { FM_SILENT_NAME, 0x1d }, 559 { FM_ADVANCED_NAME, 0x8d }, 560 MSI_EC_MODE_NULL 561 }, 562 }, 563 .cpu = { 564 .rt_temp_address = 0x68, 565 .rt_fan_speed_address = 0xc9, 566 .rt_fan_speed_base_min = 0x19, 567 .rt_fan_speed_base_max = 0x37, 568 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 569 .bs_fan_speed_base_min = 0x00, 570 .bs_fan_speed_base_max = 0x0f, 571 }, 572 .gpu = { 573 .rt_temp_address = 0x80, 574 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 575 }, 576 .leds = { 577 .micmute_led_address = MSI_EC_ADDR_UNSUPP, 578 .mute_led_address = MSI_EC_ADDR_UNSUPP, 579 .bit = 2, 580 }, 581 .kbd_bl = { 582 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 583 .bl_modes = { 0x00, 0x08 }, // ? 584 .max_mode = 1, // ? 585 .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 586 .state_base_value = 0x80, 587 .max_state = 3, 588 }, 589 }; 590 591 static const char * const ALLOWED_FW_7[] __initconst = { 592 "17FKEMS1.108", 593 "17FKEMS1.109", 594 "17FKEMS1.10A", 595 NULL 596 }; 597 598 static struct msi_ec_conf CONF7 __initdata = { 599 .allowed_fw = ALLOWED_FW_7, 600 .charge_control = { 601 .address = 0xef, 602 .offset_start = 0x8a, 603 .offset_end = 0x80, 604 .range_min = 0x8a, 605 .range_max = 0xe4, 606 }, 607 .webcam = { 608 .address = 0x2e, 609 .block_address = MSI_EC_ADDR_UNSUPP, 610 .bit = 1, 611 }, 612 .fn_super_swap = { 613 .address = 0xbf, // needs testing 614 .bit = 4, 615 }, 616 .cooler_boost = { 617 .address = 0x98, 618 .bit = 7, 619 }, 620 .shift_mode = { 621 .address = 0xf2, 622 .modes = { 623 { SM_ECO_NAME, 0xc2 }, 624 { SM_COMFORT_NAME, 0xc1 }, 625 { SM_SPORT_NAME, 0xc0 }, 626 { SM_TURBO_NAME, 0xc4 }, 627 MSI_EC_MODE_NULL 628 }, 629 }, 630 .super_battery = { 631 .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes 632 .mask = 0x0f, 633 }, 634 .fan_mode = { 635 .address = 0xf4, 636 .modes = { 637 { FM_AUTO_NAME, 0x0d }, // d may not be relevant 638 { FM_SILENT_NAME, 0x1d }, 639 { FM_ADVANCED_NAME, 0x8d }, 640 MSI_EC_MODE_NULL 641 }, 642 }, 643 .cpu = { 644 .rt_temp_address = 0x68, 645 .rt_fan_speed_address = 0xc9, // needs testing 646 .rt_fan_speed_base_min = 0x19, 647 .rt_fan_speed_base_max = 0x37, 648 .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 649 .bs_fan_speed_base_min = 0x00, 650 .bs_fan_speed_base_max = 0x0f, 651 }, 652 .gpu = { 653 .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 654 .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 655 }, 656 .leds = { 657 .micmute_led_address = MSI_EC_ADDR_UNSUPP, 658 .mute_led_address = 0x2c, 659 .bit = 2, 660 }, 661 .kbd_bl = { 662 .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 663 .bl_modes = { 0x00, 0x08 }, // ? 664 .max_mode = 1, // ? 665 .bl_state_address = 0xf3, 666 .state_base_value = 0x80, 667 .max_state = 3, 668 }, 669 }; 670 671 static struct msi_ec_conf *CONFIGS[] __initdata = { 672 &CONF0, 673 &CONF1, 674 &CONF2, 675 &CONF3, 676 &CONF4, 677 &CONF5, 678 &CONF6, 679 &CONF7, 680 NULL 681 }; 682 683 static struct msi_ec_conf conf; // current configuration 684 685 /* 686 * Helper functions 687 */ 688 689 static int ec_read_seq(u8 addr, u8 *buf, u8 len) 690 { 691 int result; 692 693 for (u8 i = 0; i < len; i++) { 694 result = ec_read(addr + i, buf + i); 695 if (result < 0) 696 return result; 697 } 698 699 return 0; 700 } 701 702 static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) 703 { 704 int result; 705 706 memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); 707 result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, 708 buf, 709 MSI_EC_FW_VERSION_LENGTH); 710 if (result < 0) 711 return result; 712 713 return MSI_EC_FW_VERSION_LENGTH + 1; 714 } 715 716 /* 717 * Sysfs power_supply subsystem 718 */ 719 720 static ssize_t charge_control_threshold_show(u8 offset, 721 struct device *device, 722 struct device_attribute *attr, 723 char *buf) 724 { 725 u8 rdata; 726 int result; 727 728 result = ec_read(conf.charge_control.address, &rdata); 729 if (result < 0) 730 return result; 731 732 return sysfs_emit(buf, "%i\n", rdata - offset); 733 } 734 735 static ssize_t charge_control_threshold_store(u8 offset, 736 struct device *dev, 737 struct device_attribute *attr, 738 const char *buf, size_t count) 739 { 740 u8 wdata; 741 int result; 742 743 result = kstrtou8(buf, 10, &wdata); 744 if (result < 0) 745 return result; 746 747 wdata += offset; 748 if (wdata < conf.charge_control.range_min || 749 wdata > conf.charge_control.range_max) 750 return -EINVAL; 751 752 result = ec_write(conf.charge_control.address, wdata); 753 if (result < 0) 754 return result; 755 756 return count; 757 } 758 759 static ssize_t charge_control_start_threshold_show(struct device *device, 760 struct device_attribute *attr, 761 char *buf) 762 { 763 return charge_control_threshold_show(conf.charge_control.offset_start, 764 device, attr, buf); 765 } 766 767 static ssize_t charge_control_start_threshold_store(struct device *dev, 768 struct device_attribute *attr, 769 const char *buf, size_t count) 770 { 771 return charge_control_threshold_store(conf.charge_control.offset_start, 772 dev, attr, buf, count); 773 } 774 775 static ssize_t charge_control_end_threshold_show(struct device *device, 776 struct device_attribute *attr, 777 char *buf) 778 { 779 return charge_control_threshold_show(conf.charge_control.offset_end, 780 device, attr, buf); 781 } 782 783 static ssize_t charge_control_end_threshold_store(struct device *dev, 784 struct device_attribute *attr, 785 const char *buf, size_t count) 786 { 787 return charge_control_threshold_store(conf.charge_control.offset_end, 788 dev, attr, buf, count); 789 } 790 791 static DEVICE_ATTR_RW(charge_control_start_threshold); 792 static DEVICE_ATTR_RW(charge_control_end_threshold); 793 794 static struct attribute *msi_battery_attrs[] = { 795 &dev_attr_charge_control_start_threshold.attr, 796 &dev_attr_charge_control_end_threshold.attr, 797 NULL 798 }; 799 800 ATTRIBUTE_GROUPS(msi_battery); 801 802 static int msi_battery_add(struct power_supply *battery, 803 struct acpi_battery_hook *hook) 804 { 805 return device_add_groups(&battery->dev, msi_battery_groups); 806 } 807 808 static int msi_battery_remove(struct power_supply *battery, 809 struct acpi_battery_hook *hook) 810 { 811 device_remove_groups(&battery->dev, msi_battery_groups); 812 return 0; 813 } 814 815 static struct acpi_battery_hook battery_hook = { 816 .add_battery = msi_battery_add, 817 .remove_battery = msi_battery_remove, 818 .name = MSI_EC_DRIVER_NAME, 819 }; 820 821 /* 822 * Module load/unload 823 */ 824 825 static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { 826 { 827 .matches = { 828 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), 829 }, 830 }, 831 { 832 .matches = { 833 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 834 }, 835 }, 836 {} 837 }; 838 MODULE_DEVICE_TABLE(dmi, msi_dmi_table); 839 840 static int __init load_configuration(void) 841 { 842 int result; 843 844 u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; 845 846 /* get firmware version */ 847 result = ec_get_firmware_version(fw_version); 848 if (result < 0) 849 return result; 850 851 /* load the suitable configuration, if exists */ 852 for (int i = 0; CONFIGS[i]; i++) { 853 if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { 854 conf = *CONFIGS[i]; 855 conf.allowed_fw = NULL; 856 return 0; 857 } 858 } 859 860 /* config not found */ 861 862 for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { 863 if (!isgraph(fw_version[i])) { 864 pr_warn("Unable to find a valid firmware version!\n"); 865 return -EOPNOTSUPP; 866 } 867 } 868 869 pr_warn("Firmware version is not supported: '%s'\n", fw_version); 870 return -EOPNOTSUPP; 871 } 872 873 static int __init msi_ec_init(void) 874 { 875 int result; 876 877 result = load_configuration(); 878 if (result < 0) 879 return result; 880 881 battery_hook_register(&battery_hook); 882 return 0; 883 } 884 885 static void __exit msi_ec_exit(void) 886 { 887 battery_hook_unregister(&battery_hook); 888 } 889 890 MODULE_LICENSE("GPL"); 891 MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>"); 892 MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>"); 893 MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>"); 894 MODULE_DESCRIPTION("MSI Embedded Controller"); 895 896 module_init(msi_ec_init); 897 module_exit(msi_ec_exit); 898