diff options
-rw-r--r-- | drivers/pci/hotplug/acpiphp.h | 5 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 126 |
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 | */ |
123 | struct acpiphp_func { | 127 | struct 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 */ | ||
342 | static 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 | |||
365 | static 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 */ |
335 | static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus) | 402 | static 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 | ||
573 | static struct pci_dev * get_apic_pci_info(acpi_handle handle) | 655 | static 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: |