diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 143 |
1 files changed, 106 insertions, 37 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a1b226eb2ce2..80b668c80073 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -752,6 +752,7 @@ static bool acpi_info_matches_ids(struct acpi_device_info *info, /* List of HIDs for which we ignore matching ACPI devices, when checking _DEP lists. */ static const char * const acpi_ignore_dep_ids[] = { "PNP0D80", /* Windows-compatible System Power Management Controller */ + "INT33BD", /* Intel Baytrail Mailbox Device */ NULL }; @@ -1635,8 +1636,6 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); acpi_init_coherency(device); - /* Assume there are unmet deps until acpi_device_dep_initialize() runs */ - device->dep_unmet = 1; } void acpi_device_add_finalize(struct acpi_device *device) @@ -1842,32 +1841,36 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) } } -static void acpi_device_dep_initialize(struct acpi_device *adev) +static u32 acpi_scan_check_dep(acpi_handle handle) { - struct acpi_dep_data *dep; struct acpi_handle_list dep_devices; acpi_status status; + u32 count; int i; - adev->dep_unmet = 0; - - if (!acpi_has_method(adev->handle, "_DEP")) - return; + /* + * Check for _HID here to avoid deferring the enumeration of: + * 1. PCI devices. + * 2. ACPI nodes describing USB ports. + * Still, checking for _HID catches more then just these cases ... + */ + if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID")) + return 0; - status = acpi_evaluate_reference(adev->handle, "_DEP", NULL, - &dep_devices); + status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); if (ACPI_FAILURE(status)) { - dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n"); - return; + acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); + return 0; } - for (i = 0; i < dep_devices.count; i++) { + for (count = 0, i = 0; i < dep_devices.count; i++) { struct acpi_device_info *info; - int skip; + struct acpi_dep_data *dep; + bool skip; status = acpi_get_object_info(dep_devices.handles[i], &info); if (ACPI_FAILURE(status)) { - dev_dbg(&adev->dev, "Error reading _DEP device info\n"); + acpi_handle_debug(handle, "Error reading _DEP device info\n"); continue; } @@ -1877,26 +1880,45 @@ static void acpi_device_dep_initialize(struct acpi_device *adev) if (skip) continue; - dep = kzalloc(sizeof(struct acpi_dep_data), GFP_KERNEL); + dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) - return; + continue; + + count++; dep->supplier = dep_devices.handles[i]; - dep->consumer = adev->handle; - adev->dep_unmet++; + dep->consumer = handle; mutex_lock(&acpi_dep_list_lock); list_add_tail(&dep->node , &acpi_dep_list); mutex_unlock(&acpi_dep_list_lock); } + + return count; } -static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, - void *not_used, void **return_value) +static void acpi_scan_dep_init(struct acpi_device *adev) +{ + struct acpi_dep_data *dep; + + mutex_lock(&acpi_dep_list_lock); + + list_for_each_entry(dep, &acpi_dep_list, node) { + if (dep->consumer == adev->handle) + adev->dep_unmet++; + } + + mutex_unlock(&acpi_dep_list_lock); +} + +static bool acpi_bus_scan_second_pass; + +static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, + struct acpi_device **adev_p) { struct acpi_device *device = NULL; - int type; unsigned long long sta; + int type; int result; acpi_bus_get_device(handle, &device); @@ -1912,20 +1934,42 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } + if (type == ACPI_BUS_TYPE_DEVICE && check_dep) { + u32 count = acpi_scan_check_dep(handle); + /* Bail out if the number of recorded dependencies is not 0. */ + if (count > 0) { + acpi_bus_scan_second_pass = true; + return AE_CTRL_DEPTH; + } + } + acpi_add_single_object(&device, handle, type, sta); if (!device) return AE_CTRL_DEPTH; acpi_scan_init_hotplug(device); - acpi_device_dep_initialize(device); + if (!check_dep) + acpi_scan_dep_init(device); - out: - if (!*return_value) - *return_value = device; +out: + if (!*adev_p) + *adev_p = device; return AE_OK; } +static acpi_status acpi_bus_check_add_1(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **ret_p) +{ + return acpi_bus_check_add(handle, true, (struct acpi_device **)ret_p); +} + +static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **ret_p) +{ + return acpi_bus_check_add(handle, false, (struct acpi_device **)ret_p); +} + static void acpi_default_enumeration(struct acpi_device *device) { /* @@ -1993,12 +2037,16 @@ static int acpi_scan_attach_handler(struct acpi_device *device) return ret; } -static void acpi_bus_attach(struct acpi_device *device) +static void acpi_bus_attach(struct acpi_device *device, bool first_pass) { struct acpi_device *child; + bool skip = !first_pass && device->flags.visited; acpi_handle ejd; int ret; + if (skip) + goto ok; + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) register_dock_dependent_device(device, ejd); @@ -2045,9 +2093,9 @@ static void acpi_bus_attach(struct acpi_device *device) ok: list_for_each_entry(child, &device->children, node) - acpi_bus_attach(child); + acpi_bus_attach(child, first_pass); - if (device->handler && device->handler->hotplug.notify_online) + if (!skip && device->handler && device->handler->hotplug.notify_online) device->handler->hotplug.notify_online(device); } @@ -2065,7 +2113,8 @@ void acpi_walk_dep_device_list(acpi_handle handle) adev->dep_unmet--; if (!adev->dep_unmet) - acpi_bus_attach(adev); + acpi_bus_attach(adev, true); + list_del(&dep->node); kfree(dep); } @@ -2090,17 +2139,37 @@ EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list); */ int acpi_bus_scan(acpi_handle handle) { - void *device = NULL; + struct acpi_device *device = NULL; + + acpi_bus_scan_second_pass = false; - if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) + /* Pass 1: Avoid enumerating devices with missing dependencies. */ + + if (ACPI_SUCCESS(acpi_bus_check_add(handle, true, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_check_add, NULL, NULL, &device); + acpi_bus_check_add_1, NULL, NULL, + (void **)&device); + + if (!device) + return -ENODEV; - if (device) { - acpi_bus_attach(device); + acpi_bus_attach(device, true); + + if (!acpi_bus_scan_second_pass) return 0; - } - return -ENODEV; + + /* Pass 2: Enumerate all of the remaining devices. */ + + device = NULL; + + if (ACPI_SUCCESS(acpi_bus_check_add(handle, false, &device))) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_check_add_2, NULL, NULL, + (void **)&device); + + acpi_bus_attach(device, false); + + return 0; } EXPORT_SYMBOL(acpi_bus_scan); |