diff options
| author | Corentin Chary <corentincj@iksaif.net> | 2009-06-25 07:25:36 -0400 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2009-06-26 00:23:29 -0400 |
| commit | 2b121bc262fa03c94e653b2d44356c2f86c1bcdc (patch) | |
| tree | 683ce60fa8911d4e394712a60e4378f4eaa6a60a | |
| parent | 28d0325ce6e0a52f53d8af687e6427fee59004d3 (diff) | |
eeepc-laptop: Register as a pci-hotplug device
The eee contains a logically (but not physically) hotpluggable PCIe slot.
Currently this is handled by adding or removing the PCI device in response
to rfkill events, but if a user has forced pciehp to bind to it (with the
force=1 argument) then both drivers will try to handle the event and
hilarity (in the form of oopses) will ensue. This can be avoided by having
eee-laptop register the slot as a hotplug slot. Only one of pciehp and
eee-laptop will successfully register this, avoiding the problem.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Tested-by: Darren Salt <linux@youmustbejoking.demon.co.uk>
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Len Brown <len.brown@intel.com>
| -rw-r--r-- | drivers/platform/x86/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 87 |
2 files changed, 89 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7232fe7104aa..fee6a4022bc1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -357,6 +357,8 @@ config EEEPC_LAPTOP | |||
| 357 | depends on RFKILL || RFKILL = n | 357 | depends on RFKILL || RFKILL = n |
| 358 | select BACKLIGHT_CLASS_DEVICE | 358 | select BACKLIGHT_CLASS_DEVICE |
| 359 | select HWMON | 359 | select HWMON |
| 360 | select HOTPLUG | ||
| 361 | select HOTPLUG_PCI if PCI | ||
| 360 | ---help--- | 362 | ---help--- |
| 361 | This driver supports the Fn-Fx keys on Eee PC laptops. | 363 | This driver supports the Fn-Fx keys on Eee PC laptops. |
| 362 | 364 | ||
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 4207b26ff990..c0b203ca29fb 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/input.h> | 31 | #include <linux/input.h> |
| 32 | #include <linux/rfkill.h> | 32 | #include <linux/rfkill.h> |
| 33 | #include <linux/pci.h> | 33 | #include <linux/pci.h> |
| 34 | #include <linux/pci_hotplug.h> | ||
| 34 | 35 | ||
| 35 | #define EEEPC_LAPTOP_VERSION "0.1" | 36 | #define EEEPC_LAPTOP_VERSION "0.1" |
| 36 | 37 | ||
| @@ -143,6 +144,7 @@ struct eeepc_hotk { | |||
| 143 | u16 *keycode_map; | 144 | u16 *keycode_map; |
| 144 | struct rfkill *eeepc_wlan_rfkill; | 145 | struct rfkill *eeepc_wlan_rfkill; |
| 145 | struct rfkill *eeepc_bluetooth_rfkill; | 146 | struct rfkill *eeepc_bluetooth_rfkill; |
| 147 | struct hotplug_slot *hotplug_slot; | ||
| 146 | }; | 148 | }; |
| 147 | 149 | ||
| 148 | /* The actual device the driver binds to */ | 150 | /* The actual device the driver binds to */ |
| @@ -213,6 +215,15 @@ static struct acpi_driver eeepc_hotk_driver = { | |||
| 213 | }, | 215 | }, |
| 214 | }; | 216 | }; |
| 215 | 217 | ||
| 218 | /* PCI hotplug ops */ | ||
| 219 | static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value); | ||
| 220 | |||
| 221 | static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { | ||
| 222 | .owner = THIS_MODULE, | ||
| 223 | .get_adapter_status = eeepc_get_adapter_status, | ||
| 224 | .get_power_status = eeepc_get_adapter_status, | ||
| 225 | }; | ||
| 226 | |||
| 216 | /* The backlight device /sys/class/backlight */ | 227 | /* The backlight device /sys/class/backlight */ |
| 217 | static struct backlight_device *eeepc_backlight_device; | 228 | static struct backlight_device *eeepc_backlight_device; |
| 218 | 229 | ||
| @@ -612,6 +623,19 @@ static int notify_brn(void) | |||
| 612 | return -1; | 623 | return -1; |
| 613 | } | 624 | } |
| 614 | 625 | ||
| 626 | static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | ||
| 627 | u8 *value) | ||
| 628 | { | ||
| 629 | int val = get_acpi(CM_ASL_WLAN); | ||
| 630 | |||
| 631 | if (val == 1 || val == 0) | ||
| 632 | *value = val; | ||
| 633 | else | ||
| 634 | return -EINVAL; | ||
| 635 | |||
| 636 | return 0; | ||
| 637 | } | ||
| 638 | |||
| 615 | static void eeepc_rfkill_hotplug(void) | 639 | static void eeepc_rfkill_hotplug(void) |
| 616 | { | 640 | { |
| 617 | struct pci_dev *dev; | 641 | struct pci_dev *dev; |
| @@ -744,6 +768,54 @@ static void eeepc_unregister_rfkill_notifier(char *node) | |||
| 744 | } | 768 | } |
| 745 | } | 769 | } |
| 746 | 770 | ||
| 771 | static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) | ||
| 772 | { | ||
| 773 | kfree(hotplug_slot->info); | ||
| 774 | kfree(hotplug_slot); | ||
| 775 | } | ||
| 776 | |||
| 777 | static int eeepc_setup_pci_hotplug(void) | ||
| 778 | { | ||
| 779 | int ret = -ENOMEM; | ||
| 780 | struct pci_bus *bus = pci_find_bus(0, 1); | ||
| 781 | |||
| 782 | if (!bus) { | ||
| 783 | printk(EEEPC_ERR "Unable to find wifi PCI bus\n"); | ||
| 784 | return -ENODEV; | ||
| 785 | } | ||
| 786 | |||
| 787 | ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | ||
| 788 | if (!ehotk->hotplug_slot) | ||
| 789 | goto error_slot; | ||
| 790 | |||
| 791 | ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), | ||
| 792 | GFP_KERNEL); | ||
| 793 | if (!ehotk->hotplug_slot->info) | ||
| 794 | goto error_info; | ||
| 795 | |||
| 796 | ehotk->hotplug_slot->private = ehotk; | ||
| 797 | ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; | ||
| 798 | ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops; | ||
| 799 | eeepc_get_adapter_status(ehotk->hotplug_slot, | ||
| 800 | &ehotk->hotplug_slot->info->adapter_status); | ||
| 801 | |||
| 802 | ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi"); | ||
| 803 | if (ret) { | ||
| 804 | printk(EEEPC_ERR "Unable to register hotplug slot - %d\n", ret); | ||
| 805 | goto error_register; | ||
| 806 | } | ||
| 807 | |||
| 808 | return 0; | ||
| 809 | |||
| 810 | error_register: | ||
| 811 | kfree(ehotk->hotplug_slot->info); | ||
| 812 | error_info: | ||
| 813 | kfree(ehotk->hotplug_slot); | ||
| 814 | ehotk->hotplug_slot = NULL; | ||
| 815 | error_slot: | ||
| 816 | return ret; | ||
| 817 | } | ||
| 818 | |||
| 747 | static int eeepc_hotk_add(struct acpi_device *device) | 819 | static int eeepc_hotk_add(struct acpi_device *device) |
| 748 | { | 820 | { |
| 749 | int result; | 821 | int result; |
| @@ -802,8 +874,21 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
| 802 | goto bluetooth_fail; | 874 | goto bluetooth_fail; |
| 803 | } | 875 | } |
| 804 | 876 | ||
| 877 | result = eeepc_setup_pci_hotplug(); | ||
| 878 | /* | ||
| 879 | * If we get -EBUSY then something else is handling the PCI hotplug - | ||
| 880 | * don't fail in this case | ||
| 881 | */ | ||
| 882 | if (result == -EBUSY) | ||
| 883 | return 0; | ||
| 884 | else if (result) | ||
| 885 | goto pci_fail; | ||
| 886 | |||
| 805 | return 0; | 887 | return 0; |
| 806 | 888 | ||
| 889 | pci_fail: | ||
| 890 | if (ehotk->eeepc_bluetooth_rfkill) | ||
| 891 | rfkill_unregister(ehotk->eeepc_bluetooth_rfkill); | ||
| 807 | bluetooth_fail: | 892 | bluetooth_fail: |
| 808 | rfkill_destroy(ehotk->eeepc_bluetooth_rfkill); | 893 | rfkill_destroy(ehotk->eeepc_bluetooth_rfkill); |
| 809 | rfkill_unregister(ehotk->eeepc_wlan_rfkill); | 894 | rfkill_unregister(ehotk->eeepc_wlan_rfkill); |
| @@ -825,6 +910,8 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) | |||
| 825 | 910 | ||
| 826 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | 911 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); |
| 827 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | 912 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); |
| 913 | if (ehotk->hotplug_slot) | ||
| 914 | pci_hp_deregister(ehotk->hotplug_slot); | ||
| 828 | 915 | ||
| 829 | kfree(ehotk); | 916 | kfree(ehotk); |
| 830 | return 0; | 917 | return 0; |
