aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c126
2 files changed, 122 insertions, 9 deletions
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 467ac70a46ff..17a93f890dba 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -75,6 +75,10 @@ struct acpiphp_bridge {
75 struct list_head list; 75 struct list_head list;
76 acpi_handle handle; 76 acpi_handle handle;
77 struct acpiphp_slot *slots; 77 struct acpiphp_slot *slots;
78
79 /* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */
80 struct acpiphp_func *func;
81
78 int type; 82 int type;
79 int nr_slots; 83 int nr_slots;
80 84
@@ -122,6 +126,7 @@ struct acpiphp_slot {
122 */ 126 */
123struct acpiphp_func { 127struct acpiphp_func {
124 struct acpiphp_slot *slot; /* parent */ 128 struct acpiphp_slot *slot; /* parent */
129 struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */
125 130
126 struct list_head sibling; 131 struct list_head sibling;
127 struct pci_dev *pci_dev; 132 struct pci_dev *pci_dev;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index fadd264b64c3..631efce3a6ce 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -319,6 +319,13 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
319 319
320 /* install notify handler */ 320 /* install notify handler */
321 if (bridge->type != BRIDGE_TYPE_HOST) { 321 if (bridge->type != BRIDGE_TYPE_HOST) {
322 if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
323 status = acpi_remove_notify_handler(bridge->func->handle,
324 ACPI_SYSTEM_NOTIFY,
325 handle_hotplug_event_func);
326 if (ACPI_FAILURE(status))
327 err("failed to remove notify handler\n");
328 }
322 status = acpi_install_notify_handler(bridge->handle, 329 status = acpi_install_notify_handler(bridge->handle,
323 ACPI_SYSTEM_NOTIFY, 330 ACPI_SYSTEM_NOTIFY,
324 handle_hotplug_event_bridge, 331 handle_hotplug_event_bridge,
@@ -331,6 +338,66 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
331} 338}
332 339
333 340
341/* find acpiphp_func from acpiphp_bridge */
342static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle)
343{
344 struct list_head *node, *l;
345 struct acpiphp_bridge *bridge;
346 struct acpiphp_slot *slot;
347 struct acpiphp_func *func;
348
349 list_for_each(node, &bridge_list) {
350 bridge = list_entry(node, struct acpiphp_bridge, list);
351 for (slot = bridge->slots; slot; slot = slot->next) {
352 list_for_each(l, &slot->funcs) {
353 func = list_entry(l, struct acpiphp_func,
354 sibling);
355 if (func->handle == handle)
356 return func;
357 }
358 }
359 }
360
361 return NULL;
362}
363
364
365static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)
366{
367 acpi_handle dummy_handle;
368
369 if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
370 "_STA", &dummy_handle)))
371 bridge->flags |= BRIDGE_HAS_STA;
372
373 if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
374 "_EJ0", &dummy_handle)))
375 bridge->flags |= BRIDGE_HAS_EJ0;
376
377 if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
378 "_PS0", &dummy_handle)))
379 bridge->flags |= BRIDGE_HAS_PS0;
380
381 if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
382 "_PS3", &dummy_handle)))
383 bridge->flags |= BRIDGE_HAS_PS3;
384
385 /* is this ejectable p2p bridge? */
386 if (bridge->flags & BRIDGE_HAS_EJ0) {
387 struct acpiphp_func *func;
388
389 dbg("found ejectable p2p bridge\n");
390
391 /* make link between PCI bridge and PCI function */
392 func = acpiphp_bridge_handle_to_function(bridge->handle);
393 if (!func)
394 return;
395 bridge->func = func;
396 func->bridge = bridge;
397 }
398}
399
400
334/* allocate and initialize host bridge data structure */ 401/* allocate and initialize host bridge data structure */
335static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus) 402static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus)
336{ 403{
@@ -364,6 +431,7 @@ static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev)
364 431
365 bridge->type = BRIDGE_TYPE_P2P; 432 bridge->type = BRIDGE_TYPE_P2P;
366 bridge->handle = handle; 433 bridge->handle = handle;
434 config_p2p_bridge_flags(bridge);
367 435
368 bridge->pci_dev = pci_dev_get(pci_dev); 436 bridge->pci_dev = pci_dev_get(pci_dev);
369 bridge->pci_bus = pci_dev->subordinate; 437 bridge->pci_bus = pci_dev->subordinate;
@@ -423,7 +491,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
423 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, 491 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
424 find_p2p_bridge, dev->subordinate, NULL); 492 find_p2p_bridge, dev->subordinate, NULL);
425 if (ACPI_FAILURE(status)) 493 if (ACPI_FAILURE(status))
426 warn("find_p2p_bridge faied (error code = 0x%x)\n", status); 494 warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
427 495
428 out: 496 out:
429 pci_dev_put(dev); 497 pci_dev_put(dev);
@@ -486,7 +554,7 @@ static int add_bridge(acpi_handle handle)
486 find_p2p_bridge, pci_bus, NULL); 554 find_p2p_bridge, pci_bus, NULL);
487 555
488 if (ACPI_FAILURE(status)) 556 if (ACPI_FAILURE(status))
489 warn("find_p2p_bridge faied (error code = 0x%x)\n",status); 557 warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
490 558
491 return 0; 559 return 0;
492} 560}
@@ -516,6 +584,16 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
516 if (ACPI_FAILURE(status)) 584 if (ACPI_FAILURE(status))
517 err("failed to remove notify handler\n"); 585 err("failed to remove notify handler\n");
518 586
587 if ((bridge->type != BRIDGE_TYPE_HOST) &&
588 ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) {
589 status = acpi_install_notify_handler(bridge->func->handle,
590 ACPI_SYSTEM_NOTIFY,
591 handle_hotplug_event_func,
592 bridge->func);
593 if (ACPI_FAILURE(status))
594 err("failed to install interrupt notify handler\n");
595 }
596
519 slot = bridge->slots; 597 slot = bridge->slots;
520 while (slot) { 598 while (slot) {
521 struct acpiphp_slot *next = slot->next; 599 struct acpiphp_slot *next = slot->next;
@@ -549,6 +627,11 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
549{ 627{
550 struct acpiphp_bridge *bridge; 628 struct acpiphp_bridge *bridge;
551 629
630 /* cleanup p2p bridges under this P2P bridge
631 in a depth-first manner */
632 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
633 cleanup_p2p_bridge, NULL, NULL);
634
552 if (!(bridge = acpiphp_handle_to_bridge(handle))) 635 if (!(bridge = acpiphp_handle_to_bridge(handle)))
553 return AE_OK; 636 return AE_OK;
554 cleanup_bridge(bridge); 637 cleanup_bridge(bridge);
@@ -559,15 +642,14 @@ static void remove_bridge(acpi_handle handle)
559{ 642{
560 struct acpiphp_bridge *bridge; 643 struct acpiphp_bridge *bridge;
561 644
645 /* cleanup p2p bridges under this host bridge
646 in a depth-first manner */
647 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
648 (u32)1, cleanup_p2p_bridge, NULL, NULL);
649
562 bridge = acpiphp_handle_to_bridge(handle); 650 bridge = acpiphp_handle_to_bridge(handle);
563 if (bridge) { 651 if (bridge)
564 cleanup_bridge(bridge); 652 cleanup_bridge(bridge);
565 } else {
566 /* clean-up p2p bridges under this host bridge */
567 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
568 ACPI_UINT32_MAX, cleanup_p2p_bridge,
569 NULL, NULL);
570 }
571} 653}
572 654
573static struct pci_dev * get_apic_pci_info(acpi_handle handle) 655static struct pci_dev * get_apic_pci_info(acpi_handle handle)
@@ -881,6 +963,7 @@ static int enable_device(struct acpiphp_slot *slot)
881 struct acpiphp_func *func; 963 struct acpiphp_func *func;
882 int retval = 0; 964 int retval = 0;
883 int num, max, pass; 965 int num, max, pass;
966 acpi_status status;
884 967
885 if (slot->flags & SLOT_ENABLED) 968 if (slot->flags & SLOT_ENABLED)
886 goto err_exit; 969 goto err_exit;
@@ -933,6 +1016,17 @@ static int enable_device(struct acpiphp_slot *slot)
933 func = list_entry(l, struct acpiphp_func, sibling); 1016 func = list_entry(l, struct acpiphp_func, sibling);
934 func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 1017 func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
935 func->function)); 1018 func->function));
1019 if (!func->pci_dev)
1020 continue;
1021
1022 if (func->pci_dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
1023 func->pci_dev->hdr_type != PCI_HEADER_TYPE_CARDBUS)
1024 continue;
1025
1026 status = find_p2p_bridge(func->handle, (u32)1, bus, NULL);
1027 if (ACPI_FAILURE(status))
1028 warn("find_p2p_bridge failed (error code = 0x%x)\n",
1029 status);
936 } 1030 }
937 1031
938 slot->flags |= SLOT_ENABLED; 1032 slot->flags |= SLOT_ENABLED;
@@ -958,6 +1052,13 @@ static int disable_device(struct acpiphp_slot *slot)
958 list_for_each (l, &slot->funcs) { 1052 list_for_each (l, &slot->funcs) {
959 func = list_entry(l, struct acpiphp_func, sibling); 1053 func = list_entry(l, struct acpiphp_func, sibling);
960 1054
1055 if (func->bridge) {
1056 /* cleanup p2p bridges under this P2P bridge */
1057 cleanup_p2p_bridge(func->bridge->handle,
1058 (u32)1, NULL, NULL);
1059 func->bridge = NULL;
1060 }
1061
961 acpiphp_bus_trim(func->handle); 1062 acpiphp_bus_trim(func->handle);
962 /* try to remove anyway. 1063 /* try to remove anyway.
963 * acpiphp_bus_add might have been failed */ 1064 * acpiphp_bus_add might have been failed */
@@ -1292,6 +1393,13 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont
1292 case ACPI_NOTIFY_EJECT_REQUEST: 1393 case ACPI_NOTIFY_EJECT_REQUEST:
1293 /* request device eject */ 1394 /* request device eject */
1294 dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); 1395 dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
1396 if ((bridge->type != BRIDGE_TYPE_HOST) &&
1397 (bridge->flags & BRIDGE_HAS_EJ0)) {
1398 struct acpiphp_slot *slot;
1399 slot = bridge->func->slot;
1400 if (!acpiphp_disable_slot(slot))
1401 acpiphp_eject_slot(slot);
1402 }
1295 break; 1403 break;
1296 1404
1297 case ACPI_NOTIFY_FREQUENCY_MISMATCH: 1405 case ACPI_NOTIFY_FREQUENCY_MISMATCH: