Commit afa7c886578ce264d9b66d4bcb1fea51fac47925

Authored by Corentin Chary
Committed by Matthew Garrett
1 parent bc40cce201

eeepc-wmi: add hotplug code for Eeepc 1000H

Implement wireless like hotplug handling (code stolen from eeepc-laptop).

Reminder: on some models rfkill is implemented by logically unplugging the
wireless card from the PCI bus. Despite sending ACPI notifications, this does
not appear to be implemented using standard ACPI hotplug - nor does the
firmware provide the _OSC method required to support native PCIe hotplug.
The only sensible choice appears to be to handle the hotplugging directly in
the platform driver.

Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Matthew Garrett <mjg@redhat.com>

Showing 1 changed file with 273 additions and 1 deletions Side-by-side Diff

drivers/platform/x86/eeepc-wmi.c
... ... @@ -37,9 +37,12 @@
37 37 #include <linux/backlight.h>
38 38 #include <linux/leds.h>
39 39 #include <linux/rfkill.h>
  40 +#include <linux/pci.h>
  41 +#include <linux/pci_hotplug.h>
40 42 #include <linux/debugfs.h>
41 43 #include <linux/seq_file.h>
42 44 #include <linux/platform_device.h>
  45 +#include <linux/dmi.h>
43 46 #include <acpi/acpi_bus.h>
44 47 #include <acpi/acpi_drivers.h>
45 48  
... ... @@ -72,6 +75,14 @@
72 75 #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
73 76 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019
74 77  
  78 +static bool hotplug_wireless;
  79 +
  80 +module_param(hotplug_wireless, bool, 0444);
  81 +MODULE_PARM_DESC(hotplug_wireless,
  82 + "Enable hotplug for wireless device. "
  83 + "If your laptop needs that, please report to "
  84 + "acpi4asus-user@lists.sourceforge.net.");
  85 +
75 86 static const struct key_entry eeepc_wmi_keymap[] = {
76 87 /* Sleep already handled via generic ACPI code */
77 88 { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
... ... @@ -109,6 +120,8 @@
109 120 };
110 121  
111 122 struct eeepc_wmi {
  123 + bool hotplug_wireless;
  124 +
112 125 struct input_dev *inputdev;
113 126 struct backlight_device *backlight_device;
114 127 struct platform_device *platform_device;
... ... @@ -122,6 +135,9 @@
122 135 struct rfkill *bluetooth_rfkill;
123 136 struct rfkill *wwan3g_rfkill;
124 137  
  138 + struct hotplug_slot *hotplug_slot;
  139 + struct mutex hotplug_lock;
  140 +
125 141 struct eeepc_wmi_debug debug;
126 142 };
127 143  
... ... @@ -177,7 +193,8 @@
177 193 u32 tmp;
178 194  
179 195 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
180   - 1, EEEPC_WMI_METHODID_DSTS, &input, &output);
  196 + 1, EEEPC_WMI_METHODID_DSTS,
  197 + &input, &output);
181 198  
182 199 if (ACPI_FAILURE(status))
183 200 return status;
... ... @@ -334,6 +351,206 @@
334 351 }
335 352  
336 353 /*
  354 + * PCI hotplug (for wlan rfkill)
  355 + */
  356 +static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc)
  357 +{
  358 + u32 retval;
  359 + acpi_status status;
  360 +
  361 + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval);
  362 +
  363 + if (ACPI_FAILURE(status))
  364 + return false;
  365 +
  366 + return !(retval & 0x1);
  367 +}
  368 +
  369 +static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc)
  370 +{
  371 + struct pci_dev *dev;
  372 + struct pci_bus *bus;
  373 + bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
  374 + bool absent;
  375 + u32 l;
  376 +
  377 + if (eeepc->wlan_rfkill)
  378 + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
  379 +
  380 + mutex_lock(&eeepc->hotplug_lock);
  381 +
  382 + if (eeepc->hotplug_slot) {
  383 + bus = pci_find_bus(0, 1);
  384 + if (!bus) {
  385 + pr_warning("Unable to find PCI bus 1?\n");
  386 + goto out_unlock;
  387 + }
  388 +
  389 + if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
  390 + pr_err("Unable to read PCI config space?\n");
  391 + goto out_unlock;
  392 + }
  393 + absent = (l == 0xffffffff);
  394 +
  395 + if (blocked != absent) {
  396 + pr_warning("BIOS says wireless lan is %s, "
  397 + "but the pci device is %s\n",
  398 + blocked ? "blocked" : "unblocked",
  399 + absent ? "absent" : "present");
  400 + pr_warning("skipped wireless hotplug as probably "
  401 + "inappropriate for this model\n");
  402 + goto out_unlock;
  403 + }
  404 +
  405 + if (!blocked) {
  406 + dev = pci_get_slot(bus, 0);
  407 + if (dev) {
  408 + /* Device already present */
  409 + pci_dev_put(dev);
  410 + goto out_unlock;
  411 + }
  412 + dev = pci_scan_single_device(bus, 0);
  413 + if (dev) {
  414 + pci_bus_assign_resources(bus);
  415 + if (pci_bus_add_device(dev))
  416 + pr_err("Unable to hotplug wifi\n");
  417 + }
  418 + } else {
  419 + dev = pci_get_slot(bus, 0);
  420 + if (dev) {
  421 + pci_remove_bus_device(dev);
  422 + pci_dev_put(dev);
  423 + }
  424 + }
  425 + }
  426 +
  427 +out_unlock:
  428 + mutex_unlock(&eeepc->hotplug_lock);
  429 +}
  430 +
  431 +static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
  432 +{
  433 + struct eeepc_wmi *eeepc = data;
  434 +
  435 + if (event != ACPI_NOTIFY_BUS_CHECK)
  436 + return;
  437 +
  438 + eeepc_rfkill_hotplug(eeepc);
  439 +}
  440 +
  441 +static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc,
  442 + char *node)
  443 +{
  444 + acpi_status status;
  445 + acpi_handle handle;
  446 +
  447 + status = acpi_get_handle(NULL, node, &handle);
  448 +
  449 + if (ACPI_SUCCESS(status)) {
  450 + status = acpi_install_notify_handler(handle,
  451 + ACPI_SYSTEM_NOTIFY,
  452 + eeepc_rfkill_notify,
  453 + eeepc);
  454 + if (ACPI_FAILURE(status))
  455 + pr_warning("Failed to register notify on %s\n", node);
  456 + } else
  457 + return -ENODEV;
  458 +
  459 + return 0;
  460 +}
  461 +
  462 +static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc,
  463 + char *node)
  464 +{
  465 + acpi_status status = AE_OK;
  466 + acpi_handle handle;
  467 +
  468 + status = acpi_get_handle(NULL, node, &handle);
  469 +
  470 + if (ACPI_SUCCESS(status)) {
  471 + status = acpi_remove_notify_handler(handle,
  472 + ACPI_SYSTEM_NOTIFY,
  473 + eeepc_rfkill_notify);
  474 + if (ACPI_FAILURE(status))
  475 + pr_err("Error removing rfkill notify handler %s\n",
  476 + node);
  477 + }
  478 +}
  479 +
  480 +static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
  481 + u8 *value)
  482 +{
  483 + u32 retval;
  484 + acpi_status status;
  485 +
  486 + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval);
  487 +
  488 + if (ACPI_FAILURE(status))
  489 + return -EIO;
  490 +
  491 + if (!retval || retval == 0x00060000)
  492 + return -ENODEV;
  493 + else
  494 + *value = (retval & 0x1);
  495 +
  496 + return 0;
  497 +}
  498 +
  499 +static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
  500 +{
  501 + kfree(hotplug_slot->info);
  502 + kfree(hotplug_slot);
  503 +}
  504 +
  505 +static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
  506 + .owner = THIS_MODULE,
  507 + .get_adapter_status = eeepc_get_adapter_status,
  508 + .get_power_status = eeepc_get_adapter_status,
  509 +};
  510 +
  511 +static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc)
  512 +{
  513 + int ret = -ENOMEM;
  514 + struct pci_bus *bus = pci_find_bus(0, 1);
  515 +
  516 + if (!bus) {
  517 + pr_err("Unable to find wifi PCI bus\n");
  518 + return -ENODEV;
  519 + }
  520 +
  521 + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
  522 + if (!eeepc->hotplug_slot)
  523 + goto error_slot;
  524 +
  525 + eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
  526 + GFP_KERNEL);
  527 + if (!eeepc->hotplug_slot->info)
  528 + goto error_info;
  529 +
  530 + eeepc->hotplug_slot->private = eeepc;
  531 + eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
  532 + eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
  533 + eeepc_get_adapter_status(eeepc->hotplug_slot,
  534 + &eeepc->hotplug_slot->info->adapter_status);
  535 +
  536 + ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
  537 + if (ret) {
  538 + pr_err("Unable to register hotplug slot - %d\n", ret);
  539 + goto error_register;
  540 + }
  541 +
  542 + return 0;
  543 +
  544 +error_register:
  545 + kfree(eeepc->hotplug_slot->info);
  546 +error_info:
  547 + kfree(eeepc->hotplug_slot);
  548 + eeepc->hotplug_slot = NULL;
  549 +error_slot:
  550 + return ret;
  551 +}
  552 +
  553 +/*
337 554 * Rfkill devices
338 555 */
339 556 static int eeepc_rfkill_set(void *data, bool blocked)
340 557  
... ... @@ -404,11 +621,22 @@
404 621  
405 622 static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
406 623 {
  624 + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
  625 + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
  626 + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
407 627 if (eeepc->wlan_rfkill) {
408 628 rfkill_unregister(eeepc->wlan_rfkill);
409 629 rfkill_destroy(eeepc->wlan_rfkill);
410 630 eeepc->wlan_rfkill = NULL;
411 631 }
  632 + /*
  633 + * Refresh pci hotplug in case the rfkill state was changed after
  634 + * eeepc_unregister_rfkill_notifier()
  635 + */
  636 + eeepc_rfkill_hotplug(eeepc);
  637 + if (eeepc->hotplug_slot)
  638 + pci_hp_deregister(eeepc->hotplug_slot);
  639 +
412 640 if (eeepc->bluetooth_rfkill) {
413 641 rfkill_unregister(eeepc->bluetooth_rfkill);
414 642 rfkill_destroy(eeepc->bluetooth_rfkill);
... ... @@ -425,6 +653,8 @@
425 653 {
426 654 int result = 0;
427 655  
  656 + mutex_init(&eeepc->hotplug_lock);
  657 +
428 658 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
429 659 "eeepc-wlan", RFKILL_TYPE_WLAN,
430 660 EEEPC_WMI_DEVID_WLAN);
... ... @@ -446,6 +676,23 @@
446 676 if (result && result != -ENODEV)
447 677 goto exit;
448 678  
  679 + result = eeepc_setup_pci_hotplug(eeepc);
  680 + /*
  681 + * If we get -EBUSY then something else is handling the PCI hotplug -
  682 + * don't fail in this case
  683 + */
  684 + if (result == -EBUSY)
  685 + result = 0;
  686 +
  687 + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
  688 + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
  689 + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
  690 + /*
  691 + * Refresh pci hotplug in case the rfkill state was changed during
  692 + * setup.
  693 + */
  694 + eeepc_rfkill_hotplug(eeepc);
  695 +
449 696 exit:
450 697 if (result && result != -ENODEV)
451 698 eeepc_wmi_rfkill_exit(eeepc);
... ... @@ -771,6 +1018,28 @@
771 1018 /*
772 1019 * WMI Driver
773 1020 */
  1021 +static void eeepc_dmi_check(struct eeepc_wmi *eeepc)
  1022 +{
  1023 + const char *model;
  1024 +
  1025 + model = dmi_get_system_info(DMI_PRODUCT_NAME);
  1026 + if (!model)
  1027 + return;
  1028 +
  1029 + /*
  1030 + * Whitelist for wlan hotplug
  1031 + *
  1032 + * Eeepc 1000H needs the current hotplug code to handle
  1033 + * Fn+F2 correctly. We may add other Eeepc here later, but
  1034 + * it seems that most of the laptops supported by eeepc-wmi
  1035 + * don't need to be on this list
  1036 + */
  1037 + if (strcmp(model, "1000H") == 0) {
  1038 + eeepc->hotplug_wireless = true;
  1039 + pr_info("wlan hotplug enabled\n");
  1040 + }
  1041 +}
  1042 +
774 1043 static struct platform_device * __init eeepc_wmi_add(void)
775 1044 {
776 1045 struct eeepc_wmi *eeepc;
... ... @@ -780,6 +1049,9 @@
780 1049 eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
781 1050 if (!eeepc)
782 1051 return ERR_PTR(-ENOMEM);
  1052 +
  1053 + eeepc->hotplug_wireless = hotplug_wireless;
  1054 + eeepc_dmi_check(eeepc);
783 1055  
784 1056 /*
785 1057 * Register the platform device first. It is used as a parent for the