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