diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-06-18 17:39:56 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-06-18 17:39:56 +0000 |
commit | b877c685c2a54e557c957f3912b545c342fe0885 (patch) | |
tree | dc65a7aa4abee9dd95d425a3a9425772830e8bee /sys/powerpc | |
parent | 861b0ae69075aef86d67ea4eea962c08fbf0b4f6 (diff) | |
download | FreeBSD-src-b877c685c2a54e557c957f3912b545c342fe0885.zip FreeBSD-src-b877c685c2a54e557c957f3912b545c342fe0885.tar.gz |
Add MSI support for PCI devices attached to the CPC925 and CPC945 bridges
found in Apple and IBM G5 systems.
Diffstat (limited to 'sys/powerpc')
-rw-r--r-- | sys/powerpc/ofw/ofw_pcib_pci.c | 5 | ||||
-rw-r--r-- | sys/powerpc/powermac/cpcht.c | 163 |
2 files changed, 165 insertions, 3 deletions
diff --git a/sys/powerpc/ofw/ofw_pcib_pci.c b/sys/powerpc/ofw/ofw_pcib_pci.c index 953f1b3..daf867f 100644 --- a/sys/powerpc/ofw/ofw_pcib_pci.c +++ b/sys/powerpc/ofw/ofw_pcib_pci.c @@ -76,6 +76,11 @@ static device_method_t ofw_pcib_pci_methods[] = { DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt), + DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, pcib_release_msi), + DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix), + DEVMETHOD(pcib_release_msix, pcib_release_msix), + DEVMETHOD(pcib_map_msi, pcib_map_msi), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node), diff --git a/sys/powerpc/powermac/cpcht.c b/sys/powerpc/powermac/cpcht.c index 6c8ad2f..aaa02d2 100644 --- a/sys/powerpc/powermac/cpcht.c +++ b/sys/powerpc/powermac/cpcht.c @@ -31,6 +31,8 @@ #include <sys/bus.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/pciio.h> +#include <sys/rman.h> #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_pci.h> @@ -45,8 +47,6 @@ #include <machine/pio.h> #include <machine/resource.h> -#include <sys/rman.h> - #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> @@ -89,6 +89,16 @@ static void cpcht_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int cpcht_route_interrupt(device_t bus, device_t dev, int pin); +static int cpcht_alloc_msi(device_t dev, device_t child, + int count, int maxcount, int *irqs); +static int cpcht_release_msi(device_t dev, device_t child, + int count, int *irqs); +static int cpcht_alloc_msix(device_t dev, device_t child, + int *irq); +static int cpcht_release_msix(device_t dev, device_t child, + int irq); +static int cpcht_map_msi(device_t dev, device_t child, + int irq, uint64_t *addr, uint32_t *data); /* * ofw_bus interface @@ -119,6 +129,11 @@ static device_method_t cpcht_methods[] = { DEVMETHOD(pcib_read_config, cpcht_read_config), DEVMETHOD(pcib_write_config, cpcht_write_config), DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt), + DEVMETHOD(pcib_alloc_msi, cpcht_alloc_msi), + DEVMETHOD(pcib_release_msi, cpcht_release_msi), + DEVMETHOD(pcib_alloc_msix, cpcht_alloc_msix), + DEVMETHOD(pcib_release_msix, cpcht_release_msix), + DEVMETHOD(pcib_map_msi, cpcht_map_msi), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, cpcht_get_node), @@ -126,6 +141,10 @@ static device_method_t cpcht_methods[] = { }; struct cpcht_irq { + enum { + IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL + } irq_type; + int ht_source; vm_offset_t ht_base; @@ -135,6 +154,7 @@ struct cpcht_irq { }; static struct cpcht_irq *cpcht_irqmap = NULL; +uint32_t cpcht_msipic = 0; struct cpcht_softc { device_t sc_dev; @@ -144,6 +164,7 @@ struct cpcht_softc { struct rman sc_mem_rman; struct cpcht_irq htirq_map[128]; + struct mtx htirq_mtx; }; static driver_t cpcht_driver = { @@ -199,7 +220,7 @@ cpcht_attach(device_t dev) struct cpcht_softc *sc; phandle_t node, child; u_int32_t reg[3]; - int error; + int i, error; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); @@ -228,6 +249,9 @@ cpcht_attach(device_t dev) */ bzero(sc->htirq_map, sizeof(sc->htirq_map)); + mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF); + for (i = 0; i < 8; i++) + sc->htirq_map[i].irq_type = IRQ_INTERNAL; for (child = OF_child(node); child != 0; child = OF_peer(child)) cpcht_configure_htbridge(dev, child); @@ -336,6 +360,7 @@ cpcht_configure_htbridge(device_t dev, phandle_t child) irq | HTAPIC_MASK, 4); irq = (irq >> 16) & 0xff; + sc->htirq_map[irq].irq_type = IRQ_HT; sc->htirq_map[irq].ht_source = i; sc->htirq_map[irq].ht_base = sc->sc_data + (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr)); @@ -584,6 +609,129 @@ cpcht_deactivate_resource(device_t bus, device_t child, int type, int rid, return (rman_deactivate_resource(res)); } +static int +cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, + int *irqs) +{ + struct cpcht_softc *sc; + int i, j; + + sc = device_get_softc(dev); + j = 0; + + /* Bail if no MSI PIC yet */ + if (cpcht_msipic == 0) + return (ENXIO); + + mtx_lock(&sc->htirq_mtx); + for (i = 8; i < 124 - count; i++) { + for (j = 0; j < count; j++) { + if (sc->htirq_map[i+j].irq_type != IRQ_NONE) + break; + } + if (j == count) + break; + + i += j; /* We know there isn't a large enough run */ + } + + if (j != count) { + mtx_unlock(&sc->htirq_mtx); + return (ENXIO); + } + + for (j = 0; j < count; j++) { + irqs[j] = INTR_VEC(cpcht_msipic, i+j); + sc->htirq_map[i+j].irq_type = IRQ_MSI; + } + mtx_unlock(&sc->htirq_mtx); + + return (0); +} + +static int +cpcht_release_msi(device_t dev, device_t child, int count, int *irqs) +{ + struct cpcht_softc *sc; + int i; + + sc = device_get_softc(dev); + + mtx_lock(&sc->htirq_mtx); + for (i = 0; i < count; i++) + sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE; + mtx_unlock(&sc->htirq_mtx); + + return (0); +} + +static int +cpcht_alloc_msix(device_t dev, device_t child, int *irq) +{ + struct cpcht_softc *sc; + int i; + + sc = device_get_softc(dev); + + /* Bail if no MSI PIC yet */ + if (cpcht_msipic == 0) + return (ENXIO); + + mtx_lock(&sc->htirq_mtx); + for (i = 8; i < 124; i++) { + if (sc->htirq_map[i].irq_type == IRQ_NONE) { + sc->htirq_map[i].irq_type = IRQ_MSI; + *irq = INTR_VEC(cpcht_msipic, i); + + mtx_unlock(&sc->htirq_mtx); + return (0); + } + } + mtx_unlock(&sc->htirq_mtx); + + return (ENXIO); +} + +static int +cpcht_release_msix(device_t dev, device_t child, int irq) +{ + struct cpcht_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock(&sc->htirq_mtx); + sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE; + mtx_unlock(&sc->htirq_mtx); + + return (0); +} + +static int +cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, + uint32_t *data) +{ + device_t pcib; + struct pci_devinfo *dinfo; + struct pcicfg_ht *ht = NULL; + + for (pcib = child; pcib != dev; pcib = + device_get_parent(device_get_parent(pcib))) { + dinfo = device_get_ivars(pcib); + ht = &dinfo->cfg.ht; + + if (ht == NULL) + continue; + } + + if (ht == NULL) + return (ENXIO); + + *addr = ht->ht_msiaddr; + *data = irq & 0xff; + + return (0); +} + /* * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945) */ @@ -671,6 +819,15 @@ openpic_cpcht_attach(device_t dev) for (irq = 4; irq < 124; irq++) openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); + /* + * Use this PIC for MSI only if it is the root PIC. This may not + * be necessary, but Linux does it, and I cannot find any U3 machines + * with MSI devices to test. + */ + + if (dev == root_pic) + cpcht_msipic = PIC_ID(dev); + return (0); } |