static void hotplug_event(acpi_handle handle, u32 type, void *data);
static void free_bridge(struct kref *kref);
-/* callback routine to check for the existence of a pci dock device */
-static acpi_status
-is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- int *count = (int *)context;
-
- if (is_dock_device(handle)) {
- (*count)++;
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
-}
-
static void acpiphp_context_handler(acpi_handle handle, void *context)
{
/* Intentionally empty. */
}
context = bridge->context;
- /* Release the reference acquired by acpiphp_enumerate_slots(). */
- if (context->handler_for_func)
+ /* Root bridges will not have hotplug context. */
+ if (context) {
+ /* Release the reference taken by acpiphp_enumerate_slots(). */
put_bridge(context->func->slot->bridge);
+ context->bridge = NULL;
+ acpiphp_put_context(context);
+ }
put_device(&bridge->pci_bus->dev);
pci_dev_put(bridge->pci_dev);
- context->bridge = NULL;
- acpiphp_put_context(context);
kfree(bridge);
mutex_unlock(&acpiphp_context_lock);
struct acpiphp_slot *slot;
struct acpiphp_func *newfunc;
acpi_status status = AE_OK;
- unsigned long long adr, sun;
- int device, function, retval;
+ unsigned long long adr;
+ int device, function;
struct pci_bus *pbus = bridge->pci_bus;
- struct pci_dev *pdev;
+ struct pci_dev *pdev = bridge->pci_dev;
u32 val;
- if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle))
+ if (pdev && device_is_managed_by_native_pciehp(pdev))
return AE_OK;
status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
if (ACPI_FAILURE(status)) {
- warn("can't evaluate _ADR (%#x)\n", status);
+ acpi_handle_warn(handle, "can't evaluate _ADR (%#x)\n", status);
return AE_OK;
}
device = (adr >> 16) & 0xffff;
function = adr & 0xffff;
- pdev = bridge->pci_dev;
- if (pdev && device_is_managed_by_native_pciehp(pdev))
- return AE_OK;
-
newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
if (!newfunc)
return AE_NO_MEMORY;
if (acpi_has_method(handle, "_DCK"))
newfunc->flags |= FUNC_HAS_DCK;
- status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
- if (ACPI_FAILURE(status)) {
- /*
- * use the count of the number of slots we've found
- * for the number of the slot
- */
- sun = bridge->nr_slots+1;
- }
-
/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
- if (slot->device == device) {
- if (slot->sun != sun)
- warn("sibling found, but _SUN doesn't match!\n");
-
+ if (slot->device == device)
goto slot_found;
- }
slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
slot->bridge = bridge;
slot->device = device;
- slot->sun = sun;
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);
- bridge->nr_slots++;
-
- dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
- slot->sun, pci_domain_nr(pbus), pbus->number, device);
- retval = acpiphp_register_hotplug_slot(slot);
- if (retval) {
- if (retval == -EBUSY)
- warn("Slot %llu already registered by another "
- "hotplug driver\n", slot->sun);
- else
- warn("acpiphp_register_hotplug_slot failed "
- "(err code = 0x%x)\n", retval);
+ /* Register slots for ejectable funtions only. */
+ if (acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) {
+ unsigned long long sun;
+ int retval;
- bridge->nr_slots--;
- mutex_lock(&bridge_mutex);
- list_del(&slot->node);
- mutex_unlock(&bridge_mutex);
- kfree(slot);
- status = AE_OK;
- goto err;
+ bridge->nr_slots++;
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
+ if (ACPI_FAILURE(status))
+ sun = bridge->nr_slots;
+
+ slot->sun = sun;
+ dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
+ slot->sun, pci_domain_nr(pbus), pbus->number, device);
+
+ retval = acpiphp_register_hotplug_slot(slot);
+ if (retval) {
+ bridge->nr_slots--;
+ if (retval == -EBUSY)
+ warn("Slot %llu already registered by another "
+ "hotplug driver\n", slot->sun);
+ else
+ warn("acpiphp_register_hotplug_slot failed "
+ "(err code = 0x%x)\n", retval);
+ }
+ /* Even if the slot registration fails, we can still use it. */
}
slot_found:
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
handle_hotplug_event,
context);
- if (ACPI_SUCCESS(status))
- context->handler_for_func = true;
- else
- err("failed to register interrupt notify handler\n");
+ if (ACPI_FAILURE(status))
+ acpi_handle_err(handle,
+ "failed to install notify handler\n");
}
return AE_OK;
return status;
}
-
-/* see if it's worth looking at this bridge */
-static int detect_ejectable_slots(acpi_handle handle)
-{
- int found = acpi_pci_detect_ejectable(handle);
- if (!found) {
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- is_pci_dock_device, NULL, (void *)&found, NULL);
- }
- return found;
-}
-
static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
{
struct acpiphp_context *context;
struct acpiphp_slot *slot;
struct acpiphp_func *func;
acpi_status status;
- acpi_handle handle = bridge->handle;
-
- if (!bridge->context->handler_for_func) {
- status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- err("failed to remove notify handler\n");
- }
list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
unregister_hotplug_dock_device(func->handle);
}
if (!(func->flags & FUNC_HAS_DCK)) {
- func->context->handler_for_func = false;
status = acpi_remove_notify_handler(func->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event);
list_for_each_entry(func, &slot->funcs, sibling) {
if (PCI_FUNC(dev->devfn) == func->function) {
- /* check if this bridge has ejectable slots */
- if ((detect_ejectable_slots(func->handle) > 0))
- dev->is_hotplug_bridge = 1;
+ dev->is_hotplug_bridge = 1;
break;
}
}
static void hotplug_event(acpi_handle handle, u32 type, void *data)
{
struct acpiphp_context *context = data;
+ struct acpiphp_func *func = context->func;
struct acpiphp_bridge *bridge;
- struct acpiphp_func *func;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
if (bridge)
get_bridge(bridge);
- /*
- * If context->func is not NULL, we are holding a reference to its
- * parent bridge, so it won't go away until we drop that reference.
- */
- func = context->func;
mutex_unlock(&acpiphp_context_lock);
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
dbg("%s: Device eject notify on %s\n", __func__, objname);
- if (!func)
- break;
-
if (bridge && !(bridge->flags & BRIDGE_HAS_EJ0))
break;
acpi_scan_lock_release();
kfree(hp_work); /* allocated in handle_hotplug_event() */
-
- mutex_lock(&acpiphp_context_lock);
- if (context->func)
- put_bridge(context->func->slot->bridge);
- else
- acpiphp_put_context(context);
-
- mutex_unlock(&acpiphp_context_lock);
+ put_bridge(context->func->slot->bridge);
}
/**
mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
if (context) {
- if (context->func) {
- get_bridge(context->func->slot->bridge);
- acpiphp_put_context(context);
- } else if (!context->bridge) {
- acpiphp_put_context(context);
- context = NULL;
- }
+ get_bridge(context->func->slot->bridge);
+ acpiphp_put_context(context);
}
mutex_unlock(&acpiphp_context_lock);
/*
*/
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
- struct acpiphp_context *context;
struct acpiphp_bridge *bridge;
acpi_handle handle;
acpi_status status;
return;
handle = ACPI_HANDLE(bus->bridge);
- if (!handle || detect_ejectable_slots(handle) <= 0)
+ if (!handle)
return;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
bridge->pci_dev = pci_dev_get(bus->self);
bridge->pci_bus = bus;
- mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
- if (!context) {
- context = acpiphp_init_context(handle);
- if (!context) {
- mutex_unlock(&acpiphp_context_lock);
- acpi_handle_err(handle, "No hotplug context\n");
- kfree(bridge);
- return;
- }
- }
- bridge->context = context;
- context->bridge = bridge;
- mutex_unlock(&acpiphp_context_lock);
-
/*
* Grab a ref to the subordinate PCI bus in case the bus is
* removed via PCI core logical hotplug. The ref pins the bus
*/
get_device(&bus->dev);
+ if (!pci_is_root_bus(bridge->pci_bus)) {
+ struct acpiphp_context *context;
+
+ /*
+ * This bridge should have been registered as a hotplug function
+ * under its parent, so the context has to be there. If not, we
+ * are in deep goo.
+ */
+ mutex_lock(&acpiphp_context_lock);
+ context = acpiphp_get_context(handle);
+ if (WARN_ON(!context || !context->func)) {
+ mutex_unlock(&acpiphp_context_lock);
+ put_device(&bus->dev);
+ kfree(bridge);
+ return;
+ }
+ bridge->context = context;
+ context->bridge = bridge;
+ /* Get a reference to the parent bridge. */
+ get_bridge(context->func->slot->bridge);
+ 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);
register_slot, NULL, bridge, NULL);
if (ACPI_FAILURE(status)) {
acpi_handle_err(bridge->handle, "failed to register slots\n");
- goto err;
- }
-
- if (pci_is_root_bus(bridge->pci_bus))
- return;
-
- if (acpi_has_method(bridge->handle, "_EJ0")) {
- dbg("found ejectable p2p bridge\n");
- bridge->flags |= BRIDGE_HAS_EJ0;
- }
- if (context->handler_for_func) {
- /* Notify handler already installed. */
- get_bridge(context->func->slot->bridge);
- return;
+ cleanup_bridge(bridge);
+ put_bridge(bridge);
}
-
- /* install notify handler for P2P bridges */
- status = acpi_install_notify_handler(bridge->handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event, NULL);
- if (ACPI_SUCCESS(status))
- return;
-
- acpi_handle_err(bridge->handle, "failed to register notify handler\n");
-
- err:
- cleanup_bridge(bridge);
- put_bridge(bridge);
}
/* Destroy hotplug slots associated with the PCI bus */