]> Zhao Yanbai Git Server - minix.git/commitdiff
ACPI pci-to-pci bridges
authorTomas Hruby <tom@minix3.org>
Thu, 21 Oct 2010 17:07:09 +0000 (17:07 +0000)
committerTomas Hruby <tom@minix3.org>
Thu, 21 Oct 2010 17:07:09 +0000 (17:07 +0000)
- 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

drivers/acpi/Makefile
drivers/acpi/acpi.c
drivers/acpi/acpi_globals.h [new file with mode: 0644]
drivers/acpi/pci.c [new file with mode: 0644]
drivers/acpi/pci.h [new file with mode: 0644]
drivers/pci/pci.c
include/minix/acpi.h

index b697f0051440e441842b6d19b63b991134d4491c..b0b4dbc41822f3729d92289b69b98d2cced4646c 100644 (file)
@@ -8,6 +8,7 @@ PROG=   acpi
 
 SRCS= \
        acpi.c \
+       pci.c \
        osminixxf.c
 
 ACPICA_SRCS= \
index 5a061d6fa19763e678c25f1f35f3b1b62cdf1e2d..65fd1e03b7039a7137c0796ac9ef260f3b8b5340 100644 (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",
diff --git a/drivers/acpi/acpi_globals.h b/drivers/acpi/acpi_globals.h
new file mode 100644 (file)
index 0000000..6dd80d5
--- /dev/null
@@ -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 (file)
index 0000000..94296f6
--- /dev/null
@@ -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));
+}
diff --git a/drivers/acpi/pci.h b/drivers/acpi/pci.h
new file mode 100644 (file)
index 0000000..0b9c690
--- /dev/null
@@ -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__ */
index a55d512900d9cfd1c71fcdc300b9b985d76e5de9..004ddcae223a3076dfcc1ea3a20e5c672a30752a 100644 (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(
index 612debb778e18f077aee1de724eb30ec80f442a3..129f4c45fc18ec05c9a06c91e28ac8bb7ad3a79b 100644 (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];
+};