From 11b88ee275ec8590a373396888c2460ee89364d6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 9 Sep 2013 23:07:47 +0200 Subject: ACPI / bind: Prefer device objects with _STA to those without it As reported at https://bugzilla.kernel.org/show_bug.cgi?id=60829, there still are cases in which do_find_child() doesn't choose the ACPI device object it is "expected" to choose if there are more such objects matching one PCI device present. This particular problem may be worked around by making do_find_child() return device obejcts witn _STA whose result indicates that the device is enabled before device objects without _STA if there's more than one device object to choose from. This change doesn't affect the case in which there's only one matching ACPI device object per PCI device. References: https://bugzilla.kernel.org/show_bug.cgi?id=60829 Reported-by: Peter Wu Tested-by: Felix Lisczyk Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers/acpi/glue.c') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 94672297e1b1..10f0f40587bb 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -79,6 +79,9 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) return ret; } +#define FIND_CHILD_MIN_SCORE 1 +#define FIND_CHILD_MAX_SCORE 2 + static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_p) { @@ -92,14 +95,17 @@ static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) +static int do_find_child_checks(acpi_handle handle, bool is_bridge) { + bool sta_present = true; unsigned long long sta; acpi_status status; - status = acpi_bus_get_status_handle(handle, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) - return false; + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (status == AE_NOT_FOUND) + sta_present = false; + else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) + return -ENODEV; if (is_bridge) { void *test = NULL; @@ -107,16 +113,17 @@ static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) /* Check if this object has at least one child device. */ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, acpi_dev_present, NULL, NULL, &test); - return !!test; + if (!test) + return -ENODEV; } - return true; + return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; } struct find_child_context { u64 addr; bool is_bridge; acpi_handle ret; - bool ret_checked; + int ret_score; }; static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, @@ -125,6 +132,7 @@ static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, struct find_child_context *context = data; unsigned long long addr; acpi_status status; + int score; status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); if (ACPI_FAILURE(status) || addr != context->addr) @@ -144,15 +152,20 @@ static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, * its handle if so. Second, check the same for the object that we've * just found. */ - if (!context->ret_checked) { - if (acpi_extra_checks_passed(context->ret, context->is_bridge)) + if (!context->ret_score) { + score = do_find_child_checks(context->ret, context->is_bridge); + if (score == FIND_CHILD_MAX_SCORE) return AE_CTRL_TERMINATE; else - context->ret_checked = true; + context->ret_score = score; } - if (acpi_extra_checks_passed(handle, context->is_bridge)) { + score = do_find_child_checks(handle, context->is_bridge); + if (score == FIND_CHILD_MAX_SCORE) { context->ret = handle; return AE_CTRL_TERMINATE; + } else if (score > context->ret_score) { + context->ret = handle; + context->ret_score = score; } return AE_OK; } -- cgit v1.2.3