| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * button.c - ACPI Button Driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
|---|
| 5 | 6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 11 | | - * the Free Software Foundation; either version 2 of the License, or (at |
|---|
| 12 | | - * your option) any later version. |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 15 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 17 | | - * General Public License for more details. |
|---|
| 18 | | - * |
|---|
| 19 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 20 | 7 | */ |
|---|
| 21 | 8 | |
|---|
| 22 | 9 | #define pr_fmt(fmt) "ACPI: button: " fmt |
|---|
| .. | .. |
|---|
| 37 | 24 | #define PREFIX "ACPI: " |
|---|
| 38 | 25 | |
|---|
| 39 | 26 | #define ACPI_BUTTON_CLASS "button" |
|---|
| 40 | | -#define ACPI_BUTTON_FILE_INFO "info" |
|---|
| 41 | 27 | #define ACPI_BUTTON_FILE_STATE "state" |
|---|
| 42 | 28 | #define ACPI_BUTTON_TYPE_UNKNOWN 0x00 |
|---|
| 43 | 29 | #define ACPI_BUTTON_NOTIFY_STATUS 0x80 |
|---|
| 44 | 30 | |
|---|
| 45 | 31 | #define ACPI_BUTTON_SUBCLASS_POWER "power" |
|---|
| 46 | | -#define ACPI_BUTTON_HID_POWER "PNP0C0C" |
|---|
| 47 | 32 | #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" |
|---|
| 48 | 33 | #define ACPI_BUTTON_TYPE_POWER 0x01 |
|---|
| 49 | 34 | |
|---|
| 50 | 35 | #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" |
|---|
| 51 | | -#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" |
|---|
| 52 | 36 | #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" |
|---|
| 53 | 37 | #define ACPI_BUTTON_TYPE_SLEEP 0x03 |
|---|
| 54 | 38 | |
|---|
| 55 | 39 | #define ACPI_BUTTON_SUBCLASS_LID "lid" |
|---|
| 56 | | -#define ACPI_BUTTON_HID_LID "PNP0C0D" |
|---|
| 57 | 40 | #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" |
|---|
| 58 | 41 | #define ACPI_BUTTON_TYPE_LID 0x05 |
|---|
| 59 | 42 | |
|---|
| 60 | | -#define ACPI_BUTTON_LID_INIT_IGNORE 0x00 |
|---|
| 61 | | -#define ACPI_BUTTON_LID_INIT_OPEN 0x01 |
|---|
| 62 | | -#define ACPI_BUTTON_LID_INIT_METHOD 0x02 |
|---|
| 43 | +enum { |
|---|
| 44 | + ACPI_BUTTON_LID_INIT_IGNORE, |
|---|
| 45 | + ACPI_BUTTON_LID_INIT_OPEN, |
|---|
| 46 | + ACPI_BUTTON_LID_INIT_METHOD, |
|---|
| 47 | + ACPI_BUTTON_LID_INIT_DISABLED, |
|---|
| 48 | +}; |
|---|
| 49 | + |
|---|
| 50 | +static const char * const lid_init_state_str[] = { |
|---|
| 51 | + [ACPI_BUTTON_LID_INIT_IGNORE] = "ignore", |
|---|
| 52 | + [ACPI_BUTTON_LID_INIT_OPEN] = "open", |
|---|
| 53 | + [ACPI_BUTTON_LID_INIT_METHOD] = "method", |
|---|
| 54 | + [ACPI_BUTTON_LID_INIT_DISABLED] = "disabled", |
|---|
| 55 | +}; |
|---|
| 63 | 56 | |
|---|
| 64 | 57 | #define _COMPONENT ACPI_BUTTON_COMPONENT |
|---|
| 65 | 58 | ACPI_MODULE_NAME("button"); |
|---|
| .. | .. |
|---|
| 78 | 71 | }; |
|---|
| 79 | 72 | MODULE_DEVICE_TABLE(acpi, button_device_ids); |
|---|
| 80 | 73 | |
|---|
| 81 | | -/* |
|---|
| 82 | | - * Some devices which don't even have a lid in anyway have a broken _LID |
|---|
| 83 | | - * method (e.g. pointing to a floating gpio pin) causing spurious LID events. |
|---|
| 84 | | - */ |
|---|
| 85 | | -static const struct dmi_system_id lid_blacklst[] = { |
|---|
| 74 | +/* Please keep this list sorted alphabetically by vendor and model */ |
|---|
| 75 | +static const struct dmi_system_id dmi_lid_quirks[] = { |
|---|
| 86 | 76 | { |
|---|
| 87 | | - /* GP-electronic T701 */ |
|---|
| 77 | + /* GP-electronic T701, _LID method points to a floating GPIO */ |
|---|
| 88 | 78 | .matches = { |
|---|
| 89 | 79 | DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), |
|---|
| 90 | 80 | DMI_MATCH(DMI_PRODUCT_NAME, "T701"), |
|---|
| 91 | 81 | DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"), |
|---|
| 92 | 82 | }, |
|---|
| 83 | + .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, |
|---|
| 93 | 84 | }, |
|---|
| 94 | 85 | { |
|---|
| 95 | 86 | /* |
|---|
| .. | .. |
|---|
| 163 | 154 | bool lid_state_initialized; |
|---|
| 164 | 155 | }; |
|---|
| 165 | 156 | |
|---|
| 166 | | -static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); |
|---|
| 167 | 157 | static struct acpi_device *lid_device; |
|---|
| 168 | | -static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
|---|
| 158 | +static long lid_init_state = -1; |
|---|
| 169 | 159 | |
|---|
| 170 | 160 | static unsigned long lid_report_interval __read_mostly = 500; |
|---|
| 171 | 161 | module_param(lid_report_interval, ulong, 0644); |
|---|
| .. | .. |
|---|
| 193 | 183 | static int acpi_lid_notify_state(struct acpi_device *device, int state) |
|---|
| 194 | 184 | { |
|---|
| 195 | 185 | struct acpi_button *button = acpi_driver_data(device); |
|---|
| 196 | | - int ret; |
|---|
| 197 | 186 | ktime_t next_report; |
|---|
| 198 | 187 | bool do_update; |
|---|
| 199 | 188 | |
|---|
| .. | .. |
|---|
| 270 | 259 | button->last_time = ktime_get(); |
|---|
| 271 | 260 | } |
|---|
| 272 | 261 | |
|---|
| 273 | | - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); |
|---|
| 274 | | - if (ret == NOTIFY_DONE) |
|---|
| 275 | | - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, |
|---|
| 276 | | - device); |
|---|
| 277 | | - if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { |
|---|
| 278 | | - /* |
|---|
| 279 | | - * It is also regarded as success if the notifier_chain |
|---|
| 280 | | - * returns NOTIFY_OK or NOTIFY_DONE. |
|---|
| 281 | | - */ |
|---|
| 282 | | - ret = 0; |
|---|
| 283 | | - } |
|---|
| 284 | | - return ret; |
|---|
| 262 | + return 0; |
|---|
| 285 | 263 | } |
|---|
| 286 | 264 | |
|---|
| 287 | 265 | static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, |
|---|
| .. | .. |
|---|
| 378 | 356 | /* -------------------------------------------------------------------------- |
|---|
| 379 | 357 | Driver Interface |
|---|
| 380 | 358 | -------------------------------------------------------------------------- */ |
|---|
| 381 | | -int acpi_lid_notifier_register(struct notifier_block *nb) |
|---|
| 382 | | -{ |
|---|
| 383 | | - return blocking_notifier_chain_register(&acpi_lid_notifier, nb); |
|---|
| 384 | | -} |
|---|
| 385 | | -EXPORT_SYMBOL(acpi_lid_notifier_register); |
|---|
| 386 | | - |
|---|
| 387 | | -int acpi_lid_notifier_unregister(struct notifier_block *nb) |
|---|
| 388 | | -{ |
|---|
| 389 | | - return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb); |
|---|
| 390 | | -} |
|---|
| 391 | | -EXPORT_SYMBOL(acpi_lid_notifier_unregister); |
|---|
| 392 | | - |
|---|
| 393 | 359 | int acpi_lid_open(void) |
|---|
| 394 | 360 | { |
|---|
| 395 | 361 | if (!lid_device) |
|---|
| .. | .. |
|---|
| 441 | 407 | switch (event) { |
|---|
| 442 | 408 | case ACPI_FIXED_HARDWARE_EVENT: |
|---|
| 443 | 409 | event = ACPI_BUTTON_NOTIFY_STATUS; |
|---|
| 444 | | - /* fall through */ |
|---|
| 410 | + fallthrough; |
|---|
| 445 | 411 | case ACPI_BUTTON_NOTIFY_STATUS: |
|---|
| 446 | 412 | input = button->input; |
|---|
| 447 | 413 | if (button->type == ACPI_BUTTON_TYPE_LID) { |
|---|
| .. | .. |
|---|
| 519 | 485 | char *name, *class; |
|---|
| 520 | 486 | int error; |
|---|
| 521 | 487 | |
|---|
| 522 | | - if (!strcmp(hid, ACPI_BUTTON_HID_LID) && dmi_check_system(lid_blacklst)) |
|---|
| 488 | + if (!strcmp(hid, ACPI_BUTTON_HID_LID) && |
|---|
| 489 | + lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED) |
|---|
| 523 | 490 | return -ENODEV; |
|---|
| 524 | 491 | |
|---|
| 525 | 492 | button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 625 | 592 | static int param_set_lid_init_state(const char *val, |
|---|
| 626 | 593 | const struct kernel_param *kp) |
|---|
| 627 | 594 | { |
|---|
| 628 | | - int result = 0; |
|---|
| 595 | + int i; |
|---|
| 629 | 596 | |
|---|
| 630 | | - if (!strncmp(val, "open", sizeof("open") - 1)) { |
|---|
| 631 | | - lid_init_state = ACPI_BUTTON_LID_INIT_OPEN; |
|---|
| 632 | | - pr_info("Notify initial lid state as open\n"); |
|---|
| 633 | | - } else if (!strncmp(val, "method", sizeof("method") - 1)) { |
|---|
| 634 | | - lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
|---|
| 635 | | - pr_info("Notify initial lid state with _LID return value\n"); |
|---|
| 636 | | - } else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) { |
|---|
| 637 | | - lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE; |
|---|
| 638 | | - pr_info("Do not notify initial lid state\n"); |
|---|
| 639 | | - } else |
|---|
| 640 | | - result = -EINVAL; |
|---|
| 641 | | - return result; |
|---|
| 597 | + i = sysfs_match_string(lid_init_state_str, val); |
|---|
| 598 | + if (i < 0) |
|---|
| 599 | + return i; |
|---|
| 600 | + |
|---|
| 601 | + lid_init_state = i; |
|---|
| 602 | + pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]); |
|---|
| 603 | + return 0; |
|---|
| 642 | 604 | } |
|---|
| 643 | 605 | |
|---|
| 644 | | -static int param_get_lid_init_state(char *buffer, |
|---|
| 645 | | - const struct kernel_param *kp) |
|---|
| 606 | +static int param_get_lid_init_state(char *buf, const struct kernel_param *kp) |
|---|
| 646 | 607 | { |
|---|
| 647 | | - switch (lid_init_state) { |
|---|
| 648 | | - case ACPI_BUTTON_LID_INIT_OPEN: |
|---|
| 649 | | - return sprintf(buffer, "open"); |
|---|
| 650 | | - case ACPI_BUTTON_LID_INIT_METHOD: |
|---|
| 651 | | - return sprintf(buffer, "method"); |
|---|
| 652 | | - case ACPI_BUTTON_LID_INIT_IGNORE: |
|---|
| 653 | | - return sprintf(buffer, "ignore"); |
|---|
| 654 | | - default: |
|---|
| 655 | | - return sprintf(buffer, "invalid"); |
|---|
| 656 | | - } |
|---|
| 657 | | - return 0; |
|---|
| 608 | + int i, c = 0; |
|---|
| 609 | + |
|---|
| 610 | + for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++) |
|---|
| 611 | + if (i == lid_init_state) |
|---|
| 612 | + c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]); |
|---|
| 613 | + else |
|---|
| 614 | + c += sprintf(buf + c, "%s ", lid_init_state_str[i]); |
|---|
| 615 | + |
|---|
| 616 | + buf[c - 1] = '\n'; /* Replace the final space with a newline */ |
|---|
| 617 | + |
|---|
| 618 | + return c; |
|---|
| 658 | 619 | } |
|---|
| 659 | 620 | |
|---|
| 660 | 621 | module_param_call(lid_init_state, |
|---|
| .. | .. |
|---|
| 664 | 625 | |
|---|
| 665 | 626 | static int acpi_button_register_driver(struct acpi_driver *driver) |
|---|
| 666 | 627 | { |
|---|
| 628 | + const struct dmi_system_id *dmi_id; |
|---|
| 629 | + |
|---|
| 630 | + if (lid_init_state == -1) { |
|---|
| 631 | + dmi_id = dmi_first_match(dmi_lid_quirks); |
|---|
| 632 | + if (dmi_id) |
|---|
| 633 | + lid_init_state = (long)dmi_id->driver_data; |
|---|
| 634 | + else |
|---|
| 635 | + lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
|---|
| 636 | + } |
|---|
| 637 | + |
|---|
| 667 | 638 | /* |
|---|
| 668 | 639 | * Modules such as nouveau.ko and i915.ko have a link time dependency |
|---|
| 669 | 640 | * on acpi_lid_open(), and would therefore not be loadable on ACPI |
|---|