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 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 NULL 280 }; 281 282 static 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_super_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 355 static const char * const ALLOWED_FW_4[] __initconst = { 356 "16V4EMS1.114", 357 NULL 358 }; 359 360 static 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_super_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 432 static const char * const ALLOWED_FW_5[] __initconst = { 433 "158LEMS1.103", 434 "158LEMS1.105", 435 "158LEMS1.106", 436 NULL 437 }; 438 439 static 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_super_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 511 static const char * const ALLOWED_FW_6[] __initconst = { 512 "1542EMS1.102", 513 "1542EMS1.104", 514 NULL 515 }; 516 517 static 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_super_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 590 static const char * const ALLOWED_FW_7[] __initconst = { 591 "17FKEMS1.108", 592 "17FKEMS1.109", 593 "17FKEMS1.10A", 594 NULL 595 }; 596 597 static 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_super_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 670 static struct msi_ec_conf *CONFIGS[] __initdata = { 671 &CONF0, 672 &CONF1, 673 &CONF2, 674 &CONF3, 675 &CONF4, 676 &CONF5, 677 &CONF6, 678 &CONF7, 679 NULL 680 }; 681 682 static struct msi_ec_conf conf; // current configuration 683 684 /* 685 * Helper functions 686 */ 687 688 static int ec_read_seq(u8 addr, u8 *buf, u8 len) 689 { 690 int result; 691 692 for (u8 i = 0; i < len; i++) { 693 result = ec_read(addr + i, buf + i); 694 if (result < 0) 695 return result; 696 } 697 698 return 0; 699 } 700 701 static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) 702 { 703 int result; 704 705 memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); 706 result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, 707 buf, 708 MSI_EC_FW_VERSION_LENGTH); 709 if (result < 0) 710 return result; 711 712 return MSI_EC_FW_VERSION_LENGTH + 1; 713 } 714 715 /* 716 * Sysfs power_supply subsystem 717 */ 718 719 static ssize_t charge_control_threshold_show(u8 offset, 720 struct device *device, 721 struct device_attribute *attr, 722 char *buf) 723 { 724 u8 rdata; 725 int result; 726 727 result = ec_read(conf.charge_control.address, &rdata); 728 if (result < 0) 729 return result; 730 731 return sysfs_emit(buf, "%i\n", rdata - offset); 732 } 733 734 static ssize_t charge_control_threshold_store(u8 offset, 735 struct device *dev, 736 struct device_attribute *attr, 737 const char *buf, size_t count) 738 { 739 u8 wdata; 740 int result; 741 742 result = kstrtou8(buf, 10, &wdata); 743 if (result < 0) 744 return result; 745 746 wdata += offset; 747 if (wdata < conf.charge_control.range_min || 748 wdata > conf.charge_control.range_max) 749 return -EINVAL; 750 751 result = ec_write(conf.charge_control.address, wdata); 752 if (result < 0) 753 return result; 754 755 return count; 756 } 757 758 static ssize_t charge_control_start_threshold_show(struct device *device, 759 struct device_attribute *attr, 760 char *buf) 761 { 762 return charge_control_threshold_show(conf.charge_control.offset_start, 763 device, attr, buf); 764 } 765 766 static ssize_t charge_control_start_threshold_store(struct device *dev, 767 struct device_attribute *attr, 768 const char *buf, size_t count) 769 { 770 return charge_control_threshold_store(conf.charge_control.offset_start, 771 dev, attr, buf, count); 772 } 773 774 static ssize_t charge_control_end_threshold_show(struct device *device, 775 struct device_attribute *attr, 776 char *buf) 777 { 778 return charge_control_threshold_show(conf.charge_control.offset_end, 779 device, attr, buf); 780 } 781 782 static ssize_t charge_control_end_threshold_store(struct device *dev, 783 struct device_attribute *attr, 784 const char *buf, size_t count) 785 { 786 return charge_control_threshold_store(conf.charge_control.offset_end, 787 dev, attr, buf, count); 788 } 789 790 static DEVICE_ATTR_RW(charge_control_start_threshold); 791 static DEVICE_ATTR_RW(charge_control_end_threshold); 792 793 static struct attribute *msi_battery_attrs[] = { 794 &dev_attr_charge_control_start_threshold.attr, 795 &dev_attr_charge_control_end_threshold.attr, 796 NULL 797 }; 798 799 ATTRIBUTE_GROUPS(msi_battery); 800 801 static int msi_battery_add(struct power_supply *battery, 802 struct acpi_battery_hook *hook) 803 { 804 return device_add_groups(&battery->dev, msi_battery_groups); 805 } 806 807 static int msi_battery_remove(struct power_supply *battery, 808 struct acpi_battery_hook *hook) 809 { 810 device_remove_groups(&battery->dev, msi_battery_groups); 811 return 0; 812 } 813 814 static struct acpi_battery_hook battery_hook = { 815 .add_battery = msi_battery_add, 816 .remove_battery = msi_battery_remove, 817 .name = MSI_EC_DRIVER_NAME, 818 }; 819 820 /* 821 * Module load/unload 822 */ 823 824 static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { 825 { 826 .matches = { 827 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), 828 }, 829 }, 830 { 831 .matches = { 832 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 833 }, 834 }, 835 {} 836 }; 837 MODULE_DEVICE_TABLE(dmi, msi_dmi_table); 838 839 static int __init load_configuration(void) 840 { 841 int result; 842 843 u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; 844 845 /* get firmware version */ 846 result = ec_get_firmware_version(fw_version); 847 if (result < 0) 848 return result; 849 850 /* load the suitable configuration, if exists */ 851 for (int i = 0; CONFIGS[i]; i++) { 852 if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { 853 conf = *CONFIGS[i]; 854 conf.allowed_fw = NULL; 855 return 0; 856 } 857 } 858 859 /* config not found */ 860 861 for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { 862 if (!isgraph(fw_version[i])) { 863 pr_warn("Unable to find a valid firmware version!\n"); 864 return -EOPNOTSUPP; 865 } 866 } 867 868 pr_warn("Firmware version is not supported: '%s'\n", fw_version); 869 return -EOPNOTSUPP; 870 } 871 872 static int __init msi_ec_init(void) 873 { 874 int result; 875 876 result = load_configuration(); 877 if (result < 0) 878 return result; 879 880 battery_hook_register(&battery_hook); 881 return 0; 882 } 883 884 static void __exit msi_ec_exit(void) 885 { 886 battery_hook_unregister(&battery_hook); 887 } 888 889 MODULE_LICENSE("GPL"); 890 MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>"); 891 MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>"); 892 MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>"); 893 MODULE_DESCRIPTION("MSI Embedded Controller"); 894 895 module_init(msi_ec_init); 896 module_exit(msi_ec_exit); 897