minix/drivers/acpi/pci.c
Tomas Hruby 71533c8a83 ACPI - fixed initialization of pci briges irqtable
- if no IRQ table is found, we report that ACPI cannot map IRQ
  correctly

- fixes mapping of IRQs in KVM because in this case we just fall
  through and use the IRQ configured by BIOS. PCI still reports
  that it failed to use ACPI. It is a hint if things go wrong.
2011-12-20 13:10:36 +00:00

326 lines
7.1 KiB
C

#include <minix/driver.h>
#include <acpi.h>
#include <assert.h>
#include <minix/acpi.h>
#include "acpi_globals.h"
#define PCI_MAX_DEVICES 32
#define PCI_MAX_PINS 4
#define IRQ_TABLE_ENTRIES (PCI_MAX_DEVICES * PCI_MAX_PINS)
struct pci_bridge {
ACPI_HANDLE handle;
int irqtable[IRQ_TABLE_ENTRIES];
int primary_bus;
int secondary_bus;
unsigned device;
struct pci_bridge * parent;
struct pci_bridge * children[PCI_MAX_DEVICES];
};
PRIVATE struct pci_bridge pci_root_bridge;
struct irq_resource {
struct pci_bridge * bridge;
ACPI_PCI_ROUTING_TABLE * tbl;
};
PRIVATE struct pci_bridge * find_bridge(struct pci_bridge * root,
int pbnr,
int dev,
int sbnr)
{
if (!root)
return NULL;
if (sbnr == -1) {
if (root->secondary_bus == pbnr)
return root->children[dev];
else {
/* serach all children */
unsigned d;
for (d = 0; d < PCI_MAX_DEVICES; d++) {
struct pci_bridge * b;
b = find_bridge(root->children[d],
pbnr, dev, sbnr);
if (b)
return b;
}
}
} else {
if (root->secondary_bus == sbnr)
return root;
else {
/* check all children */
unsigned d;
for (d = 0; d < PCI_MAX_DEVICES; d++) {
struct pci_bridge * b;
b = find_bridge(root->children[d],
pbnr, dev, sbnr);
if (b)
return b;
}
}
}
return NULL;
}
PUBLIC void do_map_bridge(message *m)
{
int err = OK;
unsigned dev = ((struct acpi_map_bridge_req *)m)->device;
unsigned pbnr = ((struct acpi_map_bridge_req *)m)->primary_bus;
unsigned sbnr = ((struct acpi_map_bridge_req *)m)->secondary_bus;
struct pci_bridge * bridge;
bridge = find_bridge(&pci_root_bridge, pbnr, dev, -1);
if (!bridge) {
err = ENODEV;
goto map_error;
}
bridge->primary_bus = pbnr;
bridge->secondary_bus = sbnr;
map_error:
((struct acpi_map_bridge_resp *)m)->err = err;
}
#if 0
PRIVATE ACPI_STATUS device_get_int(ACPI_HANDLE handle,
char * name,
ACPI_INTEGER * val)
{
ACPI_STATUS status;
char buff[sizeof(ACPI_OBJECT)];
ACPI_BUFFER abuff;
abuff.Length = sizeof(buff);
abuff.Pointer = buff;
status = AcpiEvaluateObjectTyped(handle, name, NULL,
&abuff, ACPI_TYPE_INTEGER);
if (ACPI_SUCCESS(status)) {
*val = ((ACPI_OBJECT *)abuff.Pointer)->Integer.Value;
}
return status;
}
#endif
PUBLIC void do_get_irq(message *m)
{
struct pci_bridge * bridge;
int irq;
unsigned bus = ((struct acpi_get_irq_req *)m)->bus;
unsigned dev = ((struct acpi_get_irq_req *)m)->dev;
unsigned pin = ((struct acpi_get_irq_req *)m)->pin;
assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
bridge = find_bridge(&pci_root_bridge, -1, -1, bus);
if (!bridge)
irq = -1;
else
irq = bridge->irqtable[dev * PCI_MAX_PINS + pin];
((struct acpi_get_irq_resp *)m)->irq = irq;
}
PRIVATE void add_irq(struct pci_bridge * bridge,
unsigned dev,
unsigned pin,
u8_t irq)
{
assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
bridge->irqtable[dev * PCI_MAX_PINS + pin] = irq;
}
PRIVATE ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context)
{
struct irq_resource * ires = (struct irq_resource *) context;
if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
ACPI_RESOURCE_IRQ *irq;
irq = &res->Data.Irq;
add_irq(ires->bridge, ires->tbl->Address >> 16, ires->tbl->Pin,
irq->Interrupts[ires->tbl->SourceIndex]);
} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
ACPI_RESOURCE_EXTENDED_IRQ *irq;
irq = &res->Data.ExtendedIrq;
add_irq(ires->bridge, ires->tbl->Address >> 16, ires->tbl->Pin,
irq->Interrupts[ires->tbl->SourceIndex]);
}
return AE_OK;
}
PRIVATE ACPI_STATUS get_pci_irq_routing(struct pci_bridge * bridge)
{
ACPI_STATUS status;
ACPI_BUFFER abuff;
char buff[4096];
ACPI_PCI_ROUTING_TABLE *tbl;
ACPI_DEVICE_INFO *info;
int i;
abuff.Length = sizeof(buff);
abuff.Pointer = buff;
status = AcpiGetIrqRoutingTable(bridge->handle, &abuff);
if (ACPI_FAILURE(status)) {
return AE_OK;
}
info = abuff.Pointer;
status = AcpiGetObjectInfo(bridge->handle, &info);
if (ACPI_FAILURE(status))
return status;
/*
* Decode the device number (upper half of the address) and attach the
* new bridge in the children list of its parent
*/
bridge->device = info->Address >> 16;
if (bridge != &pci_root_bridge) {
bridge->parent->children[bridge->device] = bridge;
bridge->primary_bus = bridge->secondary_bus = -1;
}
for (i = 0; i < PCI_MAX_DEVICES; i++)
bridge->children[i] = NULL;
for (tbl = (ACPI_PCI_ROUTING_TABLE *)abuff.Pointer; tbl->Length;
tbl = (ACPI_PCI_ROUTING_TABLE *)
((char *)tbl + tbl->Length)) {
ACPI_HANDLE src_handle;
struct irq_resource ires;
if (*(char*)tbl->Source == '\0') {
add_irq(bridge, tbl->Address >> 16,
tbl->Pin, tbl->SourceIndex);
continue;
}
status = AcpiGetHandle(bridge->handle, tbl->Source, &src_handle);
if (ACPI_FAILURE(status)) {
printf("Failed AcpiGetHandle\n");
continue;
}
ires.bridge = bridge;
ires.tbl = tbl;
status = AcpiWalkResources(src_handle, METHOD_NAME__CRS,
get_irq_resource, &ires);
if (ACPI_FAILURE(status)) {
printf("Failed IRQ resource\n");
continue;
}
}
return AE_OK;
}
PRIVATE void bridge_init_irqtable(struct pci_bridge * bridge)
{
int i;
for (i = 0; i < IRQ_TABLE_ENTRIES; i++)
bridge->irqtable[i] = -1;
}
PRIVATE ACPI_STATUS add_pci_dev(ACPI_HANDLE handle,
UINT32 level,
void *context,
void **retval)
{
ACPI_STATUS status;
ACPI_BUFFER abuff;
char buff[4096];
ACPI_HANDLE parent_handle;
struct pci_bridge * bridge;
struct pci_bridge * parent_bridge = (struct pci_bridge *) context;
/* skip pci root when we get to it again */
if (handle == pci_root_bridge.handle)
return AE_OK;
status = AcpiGetParent(handle, &parent_handle);
if (!ACPI_SUCCESS(status))
return status;
/* skip devices that have a different parent */
if (parent_handle != parent_bridge->handle)
return AE_OK;
abuff.Length = sizeof(buff);
abuff.Pointer = buff;
bridge = malloc(sizeof(struct pci_bridge));
if (!bridge)
return AE_NO_MEMORY;
bridge->handle = handle;
bridge->parent = parent_bridge;
bridge_init_irqtable(bridge);
status = get_pci_irq_routing(bridge);
if (!(ACPI_SUCCESS(status))) {
free(bridge);
return status;
}
/* get the pci bridges */
status = AcpiGetDevices(NULL, add_pci_dev, bridge, NULL);
return status;
}
PRIVATE ACPI_STATUS add_pci_root_dev(ACPI_HANDLE handle,
UINT32 level,
void *context,
void **retval)
{
static unsigned called;
ACPI_STATUS status;
if (++called > 1) {
printf("ACPI: Warning! Multi rooted PCI is not supported!\n");
return AE_OK;
}
pci_root_bridge.handle = handle;
pci_root_bridge.primary_bus = -1; /* undefined */
pci_root_bridge.secondary_bus = 0; /* root bus is 0 in a single root
system */
bridge_init_irqtable(&pci_root_bridge);
status = get_pci_irq_routing(&pci_root_bridge);
if (!ACPI_SUCCESS(status))
return status;
/* get the pci bridges */
status = AcpiGetDevices(NULL, add_pci_dev, &pci_root_bridge, NULL);
return status;
}
PUBLIC void pci_scan_devices(void)
{
ACPI_STATUS status;
/* do not scan devices in PIC mode */
if (!machine.apic_enabled)
return;
/* get the root first */
status = AcpiGetDevices("PNP0A03", add_pci_root_dev, NULL, NULL);
assert(ACPI_SUCCESS(status));
}