Index: sys/arm/allwinner/aintc.c =================================================================== --- sys/arm/allwinner/aintc.c +++ sys/arm/allwinner/aintc.c @@ -27,6 +27,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include @@ -34,6 +36,14 @@ #include #include #include +#include +#include +#include +#include +#include +#ifdef ARM_INTRNG +#include +#endif #include #include @@ -42,63 +52,69 @@ #include #include +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + /** * Interrupt controller registers * */ -#define SW_INT_VECTOR_REG 0x00 -#define SW_INT_BASE_ADR_REG 0x04 -#define SW_INT_PROTECTION_REG 0x08 -#define SW_INT_NMI_CTRL_REG 0x0c - -#define SW_INT_IRQ_PENDING_REG0 0x10 -#define SW_INT_IRQ_PENDING_REG1 0x14 -#define SW_INT_IRQ_PENDING_REG2 0x18 - -#define SW_INT_FIQ_PENDING_REG0 0x20 -#define SW_INT_FIQ_PENDING_REG1 0x24 -#define SW_INT_FIQ_PENDING_REG2 0x28 - -#define SW_INT_SELECT_REG0 0x30 -#define SW_INT_SELECT_REG1 0x34 -#define SW_INT_SELECT_REG2 0x38 - -#define SW_INT_ENABLE_REG0 0x40 -#define SW_INT_ENABLE_REG1 0x44 -#define SW_INT_ENABLE_REG2 0x48 - -#define SW_INT_MASK_REG0 0x50 -#define SW_INT_MASK_REG1 0x54 -#define SW_INT_MASK_REG2 0x58 - -#define SW_INT_IRQNO_ENMI 0 - -#define SW_INT_IRQ_PENDING_REG(_b) (0x10 + ((_b) * 4)) -#define SW_INT_FIQ_PENDING_REG(_b) (0x20 + ((_b) * 4)) -#define SW_INT_SELECT_REG(_b) (0x30 + ((_b) * 4)) -#define SW_INT_ENABLE_REG(_b) (0x40 + ((_b) * 4)) -#define SW_INT_MASK_REG(_b) (0x50 + ((_b) * 4)) - -static struct ofw_compat_data compat_data[] = { - {"allwinner,sun4i-a10-ic", 1}, - {"allwinner,sun7i-a20-sc-nmi", 1}, - {NULL, 0} -}; +#define SW_INT_VECTOR_REG 0x00 +#define SW_INT_BASE_ADR_REG 0x04 +#define SW_INT_PROTECTION_REG 0x08 +#define SW_INT_NMI_CTRL_REG 0x0c + +#define SW_INT_IRQ_PENDING_REG0 0x10 +#define SW_INT_IRQ_PENDING_REG1 0x14 +#define SW_INT_IRQ_PENDING_REG2 0x18 + +#define SW_INT_FIQ_PENDING_REG0 0x20 +#define SW_INT_FIQ_PENDING_REG1 0x24 +#define SW_INT_FIQ_PENDING_REG2 0x28 + +#define SW_INT_SELECT_REG0 0x30 +#define SW_INT_SELECT_REG1 0x34 +#define SW_INT_SELECT_REG2 0x38 + +#define SW_INT_ENABLE_REG0 0x40 +#define SW_INT_ENABLE_REG1 0x44 +#define SW_INT_ENABLE_REG2 0x48 + +#define SW_INT_MASK_REG0 0x50 +#define SW_INT_MASK_REG1 0x54 +#define SW_INT_MASK_REG2 0x58 + +#define SW_INT_IRQNO_ENMI 0 + +#define A10_INTC_MAX_NIRQS 80 + +#define SW_INT_IRQ_PENDING_REG(_b) (0x10 + ((_b) * 4)) +#define SW_INT_FIQ_PENDING_REG(_b) (0x20 + ((_b) * 4)) +#define SW_INT_SELECT_REG(_b) (0x30 + ((_b) * 4)) +#define SW_INT_ENABLE_REG(_b) (0x40 + ((_b) * 4)) +#define SW_INT_MASK_REG(_b) (0x50 + ((_b) * 4)) struct a10_aintc_softc { device_t sc_dev; struct resource * aintc_res; bus_space_tag_t aintc_bst; bus_space_handle_t aintc_bsh; - uint8_t ver; + struct mtx mtx; +#ifdef ARM_INTRNG + struct intr_irqsrc ** irqs; +#endif }; static struct a10_aintc_softc *a10_aintc_sc = NULL; -#define aintc_read_4(reg) \ - bus_space_read_4(a10_aintc_sc->aintc_bst, a10_aintc_sc->aintc_bsh, reg) -#define aintc_write_4(reg, val) \ - bus_space_write_4(a10_aintc_sc->aintc_bst, a10_aintc_sc->aintc_bsh, reg, val) +#define aintc_read_4(sc, reg) \ + bus_space_read_4(sc->aintc_bst, sc->aintc_bsh, reg) +#define aintc_write_4(sc, reg, val) \ + bus_space_write_4(sc->aintc_bst, sc->aintc_bsh, reg, val) + +static int a10_intr(void *arg); + static int a10_aintc_probe(device_t dev) @@ -107,7 +123,7 @@ if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ic")) return (ENXIO); device_set_desc(dev, "A10 AINTC Interrupt Controller"); return (BUS_PROBE_DEFAULT); @@ -119,62 +135,116 @@ struct a10_aintc_softc *sc = device_get_softc(dev); int rid = 0; int i; - +#ifdef ARM_INTRNG + phandle_t xref; +#endif sc->sc_dev = dev; if (a10_aintc_sc) - return (ENXIO); + goto error; - sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); if (!sc->aintc_res) { device_printf(dev, "could not allocate resource\n"); - return (ENXIO); + goto error; } sc->aintc_bst = rman_get_bustag(sc->aintc_res); sc->aintc_bsh = rman_get_bushandle(sc->aintc_res); + mtx_init(&sc->mtx, "A10 AINTC lock", "", MTX_SPIN); +#ifdef ARM_INTRNG + sc->irqs = malloc(A10_INTC_MAX_NIRQS * sizeof (*sc->irqs), M_DEVBUF, + M_WAITOK | M_ZERO); +#endif + a10_aintc_sc = sc; /* Disable & clear all interrupts */ for (i = 0; i < 3; i++) { - aintc_write_4(SW_INT_ENABLE_REG(i), 0); - aintc_write_4(SW_INT_MASK_REG(i), 0xffffffff); + aintc_write_4(sc, SW_INT_ENABLE_REG(i), 0); + aintc_write_4(sc, SW_INT_MASK_REG(i), 0xffffffff); } /* enable protection mode*/ - aintc_write_4(SW_INT_PROTECTION_REG, 0x01); + aintc_write_4(sc, SW_INT_PROTECTION_REG, 0x01); /* config the external interrupt source type*/ - aintc_write_4(SW_INT_NMI_CTRL_REG, 0x00); + aintc_write_4(sc, SW_INT_NMI_CTRL_REG, 0x00); + +#ifdef ARM_INTRNG + xref = OF_xref_from_node(ofw_bus_get_node(dev)); + if (intr_pic_register(dev, (intptr_t)xref) != 0) { + device_printf(dev, "could not register PIC\n"); + goto error; + } + + if (intr_pic_claim_root(dev, xref, a10_intr, sc, + A10_INTC_MAX_NIRQS) != 0) { + device_printf(dev, "could not set PIC as a root\n"); + intr_pic_unregister(dev, xref); + goto error; + } + + OF_device_register_xref(xref, dev); +#endif return (0); + +error: + return (ENXIO); } -static device_method_t a10_aintc_methods[] = { - DEVMETHOD(device_probe, a10_aintc_probe), - DEVMETHOD(device_attach, a10_aintc_attach), - { 0, 0 } -}; +static void +a10_intr_unmask(struct a10_aintc_softc *sc, u_int irq) +{ + uint32_t bit, block, value; -static driver_t a10_aintc_driver = { - "aintc", - a10_aintc_methods, - sizeof(struct a10_aintc_softc), -}; + bit = (irq % 32); + block = (irq / 32); -static devclass_t a10_aintc_devclass; + mtx_lock_spin(&sc->mtx); + value = aintc_read_4(sc, SW_INT_ENABLE_REG(block)); + value |= (1 << bit); + aintc_write_4(sc, SW_INT_ENABLE_REG(block), value); -EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0, - BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST); + value = aintc_read_4(sc, SW_INT_MASK_REG(block)); + value &= ~(1 << bit); + aintc_write_4(sc, SW_INT_MASK_REG(block), value); -int -arm_get_next_irq(int last_irq) + if (irq == SW_INT_IRQNO_ENMI) /* must clear pending bit when enabled */ + aintc_write_4(sc, SW_INT_IRQ_PENDING_REG(0), + (1 << SW_INT_IRQNO_ENMI)); + mtx_unlock_spin(&sc->mtx); +} + +static void +a10_intr_mask(struct a10_aintc_softc *sc, u_int irq) +{ + uint32_t bit, block, value; + + bit = (irq % 32); + block = (irq / 32); + + mtx_lock_spin(&sc->mtx); + value = aintc_read_4(sc, SW_INT_ENABLE_REG(block)); + value &= ~(1 << bit); + aintc_write_4(sc, SW_INT_ENABLE_REG(block), value); + + value = aintc_read_4(sc, SW_INT_MASK_REG(block)); + value |= (1 << bit); + aintc_write_4(sc, SW_INT_MASK_REG(block), value); + mtx_unlock_spin(&sc->mtx); +} + +static int +a10_pending_irq(struct a10_aintc_softc *sc) { uint32_t value; int i, b; for (i = 0; i < 3; i++) { - value = aintc_read_4(SW_INT_IRQ_PENDING_REG(i)); + value = aintc_read_4(sc, SW_INT_IRQ_PENDING_REG(i)); for (b = 0; b < 32; b++) if (value & (1 << b)) { return (i * 32 + b); @@ -184,39 +254,196 @@ return (-1); } +#ifndef ARM_INTRNG + +int +arm_get_next_irq(int last_irq) +{ + return (a10_pending_irq(a10_aintc_sc)); +} + void -arm_mask_irq(uintptr_t nb) +arm_mask_irq(uintptr_t irq) { - uint32_t bit, block, value; - - bit = (nb % 32); - block = (nb / 32); + a10_intr_mask(a10_aintc_sc, irq); +} - value = aintc_read_4(SW_INT_ENABLE_REG(block)); - value &= ~(1 << bit); - aintc_write_4(SW_INT_ENABLE_REG(block), value); +void +arm_unmask_irq(uintptr_t irq) +{ + a10_intr_unmask(a10_aintc_sc, irq); +} - value = aintc_read_4(SW_INT_MASK_REG(block)); - value |= (1 << bit); - aintc_write_4(SW_INT_MASK_REG(block), value); +#else /* ARM_INTRNG */ + +static int +a10_intr(void *arg) +{ + struct a10_aintc_softc *sc = arg; + struct intr_irqsrc *isrc; + u_int irq; + + mtx_lock_spin(&sc->mtx); + irq = a10_pending_irq(sc); + mtx_unlock_spin(&sc->mtx); + + if (irq == -1) + return (FILTER_HANDLED); + + if (irq > A10_INTC_MAX_NIRQS) { + device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq); + return (FILTER_HANDLED); + } + + isrc = sc->irqs[irq]; + if (isrc == NULL) { + device_printf(sc->sc_dev, "Stray interrupt %d\n", irq); + a10_intr_mask(sc, irq); + return (FILTER_HANDLED); + } + + intr_irq_dispatch(isrc, curthread->td_intr_frame); + arm_irq_memory_barrier(irq); + + return (FILTER_HANDLED); } -void -arm_unmask_irq(uintptr_t nb) +static void +a10_intr_enable_intr(device_t dev, struct intr_irqsrc *isrc) { - uint32_t bit, block, value; + struct a10_aintc_softc *sc; + u_int irq = isrc->isrc_data; - bit = (nb % 32); - block = (nb / 32); + sc = device_get_softc(dev); + a10_intr_unmask(sc, irq); +} - value = aintc_read_4(SW_INT_ENABLE_REG(block)); - value |= (1 << bit); - aintc_write_4(SW_INT_ENABLE_REG(block), value); +static void +a10_intr_enable_source(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc; + u_int irq = isrc->isrc_data; - value = aintc_read_4(SW_INT_MASK_REG(block)); - value &= ~(1 << bit); - aintc_write_4(SW_INT_MASK_REG(block), value); + sc = device_get_softc(dev); + arm_irq_memory_barrier(irq); + a10_intr_unmask(sc, irq); +} + +static void +a10_intr_disable_source(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc; + u_int irq = isrc->isrc_data; - if(nb == SW_INT_IRQNO_ENMI) /* must clear pending bit when enabled */ - aintc_write_4(SW_INT_IRQ_PENDING_REG(0), (1 << SW_INT_IRQNO_ENMI)); + sc = device_get_softc(dev); + a10_intr_mask(sc, irq); } + +static int +a10_intr_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +{ + struct a10_aintc_softc *sc; + const char *name; + u_int irq; + + sc = device_get_softc(dev); + + if (isrc->isrc_ncells != 1) + return (EINVAL); + + irq = isrc->isrc_cells[0]; + if (irq > A10_INTC_MAX_NIRQS) + return (EINVAL); + + mtx_lock_spin(&sc->mtx); + if (sc->irqs[irq] != NULL) { + mtx_unlock_spin(&sc->mtx); + return (sc->irqs[irq] == isrc ? 0 : EEXIST); + } + sc->irqs[irq] = isrc; + mtx_unlock_spin(&sc->mtx); + + name = device_get_nameunit(dev); + intr_irq_set_name(isrc, "%s,i%u", name, irq); + isrc->isrc_nspc_num = irq; + isrc->isrc_data = irq; + isrc->isrc_pol = INTR_POLARITY_CONFORM; + isrc->isrc_trig = INTR_TRIGGER_CONFORM; + + return (0); +} + +static int +a10_intr_unregister(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc; + u_int irq = isrc->isrc_data; + int ret; + + sc = device_get_softc(dev); + mtx_lock_spin(&sc->mtx); + if (sc->irqs[irq] != isrc) { + ret = sc->irqs[irq] == NULL ? 0 : EINVAL; + mtx_unlock_spin(&sc->mtx); + return (ret); + } + sc->irqs[irq] = NULL; + isrc->isrc_data = 0; + mtx_unlock_spin(&sc->mtx); + + intr_irq_set_name(isrc, ""); + return (0); +} + +static void +a10_intr_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + a10_intr_disable_source(dev, isrc); +} + +static void +a10_intr_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + arm_irq_memory_barrier(0); + a10_intr_enable_source(dev, isrc); +} + +static void +a10_intr_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc = device_get_softc(dev); + + arm_irq_memory_barrier(0); + a10_intr_unmask(sc, isrc->isrc_data); +} + +#endif /* ARM_INTRNG */ + +static device_method_t a10_aintc_methods[] = { + DEVMETHOD(device_probe, a10_aintc_probe), + DEVMETHOD(device_attach, a10_aintc_attach), +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_source, a10_intr_disable_source), + DEVMETHOD(pic_enable_intr, a10_intr_enable_intr), + DEVMETHOD(pic_enable_source, a10_intr_enable_source), + DEVMETHOD(pic_post_filter, a10_intr_post_filter), + DEVMETHOD(pic_post_ithread, a10_intr_post_ithread), + DEVMETHOD(pic_pre_ithread, a10_intr_pre_ithread), + DEVMETHOD(pic_register, a10_intr_register), + DEVMETHOD(pic_unregister, a10_intr_unregister), +#endif + { 0, 0 } +}; + +static driver_t a10_aintc_driver = { + "aintc", + a10_aintc_methods, + sizeof(struct a10_aintc_softc), +}; + +static devclass_t a10_aintc_devclass; + +EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST); Index: sys/arm/conf/A10 =================================================================== --- sys/arm/conf/A10 +++ sys/arm/conf/A10 @@ -23,6 +23,8 @@ include "std.armv6" include "../allwinner/std.a10" +options ARM_INTRNG + options SOC_ALLWINNER_A10 options HZ=100