aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug/acpiphp_glue.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-07-16 16:10:35 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-07-22 22:00:27 -0400
commit4ebe34503baa0644c9352bcd76d4cf573bffe206 (patch)
tree9d3718ba4012546bc76f3d958ca52e12873396f7 /drivers/pci/hotplug/acpiphp_glue.c
parentb91182a67c53db227e34921838dd683090ecfabc (diff)
ACPI / hotplug / PCI: Check for new devices on enabled slots
The current implementation of acpiphp_check_bridge() is pretty dumb: - It enables a slot if it's not enabled and the slot status is ACPI_STA_ALL. - It disables a slot if it's enabled and the slot status is not ACPI_STA_ALL. This behavior is not sufficient to handle the Thunderbolt daisy chaining case properly, however, because in that case the bus behind the already enabled slot needs to be rescanned for new devices. For this reason, modify acpiphp_check_bridge() so that slots are disabled and stopped if they are not in the ACPI_STA_ALL state. For slots in the ACPI_STA_ALL state, devices behind them that don't respond are trimmed using a new function, trim_stale_devices(), introduced specifically for this purpose. That function walks the given bus and checks each device on it. If the device doesn't respond, it is assumed to be gone and is removed. Once all of the stale devices directy behind the slot have been removed, acpiphp_check_bridge() will start looking for new devices that might have appeared on the given bus. It will do that even if the slot is already enabled (SLOT_ENABLED is set for it). In addition to that, make the bus check notification ignore SLOT_ENABLED and go for enable_device() directly if bridge is NULL, so that devices behind the slot are re-enumerated in that case too. This change is based on earlier patches from Kirill A Shutemov and Mika Westerberg. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/pci/hotplug/acpiphp_glue.c')
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c91
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 */
694static 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 */
696static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) 736static 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
733static void acpiphp_set_hpp_values(struct pci_bus *bus) 762static 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