]> 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 a251071b7d7984ef588cd8ab16d6b26b4e8c0817..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>
@@ -128,7 +129,7 @@ static void acpiphp_put_context(struct acpiphp_context *context)
        if (--context->refcount)
                return;
 
-       WARN_ON(context->func || context->bridge);
+       WARN_ON(context->bridge);
        acpi_detach_data(context->handle, acpiphp_context_handler);
        kfree(context);
 }
@@ -155,12 +156,9 @@ static void free_bridge(struct kref *kref)
        bridge = container_of(kref, struct acpiphp_bridge, ref);
 
        list_for_each_entry_safe(slot, next, &bridge->slots, node) {
-               list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) {
-                       context = func->context;
-                       context->func = NULL;
-                       acpiphp_put_context(context);
-                       kfree(func);
-               }
+               list_for_each_entry_safe(func, tmp, &slot->funcs, sibling)
+                       acpiphp_put_context(func_to_context(func));
+
                kfree(slot);
        }
 
@@ -168,7 +166,7 @@ static void free_bridge(struct kref *kref)
        /* Root bridges will not have hotplug context. */
        if (context) {
                /* Release the reference taken by acpiphp_enumerate_slots(). */
-               put_bridge(context->func->slot->bridge);
+               put_bridge(context->func.parent);
                context->bridge = NULL;
                acpiphp_put_context(context);
        }
@@ -190,7 +188,7 @@ static void free_bridge(struct kref *kref)
 static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
 {
        struct acpiphp_context *context = data;
-       struct pci_bus *bus = context->func->slot->bridge->pci_bus;
+       struct pci_bus *bus = context->func.slot->bus;
        u32 buses;
 
        if (!bus->self)
@@ -251,14 +249,14 @@ static void acpiphp_dock_init(void *data)
 {
        struct acpiphp_context *context = data;
 
-       get_bridge(context->func->slot->bridge);
+       get_bridge(context->func.parent);
 }
 
 static void acpiphp_dock_release(void *data)
 {
        struct acpiphp_context *context = data;
 
-       put_bridge(context->func->slot->bridge);
+       put_bridge(context->func.parent);
 }
 
 /* callback routine to register each ACPI PCI slot object */
@@ -288,23 +286,16 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
        device = (adr >> 16) & 0xffff;
        function = adr & 0xffff;
 
-       newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
-       if (!newfunc)
-               return AE_NO_MEMORY;
-
-       newfunc->handle = handle;
-       newfunc->function = function;
-
        mutex_lock(&acpiphp_context_lock);
        context = acpiphp_init_context(handle);
        if (!context) {
                mutex_unlock(&acpiphp_context_lock);
                acpi_handle_err(handle, "No hotplug context\n");
-               kfree(newfunc);
                return AE_NOT_EXIST;
        }
-       newfunc->context = context;
-       context->func = newfunc;
+       newfunc = &context->func;
+       newfunc->function = function;
+       newfunc->parent = bridge;
        mutex_unlock(&acpiphp_context_lock);
 
        if (acpi_has_method(handle, "_EJ0"))
@@ -313,12 +304,6 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
        if (acpi_has_method(handle, "_STA"))
                newfunc->flags |= FUNC_HAS_STA;
 
-       if (acpi_has_method(handle, "_PS0"))
-               newfunc->flags |= FUNC_HAS_PS0;
-
-       if (acpi_has_method(handle, "_PS3"))
-               newfunc->flags |= FUNC_HAS_PS3;
-
        if (acpi_has_method(handle, "_DCK"))
                newfunc->flags |= FUNC_HAS_DCK;
 
@@ -333,14 +318,12 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
                goto err;
        }
 
-       slot->bridge = bridge;
+       slot->bus = bridge->pci_bus;
        slot->device = device;
        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)) {
@@ -357,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 "
@@ -370,13 +354,11 @@ 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))
-               slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
+               slot->flags |= SLOT_ENABLED;
 
        if (is_dock_device(handle)) {
                /* we don't want to call this device's _EJ0
@@ -404,10 +386,8 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
 
  err:
        mutex_lock(&acpiphp_context_lock);
-       context->func = NULL;
        acpiphp_put_context(context);
        mutex_unlock(&acpiphp_context_lock);
-       kfree(newfunc);
        return status;
 }
 
@@ -437,18 +417,21 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
 
        list_for_each_entry(slot, &bridge->slots, node) {
                list_for_each_entry(func, &slot->funcs, sibling) {
-                       if (is_dock_device(func->handle)) {
-                               unregister_hotplug_dock_device(func->handle);
-                       }
+                       acpi_handle handle = func_to_handle(func);
+
+                       if (is_dock_device(handle))
+                               unregister_hotplug_dock_device(handle);
+
                        if (!(func->flags & FUNC_HAS_DCK)) {
-                               status = acpi_remove_notify_handler(func->handle,
+                               status = acpi_remove_notify_handler(handle,
                                                        ACPI_SYSTEM_NOTIFY,
                                                        handle_hotplug_event);
                                if (ACPI_FAILURE(status))
                                        err("failed to remove notify handler\n");
                        }
                }
-               acpiphp_unregister_hotplug_slot(slot);
+               if (slot->slot)
+                       acpiphp_unregister_hotplug_slot(slot);
        }
 
        mutex_lock(&bridge_mutex);
@@ -456,71 +439,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
        mutex_unlock(&bridge_mutex);
 }
 
-static int power_on_slot(struct acpiphp_slot *slot)
-{
-       acpi_status status;
-       struct acpiphp_func *func;
-       int retval = 0;
-
-       /* if already enabled, just skip */
-       if (slot->flags & SLOT_POWEREDON)
-               goto err_exit;
-
-       list_for_each_entry(func, &slot->funcs, sibling) {
-               if (func->flags & FUNC_HAS_PS0) {
-                       dbg("%s: executing _PS0\n", __func__);
-                       status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);
-                       if (ACPI_FAILURE(status)) {
-                               warn("%s: _PS0 failed\n", __func__);
-                               retval = -1;
-                               goto err_exit;
-                       } else
-                               break;
-               }
-       }
-
-       /* TBD: evaluate _STA to check if the slot is enabled */
-
-       slot->flags |= SLOT_POWEREDON;
-
- err_exit:
-       return retval;
-}
-
-
-static int power_off_slot(struct acpiphp_slot *slot)
-{
-       acpi_status status;
-       struct acpiphp_func *func;
-
-       int retval = 0;
-
-       /* if already disabled, just skip */
-       if ((slot->flags & SLOT_POWEREDON) == 0)
-               goto err_exit;
-
-       list_for_each_entry(func, &slot->funcs, sibling) {
-               if (func->flags & FUNC_HAS_PS3) {
-                       status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
-                       if (ACPI_FAILURE(status)) {
-                               warn("%s: _PS3 failed\n", __func__);
-                               retval = -1;
-                               goto err_exit;
-                       } else
-                               break;
-               }
-       }
-
-       /* TBD: evaluate _STA to check if the slot is disabled */
-
-       slot->flags &= (~SLOT_POWEREDON);
-
- err_exit:
-       return retval;
-}
-
-
-
 /**
  * acpiphp_max_busnr - return the highest reserved bus number under the given bus.
  * @bus: bus to start search with
@@ -548,52 +466,32 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
        return max;
 }
 
-
 /**
- * acpiphp_bus_add - add a new bus to acpi subsystem
- * @func: acpiphp_func of the bridge
+ * acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree.
+ * @handle: ACPI device object handle to start from.
  */
-static int acpiphp_bus_add(struct acpiphp_func *func)
+static void acpiphp_bus_trim(acpi_handle handle)
 {
-       struct acpi_device *device;
-       int ret_val;
+       struct acpi_device *adev = NULL;
 
-       if (!acpi_bus_get_device(func->handle, &device)) {
-               dbg("bus exists... trim\n");
-               /* this shouldn't be in here, so remove
-                * the bus then re-add it...
-                */
-               acpi_bus_trim(device);
-       }
-
-       ret_val = acpi_bus_scan(func->handle);
-       if (!ret_val)
-               ret_val = acpi_bus_get_device(func->handle, &device);
-
-       if (ret_val)
-               dbg("error adding bus, %x\n", -ret_val);
-
-       return ret_val;
+       acpi_bus_get_device(handle, &adev);
+       if (adev)
+               acpi_bus_trim(adev);
 }
 
-
 /**
- * acpiphp_bus_trim - trim a bus from acpi subsystem
- * @handle: handle to acpi namespace
+ * acpiphp_bus_add - Scan ACPI namespace subtree.
+ * @handle: ACPI object handle to start the scan from.
  */
-static int acpiphp_bus_trim(acpi_handle handle)
+static void acpiphp_bus_add(acpi_handle handle)
 {
-       struct acpi_device *device;
-       int retval;
+       struct acpi_device *adev = NULL;
 
-       retval = acpi_bus_get_device(handle, &device);
-       if (retval) {
-               dbg("acpi_device not found\n");
-               return retval;
-       }
-
-       acpi_bus_trim(device);
-       return 0;
+       acpiphp_bus_trim(handle);
+       acpi_bus_scan(handle);
+       acpi_bus_get_device(handle, &adev);
+       if (adev)
+               acpi_device_set_power(adev, ACPI_STATE_D0);
 }
 
 static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
@@ -610,7 +508,8 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
                params[1].type = ACPI_TYPE_INTEGER;
                params[1].integer.value = 1;
                /* _REG is optional, we don't care about if there is failure */
-               acpi_evaluate_object(func->handle, "_REG", &arg_list, NULL);
+               acpi_evaluate_object(func_to_handle(func), "_REG", &arg_list,
+                                    NULL);
        }
 }
 
@@ -618,16 +517,10 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
 {
        struct acpiphp_func *func;
 
-       if (!dev->subordinate)
-               return;
-
        /* quirk, or pcie could set it already */
        if (dev->is_hotplug_bridge)
                return;
 
-       if (PCI_SLOT(dev->devfn) != slot->device)
-               return;
-
        list_for_each_entry(func, &slot->funcs, sibling) {
                if (PCI_FUNC(dev->devfn) == func->function) {
                        dev->is_hotplug_bridge = 1;
@@ -637,38 +530,31 @@ 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->bridge->pci_bus;
+       struct pci_bus *bus = slot->bus;
        struct acpiphp_func *func;
-       int num, max, pass;
+       int max, pass;
        LIST_HEAD(add_list);
 
-       if (slot->flags & SLOT_ENABLED)
-               goto err_exit;
-
        list_for_each_entry(func, &slot->funcs, sibling)
-               acpiphp_bus_add(func);
+               acpiphp_bus_add(func_to_handle(func));
 
-       num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
-       if (num == 0) {
-               /* Maybe only part of funcs are added. */
-               dbg("No new device found\n");
-               goto err_exit;
-       }
+       pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
 
        max = acpiphp_max_busnr(bus);
        for (pass = 0; pass < 2; pass++) {
                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);
@@ -707,16 +593,12 @@ static int __ref enable_device(struct acpiphp_slot *slot)
                        continue;
                }
        }
-
-
- err_exit:
-       return 0;
 }
 
 /* return first device in slot, acquiring a reference on it */
 static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
 {
-       struct pci_bus *bus = slot->bridge->pci_bus;
+       struct pci_bus *bus = slot->bus;
        struct pci_dev *dev;
        struct pci_dev *ret = NULL;
 
@@ -732,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.
@@ -751,13 +633,10 @@ static int disable_device(struct acpiphp_slot *slot)
                pci_dev_put(pdev);
        }
 
-       list_for_each_entry(func, &slot->funcs, sibling) {
-               acpiphp_bus_trim(func->handle);
-       }
+       list_for_each_entry(func, &slot->funcs, sibling)
+               acpiphp_bus_trim(func_to_handle(func));
 
        slot->flags &= (~SLOT_ENABLED);
-
-       return 0;
 }
 
 
@@ -775,18 +654,21 @@ static int disable_device(struct acpiphp_slot *slot)
  */
 static unsigned int get_slot_status(struct acpiphp_slot *slot)
 {
-       acpi_status status;
        unsigned long long sta = 0;
-       u32 dvid;
        struct acpiphp_func *func;
 
        list_for_each_entry(func, &slot->funcs, sibling) {
                if (func->flags & FUNC_HAS_STA) {
-                       status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta);
+                       acpi_status status;
+
+                       status = acpi_evaluate_integer(func_to_handle(func),
+                                                      "_STA", NULL, &sta);
                        if (ACPI_SUCCESS(status) && sta)
                                break;
                } else {
-                       pci_bus_read_config_dword(slot->bridge->pci_bus,
+                       u32 dvid;
+
+                       pci_bus_read_config_dword(slot->bus,
                                                  PCI_DEVFN(slot->device,
                                                            func->function),
                                                  PCI_VENDOR_ID, &dvid);
@@ -801,23 +683,42 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
 }
 
 /**
- * acpiphp_eject_slot - physically eject the slot
- * @slot: ACPI PHP slot
+ * trim_stale_devices - remove PCI devices that are not responding.
+ * @dev: PCI device to start walking the hierarchy from.
  */
-int acpiphp_eject_slot(struct acpiphp_slot *slot)
+static void trim_stale_devices(struct pci_dev *dev)
 {
-       struct acpiphp_func *func;
+       acpi_handle handle = ACPI_HANDLE(&dev->dev);
+       struct pci_bus *bus = dev->subordinate;
+       bool alive = false;
 
-       list_for_each_entry(func, &slot->funcs, sibling) {
-               /* We don't want to call _EJ0 on non-existing functions. */
-               if ((func->flags & FUNC_HAS_EJ0)) {
-                       if (ACPI_FAILURE(acpi_evaluate_ej0(func->handle)))
-                               return -1;
-                       else
-                               break;
-               }
+       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);
        }
-       return 0;
 }
 
 /**
@@ -827,43 +728,30 @@ int acpiphp_eject_slot(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_slot(slot);
-                       if (retval) {
-                               err("Error occurred in disabling\n");
-                               goto err_exit;
-                       } else {
-                               acpiphp_eject_slot(slot);
-                       }
-                       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)
@@ -902,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;
@@ -930,15 +799,12 @@ 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)
 {
        struct acpiphp_context *context = data;
-       struct acpiphp_func *func = context->func;
+       struct acpiphp_func *func = &context->func;
        struct acpiphp_bridge *bridge;
        char objname[64];
        struct acpi_buffer buffer = { .length = sizeof(objname),
@@ -960,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;
 
@@ -974,44 +841,14 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
                if (bridge)
                        acpiphp_check_bridge(bridge);
                else
-                       acpiphp_check_bridge(func->slot->bridge);
+                       acpiphp_check_bridge(func->parent);
 
                break;
 
-       case ACPI_NOTIFY_DEVICE_WAKE:
-               /* wake event */
-               dbg("%s: Device wake notify on %s\n", __func__, objname);
-               break;
-
        case ACPI_NOTIFY_EJECT_REQUEST:
                /* request device eject */
                dbg("%s: Device eject notify on %s\n", __func__, objname);
-               if (bridge && !(bridge->flags & BRIDGE_HAS_EJ0))
-                       break;
-
-               if (!(acpiphp_disable_slot(func->slot)))
-                       acpiphp_eject_slot(func->slot);
-
-               break;
-
-       case ACPI_NOTIFY_FREQUENCY_MISMATCH:
-               printk(KERN_ERR "Device %s cannot be configured due"
-                               " to a frequency mismatch\n", objname);
-               break;
-
-       case ACPI_NOTIFY_BUS_MODE_MISMATCH:
-               printk(KERN_ERR "Device %s cannot be configured due"
-                               " to a bus mode mismatch\n", objname);
-               break;
-
-       case ACPI_NOTIFY_POWER_FAULT:
-               printk(KERN_ERR "Device %s has suffered a power fault\n",
-                               objname);
-               break;
-
-       default:
-               warn("notify_handler: unknown event type 0x%x for %s\n", type,
-                    objname);
+               acpiphp_disable_and_eject_slot(func->slot);
                break;
        }
 
@@ -1032,7 +869,7 @@ static void hotplug_event_work(struct work_struct *work)
 
        acpi_scan_lock_release();
        kfree(hp_work); /* allocated in handle_hotplug_event() */
-       put_bridge(context->func->slot->bridge);
+       put_bridge(context->func.parent);
 }
 
 /**
@@ -1047,23 +884,42 @@ static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
 {
        struct acpiphp_context *context;
 
+       switch (type) {
+       case ACPI_NOTIFY_BUS_CHECK:
+       case ACPI_NOTIFY_DEVICE_CHECK:
+       case ACPI_NOTIFY_EJECT_REQUEST:
+               break;
+
+       case ACPI_NOTIFY_DEVICE_WAKE:
+               return;
+
+       case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+               acpi_handle_err(handle, "Device cannot be configured due "
+                               "to a frequency mismatch\n");
+               return;
+
+       case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+               acpi_handle_err(handle, "Device cannot be configured due "
+                               "to a bus mode mismatch\n");
+               return;
+
+       case ACPI_NOTIFY_POWER_FAULT:
+               acpi_handle_err(handle, "Device has suffered a power fault\n");
+               return;
+
+       default:
+               acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+               return;
+       }
+
        mutex_lock(&acpiphp_context_lock);
        context = acpiphp_get_context(handle);
        if (context) {
-               get_bridge(context->func->slot->bridge);
+               get_bridge(context->func.parent);
                acpiphp_put_context(context);
+               alloc_acpi_hp_work(handle, type, context, hotplug_event_work);
        }
        mutex_unlock(&acpiphp_context_lock);
-       /*
-        * Currently the code adds all hotplug events to the kacpid_wq
-        * queue when it should add hotplug events to the kacpi_hotplug_wq.
-        * The proper way to fix this is to reorganize the code so that
-        * drivers (dock, etc.) do not call acpi_os_execute(), etc.
-        * For now just re-add this work to the kacpi_hotplug_wq so we
-        * don't deadlock on hotplug actions.
-        */
-       if (context)
-               alloc_acpi_hp_work(handle, type, context, hotplug_event_work);
 }
 
 /*
@@ -1091,7 +947,6 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
 
        INIT_LIST_HEAD(&bridge->slots);
        kref_init(&bridge->ref);
-       bridge->handle = handle;
        bridge->pci_dev = pci_dev_get(bus->self);
        bridge->pci_bus = bus;
 
@@ -1112,7 +967,7 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
                 */
                mutex_lock(&acpiphp_context_lock);
                context = acpiphp_get_context(handle);
-               if (WARN_ON(!context || !context->func)) {
+               if (WARN_ON(!context)) {
                        mutex_unlock(&acpiphp_context_lock);
                        put_device(&bus->dev);
                        kfree(bridge);
@@ -1121,26 +976,20 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
                bridge->context = context;
                context->bridge = bridge;
                /* Get a reference to the parent bridge. */
-               get_bridge(context->func->slot->bridge);
+               get_bridge(context->func.parent);
                mutex_unlock(&acpiphp_context_lock);
        }
 
-       status = acpi_get_handle(bridge->handle, "_EJ0", &handle);
-       if (ACPI_SUCCESS(status)) {
-               dbg("found ejectable p2p bridge\n");
-               bridge->flags |= BRIDGE_HAS_EJ0;
-       }
-
        /* must be added to the list prior to calling register_slot */
        mutex_lock(&bridge_mutex);
        list_add(&bridge->list, &bridge_list);
        mutex_unlock(&bridge_mutex);
 
        /* register all slot objects under this bridge */
-       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, 1,
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
                                     register_slot, NULL, bridge, NULL);
        if (ACPI_FAILURE(status)) {
-               acpi_handle_err(bridge->handle, "failed to register slots\n");
+               acpi_handle_err(handle, "failed to register slots\n");
                cleanup_bridge(bridge);
                put_bridge(bridge);
        }
@@ -1149,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);
 }
 
 /**
@@ -1168,51 +1021,39 @@ void acpiphp_remove_slots(struct pci_bus *bus)
  */
 int acpiphp_enable_slot(struct acpiphp_slot *slot)
 {
-       int retval;
-
        mutex_lock(&slot->crit_sect);
+       /* configure all functions */
+       if (!(slot->flags & SLOT_ENABLED))
+               enable_slot(slot);
 
-       /* wake up all functions */
-       retval = power_on_slot(slot);
-       if (retval)
-               goto err_exit;
-
-       if (get_slot_status(slot) == ACPI_STA_ALL) {
-               /* configure all functions */
-               retval = enable_device(slot);
-               if (retval)
-                       power_off_slot(slot);
-       } else {
-               dbg("%s: Slot status is not ACPI_STA_ALL\n", __func__);
-               power_off_slot(slot);
-       }
-
- err_exit:
        mutex_unlock(&slot->crit_sect);
-       return retval;
+       return 0;
 }
 
 /**
- * acpiphp_disable_slot - power off slot
+ * acpiphp_disable_and_eject_slot - power off and eject slot
  * @slot: ACPI PHP slot
  */
-int acpiphp_disable_slot(struct acpiphp_slot *slot)
+int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
 {
+       struct acpiphp_func *func;
        int retval = 0;
 
        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) {
+                       acpi_handle handle = func_to_handle(func);
 
-       /* power off all functions */
-       retval = power_off_slot(slot);
-       if (retval)
-               goto err_exit;
+                       if (ACPI_FAILURE(acpi_evaluate_ej0(handle)))
+                               acpi_handle_err(handle, "_EJ0 failed\n");
+
+                       break;
+               }
 
- err_exit:
        mutex_unlock(&slot->crit_sect);
        return retval;
 }
@@ -1224,7 +1065,7 @@ int acpiphp_disable_slot(struct acpiphp_slot *slot)
  */
 u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
 {
-       return (slot->flags & SLOT_POWEREDON);
+       return (slot->flags & SLOT_ENABLED);
 }
 
 
@@ -1234,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);
 }
 
 
@@ -1248,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);
 }