diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/glue.c | 99 | ||||
-rw-r--r-- | drivers/pci/pci-acpi.c | 15 |
2 files changed, 93 insertions, 21 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; |