diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/pci_root.c | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 888cb9f5c5fb..e5099919e574 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c | |||
@@ -329,6 +329,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) | |||
329 | return NULL; | 329 | return NULL; |
330 | } | 330 | } |
331 | 331 | ||
332 | struct acpi_handle_node { | ||
333 | struct list_head node; | ||
334 | acpi_handle handle; | ||
335 | }; | ||
336 | |||
337 | /** | ||
338 | * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev | ||
339 | * @handle: the handle in question | ||
340 | * | ||
341 | * Given an ACPI CA handle, the desired PCI device is located in the | ||
342 | * list of PCI devices. | ||
343 | * | ||
344 | * If the device is found, its reference count is increased and this | ||
345 | * function returns a pointer to its data structure. The caller must | ||
346 | * decrement the reference count by calling pci_dev_put(). | ||
347 | * If no device is found, %NULL is returned. | ||
348 | */ | ||
349 | struct pci_dev *acpi_get_pci_dev(acpi_handle handle) | ||
350 | { | ||
351 | int dev, fn; | ||
352 | unsigned long long adr; | ||
353 | acpi_status status; | ||
354 | acpi_handle phandle; | ||
355 | struct pci_bus *pbus; | ||
356 | struct pci_dev *pdev = NULL; | ||
357 | struct acpi_handle_node *node, *tmp; | ||
358 | struct acpi_pci_root *root; | ||
359 | LIST_HEAD(device_list); | ||
360 | |||
361 | /* | ||
362 | * Walk up the ACPI CA namespace until we reach a PCI root bridge. | ||
363 | */ | ||
364 | phandle = handle; | ||
365 | while (!acpi_is_root_bridge(phandle)) { | ||
366 | node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL); | ||
367 | if (!node) | ||
368 | goto out; | ||
369 | |||
370 | INIT_LIST_HEAD(&node->node); | ||
371 | node->handle = phandle; | ||
372 | list_add(&node->node, &device_list); | ||
373 | |||
374 | status = acpi_get_parent(phandle, &phandle); | ||
375 | if (ACPI_FAILURE(status)) | ||
376 | goto out; | ||
377 | } | ||
378 | |||
379 | root = acpi_pci_find_root(phandle); | ||
380 | if (!root) | ||
381 | goto out; | ||
382 | |||
383 | pbus = root->bus; | ||
384 | |||
385 | /* | ||
386 | * Now, walk back down the PCI device tree until we return to our | ||
387 | * original handle. Assumes that everything between the PCI root | ||
388 | * bridge and the device we're looking for must be a P2P bridge. | ||
389 | */ | ||
390 | list_for_each_entry(node, &device_list, node) { | ||
391 | acpi_handle hnd = node->handle; | ||
392 | status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr); | ||
393 | if (ACPI_FAILURE(status)) | ||
394 | goto out; | ||
395 | dev = (adr >> 16) & 0xffff; | ||
396 | fn = adr & 0xffff; | ||
397 | |||
398 | pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn)); | ||
399 | if (hnd == handle) | ||
400 | break; | ||
401 | |||
402 | pbus = pdev->subordinate; | ||
403 | pci_dev_put(pdev); | ||
404 | } | ||
405 | out: | ||
406 | list_for_each_entry_safe(node, tmp, &device_list, node) | ||
407 | kfree(node); | ||
408 | |||
409 | return pdev; | ||
410 | } | ||
411 | EXPORT_SYMBOL_GPL(acpi_get_pci_dev); | ||
412 | |||
332 | /** | 413 | /** |
333 | * acpi_pci_osc_control_set - commit requested control to Firmware | 414 | * acpi_pci_osc_control_set - commit requested control to Firmware |
334 | * @handle: acpi_handle for the target ACPI object | 415 | * @handle: acpi_handle for the target ACPI object |