Index: sys/arm/amlogic/aml8726/aml8726_pic.c =================================================================== --- sys/arm/amlogic/aml8726/aml8726_pic.c +++ sys/arm/amlogic/aml8726/aml8726_pic.c @@ -1,5 +1,5 @@ /*- - * Copyright 2013-2015 John Wehle + * Copyright 2013-2016 John Wehle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -56,9 +57,25 @@ #include #include +#include + +#include "pic_if.h" + +#define AML_PIC_NCNTRLS 4 +#define AML_PIC_IRQS_PER_CNTRL 32 + +#define AML_PIC_NIRQS (AML_PIC_NCNTRLS * AML_PIC_IRQS_PER_CNTRL) + +struct aml8726_pic_irqsrc { + struct intr_irqsrc isrc; + u_int irq; +}; + struct aml8726_pic_softc { - device_t dev; - struct resource * res[1]; + device_t dev; + struct resource * res[1]; + struct mtx mtx; + struct aml8726_pic_irqsrc isrcs[AML_PIC_NIRQS]; }; static struct resource_spec aml8726_pic_spec[] = { @@ -66,19 +83,13 @@ { -1, 0 } }; -/* - * devclass_get_device / device_get_softc could be used - * to dynamically locate this, however the pic is a - * required device which can't be unloaded so there's - * no need for the overhead. - */ -static struct aml8726_pic_softc *aml8726_pic_sc = NULL; +#define AML_PIC_LOCK(sc) mtx_lock_spin(&(sc)->mtx) +#define AML_PIC_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) +#define AML_PIC_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "pic", MTX_SPIN) +#define AML_PIC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); -#define AML_PIC_NCNTRLS 4 -#define AML_PIC_IRQS_PER_CNTRL 32 - -#define AML_PIC_NIRQS (AML_PIC_NCNTRLS * AML_PIC_IRQS_PER_CNTRL) - #define AML_PIC_0_STAT_REG 0 #define AML_PIC_0_STAT_CLR_REG 4 #define AML_PIC_0_MASK_REG 8 @@ -100,7 +111,7 @@ #define AML_PIC_3_FIRQ_SEL 60 #define AML_PIC_CTRL(x) ((x) >> 5) -#define AML_PIC_BIT(x) (1 << ((x) & 0x1f)) +#define AML_PIC_BIT(x) (1U << ((x) & 0x1f)) #define AML_PIC_STAT_REG(x) (AML_PIC_0_STAT_REG + AML_PIC_CTRL(x) * 16) #define AML_PIC_STAT_CLR_REG(x) (AML_PIC_0_STAT_CLR_REG + AML_PIC_CTRL(x) * 16) @@ -112,22 +123,101 @@ #define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) -static void -aml8726_pic_eoi(void *arg) +static inline void +aml8726_pic_eoi(struct aml8726_pic_softc *sc, u_int irq) { - uintptr_t nb = (uintptr_t) arg; - if (nb >= AML_PIC_NIRQS) + if (irq >= AML_PIC_NIRQS) return; - arm_irq_memory_barrier(nb); + CSR_WRITE_4(sc, AML_PIC_STAT_CLR_REG(irq), AML_PIC_BIT(irq)); - CSR_WRITE_4(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb), AML_PIC_BIT(nb)); + CSR_BARRIER(sc, AML_PIC_STAT_CLR_REG(irq)); +} - CSR_BARRIER(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb)); +static inline void +aml8726_pic_mask_irq(struct aml8726_pic_softc *sc, u_int irq) +{ + uint32_t mask; + + if (irq >= AML_PIC_NIRQS) + return; + + AML_PIC_LOCK(sc); + + mask = CSR_READ_4(sc, AML_PIC_MASK_REG(irq)); + mask &= ~AML_PIC_BIT(irq); + CSR_WRITE_4(sc, AML_PIC_MASK_REG(irq), mask); + + CSR_BARRIER(sc, AML_PIC_MASK_REG(irq)); + + AML_PIC_UNLOCK(sc); } +static inline void +aml8726_pic_unmask_irq(struct aml8726_pic_softc *sc, u_int irq) +{ + uint32_t mask; + + if (irq >= AML_PIC_NIRQS) + return; + + AML_PIC_LOCK(sc); + + mask = CSR_READ_4(sc, AML_PIC_MASK_REG(irq)); + mask |= AML_PIC_BIT(irq); + CSR_WRITE_4(sc, AML_PIC_MASK_REG(irq), mask); + + CSR_BARRIER(sc, AML_PIC_MASK_REG(irq)); + + AML_PIC_UNLOCK(sc); +} + +static u_int +aml8726_pic_pending_irq(struct aml8726_pic_softc *sc) +{ + uint32_t value; + u_int irq; + + for (irq = 0; irq < AML_PIC_NIRQS; irq += AML_PIC_IRQS_PER_CNTRL) { + value = CSR_READ_4(sc, AML_PIC_STAT_REG(irq)); + if (value) + return (irq + ffs(value) - 1); + } + + return (AML_PIC_NIRQS); +} + static int +aml8726_pic_intr(void *arg) +{ + struct aml8726_pic_softc *sc = (struct aml8726_pic_softc *)arg; + boolean_t spurious; + u_int irq; + + for (spurious = true; ; spurious = false) { + irq = aml8726_pic_pending_irq(sc); + if (irq >= AML_PIC_NIRQS) + break; + + if (intr_isrc_dispatch(&sc->isrcs[irq].isrc, + curthread->td_intr_frame) != 0) { + aml8726_pic_mask_irq(sc, irq); + aml8726_pic_eoi(sc, irq); + device_printf(sc->dev, + "Stray interrupt %d disabled\n", irq); + } + + arm_irq_memory_barrier(irq); + } + + if (spurious) + device_printf(sc->dev, "Spurious interrupt %d\n", irq); + + return (FILTER_HANDLED); +} + +static int aml8726_pic_probe(device_t dev) { @@ -146,12 +236,13 @@ aml8726_pic_attach(device_t dev) { struct aml8726_pic_softc *sc = device_get_softc(dev); + const char *name; int i; + int error; + uint32_t irq; + phandle_t xref; + struct intr_pic *pic; - /* There should be exactly one instance. */ - if (aml8726_pic_sc != NULL) - return (ENXIO); - sc->dev = dev; if (bus_alloc_resources(dev, aml8726_pic_spec, sc->res)) { @@ -168,15 +259,48 @@ CSR_WRITE_4(sc, AML_PIC_0_FIRQ_SEL + i * 16, 0); } -#ifndef DEV_GIC - arm_post_filter = aml8726_pic_eoi; -#else - device_printf(dev, "disabled in favor of gic\n"); +#ifdef DEV_GIC + /* + * When GIC is present only claim the root if running on a + * single core chip. + */ + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M3: + break; + default: + device_printf(dev, "disabled in favor of gic\n"); + return (0); + /* NOTREACHED */ + } #endif - aml8726_pic_sc = sc; + AML_PIC_LOCK_INIT(sc); - return (0); + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < AML_PIC_NIRQS; irq++) { + sc->isrcs[irq].irq = irq; + + error = intr_isrc_register(&sc->isrcs[irq].isrc, + sc->dev, 0, "%s,%u", name, irq); + if (error != 0) + goto fail; + } + + xref = OF_xref_from_node(ofw_bus_get_node(sc->dev)); + pic = intr_pic_register(sc->dev, xref); + if (pic == NULL) { + error = ENXIO; + goto fail; + } + + return (intr_pic_claim_root(sc->dev, xref, aml8726_pic_intr, sc, 0)); + +fail: + AML_PIC_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_pic_spec, sc->res); + + return (error); } static int @@ -186,92 +310,101 @@ return (EBUSY); } -static device_method_t aml8726_pic_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, aml8726_pic_probe), - DEVMETHOD(device_attach, aml8726_pic_attach), - DEVMETHOD(device_detach, aml8726_pic_detach), +static void +aml8726_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct aml8726_pic_softc *sc = device_get_softc(dev); + u_int irq = ((struct aml8726_pic_irqsrc *)isrc)->irq; - DEVMETHOD_END -}; + aml8726_pic_mask_irq(sc, irq); +} -static driver_t aml8726_pic_driver = { - "pic", - aml8726_pic_methods, - sizeof(struct aml8726_pic_softc), -}; +static void +aml8726_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct aml8726_pic_softc *sc = device_get_softc(dev); + u_int irq = ((struct aml8726_pic_irqsrc *)isrc)->irq; -static devclass_t aml8726_pic_devclass; + arm_irq_memory_barrier(irq); + aml8726_pic_unmask_irq(sc, irq); +} -EARLY_DRIVER_MODULE(pic, simplebus, aml8726_pic_driver, aml8726_pic_devclass, - 0, 0, BUS_PASS_INTERRUPT); - -#ifndef DEV_GIC -int -arm_get_next_irq(int last) +static int +aml8726_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { - uint32_t value; - int irq; - int start; + struct aml8726_pic_softc *sc = device_get_softc(dev); + struct intr_map_data_fdt *daf; + u_int irq; - /* - * The extra complexity is simply so that all IRQs are checked - * round robin so a particularly busy interrupt can't prevent - * other interrupts from being serviced. - */ + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); - start = (last + 1) % AML_PIC_NIRQS; - irq = start; + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells == 1) + irq = daf->cells[0]; + else if (daf->ncells == 3 && daf->cells[0] == 0) + irq = daf->cells[1]; + else + return (EINVAL); + if (irq >= AML_PIC_NIRQS) + return (EINVAL); - for ( ; ; ) { - value = CSR_READ_4(aml8726_pic_sc, AML_PIC_STAT_REG(irq)); + *isrcp = &sc->isrcs[irq].isrc; + return (0); +} - for ( ; ; ) { - if ((value & AML_PIC_BIT(irq)) != 0) - return (irq); +static void +aml8726_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct aml8726_pic_softc *sc = device_get_softc(dev); + u_int irq = ((struct aml8726_pic_irqsrc *)isrc)->irq; - irq = (irq + 1) % AML_PIC_NIRQS; + arm_irq_memory_barrier(irq); + aml8726_pic_eoi(sc, irq); +} - if (irq == start) - return (-1); +static void +aml8726_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ - if ((irq % AML_PIC_IRQS_PER_CNTRL) == 0) - break; - } - } + aml8726_pic_enable_intr(dev, isrc); } -void -arm_mask_irq(uintptr_t nb) +static void +aml8726_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { - uint32_t mask; + struct aml8726_pic_softc *sc = device_get_softc(dev); + u_int irq = ((struct aml8726_pic_irqsrc *)isrc)->irq; - if (nb >= AML_PIC_NIRQS) - return; + aml8726_pic_mask_irq(sc, irq); + aml8726_pic_eoi(sc, irq); +} - mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); - mask &= ~AML_PIC_BIT(nb); - CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask); +static device_method_t aml8726_pic_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_pic_probe), + DEVMETHOD(device_attach, aml8726_pic_attach), + DEVMETHOD(device_detach, aml8726_pic_detach), - CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, aml8726_pic_disable_intr), + DEVMETHOD(pic_enable_intr, aml8726_pic_enable_intr), + DEVMETHOD(pic_map_intr, aml8726_pic_map_intr), + DEVMETHOD(pic_post_filter, aml8726_pic_post_filter), + DEVMETHOD(pic_post_ithread, aml8726_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, aml8726_pic_pre_ithread), - aml8726_pic_eoi((void *)nb); -} + DEVMETHOD_END +}; -void -arm_unmask_irq(uintptr_t nb) -{ - uint32_t mask; +static driver_t aml8726_pic_driver = { + "pic", + aml8726_pic_methods, + sizeof(struct aml8726_pic_softc), +}; - if (nb >= AML_PIC_NIRQS) - return; +static devclass_t aml8726_pic_devclass; - arm_irq_memory_barrier(nb); - - mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); - mask |= AML_PIC_BIT(nb); - CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask); - - CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); -} -#endif +EARLY_DRIVER_MODULE(pic, simplebus, aml8726_pic_driver, aml8726_pic_devclass, + 0, 0, BUS_PASS_INTERRUPT); Index: sys/arm/conf/AML8726 =================================================================== --- sys/arm/conf/AML8726 +++ sys/arm/conf/AML8726 @@ -24,7 +24,6 @@ include "../amlogic/aml8726/std.aml8726" options SCHED_ULE # ULE scheduler -options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed. options LINUX_BOOT_ABI # NFS root from boopt/dhcp @@ -36,6 +35,8 @@ # Interrupt controller device aml_pic +device gic +options INTRNG # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus Index: sys/arm/conf/ODROIDC1 =================================================================== --- sys/arm/conf/ODROIDC1 +++ sys/arm/conf/ODROIDC1 @@ -24,9 +24,5 @@ options SMP # Enable multiple cores -# Interrupt controller -device gic -options INTRNG - options FDT_DTB_STATIC makeoptions FDT_DTS_FILE=odroidc1.dts Index: sys/arm/conf/VSATV102 =================================================================== --- sys/arm/conf/VSATV102 +++ sys/arm/conf/VSATV102 @@ -24,9 +24,5 @@ options SMP # Enable multiple cores -# Interrupt controller -device gic -options INTRNG - options FDT_DTB_STATIC makeoptions FDT_DTS_FILE=vsatv102-m6.dts