Index: head/sys/arm/broadcom/bcm2835/bcm2835_intr.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_intr.c +++ head/sys/arm/broadcom/bcm2835/bcm2835_intr.c @@ -30,12 +30,15 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include #include #include #include +#include #include #include #include @@ -49,6 +52,10 @@ #include #endif +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + #define INTC_PENDING_BASIC 0x00 #define INTC_PENDING_BANK1 0x04 #define INTC_PENDING_BANK2 0x08 @@ -60,17 +67,55 @@ #define INTC_DISABLE_BANK2 0x20 #define INTC_DISABLE_BASIC 0x24 +#define INTC_PENDING_BASIC_ARM 0x0000FF +#define INTC_PENDING_BASIC_GPU1_PEND 0x000100 +#define INTC_PENDING_BASIC_GPU2_PEND 0x000200 +#define INTC_PENDING_BASIC_GPU1_7 0x000400 +#define INTC_PENDING_BASIC_GPU1_9 0x000800 +#define INTC_PENDING_BASIC_GPU1_10 0x001000 +#define INTC_PENDING_BASIC_GPU1_18 0x002000 +#define INTC_PENDING_BASIC_GPU1_19 0x004000 +#define INTC_PENDING_BASIC_GPU2_21 0x008000 +#define INTC_PENDING_BASIC_GPU2_22 0x010000 +#define INTC_PENDING_BASIC_GPU2_23 0x020000 +#define INTC_PENDING_BASIC_GPU2_24 0x040000 +#define INTC_PENDING_BASIC_GPU2_25 0x080000 +#define INTC_PENDING_BASIC_GPU2_30 0x100000 +#define INTC_PENDING_BASIC_MASK 0x1FFFFF + +#define INTC_PENDING_BASIC_GPU1_MASK (INTC_PENDING_BASIC_GPU1_7 | \ + INTC_PENDING_BASIC_GPU1_9 | \ + INTC_PENDING_BASIC_GPU1_10 | \ + INTC_PENDING_BASIC_GPU1_18 | \ + INTC_PENDING_BASIC_GPU1_19) + +#define INTC_PENDING_BASIC_GPU2_MASK (INTC_PENDING_BASIC_GPU2_21 | \ + INTC_PENDING_BASIC_GPU2_22 | \ + INTC_PENDING_BASIC_GPU2_23 | \ + INTC_PENDING_BASIC_GPU2_24 | \ + INTC_PENDING_BASIC_GPU2_25 | \ + INTC_PENDING_BASIC_GPU2_30) + +#define INTC_PENDING_BANK1_MASK (~((1 << 7) | (1 << 9) | (1 << 10) | \ + (1 << 18) | (1 << 19))) +#define INTC_PENDING_BANK2_MASK (~((1 << 21) | (1 << 22) | (1 << 23) | \ + (1 << 24) | (1 << 25) | (1 << 30))) + #define BANK1_START 8 #define BANK1_END (BANK1_START + 32 - 1) #define BANK2_START (BANK1_START + 32) #define BANK2_END (BANK2_START + 32 - 1) +#ifndef ARM_INTRNG #define BANK3_START (BANK2_START + 32) #define BANK3_END (BANK3_START + 32 - 1) +#endif #define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START)) #define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) #define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) +#ifndef ARM_INTRNG #define ID_IRQ_BCM2836(n) (((n) >= BANK3_START) && ((n) <= BANK3_END)) +#endif #define IRQ_BANK1(n) ((n) - BANK1_START) #define IRQ_BANK2(n) ((n) - BANK2_START) @@ -80,11 +125,28 @@ #define dprintf(fmt, args...) #endif +#ifdef ARM_INTRNG +#define BCM_INTC_NIRQS 72 /* 8 + 32 + 32 */ + +struct bcm_intc_irqsrc { + struct intr_irqsrc bii_isrc; + u_int bii_irq; + uint16_t bii_disable_reg; + uint16_t bii_enable_reg; + uint32_t bii_mask; +}; +#endif + struct bcm_intc_softc { device_t sc_dev; struct resource * intc_res; bus_space_tag_t intc_bst; bus_space_handle_t intc_bsh; +#ifdef ARM_INTRNG + struct resource * intc_irq_res; + void * intc_irq_hdl; + struct bcm_intc_irqsrc intc_isrcs[BCM_INTC_NIRQS]; +#endif }; static struct bcm_intc_softc *bcm_intc_sc = NULL; @@ -94,6 +156,192 @@ #define intc_write_4(_sc, reg, val) \ bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val)) +#ifdef ARM_INTRNG +static inline void +bcm_intc_isrc_mask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii) +{ + + intc_write_4(sc, bii->bii_disable_reg, bii->bii_mask); +} + +static inline void +bcm_intc_isrc_unmask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii) +{ + + intc_write_4(sc, bii->bii_enable_reg, bii->bii_mask); +} + +static inline int +bcm2835_intc_active_intr(struct bcm_intc_softc *sc) +{ + uint32_t pending, pending_gpu; + + pending = intc_read_4(sc, INTC_PENDING_BASIC) & INTC_PENDING_BASIC_MASK; + if (pending == 0) + return (-1); + if (pending & INTC_PENDING_BASIC_ARM) + return (ffs(pending) - 1); + if (pending & INTC_PENDING_BASIC_GPU1_MASK) { + if (pending & INTC_PENDING_BASIC_GPU1_7) + return (BANK1_START + 7); + if (pending & INTC_PENDING_BASIC_GPU1_9) + return (BANK1_START + 9); + if (pending & INTC_PENDING_BASIC_GPU1_10) + return (BANK1_START + 10); + if (pending & INTC_PENDING_BASIC_GPU1_18) + return (BANK1_START + 18); + if (pending & INTC_PENDING_BASIC_GPU1_19) + return (BANK1_START + 19); + } + if (pending & INTC_PENDING_BASIC_GPU2_MASK) { + if (pending & INTC_PENDING_BASIC_GPU2_21) + return (BANK2_START + 21); + if (pending & INTC_PENDING_BASIC_GPU2_22) + return (BANK2_START + 22); + if (pending & INTC_PENDING_BASIC_GPU2_23) + return (BANK2_START + 23); + if (pending & INTC_PENDING_BASIC_GPU2_24) + return (BANK2_START + 24); + if (pending & INTC_PENDING_BASIC_GPU2_25) + return (BANK2_START + 25); + if (pending & INTC_PENDING_BASIC_GPU2_30) + return (BANK2_START + 30); + } + if (pending & INTC_PENDING_BASIC_GPU1_PEND) { + pending_gpu = intc_read_4(sc, INTC_PENDING_BANK1); + pending_gpu &= INTC_PENDING_BANK1_MASK; + if (pending_gpu != 0) + return (BANK1_START + ffs(pending_gpu) - 1); + } + if (pending & INTC_PENDING_BASIC_GPU2_PEND) { + pending_gpu = intc_read_4(sc, INTC_PENDING_BANK2); + pending_gpu &= INTC_PENDING_BANK2_MASK; + if (pending_gpu != 0) + return (BANK2_START + ffs(pending_gpu) - 1); + } + return (-1); /* It shouldn't end here, but it's hardware. */ +} + +static int +bcm2835_intc_intr(void *arg) +{ + int irq, num; + struct bcm_intc_softc *sc = arg; + + for (num = 0; ; num++) { + irq = bcm2835_intc_active_intr(sc); + if (irq == -1) + break; + if (intr_isrc_dispatch(&sc->intc_isrcs[irq].bii_isrc, + curthread->td_intr_frame) != 0) { + bcm_intc_isrc_mask(sc, &sc->intc_isrcs[irq]); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", + irq); + } + arm_irq_memory_barrier(0); /* XXX */ + } + if (num == 0) + device_printf(sc->sc_dev, "Spurious interrupt detected\n"); + + return (FILTER_HANDLED); +} + +static void +bcm_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_intc_irqsrc *bii = (struct bcm_intc_irqsrc *)isrc; + + arm_irq_memory_barrier(bii->bii_irq); + bcm_intc_isrc_unmask(device_get_softc(dev), bii); +} + +static void +bcm_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_intc_isrc_mask(device_get_softc(dev), + (struct bcm_intc_irqsrc *)isrc); +} + +static int +bcm_intc_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + u_int irq; + struct bcm_intc_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + if (data->fdt.ncells == 1) + irq = data->fdt.cells[0]; + else if (data->fdt.ncells == 2) + irq = data->fdt.cells[0] * 32 + data->fdt.cells[1]; + else + return (EINVAL); + + if (irq >= BCM_INTC_NIRQS) + return (EINVAL); + + sc = device_get_softc(dev); + *isrcp = &sc->intc_isrcs[irq].bii_isrc; + return (0); +} + +static void +bcm_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_intc_disable_intr(dev, isrc); +} + +static void +bcm_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_intc_enable_intr(dev, isrc); +} + +static void +bcm_intc_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static int +bcm_intc_pic_register(struct bcm_intc_softc *sc, intptr_t xref) +{ + struct bcm_intc_irqsrc *bii; + int error; + uint32_t irq; + const char *name; + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < BCM_INTC_NIRQS; irq++) { + bii = &sc->intc_isrcs[irq]; + bii->bii_irq = irq; + if (IS_IRQ_BASIC(irq)) { + bii->bii_disable_reg = INTC_DISABLE_BASIC; + bii->bii_enable_reg = INTC_ENABLE_BASIC; + bii->bii_mask = 1 << irq; + } else if (IS_IRQ_BANK1(irq)) { + bii->bii_disable_reg = INTC_DISABLE_BANK1; + bii->bii_enable_reg = INTC_ENABLE_BANK1; + bii->bii_mask = 1 << IRQ_BANK1(irq); + } else if (IS_IRQ_BANK2(irq)) { + bii->bii_disable_reg = INTC_DISABLE_BANK2; + bii->bii_enable_reg = INTC_ENABLE_BANK2; + bii->bii_mask = 1 << IRQ_BANK2(irq); + } else + return (ENXIO); + + error = intr_isrc_register(&bii->bii_isrc, sc->sc_dev, 0, + "%s,%u", name, irq); + if (error != 0) + return (error); + } + return (intr_pic_register(sc->sc_dev, xref)); +} +#endif + static int bcm_intc_probe(device_t dev) { @@ -112,7 +360,9 @@ { struct bcm_intc_softc *sc = device_get_softc(dev); int rid = 0; - +#ifdef ARM_INTRNG + intptr_t xref; +#endif sc->sc_dev = dev; if (bcm_intc_sc) @@ -124,6 +374,32 @@ return (ENXIO); } +#ifdef ARM_INTRNG + xref = OF_xref_from_node(ofw_bus_get_node(dev)); + if (bcm_intc_pic_register(sc, xref) != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->intc_res); + device_printf(dev, "could not register PIC\n"); + return (ENXIO); + } + + rid = 0; + sc->intc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->intc_irq_res == NULL) { + if (intr_pic_claim_root(dev, xref, bcm2835_intc_intr, sc, 0) != 0) { + /* XXX clean up */ + device_printf(dev, "could not set PIC as a root\n"); + return (ENXIO); + } + } else { + if (bus_setup_intr(dev, sc->intc_irq_res, INTR_TYPE_CLK, + bcm2835_intc_intr, NULL, sc, &sc->intc_irq_hdl)) { + /* XXX clean up */ + device_printf(dev, "could not setup irq handler\n"); + return (ENXIO); + } + } +#endif sc->intc_bst = rman_get_bustag(sc->intc_res); sc->intc_bsh = rman_get_bushandle(sc->intc_res); @@ -135,6 +411,16 @@ static device_method_t bcm_intc_methods[] = { DEVMETHOD(device_probe, bcm_intc_probe), DEVMETHOD(device_attach, bcm_intc_attach), + +#ifdef ARM_INTRNG + DEVMETHOD(pic_disable_intr, bcm_intc_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_intc_enable_intr), + DEVMETHOD(pic_map_intr, bcm_intc_map_intr), + DEVMETHOD(pic_post_filter, bcm_intc_post_filter), + DEVMETHOD(pic_post_ithread, bcm_intc_post_ithread), + DEVMETHOD(pic_pre_ithread, bcm_intc_pre_ithread), +#endif + { 0, 0 } }; @@ -148,6 +434,7 @@ DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, 0, 0); +#ifndef ARM_INTRNG int arm_get_next_irq(int last_irq) { @@ -247,3 +534,4 @@ { } #endif +#endif Index: head/sys/arm/conf/RPI-B =================================================================== --- head/sys/arm/conf/RPI-B +++ head/sys/arm/conf/RPI-B @@ -24,6 +24,8 @@ include "../broadcom/bcm2835/std.rpi" include "../broadcom/bcm2835/std.bcm2835" +options ARM_INTRNG + options HZ=100 options SCHED_4BSD # 4BSD scheduler options PLATFORM