|
@@ -329,6 +329,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+struct acpi_handle_node {
|
|
|
+ struct list_head node;
|
|
|
+ acpi_handle handle;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev
|
|
|
+ * @handle: the handle in question
|
|
|
+ *
|
|
|
+ * Given an ACPI CA handle, the desired PCI device is located in the
|
|
|
+ * list of PCI devices.
|
|
|
+ *
|
|
|
+ * If the device is found, its reference count is increased and this
|
|
|
+ * function returns a pointer to its data structure. The caller must
|
|
|
+ * decrement the reference count by calling pci_dev_put().
|
|
|
+ * If no device is found, %NULL is returned.
|
|
|
+ */
|
|
|
+struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
|
|
|
+{
|
|
|
+ int dev, fn;
|
|
|
+ unsigned long long adr;
|
|
|
+ acpi_status status;
|
|
|
+ acpi_handle phandle;
|
|
|
+ struct pci_bus *pbus;
|
|
|
+ struct pci_dev *pdev = NULL;
|
|
|
+ struct acpi_handle_node *node, *tmp;
|
|
|
+ struct acpi_pci_root *root;
|
|
|
+ LIST_HEAD(device_list);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Walk up the ACPI CA namespace until we reach a PCI root bridge.
|
|
|
+ */
|
|
|
+ phandle = handle;
|
|
|
+ while (!acpi_is_root_bridge(phandle)) {
|
|
|
+ node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL);
|
|
|
+ if (!node)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&node->node);
|
|
|
+ node->handle = phandle;
|
|
|
+ list_add(&node->node, &device_list);
|
|
|
+
|
|
|
+ status = acpi_get_parent(phandle, &phandle);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ root = acpi_pci_find_root(phandle);
|
|
|
+ if (!root)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ pbus = root->bus;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now, walk back down the PCI device tree until we return to our
|
|
|
+ * original handle. Assumes that everything between the PCI root
|
|
|
+ * bridge and the device we're looking for must be a P2P bridge.
|
|
|
+ */
|
|
|
+ list_for_each_entry(node, &device_list, node) {
|
|
|
+ acpi_handle hnd = node->handle;
|
|
|
+ status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ goto out;
|
|
|
+ dev = (adr >> 16) & 0xffff;
|
|
|
+ fn = adr & 0xffff;
|
|
|
+
|
|
|
+ pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn));
|
|
|
+ if (hnd == handle)
|
|
|
+ break;
|
|
|
+
|
|
|
+ pbus = pdev->subordinate;
|
|
|
+ pci_dev_put(pdev);
|
|
|
+ }
|
|
|
+out:
|
|
|
+ list_for_each_entry_safe(node, tmp, &device_list, node)
|
|
|
+ kfree(node);
|
|
|
+
|
|
|
+ return pdev;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
|
|
|
+
|
|
|
/**
|
|
|
* acpi_pci_osc_control_set - commit requested control to Firmware
|
|
|
* @handle: acpi_handle for the target ACPI object
|