ACPI pci-to-pci bridges

- every pci device which implements _PRT acpi method is considered to
  be a pci-to-pci bridge

- acpi driver constructs a hierarchy of pci-to-pci bridges

- when pci driver identifies a pci-to-pci bridge it tells acpi driver
  what is the primary and the secondary bus for this device

- when pci requests IRQ routing information from acpi, it passes the
  bus number too to be able to identify the device accurately
This commit is contained in:
Tomas Hruby 2010-10-21 17:07:09 +00:00
parent 98c93e76d7
commit 40bfed28cd
7 changed files with 392 additions and 159 deletions

View file

@ -8,6 +8,7 @@ PROG= acpi
SRCS= \
acpi.c \
pci.c \
osminixxf.c
ACPICA_SRCS= \

View file

@ -3,17 +3,11 @@
#include <assert.h>
#include <minix/acpi.h>
#include "pci.h"
PUBLIC int acpi_enabled;
PUBLIC struct machine machine;
#define PCI_MAX_DEVICES 32
#define PCI_MAX_PINS 4
#define IRQ_TABLE_ENTRIES (PCI_MAX_DEVICES * PCI_MAX_PINS)
PRIVATE int irqtable[IRQ_TABLE_ENTRIES];
PRIVATE ACPI_HANDLE pci_root_handle;
/* don't know where ACPI tables are, we may need to access any memory */
PRIVATE int init_mem_priv(void)
{
@ -46,152 +40,6 @@ PRIVATE void set_machine_mode(void)
machine.apic_enabled ? "APIC" : "PIC");
}
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;
}
PRIVATE void do_get_irq(message *m)
{
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);
((struct acpi_get_irq_resp *)m)->irq =
irqtable[dev * PCI_MAX_PINS + pin];
}
PRIVATE void add_irq(unsigned dev, unsigned pin, u8_t irq)
{
assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
irqtable[dev * PCI_MAX_PINS + pin] = irq;
}
PRIVATE ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context)
{
ACPI_PCI_ROUTING_TABLE *tbl = (ACPI_PCI_ROUTING_TABLE *) context;
if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
ACPI_RESOURCE_IRQ *irq;
irq = &res->Data.Irq;
add_irq(tbl->Address >> 16, tbl->Pin,
irq->Interrupts[tbl->SourceIndex]);
} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
ACPI_RESOURCE_EXTENDED_IRQ *irq;
add_irq(tbl->Address >> 16, tbl->Pin,
irq->Interrupts[tbl->SourceIndex]);
}
return AE_OK;
}
PRIVATE ACPI_STATUS get_pci_irq_routing(ACPI_HANDLE handle)
{
ACPI_STATUS status;
ACPI_BUFFER abuff;
char buff[4096];
ACPI_PCI_ROUTING_TABLE *tbl;
abuff.Length = sizeof(buff);
abuff.Pointer = buff;
status = AcpiGetIrqRoutingTable(handle, &abuff);
if (ACPI_FAILURE(status)) {
return AE_OK;
}
for (tbl = (ACPI_PCI_ROUTING_TABLE *)abuff.Pointer; tbl->Length;
tbl = (ACPI_PCI_ROUTING_TABLE *)
((char *)tbl + tbl->Length)) {
ACPI_HANDLE src_handle;
if (*(char*)tbl->Source == '\0') {
add_irq(tbl->Address >> 16, tbl->Pin, tbl->SourceIndex);
continue;
}
status = AcpiGetHandle(handle, tbl->Source, &src_handle);
if (ACPI_FAILURE(status)) {
printf("Failed AcpiGetHandle\n");
continue;
}
status = AcpiWalkResources(src_handle, METHOD_NAME__CRS,
get_irq_resource, tbl);
if (ACPI_FAILURE(status)) {
printf("Failed IRQ resource\n");
continue;
}
}
return AE_OK;
}
PRIVATE ACPI_STATUS add_pci_root_dev(ACPI_HANDLE handle,
UINT32 level,
void *context,
void **retval)
{
int i;
static unsigned called;
if (++called > 1) {
printf("ACPI: Warning! Multi rooted PCI is not supported!\n");
return AE_OK;
}
for (i = 0; i < IRQ_TABLE_ENTRIES; i++)
irqtable[i] = -1;
return get_pci_irq_routing(handle);
}
PRIVATE ACPI_STATUS add_pci_dev(ACPI_HANDLE handle,
UINT32 level,
void *context,
void **retval)
{
/* skip pci root when we get to it again */
if (handle == pci_root_handle)
return AE_OK;
return get_pci_irq_routing(handle);
}
PRIVATE void 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));
/* get the rest of the devices that implement _PRT */
status = AcpiGetDevices(NULL, add_pci_dev, NULL, NULL);
assert(ACPI_SUCCESS(status));
}
PRIVATE ACPI_STATUS init_acpica(void)
{
ACPI_STATUS status;
@ -218,7 +66,7 @@ PRIVATE ACPI_STATUS init_acpica(void)
set_machine_mode();
scan_devices();
pci_scan_devices();
return AE_OK;
}
@ -291,6 +139,9 @@ int main(void)
case ACPI_REQ_GET_IRQ:
do_get_irq(&m);
break;
case ACPI_REQ_MAP_BRIDGE:
do_map_bridge(&m);
break;
default:
printf("ACPI: ignoring unsupported request %d "
"from %d\n",

View file

@ -0,0 +1,7 @@
#ifndef __ACPI_GLOBALS_H__
#define __ACPI_GLOBALS_H__
EXTERN int acpi_enabled;
EXTERN struct machine machine;
#endif /* __ACPI_GLOBALS_H__ */

314
drivers/acpi/pci.c Normal file
View file

@ -0,0 +1,314 @@
#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;
}
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;
}
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;
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 (i = 0; i < IRQ_TABLE_ENTRIES; i++)
bridge->irqtable[i] = -1;
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 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;
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 */
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));
}

12
drivers/acpi/pci.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef __ACPI_PCI_H__
#define __ACPI_PCI_H__
#include <minix/ipc.h>
_PROTOTYPE(void do_map_bridge, (message *m));
_PROTOTYPE(void do_get_irq, (message *m));
_PROTOTYPE(void pci_scan_devices, (void));
#endif /* __ACPI_PCI_H__ */

View file

@ -945,12 +945,13 @@ PRIVATE int is_duplicate(u8_t busnr, u8_t dev, u8_t func)
return 0;
}
PRIVATE int acpi_get_irq(unsigned dev, unsigned pin)
PRIVATE int acpi_get_irq(unsigned bus, unsigned dev, unsigned pin)
{
int err;
message m;
((struct acpi_get_irq_req *)&m)->hdr.request = ACPI_REQ_GET_IRQ;
((struct acpi_get_irq_req *)&m)->bus = bus;
((struct acpi_get_irq_req *)&m)->dev = dev;
((struct acpi_get_irq_req *)&m)->pin = pin;
@ -973,7 +974,8 @@ PRIVATE int derive_irq(struct pcidev * dev, int pin)
*/
slot = ((dev->pd_func) >> 3) & 0x1f;
return acpi_get_irq(parent_bridge->pd_dev, (pin + slot) % 4);
return acpi_get_irq(parent_bridge->pd_busnr,
parent_bridge->pd_dev, (pin + slot) % 4);
}
/*===========================================================================*
@ -990,7 +992,8 @@ int devind;
if (ipr && machine.apic_enabled) {
int irq;
irq = acpi_get_irq(pcidev[devind].pd_dev, ipr - 1);
irq = acpi_get_irq(pcidev[devind].pd_busnr,
pcidev[devind].pd_dev, ipr - 1);
if (irq < 0)
irq = derive_irq(&pcidev[devind], ipr - 1);
@ -1834,6 +1837,29 @@ int busind;
return 0;
}
/*
* tells acpi which two busses are connected by this bridge. The primary bus
* (pbnr) must be already known to acpi and it must map dev as the connection to
* the secondary (sbnr) bus
*/
PRIVATE void acpi_map_bridge(unsigned pbnr, unsigned dev, unsigned sbnr)
{
int err;
message m;
((struct acpi_map_bridge_req *)&m)->hdr.request = ACPI_REQ_MAP_BRIDGE;
((struct acpi_map_bridge_req *)&m)->primary_bus = pbnr;
((struct acpi_map_bridge_req *)&m)->secondary_bus = sbnr;
((struct acpi_map_bridge_req *)&m)->device = dev;
if ((err = sendrec(acpi_ep, &m)) != OK)
panic("PCI: error %d while receiveing from ACPI\n", err);
if (((struct acpi_map_bridge_resp *)&m)->err != OK)
panic("PCI: acpi failed to map pci (%d) to pci (%d) bridge\n",
pbnr, sbnr);
}
/*===========================================================================*
* do_pcibridge *
*===========================================================================*/
@ -1963,6 +1989,11 @@ int busind;
default:
panic("unknown PCI-PCI bridge type: %d", type);
}
if (machine.apic_enabled)
acpi_map_bridge(pcidev[devind].pd_busnr,
pcidev[devind].pd_dev, sbusn);
if (debug)
{
printf(

View file

@ -2,6 +2,7 @@
#include <minix/ipc.h>
#define ACPI_REQ_GET_IRQ 1
#define ACPI_REQ_MAP_BRIDGE 2
struct acpi_request_hdr {
endpoint_t m_source; /* message header */
@ -14,9 +15,10 @@ struct acpi_request_hdr {
*/
struct acpi_get_irq_req {
struct acpi_request_hdr hdr;
u32_t bus;
u32_t dev;
u32_t pin;
u32_t __padding[5];
u32_t __padding[4];
};
/* response from acpi to acpi_get_irq_req */
@ -25,3 +27,18 @@ struct acpi_get_irq_resp {
i32_t irq;
u32_t __padding[7];
};
/* message format for pci bridge mappings to acpi */
struct acpi_map_bridge_req {
struct acpi_request_hdr hdr;
u32_t primary_bus;
u32_t secondary_bus;
u32_t device;
u32_t __padding[4];
};
struct acpi_map_bridge_resp {
endpoint_t m_source; /* message header */
int err;
u32_t __padding[7];
};