diff --git a/sys/arm/arm/gic.h b/sys/arm/arm/gic.h --- a/sys/arm/arm/gic.h +++ b/sys/arm/arm/gic.h @@ -63,17 +63,17 @@ int nranges; struct arm_gic_range * ranges; + + u_int sc_spi_start; + u_int sc_spi_end; + u_int sc_spi_count; }; DECLARE_CLASS(arm_gic_driver); struct arm_gicv2m_softc { struct resource *sc_mem; - struct mtx sc_mutex; uintptr_t sc_xref; - u_int sc_spi_start; - u_int sc_spi_end; - u_int sc_spi_count; }; DECLARE_CLASS(arm_gicv2m_driver); diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c --- a/sys/arm/arm/gic.c +++ b/sys/arm/arm/gic.c @@ -501,6 +501,56 @@ ("arm_gic_read_ivar: Invalid bus type %u", sc->gic_bus)); *result = sc->gic_bus; return (0); + case GIC_IVAR_MBI_START: + *result = sc->sc_spi_start; + return (0); + case GIC_IVAR_MBI_COUNT: + *result = sc->sc_spi_count; + return (0); + } + + return (ENOENT); +} + +static int +arm_gic_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct arm_gic_softc *sc; + + sc = device_get_softc(dev); + + switch(which) { + case GIC_IVAR_HW_REV: + case GIC_IVAR_BUS: + return (EINVAL); + case GIC_IVAR_MBI_START: + /* + * GIC_IVAR_MBI_START must be set once and first. This allows + * us to reserve the registers when GIC_IVAR_MBI_COUNT is set. + */ + MPASS(sc->sc_spi_start == 0); + MPASS(sc->sc_spi_count == 0); + MPASS(value >= GIC_FIRST_SPI); + MPASS(value < sc->nirqs); + + sc->sc_spi_start = value; + return (0); + case GIC_IVAR_MBI_COUNT: + MPASS(sc->sc_spi_start != 0); + MPASS(sc->sc_spi_count == 0); + MPASS(value >= sc->sc_spi_start); + MPASS(value >= GIC_FIRST_SPI); + + sc->sc_spi_count = value; + sc->sc_spi_end = sc->sc_spi_start + sc->sc_spi_count; + + MPASS(sc->sc_spi_end <= sc->nirqs); + + /* Reserve these interrupts for MSI/MSI-X use */ + arm_gic_reserve_msi_range(dev, sc->sc_spi_start, + sc->sc_spi_count); + + return (0); } return (ENOENT); @@ -995,99 +1045,20 @@ } #endif -static device_method_t arm_gic_methods[] = { - /* Bus interface */ - DEVMETHOD(bus_print_child, arm_gic_print_child), - DEVMETHOD(bus_add_child, bus_generic_add_child), - DEVMETHOD(bus_alloc_resource, arm_gic_alloc_resource), - DEVMETHOD(bus_release_resource, bus_generic_release_resource), - DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), - DEVMETHOD(bus_read_ivar, arm_gic_read_ivar), - - /* Interrupt controller interface */ - DEVMETHOD(pic_disable_intr, arm_gic_disable_intr), - DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), - DEVMETHOD(pic_map_intr, arm_gic_map_intr), - DEVMETHOD(pic_setup_intr, arm_gic_setup_intr), - DEVMETHOD(pic_teardown_intr, arm_gic_teardown_intr), - DEVMETHOD(pic_post_filter, arm_gic_post_filter), - DEVMETHOD(pic_post_ithread, arm_gic_post_ithread), - DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread), -#ifdef SMP - DEVMETHOD(pic_bind_intr, arm_gic_bind_intr), - DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), - DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), - DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup), -#endif - { 0, 0 } -}; - -DEFINE_CLASS_0(gic, arm_gic_driver, arm_gic_methods, - sizeof(struct arm_gic_softc)); - -/* - * GICv2m support -- the GICv2 MSI/MSI-X controller. - */ - -#define GICV2M_MSI_TYPER 0x008 -#define MSI_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff) -#define MSI_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff) -#define GICv2M_MSI_SETSPI_NS 0x040 -#define GICV2M_MSI_IIDR 0xFCC - -int -arm_gicv2m_attach(device_t dev) -{ - struct arm_gicv2m_softc *sc; - uint32_t typer; - int rid; - - sc = device_get_softc(dev); - - rid = 0; - sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (sc->sc_mem == NULL) { - device_printf(dev, "Unable to allocate resources\n"); - return (ENXIO); - } - - typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER); - sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer); - sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer); - sc->sc_spi_end = sc->sc_spi_start + sc->sc_spi_count; - - /* Reserve these interrupts for MSI/MSI-X use */ - arm_gic_reserve_msi_range(device_get_parent(dev), sc->sc_spi_start, - sc->sc_spi_count); - - mtx_init(&sc->sc_mutex, "GICv2m lock", NULL, MTX_DEF); - - intr_msi_register(dev, sc->sc_xref); - - if (bootverbose) - device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start, - sc->sc_spi_start + sc->sc_spi_count - 1); - - return (0); -} - static int -arm_gicv2m_alloc_msi(device_t dev, device_t child, int count, int maxcount, +arm_gic_alloc_msi(device_t dev, device_t child, int count, int maxcount, device_t *pic, struct intr_irqsrc **srcs) { - struct arm_gic_softc *psc; - struct arm_gicv2m_softc *sc; + struct arm_gic_softc *sc; int i, irq, end_irq; bool found; KASSERT(powerof2(count), ("%s: bad count", __func__)); KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__)); - psc = device_get_softc(device_get_parent(dev)); sc = device_get_softc(dev); - mtx_lock(&sc->sc_mutex); + mtx_lock(&sc->mutex); found = false; for (irq = sc->sc_spi_start; irq < sc->sc_spi_end; irq++) { @@ -1106,11 +1077,11 @@ break; } - KASSERT((psc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI)!= 0, + KASSERT((sc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI)!= 0, ("%s: Non-MSI interrupt found", __func__)); /* This is already used */ - if ((psc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI_USED) == + if ((sc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED) { found = false; break; @@ -1122,34 +1093,34 @@ /* Not enough interrupts were found */ if (!found || irq == sc->sc_spi_end) { - mtx_unlock(&sc->sc_mutex); + mtx_unlock(&sc->mutex); return (ENXIO); } for (i = 0; i < count; i++) { /* Mark the interrupt as used */ - psc->gic_irqs[irq + i].gi_flags |= GI_FLAG_MSI_USED; + sc->gic_irqs[irq + i].gi_flags |= GI_FLAG_MSI_USED; } - mtx_unlock(&sc->sc_mutex); + mtx_unlock(&sc->mutex); for (i = 0; i < count; i++) - srcs[i] = (struct intr_irqsrc *)&psc->gic_irqs[irq + i]; - *pic = device_get_parent(dev); + srcs[i] = (struct intr_irqsrc *)&sc->gic_irqs[irq + i]; + *pic = dev; return (0); } static int -arm_gicv2m_release_msi(device_t dev, device_t child, int count, +arm_gic_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **isrc) { - struct arm_gicv2m_softc *sc; + struct arm_gic_softc *sc; struct gic_irqsrc *gi; int i; sc = device_get_softc(dev); - mtx_lock(&sc->sc_mutex); + mtx_lock(&sc->mutex); for (i = 0; i < count; i++) { gi = (struct gic_irqsrc *)isrc[i]; @@ -1159,50 +1130,48 @@ gi->gi_flags &= ~GI_FLAG_MSI_USED; } - mtx_unlock(&sc->sc_mutex); + mtx_unlock(&sc->mutex); return (0); } static int -arm_gicv2m_alloc_msix(device_t dev, device_t child, device_t *pic, +arm_gic_alloc_msix(device_t dev, device_t child, device_t *pic, struct intr_irqsrc **isrcp) { - struct arm_gicv2m_softc *sc; - struct arm_gic_softc *psc; + struct arm_gic_softc *sc; int irq; - psc = device_get_softc(device_get_parent(dev)); sc = device_get_softc(dev); - mtx_lock(&sc->sc_mutex); + mtx_lock(&sc->mutex); /* Find an unused interrupt */ for (irq = sc->sc_spi_start; irq < sc->sc_spi_end; irq++) { - KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) != 0, + KASSERT((sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) != 0, ("%s: Non-MSI interrupt found", __func__)); - if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == 0) + if ((sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == 0) break; } /* No free interrupt was found */ if (irq == sc->sc_spi_end) { - mtx_unlock(&sc->sc_mutex); + mtx_unlock(&sc->mutex); return (ENXIO); } /* Mark the interrupt as used */ - psc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED; - mtx_unlock(&sc->sc_mutex); + sc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED; + mtx_unlock(&sc->mutex); - *isrcp = (struct intr_irqsrc *)&psc->gic_irqs[irq]; - *pic = device_get_parent(dev); + *isrcp = (struct intr_irqsrc *)&sc->gic_irqs[irq]; + *pic = dev; return (0); } static int -arm_gicv2m_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) +arm_gic_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) { - struct arm_gicv2m_softc *sc; + struct arm_gic_softc *sc; struct gic_irqsrc *gi; sc = device_get_softc(dev); @@ -1211,13 +1180,122 @@ KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED, ("%s: Trying to release an unused MSI-X interrupt", __func__)); - mtx_lock(&sc->sc_mutex); + mtx_lock(&sc->mutex); gi->gi_flags &= ~GI_FLAG_MSI_USED; - mtx_unlock(&sc->sc_mutex); + mtx_unlock(&sc->mutex); return (0); } +static device_method_t arm_gic_methods[] = { + /* Bus interface */ + DEVMETHOD(bus_print_child, arm_gic_print_child), + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_alloc_resource, arm_gic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), + DEVMETHOD(bus_read_ivar, arm_gic_read_ivar), + DEVMETHOD(bus_write_ivar, arm_gic_write_ivar), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, arm_gic_disable_intr), + DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), + DEVMETHOD(pic_map_intr, arm_gic_map_intr), + DEVMETHOD(pic_setup_intr, arm_gic_setup_intr), + DEVMETHOD(pic_teardown_intr, arm_gic_teardown_intr), + DEVMETHOD(pic_post_filter, arm_gic_post_filter), + DEVMETHOD(pic_post_ithread, arm_gic_post_ithread), + DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread), +#ifdef SMP + DEVMETHOD(pic_bind_intr, arm_gic_bind_intr), + DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), + DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), + DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup), +#endif + + /* MSI/MSI-X */ + DEVMETHOD(msi_alloc_msi, arm_gic_alloc_msi), + DEVMETHOD(msi_release_msi, arm_gic_release_msi), + DEVMETHOD(msi_alloc_msix, arm_gic_alloc_msix), + DEVMETHOD(msi_release_msix, arm_gic_release_msix), + + { 0, 0 } +}; + +DEFINE_CLASS_0(gic, arm_gic_driver, arm_gic_methods, + sizeof(struct arm_gic_softc)); + +/* + * GICv2m support -- the GICv2 MSI/MSI-X controller. + */ + +#define GICV2M_MSI_TYPER 0x008 +#define MSI_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff) +#define MSI_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff) +#define GICv2M_MSI_SETSPI_NS 0x040 +#define GICV2M_MSI_IIDR 0xFCC + +int +arm_gicv2m_attach(device_t dev) +{ + struct arm_gicv2m_softc *sc; + uint32_t typer; + u_int spi_start, spi_count; + int rid; + + sc = device_get_softc(dev); + + rid = 0; + sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem == NULL) { + device_printf(dev, "Unable to allocate resources\n"); + return (ENXIO); + } + + typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER); + spi_start = MSI_TYPER_SPI_BASE(typer); + spi_count = MSI_TYPER_SPI_COUNT(typer); + gic_set_mbi_start(dev, spi_start); + gic_set_mbi_count(dev, spi_count); + + intr_msi_register(dev, sc->sc_xref); + + if (bootverbose) + device_printf(dev, "using spi %u to %u\n", spi_start, + spi_start + spi_count - 1); + + return (0); +} + +static int +arm_gicv2m_alloc_msi(device_t dev, device_t child, int count, int maxcount, + device_t *pic, struct intr_irqsrc **srcs) +{ + return (MSI_ALLOC_MSI(device_get_parent(dev), child, count, maxcount, + pic, srcs)); +} + +static int +arm_gicv2m_release_msi(device_t dev, device_t child, int count, + struct intr_irqsrc **isrc) +{ + return (MSI_RELEASE_MSI(device_get_parent(dev), child, count, isrc)); +} + +static int +arm_gicv2m_alloc_msix(device_t dev, device_t child, device_t *pic, + struct intr_irqsrc **isrcp) +{ + return (MSI_ALLOC_MSIX(device_get_parent(dev), child, pic, isrcp)); +} + +static int +arm_gicv2m_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) +{ + return (MSI_RELEASE_MSIX(device_get_parent(dev), child, isrc)); +} + static int arm_gicv2m_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, uint64_t *addr, uint32_t *data) diff --git a/sys/arm/arm/gic_common.h b/sys/arm/arm/gic_common.h --- a/sys/arm/arm/gic_common.h +++ b/sys/arm/arm/gic_common.h @@ -33,6 +33,8 @@ #define GIC_IVAR_HW_REV 500 #define GIC_IVAR_BUS 501 +#define GIC_IVAR_MBI_START 510 +#define GIC_IVAR_MBI_COUNT 511 /* GIC_IVAR_BUS values */ #define GIC_BUS_UNKNOWN 0 @@ -42,6 +44,8 @@ __BUS_ACCESSOR(gic, hw_rev, GIC, HW_REV, u_int); __BUS_ACCESSOR(gic, bus, GIC, BUS, u_int); +__BUS_ACCESSOR(gic, mbi_start, GIC, MBI_START, u_int); +__BUS_ACCESSOR(gic, mbi_count, GIC, MBI_COUNT, u_int); /* Software Generated Interrupts */ #define GIC_FIRST_SGI 0 /* Irqs 0-15 are SGIs/IPIs. */