diff options
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 91 |
1 files changed, 62 insertions, 29 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 21a6269501e1..e2e5e3088816 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/pci.h> | 46 | #include <linux/pci.h> |
47 | #include <linux/pci_hotplug.h> | 47 | #include <linux/pci_hotplug.h> |
48 | #include <linux/pci-acpi.h> | 48 | #include <linux/pci-acpi.h> |
49 | #include <linux/pm_runtime.h> | ||
49 | #include <linux/mutex.h> | 50 | #include <linux/mutex.h> |
50 | #include <linux/slab.h> | 51 | #include <linux/slab.h> |
51 | #include <linux/acpi.h> | 52 | #include <linux/acpi.h> |
@@ -687,47 +688,75 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) | |||
687 | } | 688 | } |
688 | 689 | ||
689 | /** | 690 | /** |
691 | * trim_stale_devices - remove PCI devices that are not responding. | ||
692 | * @dev: PCI device to start walking the hierarchy from. | ||
693 | */ | ||
694 | static void trim_stale_devices(struct pci_dev *dev) | ||
695 | { | ||
696 | acpi_handle handle = ACPI_HANDLE(&dev->dev); | ||
697 | struct pci_bus *bus = dev->subordinate; | ||
698 | bool alive = false; | ||
699 | |||
700 | if (handle) { | ||
701 | acpi_status status; | ||
702 | unsigned long long sta; | ||
703 | |||
704 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | ||
705 | alive = ACPI_SUCCESS(status) && sta == ACPI_STA_ALL; | ||
706 | } | ||
707 | if (!alive) { | ||
708 | u32 v; | ||
709 | |||
710 | /* Check if the device responds. */ | ||
711 | alive = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &v, 0); | ||
712 | } | ||
713 | if (!alive) { | ||
714 | pci_stop_and_remove_bus_device(dev); | ||
715 | if (handle) | ||
716 | acpiphp_bus_trim(handle); | ||
717 | } else if (bus) { | ||
718 | struct pci_dev *child, *tmp; | ||
719 | |||
720 | /* The device is a bridge. so check the bus below it. */ | ||
721 | pm_runtime_get_sync(&dev->dev); | ||
722 | list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) | ||
723 | trim_stale_devices(child); | ||
724 | |||
725 | pm_runtime_put(&dev->dev); | ||
726 | } | ||
727 | } | ||
728 | |||
729 | /** | ||
690 | * acpiphp_check_bridge - re-enumerate devices | 730 | * acpiphp_check_bridge - re-enumerate devices |
691 | * @bridge: where to begin re-enumeration | 731 | * @bridge: where to begin re-enumeration |
692 | * | 732 | * |
693 | * Iterate over all slots under this bridge and make sure that if a | 733 | * Iterate over all slots under this bridge and make sure that if a |
694 | * card is present they are enabled, and if not they are disabled. | 734 | * card is present they are enabled, and if not they are disabled. |
695 | */ | 735 | */ |
696 | static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) | 736 | static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) |
697 | { | 737 | { |
698 | struct acpiphp_slot *slot; | 738 | struct acpiphp_slot *slot; |
699 | int retval = 0; | ||
700 | int enabled, disabled; | ||
701 | |||
702 | enabled = disabled = 0; | ||
703 | 739 | ||
704 | list_for_each_entry(slot, &bridge->slots, node) { | 740 | list_for_each_entry(slot, &bridge->slots, node) { |
705 | unsigned int status = get_slot_status(slot); | 741 | struct pci_bus *bus = slot->bus; |
706 | if (slot->flags & SLOT_ENABLED) { | 742 | struct pci_dev *dev, *tmp; |
707 | if (status == ACPI_STA_ALL) | 743 | |
708 | continue; | 744 | mutex_lock(&slot->crit_sect); |
709 | 745 | /* wake up all functions */ | |
710 | retval = acpiphp_disable_and_eject_slot(slot); | 746 | if (get_slot_status(slot) == ACPI_STA_ALL) { |
711 | if (retval) | 747 | /* remove stale devices if any */ |
712 | goto err_exit; | 748 | list_for_each_entry_safe(dev, tmp, &bus->devices, |
713 | 749 | bus_list) | |
714 | disabled++; | 750 | if (PCI_SLOT(dev->devfn) == slot->device) |
751 | trim_stale_devices(dev); | ||
752 | |||
753 | /* configure all functions */ | ||
754 | enable_device(slot); | ||
715 | } else { | 755 | } else { |
716 | if (status != ACPI_STA_ALL) | 756 | disable_device(slot); |
717 | continue; | ||
718 | retval = acpiphp_enable_slot(slot); | ||
719 | if (retval) { | ||
720 | err("Error occurred in enabling\n"); | ||
721 | goto err_exit; | ||
722 | } | ||
723 | enabled++; | ||
724 | } | 757 | } |
758 | mutex_unlock(&slot->crit_sect); | ||
725 | } | 759 | } |
726 | |||
727 | dbg("%s: %d enabled, %d disabled\n", __func__, enabled, disabled); | ||
728 | |||
729 | err_exit: | ||
730 | return retval; | ||
731 | } | 760 | } |
732 | 761 | ||
733 | static void acpiphp_set_hpp_values(struct pci_bus *bus) | 762 | static void acpiphp_set_hpp_values(struct pci_bus *bus) |
@@ -828,7 +857,11 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) | |||
828 | ACPI_UINT32_MAX, check_sub_bridges, | 857 | ACPI_UINT32_MAX, check_sub_bridges, |
829 | NULL, NULL, NULL); | 858 | NULL, NULL, NULL); |
830 | } else { | 859 | } else { |
831 | acpiphp_enable_slot(func->slot); | 860 | struct acpiphp_slot *slot = func->slot; |
861 | |||
862 | mutex_lock(&slot->crit_sect); | ||
863 | enable_device(slot); | ||
864 | mutex_unlock(&slot->crit_sect); | ||
832 | } | 865 | } |
833 | break; | 866 | break; |
834 | 867 | ||