diff options
Diffstat (limited to 'drivers/acpi/pci_root.c')
-rw-r--r-- | drivers/acpi/pci_root.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index bf5108ad4d63..417487a201fb 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c | |||
@@ -655,3 +655,133 @@ int __init acpi_pci_root_init(void) | |||
655 | 655 | ||
656 | return 0; | 656 | return 0; |
657 | } | 657 | } |
658 | /* Support root bridge hotplug */ | ||
659 | |||
660 | static void handle_root_bridge_insertion(acpi_handle handle) | ||
661 | { | ||
662 | struct acpi_device *device; | ||
663 | |||
664 | if (!acpi_bus_get_device(handle, &device)) { | ||
665 | printk(KERN_DEBUG "acpi device exists...\n"); | ||
666 | return; | ||
667 | } | ||
668 | |||
669 | if (acpi_bus_scan(handle)) | ||
670 | printk(KERN_ERR "cannot add bridge to acpi list\n"); | ||
671 | } | ||
672 | |||
673 | static void handle_root_bridge_removal(struct acpi_device *device) | ||
674 | { | ||
675 | struct acpi_eject_event *ej_event; | ||
676 | |||
677 | ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); | ||
678 | if (!ej_event) { | ||
679 | /* Inform firmware the hot-remove operation has error */ | ||
680 | (void) acpi_evaluate_hotplug_ost(device->handle, | ||
681 | ACPI_NOTIFY_EJECT_REQUEST, | ||
682 | ACPI_OST_SC_NON_SPECIFIC_FAILURE, | ||
683 | NULL); | ||
684 | return; | ||
685 | } | ||
686 | |||
687 | ej_event->device = device; | ||
688 | ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; | ||
689 | |||
690 | acpi_bus_hot_remove_device(ej_event); | ||
691 | } | ||
692 | |||
693 | static void _handle_hotplug_event_root(struct work_struct *work) | ||
694 | { | ||
695 | struct acpi_pci_root *root; | ||
696 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; | ||
697 | struct acpi_hp_work *hp_work; | ||
698 | acpi_handle handle; | ||
699 | u32 type; | ||
700 | |||
701 | hp_work = container_of(work, struct acpi_hp_work, work); | ||
702 | handle = hp_work->handle; | ||
703 | type = hp_work->type; | ||
704 | |||
705 | root = acpi_pci_find_root(handle); | ||
706 | |||
707 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | ||
708 | |||
709 | switch (type) { | ||
710 | case ACPI_NOTIFY_BUS_CHECK: | ||
711 | /* bus enumerate */ | ||
712 | printk(KERN_DEBUG "%s: Bus check notify on %s\n", __func__, | ||
713 | (char *)buffer.pointer); | ||
714 | if (!root) | ||
715 | handle_root_bridge_insertion(handle); | ||
716 | |||
717 | break; | ||
718 | |||
719 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
720 | /* device check */ | ||
721 | printk(KERN_DEBUG "%s: Device check notify on %s\n", __func__, | ||
722 | (char *)buffer.pointer); | ||
723 | if (!root) | ||
724 | handle_root_bridge_insertion(handle); | ||
725 | break; | ||
726 | |||
727 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
728 | /* request device eject */ | ||
729 | printk(KERN_DEBUG "%s: Device eject notify on %s\n", __func__, | ||
730 | (char *)buffer.pointer); | ||
731 | if (root) | ||
732 | handle_root_bridge_removal(root->device); | ||
733 | break; | ||
734 | default: | ||
735 | printk(KERN_WARNING "notify_handler: unknown event type 0x%x for %s\n", | ||
736 | type, (char *)buffer.pointer); | ||
737 | break; | ||
738 | } | ||
739 | |||
740 | kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ | ||
741 | kfree(buffer.pointer); | ||
742 | } | ||
743 | |||
744 | static void handle_hotplug_event_root(acpi_handle handle, u32 type, | ||
745 | void *context) | ||
746 | { | ||
747 | alloc_acpi_hp_work(handle, type, context, | ||
748 | _handle_hotplug_event_root); | ||
749 | } | ||
750 | |||
751 | static acpi_status __init | ||
752 | find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
753 | { | ||
754 | acpi_status status; | ||
755 | char objname[64]; | ||
756 | struct acpi_buffer buffer = { .length = sizeof(objname), | ||
757 | .pointer = objname }; | ||
758 | int *count = (int *)context; | ||
759 | |||
760 | if (!acpi_is_root_bridge(handle)) | ||
761 | return AE_OK; | ||
762 | |||
763 | (*count)++; | ||
764 | |||
765 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | ||
766 | |||
767 | status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | ||
768 | handle_hotplug_event_root, NULL); | ||
769 | if (ACPI_FAILURE(status)) | ||
770 | printk(KERN_DEBUG "acpi root: %s notify handler is not installed, exit status: %u\n", | ||
771 | objname, (unsigned int)status); | ||
772 | else | ||
773 | printk(KERN_DEBUG "acpi root: %s notify handler is installed\n", | ||
774 | objname); | ||
775 | |||
776 | return AE_OK; | ||
777 | } | ||
778 | |||
779 | void __init acpi_pci_root_hp_init(void) | ||
780 | { | ||
781 | int num = 0; | ||
782 | |||
783 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
784 | ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); | ||
785 | |||
786 | printk(KERN_DEBUG "Found %d acpi root devices\n", num); | ||
787 | } | ||