diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index b697f0051..b0b4dbc41 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -8,6 +8,7 @@ PROG= acpi SRCS= \ acpi.c \ + pci.c \ osminixxf.c ACPICA_SRCS= \ diff --git a/drivers/acpi/acpi.c b/drivers/acpi/acpi.c index 5a061d6fa..65fd1e03b 100644 --- a/drivers/acpi/acpi.c +++ b/drivers/acpi/acpi.c @@ -3,17 +3,11 @@ #include #include +#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", diff --git a/drivers/acpi/acpi_globals.h b/drivers/acpi/acpi_globals.h new file mode 100644 index 000000000..6dd80d5da --- /dev/null +++ b/drivers/acpi/acpi_globals.h @@ -0,0 +1,7 @@ +#ifndef __ACPI_GLOBALS_H__ +#define __ACPI_GLOBALS_H__ + +EXTERN int acpi_enabled; +EXTERN struct machine machine; + +#endif /* __ACPI_GLOBALS_H__ */ diff --git a/drivers/acpi/pci.c b/drivers/acpi/pci.c new file mode 100644 index 000000000..94296f62d --- /dev/null +++ b/drivers/acpi/pci.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include + +#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)); +} diff --git a/drivers/acpi/pci.h b/drivers/acpi/pci.h new file mode 100644 index 000000000..0b9c69025 --- /dev/null +++ b/drivers/acpi/pci.h @@ -0,0 +1,12 @@ +#ifndef __ACPI_PCI_H__ +#define __ACPI_PCI_H__ + +#include + +_PROTOTYPE(void do_map_bridge, (message *m)); +_PROTOTYPE(void do_get_irq, (message *m)); + +_PROTOTYPE(void pci_scan_devices, (void)); + + +#endif /* __ACPI_PCI_H__ */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a55d51290..004ddcae2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -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( diff --git a/include/minix/acpi.h b/include/minix/acpi.h index 612debb77..129f4c45f 100644 --- a/include/minix/acpi.h +++ b/include/minix/acpi.h @@ -2,6 +2,7 @@ #include #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]; +};