diff options
| -rw-r--r-- | drivers/acpi/glue.c | 137 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 3 |
2 files changed, 58 insertions, 82 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index a22a295edb69..ea77512ad70c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c | |||
| @@ -82,107 +82,80 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) | |||
| 82 | #define FIND_CHILD_MIN_SCORE 1 | 82 | #define FIND_CHILD_MIN_SCORE 1 |
| 83 | #define FIND_CHILD_MAX_SCORE 2 | 83 | #define FIND_CHILD_MAX_SCORE 2 |
| 84 | 84 | ||
| 85 | static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, | 85 | static int find_child_checks(struct acpi_device *adev, bool check_children) |
| 86 | void *not_used, void **ret_p) | ||
| 87 | { | ||
| 88 | struct acpi_device *adev = NULL; | ||
| 89 | |||
| 90 | acpi_bus_get_device(handle, &adev); | ||
| 91 | if (adev) { | ||
| 92 | *ret_p = handle; | ||
| 93 | return AE_CTRL_TERMINATE; | ||
| 94 | } | ||
| 95 | return AE_OK; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int do_find_child_checks(acpi_handle handle, bool is_bridge) | ||
| 99 | { | 86 | { |
| 100 | bool sta_present = true; | 87 | bool sta_present = true; |
| 101 | unsigned long long sta; | 88 | unsigned long long sta; |
| 102 | acpi_status status; | 89 | acpi_status status; |
| 103 | 90 | ||
| 104 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | 91 | status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); |
| 105 | if (status == AE_NOT_FOUND) | 92 | if (status == AE_NOT_FOUND) |
| 106 | sta_present = false; | 93 | sta_present = false; |
| 107 | else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) | 94 | else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) |
| 108 | return -ENODEV; | 95 | return -ENODEV; |
| 109 | 96 | ||
| 110 | if (is_bridge) { | 97 | if (check_children && list_empty(&adev->children)) |
| 111 | void *test = NULL; | 98 | return -ENODEV; |
| 112 | 99 | ||
| 113 | /* Check if this object has at least one child device. */ | ||
| 114 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, | ||
| 115 | acpi_dev_present, NULL, NULL, &test); | ||
| 116 | if (!test) | ||
| 117 | return -ENODEV; | ||
| 118 | } | ||
| 119 | return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; | 100 | return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; |
| 120 | } | 101 | } |
| 121 | 102 | ||
| 122 | struct find_child_context { | 103 | struct acpi_device *acpi_find_child_device(struct acpi_device *parent, |
| 123 | u64 addr; | 104 | u64 address, bool check_children) |
| 124 | bool is_bridge; | ||
| 125 | acpi_handle ret; | ||
| 126 | int ret_score; | ||
| 127 | }; | ||
| 128 | |||
| 129 | static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, | ||
| 130 | void *data, void **not_used) | ||
| 131 | { | 105 | { |
| 132 | struct find_child_context *context = data; | 106 | struct acpi_device *adev, *ret = NULL; |
| 133 | unsigned long long addr; | 107 | int ret_score = 0; |
| 134 | acpi_status status; | 108 | |
| 135 | int score; | 109 | list_for_each_entry(adev, &parent->children, node) { |
| 136 | 110 | unsigned long long addr; | |
| 137 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); | 111 | acpi_status status; |
| 138 | if (ACPI_FAILURE(status) || addr != context->addr) | 112 | int score; |
| 139 | return AE_OK; | 113 | |
| 140 | 114 | status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR, | |
| 141 | if (!context->ret) { | 115 | NULL, &addr); |
| 142 | /* This is the first matching object. Save its handle. */ | 116 | if (ACPI_FAILURE(status) || addr != address) |
| 143 | context->ret = handle; | 117 | continue; |
| 144 | return AE_OK; | 118 | |
| 145 | } | 119 | if (!ret) { |
| 146 | /* | 120 | /* This is the first matching object. Save it. */ |
| 147 | * There is more than one matching object with the same _ADR value. | 121 | ret = adev; |
| 148 | * That really is unexpected, so we are kind of beyond the scope of the | 122 | continue; |
| 149 | * spec here. We have to choose which one to return, though. | 123 | } |
| 150 | * | 124 | /* |
| 151 | * First, check if the previously found object is good enough and return | 125 | * There is more than one matching device object with the same |
| 152 | * its handle if so. Second, check the same for the object that we've | 126 | * _ADR value. That really is unexpected, so we are kind of |
| 153 | * just found. | 127 | * beyond the scope of the spec here. We have to choose which |
| 154 | */ | 128 | * one to return, though. |
| 155 | if (!context->ret_score) { | 129 | * |
| 156 | score = do_find_child_checks(context->ret, context->is_bridge); | 130 | * First, check if the previously found object is good enough |
| 157 | if (score == FIND_CHILD_MAX_SCORE) | 131 | * and return it if so. Second, do the same for the object that |
| 158 | return AE_CTRL_TERMINATE; | 132 | * we've just found. |
| 159 | else | 133 | */ |
| 160 | context->ret_score = score; | 134 | if (!ret_score) { |
| 161 | } | 135 | ret_score = find_child_checks(ret, check_children); |
| 162 | score = do_find_child_checks(handle, context->is_bridge); | 136 | if (ret_score == FIND_CHILD_MAX_SCORE) |
| 163 | if (score == FIND_CHILD_MAX_SCORE) { | 137 | return ret; |
| 164 | context->ret = handle; | 138 | } |
| 165 | return AE_CTRL_TERMINATE; | 139 | score = find_child_checks(adev, check_children); |
| 166 | } else if (score > context->ret_score) { | 140 | if (score == FIND_CHILD_MAX_SCORE) { |
| 167 | context->ret = handle; | 141 | return adev; |
| 168 | context->ret_score = score; | 142 | } else if (score > ret_score) { |
| 143 | ret = adev; | ||
| 144 | ret_score = score; | ||
| 145 | } | ||
| 169 | } | 146 | } |
| 170 | return AE_OK; | 147 | return ret; |
| 171 | } | 148 | } |
| 172 | 149 | ||
| 173 | acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge) | 150 | acpi_handle acpi_find_child(acpi_handle handle, u64 addr, bool is_bridge) |
| 174 | { | 151 | { |
| 175 | if (parent) { | 152 | struct acpi_device *adev; |
| 176 | struct find_child_context context = { | 153 | |
| 177 | .addr = addr, | 154 | if (!handle || acpi_bus_get_device(handle, &adev)) |
| 178 | .is_bridge = is_bridge, | 155 | return NULL; |
| 179 | }; | 156 | |
| 180 | 157 | adev = acpi_find_child_device(adev, addr, is_bridge); | |
| 181 | acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child, | 158 | return adev ? adev->handle : NULL; |
| 182 | NULL, &context, NULL); | ||
| 183 | return context.ret; | ||
| 184 | } | ||
| 185 | return NULL; | ||
| 186 | } | 159 | } |
| 187 | EXPORT_SYMBOL_GPL(acpi_find_child); | 160 | EXPORT_SYMBOL_GPL(acpi_find_child); |
| 188 | 161 | ||
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b241b733052e..6d82c5c14c0f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
| @@ -431,6 +431,9 @@ struct acpi_pci_root { | |||
| 431 | }; | 431 | }; |
| 432 | 432 | ||
| 433 | /* helper */ | 433 | /* helper */ |
| 434 | |||
| 435 | struct acpi_device *acpi_find_child_device(struct acpi_device *parent, | ||
| 436 | u64 address, bool check_children); | ||
| 434 | acpi_handle acpi_find_child(acpi_handle, u64, bool); | 437 | acpi_handle acpi_find_child(acpi_handle, u64, bool); |
| 435 | static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr) | 438 | static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr) |
| 436 | { | 439 | { |
