#include #include #include #include #include #include "acpi.h" #include "acpi_bus.h" #define PREFIX "ACPI: " struct acpi_handle_node { struct list_head node; ACPI_HANDLE handle; }; static const struct acpi_device_ids root_device_ids[] = { {"PNP0A03", 0}, {"", 0}, }; LIST_HEAD(acpi_pci_roots); /** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge * @handle - the ACPI CA node in question. * * Note: we could make this API take a struct acpi_device * instead, but * for now, it's more convenient to operate on an acpi_handle. */ int acpi_is_root_bridge(ACPI_HANDLE handle) { int ret; struct acpi_device *device; ret = acpi_bus_get_device(handle, &device); if (ret) return 0; ret = acpi_match_device_ids(device, root_device_ids); if (ret) return 0; else return 1; } struct acpi_pci_root *acpi_pci_find_root(ACPI_HANDLE handle) { struct acpi_pci_root *root; list_for_each_entry(root, &acpi_pci_roots, node) { if (root->device->handle == handle) return root; } return NULL; } /** * 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 = AcpiGetParent(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 (!pdev || hnd == handle) break; pbus = pdev->subordinate; // pci_dev_put(pdev); /* * This function may be called for a non-PCI device that has a * PCI parent (eg. a disk under a PCI SATA controller). In that * case pdev->subordinate will be NULL for the parent. */ if (!pbus) { dbgprintf("Not a PCI-to-PCI bridge\n"); pdev = NULL; break; } } out: list_for_each_entry_safe(node, tmp, &device_list, node) kfree(node); return pdev; } static void print_bus_irqs(struct pci_bus *bus) { struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { if(dev->pin) { dbgprintf("PCI_%x_%x bus:%d devfn: %x pin %d bios irq: %d acpi irq: %d\n", dev->vendor, dev->device, dev->busnr, dev->devfn, dev->pin, dev->irq, acpi_get_irq(dev)); }; }; } void print_pci_irqs() { struct acpi_pci_root *root; list_for_each_entry(root, &acpi_pci_roots, node) { struct pci_bus *pbus, *tbus; struct pci_dev *dev; pbus = root->bus; list_for_each_entry(dev, &pbus->devices, bus_list) { if(dev->pin) dbgprintf("PCI_%x_%x bus:%d devfn: %x pin %d bios irq: %d acpi irq: %d\n", dev->vendor, dev->device, dev->busnr, dev->devfn, dev->pin, dev->irq, acpi_get_irq(dev)); }; list_for_each_entry(tbus, &pbus->children, node) { print_bus_irqs(tbus); }; } };