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 /drivers/platform/x86/eeepc-laptop.c | |
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>
Diffstat (limited to 'drivers/platform/x86/eeepc-laptop.c')
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 87 |
1 files changed, 87 insertions, 0 deletions
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; |