kolibrios-gitea/drivers/devman/pci_root.c

191 lines
4.7 KiB
C
Raw Normal View History

#include <ddk.h>
#include <linux/errno.h>
#include <mutex.h>
#include <pci.h>
#include <syscall.h>
#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)
{
dbgprintf("PCI_%x_%x bus:%d devfn: %x bios irq %d acpi irq %d\n",
dev->vendor, dev->device, dev->busnr, dev->devfn,
dev->irq, acpi_get_irq(dev));
};
}
void print_pci_irqs()
{
struct acpi_pci_root *root;
ENTER();
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)
{
dbgprintf("PCI_%x_%x bus:%d devfn: %x bios irq %d acpi irq %d\n",
dev->vendor, dev->device, dev->busnr, dev->devfn,
dev->irq, acpi_get_irq(dev));
};
list_for_each_entry(tbus, &pbus->children, node)
{
print_bus_irqs(tbus);
};
}
LEAVE();
};