diff options
| -rw-r--r-- | drivers/acpi/glue.c | 99 | ||||
| -rw-r--r-- | drivers/pci/pci-acpi.c | 15 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 6 |
3 files changed, 98 insertions, 22 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 17e15d11bd39..408f6b2a5fa8 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c | |||
| @@ -79,34 +79,99 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) | |||
| 79 | return ret; | 79 | return ret; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used, | 82 | static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, |
| 83 | void *addr_p, void **ret_p) | 83 | void *not_used, void **ret_p) |
| 84 | { | 84 | { |
| 85 | unsigned long long addr, sta; | 85 | struct acpi_device *adev = NULL; |
| 86 | acpi_status status; | ||
| 87 | 86 | ||
| 88 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); | 87 | acpi_bus_get_device(handle, &adev); |
| 89 | if (ACPI_SUCCESS(status) && addr == *((u64 *)addr_p)) { | 88 | if (adev) { |
| 90 | *ret_p = handle; | 89 | *ret_p = handle; |
| 91 | status = acpi_bus_get_status_handle(handle, &sta); | 90 | return AE_CTRL_TERMINATE; |
| 92 | if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_ENABLED)) | ||
| 93 | return AE_CTRL_TERMINATE; | ||
| 94 | } | 91 | } |
| 95 | return AE_OK; | 92 | return AE_OK; |
| 96 | } | 93 | } |
| 97 | 94 | ||
| 98 | acpi_handle acpi_get_child(acpi_handle parent, u64 address) | 95 | static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) |
| 99 | { | 96 | { |
| 100 | void *ret = NULL; | 97 | unsigned long long sta; |
| 98 | acpi_status status; | ||
| 99 | |||
| 100 | status = acpi_bus_get_status_handle(handle, &sta); | ||
| 101 | if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) | ||
| 102 | return false; | ||
| 103 | |||
| 104 | if (is_bridge) { | ||
| 105 | void *test = NULL; | ||
| 106 | |||
| 107 | /* Check if this object has at least one child device. */ | ||
| 108 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, | ||
| 109 | acpi_dev_present, NULL, NULL, &test); | ||
| 110 | return !!test; | ||
| 111 | } | ||
| 112 | return true; | ||
| 113 | } | ||
| 114 | |||
| 115 | struct find_child_context { | ||
| 116 | u64 addr; | ||
| 117 | bool is_bridge; | ||
| 118 | acpi_handle ret; | ||
| 119 | bool ret_checked; | ||
| 120 | }; | ||
| 121 | |||
| 122 | static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, | ||
| 123 | void *data, void **not_used) | ||
| 124 | { | ||
| 125 | struct find_child_context *context = data; | ||
| 126 | unsigned long long addr; | ||
| 127 | acpi_status status; | ||
| 101 | 128 | ||
| 102 | if (!parent) | 129 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); |
| 103 | return NULL; | 130 | if (ACPI_FAILURE(status) || addr != context->addr) |
| 131 | return AE_OK; | ||
| 104 | 132 | ||
| 105 | acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, NULL, | 133 | if (!context->ret) { |
| 106 | do_acpi_find_child, &address, &ret); | 134 | /* This is the first matching object. Save its handle. */ |
| 107 | return (acpi_handle)ret; | 135 | context->ret = handle; |
| 136 | return AE_OK; | ||
| 137 | } | ||
| 138 | /* | ||
| 139 | * There is more than one matching object with the same _ADR value. | ||
| 140 | * That really is unexpected, so we are kind of beyond the scope of the | ||
| 141 | * spec here. We have to choose which one to return, though. | ||
| 142 | * | ||
| 143 | * First, check if the previously found object is good enough and return | ||
| 144 | * its handle if so. Second, check the same for the object that we've | ||
| 145 | * just found. | ||
| 146 | */ | ||
| 147 | if (!context->ret_checked) { | ||
| 148 | if (acpi_extra_checks_passed(context->ret, context->is_bridge)) | ||
| 149 | return AE_CTRL_TERMINATE; | ||
| 150 | else | ||
| 151 | context->ret_checked = true; | ||
| 152 | } | ||
| 153 | if (acpi_extra_checks_passed(handle, context->is_bridge)) { | ||
| 154 | context->ret = handle; | ||
| 155 | return AE_CTRL_TERMINATE; | ||
| 156 | } | ||
| 157 | return AE_OK; | ||
| 158 | } | ||
| 159 | |||
| 160 | acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge) | ||
| 161 | { | ||
| 162 | if (parent) { | ||
| 163 | struct find_child_context context = { | ||
| 164 | .addr = addr, | ||
| 165 | .is_bridge = is_bridge, | ||
| 166 | }; | ||
| 167 | |||
| 168 | acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child, | ||
| 169 | NULL, &context, NULL); | ||
| 170 | return context.ret; | ||
| 171 | } | ||
| 172 | return NULL; | ||
| 108 | } | 173 | } |
| 109 | EXPORT_SYMBOL(acpi_get_child); | 174 | EXPORT_SYMBOL_GPL(acpi_find_child); |
| 110 | 175 | ||
| 111 | int acpi_bind_one(struct device *dev, acpi_handle handle) | 176 | int acpi_bind_one(struct device *dev, acpi_handle handle) |
| 112 | { | 177 | { |
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index dbdc5f7e2b29..01e264fb50e0 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c | |||
| @@ -317,13 +317,20 @@ void acpi_pci_remove_bus(struct pci_bus *bus) | |||
| 317 | /* ACPI bus type */ | 317 | /* ACPI bus type */ |
| 318 | static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) | 318 | static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) |
| 319 | { | 319 | { |
| 320 | struct pci_dev * pci_dev; | 320 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 321 | u64 addr; | 321 | bool is_bridge; |
| 322 | u64 addr; | ||
| 322 | 323 | ||
| 323 | pci_dev = to_pci_dev(dev); | 324 | /* |
| 325 | * pci_is_bridge() is not suitable here, because pci_dev->subordinate | ||
| 326 | * is set only after acpi_pci_find_device() has been called for the | ||
| 327 | * given device. | ||
| 328 | */ | ||
| 329 | is_bridge = pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE | ||
| 330 | || pci_dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; | ||
| 324 | /* Please ref to ACPI spec for the syntax of _ADR */ | 331 | /* Please ref to ACPI spec for the syntax of _ADR */ |
| 325 | addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); | 332 | addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); |
| 326 | *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr); | 333 | *handle = acpi_find_child(ACPI_HANDLE(dev->parent), addr, is_bridge); |
| 327 | if (!*handle) | 334 | if (!*handle) |
| 328 | return -ENODEV; | 335 | return -ENODEV; |
| 329 | return 0; | 336 | return 0; |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5026aaa35133..94383a70c1a3 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
| @@ -441,7 +441,11 @@ struct acpi_pci_root { | |||
| 441 | }; | 441 | }; |
| 442 | 442 | ||
| 443 | /* helper */ | 443 | /* helper */ |
| 444 | acpi_handle acpi_get_child(acpi_handle, u64); | 444 | acpi_handle acpi_find_child(acpi_handle, u64, bool); |
| 445 | static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr) | ||
| 446 | { | ||
| 447 | return acpi_find_child(handle, addr, false); | ||
| 448 | } | ||
| 445 | int acpi_is_root_bridge(acpi_handle); | 449 | int acpi_is_root_bridge(acpi_handle); |
| 446 | struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); | 450 | struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); |
| 447 | #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) | 451 | #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) |
