]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/pci/hotplug/acpiphp_glue.c
Merge branch 'acpi-processor'
[karo-tx-linux.git] / drivers / pci / hotplug / acpiphp_glue.c
index 21a6269501e18d50cac1e8d0e3b2633fedc8392a..8054ddcdaed0a195f02e83d19513c61b8b284368 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
 #include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
@@ -322,9 +323,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
        INIT_LIST_HEAD(&slot->funcs);
        mutex_init(&slot->crit_sect);
 
-       mutex_lock(&bridge_mutex);
        list_add_tail(&slot->node, &bridge->slots);
-       mutex_unlock(&bridge_mutex);
 
        /* Register slots for ejectable funtions only. */
        if (acpi_pci_check_ejectable(pbus, handle)  || is_dock_device(handle)) {
@@ -341,6 +340,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
 
                retval = acpiphp_register_hotplug_slot(slot, sun);
                if (retval) {
+                       slot->slot = NULL;
                        bridge->nr_slots--;
                        if (retval == -EBUSY)
                                warn("Slot %llu already registered by another "
@@ -354,9 +354,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
 
  slot_found:
        newfunc->slot = slot;
-       mutex_lock(&bridge_mutex);
        list_add_tail(&newfunc->sibling, &slot->funcs);
-       mutex_unlock(&bridge_mutex);
 
        if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function),
                                       &val, 60*1000))
@@ -432,7 +430,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
                                        err("failed to remove notify handler\n");
                        }
                }
-               acpiphp_unregister_hotplug_slot(slot);
+               if (slot->slot)
+                       acpiphp_unregister_hotplug_slot(slot);
        }
 
        mutex_lock(&bridge_mutex);
@@ -531,13 +530,13 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
 }
 
 /**
- * enable_device - enable, configure a slot
+ * enable_slot - enable, configure a slot
  * @slot: slot to be enabled
  *
  * This function should be called per *physical slot*,
  * not per each slot object in ACPI namespace.
  */
-static int __ref enable_device(struct acpiphp_slot *slot)
+static void __ref enable_slot(struct acpiphp_slot *slot)
 {
        struct pci_dev *dev;
        struct pci_bus *bus = slot->bus;
@@ -555,6 +554,7 @@ static int __ref enable_device(struct acpiphp_slot *slot)
                list_for_each_entry(dev, &bus->devices, bus_list) {
                        if (PCI_SLOT(dev->devfn) != slot->device)
                                continue;
+
                        if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
                            dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
                                max = pci_scan_bridge(bus, dev, max, pass);
@@ -593,8 +593,6 @@ static int __ref enable_device(struct acpiphp_slot *slot)
                        continue;
                }
        }
-
-       return 0;
 }
 
 /* return first device in slot, acquiring a reference on it */
@@ -616,16 +614,16 @@ static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
 }
 
 /**
- * disable_device - disable a slot
+ * disable_slot - disable a slot
  * @slot: ACPI PHP slot
  */
-static int disable_device(struct acpiphp_slot *slot)
+static void disable_slot(struct acpiphp_slot *slot)
 {
        struct acpiphp_func *func;
        struct pci_dev *pdev;
 
        /*
-        * enable_device() enumerates all functions in this device via
+        * enable_slot() enumerates all functions in this device via
         * pci_scan_slot(), whether they have associated ACPI hotplug
         * methods (_EJ0, etc.) or not.  Therefore, we remove all functions
         * here.
@@ -639,8 +637,6 @@ static int disable_device(struct acpiphp_slot *slot)
                acpiphp_bus_trim(func_to_handle(func));
 
        slot->flags &= (~SLOT_ENABLED);
-
-       return 0;
 }
 
 
@@ -686,6 +682,45 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
        return (unsigned int)sta;
 }
 
+/**
+ * trim_stale_devices - remove PCI devices that are not responding.
+ * @dev: PCI device to start walking the hierarchy from.
+ */
+static void trim_stale_devices(struct pci_dev *dev)
+{
+       acpi_handle handle = ACPI_HANDLE(&dev->dev);
+       struct pci_bus *bus = dev->subordinate;
+       bool alive = false;
+
+       if (handle) {
+               acpi_status status;
+               unsigned long long sta;
+
+               status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+               alive = ACPI_SUCCESS(status) && sta == ACPI_STA_ALL;
+       }
+       if (!alive) {
+               u32 v;
+
+               /* Check if the device responds. */
+               alive = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &v, 0);
+       }
+       if (!alive) {
+               pci_stop_and_remove_bus_device(dev);
+               if (handle)
+                       acpiphp_bus_trim(handle);
+       } else if (bus) {
+               struct pci_dev *child, *tmp;
+
+               /* The device is a bridge. so check the bus below it. */
+               pm_runtime_get_sync(&dev->dev);
+               list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
+                       trim_stale_devices(child);
+
+               pm_runtime_put(&dev->dev);
+       }
+}
+
 /**
  * acpiphp_check_bridge - re-enumerate devices
  * @bridge: where to begin re-enumeration
@@ -693,41 +728,30 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
  * Iterate over all slots under this bridge and make sure that if a
  * card is present they are enabled, and if not they are disabled.
  */
-static int acpiphp_check_bridge(struct acpiphp_bridge *bridge)
+static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
 {
        struct acpiphp_slot *slot;
-       int retval = 0;
-       int enabled, disabled;
-
-       enabled = disabled = 0;
 
        list_for_each_entry(slot, &bridge->slots, node) {
-               unsigned int status = get_slot_status(slot);
-               if (slot->flags & SLOT_ENABLED) {
-                       if (status == ACPI_STA_ALL)
-                               continue;
-
-                       retval = acpiphp_disable_and_eject_slot(slot);
-                       if (retval)
-                               goto err_exit;
-
-                       disabled++;
+               struct pci_bus *bus = slot->bus;
+               struct pci_dev *dev, *tmp;
+
+               mutex_lock(&slot->crit_sect);
+               /* wake up all functions */
+               if (get_slot_status(slot) == ACPI_STA_ALL) {
+                       /* remove stale devices if any */
+                       list_for_each_entry_safe(dev, tmp, &bus->devices,
+                                                bus_list)
+                               if (PCI_SLOT(dev->devfn) == slot->device)
+                                       trim_stale_devices(dev);
+
+                       /* configure all functions */
+                       enable_slot(slot);
                } else {
-                       if (status != ACPI_STA_ALL)
-                               continue;
-                       retval = acpiphp_enable_slot(slot);
-                       if (retval) {
-                               err("Error occurred in enabling\n");
-                               goto err_exit;
-                       }
-                       enabled++;
+                       disable_slot(slot);
                }
+               mutex_unlock(&slot->crit_sect);
        }
-
-       dbg("%s: %d enabled, %d disabled\n", __func__, enabled, disabled);
-
- err_exit:
-       return retval;
 }
 
 static void acpiphp_set_hpp_values(struct pci_bus *bus)
@@ -766,25 +790,6 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
  * ACPI event handlers
  */
 
-static acpi_status
-check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-       struct acpiphp_bridge *bridge;
-       char objname[64];
-       struct acpi_buffer buffer = { .length = sizeof(objname),
-                                     .pointer = objname };
-
-       bridge = acpiphp_handle_to_bridge(handle);
-       if (bridge) {
-               acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
-               dbg("%s: re-enumerating slots under %s\n",
-                       __func__, objname);
-               acpiphp_check_bridge(bridge);
-               put_bridge(bridge);
-       }
-       return AE_OK ;
-}
-
 void acpiphp_check_host_bridge(acpi_handle handle)
 {
        struct acpiphp_bridge *bridge;
@@ -794,9 +799,6 @@ void acpiphp_check_host_bridge(acpi_handle handle)
                acpiphp_check_bridge(bridge);
                put_bridge(bridge);
        }
-
-       acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-               ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
 }
 
 static void hotplug_event(acpi_handle handle, u32 type, void *data)
@@ -824,11 +826,12 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
                dbg("%s: re-enumerating slots under %s\n", __func__, objname);
                if (bridge) {
                        acpiphp_check_bridge(bridge);
-                       acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-                                           ACPI_UINT32_MAX, check_sub_bridges,
-                                           NULL, NULL, NULL);
                } else {
-                       acpiphp_enable_slot(func->slot);
+                       struct acpiphp_slot *slot = func->slot;
+
+                       mutex_lock(&slot->crit_sect);
+                       enable_slot(slot);
+                       mutex_unlock(&slot->crit_sect);
                }
                break;
 
@@ -995,17 +998,21 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
 /* Destroy hotplug slots associated with the PCI bus */
 void acpiphp_remove_slots(struct pci_bus *bus)
 {
-       struct acpiphp_bridge *bridge, *tmp;
+       struct acpiphp_bridge *bridge;
 
        if (acpiphp_disabled)
                return;
 
-       list_for_each_entry_safe(bridge, tmp, &bridge_list, list)
+       mutex_lock(&bridge_mutex);
+       list_for_each_entry(bridge, &bridge_list, list)
                if (bridge->pci_bus == bus) {
+                       mutex_unlock(&bridge_mutex);
                        cleanup_bridge(bridge);
                        put_bridge(bridge);
-                       break;
+                       return;
                }
+
+       mutex_unlock(&bridge_mutex);
 }
 
 /**
@@ -1014,16 +1021,13 @@ void acpiphp_remove_slots(struct pci_bus *bus)
  */
 int acpiphp_enable_slot(struct acpiphp_slot *slot)
 {
-       int retval = 0;
-
        mutex_lock(&slot->crit_sect);
-
        /* configure all functions */
        if (!(slot->flags & SLOT_ENABLED))
-               retval = enable_device(slot);
+               enable_slot(slot);
 
        mutex_unlock(&slot->crit_sect);
-       return retval;
+       return 0;
 }
 
 /**
@@ -1038,9 +1042,7 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
        mutex_lock(&slot->crit_sect);
 
        /* unconfigure all functions */
-       retval = disable_device(slot);
-       if (retval)
-               goto err_exit;
+       disable_slot(slot);
 
        list_for_each_entry(func, &slot->funcs, sibling)
                if (func->flags & FUNC_HAS_EJ0) {
@@ -1052,7 +1054,6 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
                        break;
                }
 
- err_exit:
        mutex_unlock(&slot->crit_sect);
        return retval;
 }
@@ -1074,11 +1075,7 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
  */
 u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
 {
-       unsigned int sta;
-
-       sta = get_slot_status(slot);
-
-       return (sta & ACPI_STA_DEVICE_UI) ? 0 : 1;
+       return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
 }
 
 
@@ -1088,9 +1085,5 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
  */
 u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
 {
-       unsigned int sta;
-
-       sta = get_slot_status(slot);
-
-       return (sta == 0) ? 0 : 1;
+       return !!get_slot_status(slot);
 }