aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/glue.c99
-rw-r--r--drivers/pci/pci-acpi.c15
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
82static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used, 82static 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
98acpi_handle acpi_get_child(acpi_handle parent, u64 address) 95static 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
115struct find_child_context {
116 u64 addr;
117 bool is_bridge;
118 acpi_handle ret;
119 bool ret_checked;
120};
121
122static 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
160acpi_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}
109EXPORT_SYMBOL(acpi_get_child); 174EXPORT_SYMBOL_GPL(acpi_find_child);
110 175
111int acpi_bind_one(struct device *dev, acpi_handle handle) 176int 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 */
318static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) 318static 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;