Index: head/sys/arm/allwinner/a10/a10_intc.c =================================================================== --- head/sys/arm/allwinner/a10/a10_intc.c (revision 300148) +++ head/sys/arm/allwinner/a10/a10_intc.c (revision 300149) @@ -1,434 +1,435 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * Copyright (c) 2016 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INTRNG #include #endif #include #include #include #include #include #include #ifdef 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 A10_INTR_MAX_NIRQS 81 #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)) #ifdef INTRNG struct a10_intr_irqsrc { struct intr_irqsrc isrc; u_int irq; }; #endif struct a10_aintc_softc { device_t sc_dev; struct resource * aintc_res; bus_space_tag_t aintc_bst; bus_space_handle_t aintc_bsh; struct mtx mtx; #ifdef INTRNG struct a10_intr_irqsrc isrcs[A10_INTR_MAX_NIRQS]; #endif }; #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 __inline void a10_intr_eoi(struct a10_aintc_softc *sc, u_int irq) { if (irq != SW_INT_IRQNO_ENMI) return; mtx_lock_spin(&sc->mtx); aintc_write_4(sc, SW_INT_IRQ_PENDING_REG(0), (1 << SW_INT_IRQNO_ENMI)); mtx_unlock_spin(&sc->mtx); } static void a10_intr_unmask(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 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(sc, SW_INT_IRQ_PENDING_REG(i)); if (value == 0) continue; for (b = 0; b < 32; b++) if (value & (1 << b)) { return (i * 32 + b); } } return (-1); } #ifndef INTRNG static struct a10_aintc_softc *a10_aintc_sc = NULL; int arm_get_next_irq(int last_irq) { return (a10_pending_irq(a10_aintc_sc)); } void arm_mask_irq(uintptr_t irq) { a10_intr_mask(a10_aintc_sc, irq); } void arm_unmask_irq(uintptr_t irq) { a10_intr_unmask(a10_aintc_sc, irq); a10_intr_eoi(a10_aintc_sc, irq); } #else /* INTRNG */ static int a10_intr(void *arg) { struct a10_aintc_softc *sc = arg; u_int irq; irq = a10_pending_irq(sc); if (irq == -1 || irq > A10_INTR_MAX_NIRQS) { device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq); return (FILTER_HANDLED); } while (irq != -1) { if (irq > A10_INTR_MAX_NIRQS) { device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq); return (FILTER_HANDLED); } if (intr_isrc_dispatch(&sc->isrcs[irq].isrc, curthread->td_intr_frame) != 0) { a10_intr_mask(sc, irq); a10_intr_eoi(sc, irq); device_printf(sc->sc_dev, "Stray interrupt %d disabled\n", irq); } arm_irq_memory_barrier(irq); irq = a10_pending_irq(sc); } return (FILTER_HANDLED); } static int a10_intr_pic_attach(struct a10_aintc_softc *sc) { + struct intr_pic *pic; int error; uint32_t irq; const char *name; intptr_t xref; name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < A10_INTR_MAX_NIRQS; irq++) { sc->isrcs[irq].irq = irq; error = intr_isrc_register(&sc->isrcs[irq].isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); } xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)); - error = intr_pic_register(sc->sc_dev, xref); - if (error != 0) - return (error); + pic = intr_pic_register(sc->sc_dev, xref); + if (pic == NULL) + return (ENXIO); return (intr_pic_claim_root(sc->sc_dev, xref, a10_intr, sc, 0)); } static void a10_intr_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct a10_aintc_softc *sc; u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; sc = device_get_softc(dev); arm_irq_memory_barrier(irq); a10_intr_unmask(sc, irq); } static void a10_intr_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct a10_aintc_softc *sc; u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; sc = device_get_softc(dev); a10_intr_mask(sc, irq); } static int a10_intr_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct a10_aintc_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= A10_INTR_MAX_NIRQS) return (EINVAL); sc = device_get_softc(dev); *isrcp = &sc->isrcs[daf->cells[0]].isrc; return (0); } static void a10_intr_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct a10_aintc_softc *sc = device_get_softc(dev); u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; a10_intr_mask(sc, irq); a10_intr_eoi(sc, irq); } static void a10_intr_post_ithread(device_t dev, struct intr_irqsrc *isrc) { a10_intr_enable_intr(dev, isrc); } static void a10_intr_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct a10_aintc_softc *sc = device_get_softc(dev); u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; a10_intr_eoi(sc, irq); } #endif /* INTRNG */ static int a10_aintc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ic")) return (ENXIO); device_set_desc(dev, "A10 AINTC Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int a10_aintc_attach(device_t dev) { struct a10_aintc_softc *sc = device_get_softc(dev); int rid = 0; int i; sc->sc_dev = dev; #ifndef INTRNG if (a10_aintc_sc) goto error; a10_aintc_sc = sc; #endif 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"); 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); /* Disable & clear all interrupts */ for (i = 0; i < 3; i++) { 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(sc, SW_INT_PROTECTION_REG, 0x01); /* config the external interrupt source type*/ aintc_write_4(sc, SW_INT_NMI_CTRL_REG, 0x00); #ifdef INTRNG if (a10_intr_pic_attach(sc) != 0) { device_printf(dev, "could not attach PIC\n"); return (ENXIO); } #endif return (0); error: bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->aintc_res); return (ENXIO); } static device_method_t a10_aintc_methods[] = { DEVMETHOD(device_probe, a10_aintc_probe), DEVMETHOD(device_attach, a10_aintc_attach), #ifdef INTRNG /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, a10_intr_disable_intr), DEVMETHOD(pic_enable_intr, a10_intr_enable_intr), DEVMETHOD(pic_map_intr, a10_intr_map_intr), DEVMETHOD(pic_post_filter, a10_intr_post_filter), DEVMETHOD(pic_post_ithread, a10_intr_post_ithread), DEVMETHOD(pic_pre_ithread, a10_intr_pre_ithread), #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: head/sys/arm/allwinner/aw_nmi.c =================================================================== --- head/sys/arm/allwinner/aw_nmi.c (revision 300148) +++ head/sys/arm/allwinner/aw_nmi.c (revision 300149) @@ -1,403 +1,403 @@ /*- * Copyright (c) 2016 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define NMI_IRQ_CTRL_REG 0x0 #define NMI_IRQ_LOW_LEVEL 0x0 #define NMI_IRQ_LOW_EDGE 0x1 #define NMI_IRQ_HIGH_LEVEL 0x2 #define NMI_IRQ_HIGH_EDGE 0x3 #define NMI_IRQ_PENDING_REG 0x4 #define NMI_IRQ_ACK (1U << 0) #define A20_NMI_IRQ_ENABLE_REG 0x8 #define A31_NMI_IRQ_ENABLE_REG 0x34 #define NMI_IRQ_ENABLE (1U << 0) #define SC_NMI_READ(_sc, _reg) bus_read_4(_sc->res[0], _reg) #define SC_NMI_WRITE(_sc, _reg, _val) bus_write_4(_sc->res[0], _reg, _val) static struct resource_spec aw_nmi_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0, 0 } }; struct aw_nmi_intr { struct intr_irqsrc isrc; u_int irq; enum intr_polarity pol; enum intr_trigger tri; }; struct aw_nmi_softc { device_t dev; struct resource * res[2]; void * intrcookie; struct aw_nmi_intr intr; uint8_t enable_reg; }; #define A20_NMI 1 #define A31_NMI 2 static struct ofw_compat_data compat_data[] = { {"allwinner,sun7i-a20-sc-nmi", A20_NMI}, {"allwinner,sun6i-a31-sc-nmi", A31_NMI}, {NULL, 0}, }; static int aw_nmi_intr(void *arg) { struct aw_nmi_softc *sc; sc = arg; if (SC_NMI_READ(sc, NMI_IRQ_PENDING_REG) == 0) { device_printf(sc->dev, "Spurious interrupt\n"); return (FILTER_HANDLED); } if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) { SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); device_printf(sc->dev, "Stray interrupt, NMI disabled\n"); } return (FILTER_HANDLED); } static void aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct aw_nmi_softc *sc; sc = device_get_softc(dev); SC_NMI_WRITE(sc, sc->enable_reg, NMI_IRQ_ENABLE); } static void aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct aw_nmi_softc *sc; sc = device_get_softc(dev); SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); } static int aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, enum intr_polarity *polp, enum intr_trigger *trigp) { u_int irq, tripol; enum intr_polarity pol; enum intr_trigger trig; if (ncells != 2) { device_printf(dev, "Invalid #interrupt-cells\n"); return (EINVAL); } irq = cells[0]; if (irq != 0) { device_printf(dev, "Controller only support irq 0\n"); return (EINVAL); } tripol = cells[1]; switch (tripol) { case IRQ_TYPE_EDGE_RISING: trig = INTR_TRIGGER_EDGE; pol = INTR_POLARITY_HIGH; break; case IRQ_TYPE_EDGE_FALLING: trig = INTR_TRIGGER_EDGE; pol = INTR_POLARITY_LOW; break; case IRQ_TYPE_LEVEL_HIGH: trig = INTR_TRIGGER_LEVEL; pol = INTR_POLARITY_HIGH; break; case IRQ_TYPE_LEVEL_LOW: trig = INTR_TRIGGER_LEVEL; pol = INTR_POLARITY_LOW; break; default: device_printf(dev, "unsupported trigger/polarity 0x%2x\n", tripol); return (ENOTSUP); } *irqp = irq; if (polp != NULL) *polp = pol; if (trigp != NULL) *trigp = trig; return (0); } static int aw_nmi_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct aw_nmi_softc *sc; int error; u_int irq; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL); if (error == 0) *isrcp = &sc->intr.isrc; return (error); } static int aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct intr_map_data_fdt *daf; struct aw_nmi_softc *sc; struct aw_nmi_intr *nmi_intr; int error, icfg; u_int irq; enum intr_trigger trig; enum intr_polarity pol; /* Get config for interrupt. */ if (data == NULL || data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); nmi_intr = (struct aw_nmi_intr *)isrc; daf = (struct intr_map_data_fdt *)data; error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig); if (error != 0) return (error); if (nmi_intr->irq != irq) return (EINVAL); /* Compare config if this is not first setup. */ if (isrc->isrc_handlers != 0) { if (pol != nmi_intr->pol || trig != nmi_intr->tri) return (EINVAL); else return (0); } nmi_intr->pol = pol; nmi_intr->tri = trig; if (trig == INTR_TRIGGER_LEVEL) { if (pol == INTR_POLARITY_LOW) icfg = NMI_IRQ_LOW_LEVEL; else icfg = NMI_IRQ_HIGH_LEVEL; } else { if (pol == INTR_POLARITY_HIGH) icfg = NMI_IRQ_HIGH_EDGE; else icfg = NMI_IRQ_LOW_EDGE; } SC_NMI_WRITE(sc, NMI_IRQ_CTRL_REG, icfg); return (0); } static int aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct aw_nmi_softc *sc; sc = device_get_softc(dev); if (isrc->isrc_handlers == 0) { sc->intr.pol = INTR_POLARITY_CONFORM; sc->intr.tri = INTR_TRIGGER_CONFORM; SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); } return (0); } static void aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct aw_nmi_softc *sc; sc = device_get_softc(dev); aw_nmi_disable_intr(dev, isrc); SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK); } static void aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc) { arm_irq_memory_barrier(0); aw_nmi_enable_intr(dev, isrc); } static void aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct aw_nmi_softc *sc; sc = device_get_softc(dev); arm_irq_memory_barrier(0); SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK); } static int aw_nmi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner NMI Controller"); return (BUS_PROBE_DEFAULT); } static int aw_nmi_attach(device_t dev) { struct aw_nmi_softc *sc; phandle_t xref; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) { device_printf(dev, "can't allocate device resources\n"); return (ENXIO); } if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC, aw_nmi_intr, NULL, sc, &sc->intrcookie))) { device_printf(dev, "unable to register interrupt handler\n"); bus_release_resources(dev, aw_nmi_res_spec, sc->res); return (ENXIO); } switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case A20_NMI: sc->enable_reg = A20_NMI_IRQ_ENABLE_REG; break; case A31_NMI: sc->enable_reg = A31_NMI_IRQ_ENABLE_REG; break; } /* Disable and clear interrupts */ SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK); xref = OF_xref_from_node(ofw_bus_get_node(dev)); /* Register our isrc */ sc->intr.irq = 0; sc->intr.pol = INTR_POLARITY_CONFORM; sc->intr.tri = INTR_TRIGGER_CONFORM; if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u", device_get_nameunit(sc->dev), sc->intr.irq) != 0) goto error; - if (intr_pic_register(dev, (intptr_t)xref) != 0) { + if (intr_pic_register(dev, (intptr_t)xref) == NULL) { device_printf(dev, "could not register pic\n"); goto error; } return (0); error: bus_teardown_intr(dev, sc->res[1], sc->intrcookie); bus_release_resources(dev, aw_nmi_res_spec, sc->res); return (ENXIO); } static device_method_t aw_nmi_methods[] = { DEVMETHOD(device_probe, aw_nmi_probe), DEVMETHOD(device_attach, aw_nmi_attach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, aw_nmi_disable_intr), DEVMETHOD(pic_enable_intr, aw_nmi_enable_intr), DEVMETHOD(pic_map_intr, aw_nmi_map_intr), DEVMETHOD(pic_setup_intr, aw_nmi_setup_intr), DEVMETHOD(pic_teardown_intr, aw_nmi_teardown_intr), DEVMETHOD(pic_post_filter, aw_nmi_post_filter), DEVMETHOD(pic_post_ithread, aw_nmi_post_ithread), DEVMETHOD(pic_pre_ithread, aw_nmi_pre_ithread), {0, 0}, }; static driver_t aw_nmi_driver = { "aw_nmi", aw_nmi_methods, sizeof(struct aw_nmi_softc), }; static devclass_t aw_nmi_devclass; EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver, aw_nmi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); Index: head/sys/arm/arm/gic.c =================================================================== --- head/sys/arm/arm/gic.c (revision 300148) +++ head/sys/arm/arm/gic.c (revision 300149) @@ -1,1806 +1,1806 @@ /*- * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * * Developed by Damjan Marion * * Based on OMAP4 GIC code by Ben Gray * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INTRNG #include #endif #include #include #include #include #include #include #include #include #include #ifdef INTRNG #include "pic_if.h" #include "msi_if.h" #endif #define GIC_DEBUG_SPURIOUS /* We are using GICv2 register naming */ /* Distributor Registers */ #define GICD_CTLR 0x000 /* v1 ICDDCR */ #define GICD_TYPER 0x004 /* v1 ICDICTR */ #define GICD_IIDR 0x008 /* v1 ICDIIDR */ #define GICD_IGROUPR(n) (0x0080 + ((n) * 4)) /* v1 ICDISER */ #define GICD_ISENABLER(n) (0x0100 + ((n) * 4)) /* v1 ICDISER */ #define GICD_ICENABLER(n) (0x0180 + ((n) * 4)) /* v1 ICDICER */ #define GICD_ISPENDR(n) (0x0200 + ((n) * 4)) /* v1 ICDISPR */ #define GICD_ICPENDR(n) (0x0280 + ((n) * 4)) /* v1 ICDICPR */ #define GICD_ICACTIVER(n) (0x0380 + ((n) * 4)) /* v1 ICDABR */ #define GICD_IPRIORITYR(n) (0x0400 + ((n) * 4)) /* v1 ICDIPR */ #define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */ #define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */ #define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */ #define GICD_SGI_TARGET_SHIFT 16 /* CPU Registers */ #define GICC_CTLR 0x0000 /* v1 ICCICR */ #define GICC_PMR 0x0004 /* v1 ICCPMR */ #define GICC_BPR 0x0008 /* v1 ICCBPR */ #define GICC_IAR 0x000C /* v1 ICCIAR */ #define GICC_EOIR 0x0010 /* v1 ICCEOIR */ #define GICC_RPR 0x0014 /* v1 ICCRPR */ #define GICC_HPPIR 0x0018 /* v1 ICCHPIR */ #define GICC_ABPR 0x001C /* v1 ICCABPR */ #define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ #define GIC_FIRST_SGI 0 /* Irqs 0-15 are SGIs/IPIs. */ #define GIC_LAST_SGI 15 #define GIC_FIRST_PPI 16 /* Irqs 16-31 are private (per */ #define GIC_LAST_PPI 31 /* core) peripheral interrupts. */ #define GIC_FIRST_SPI 32 /* Irqs 32+ are shared peripherals. */ /* TYPER Registers */ #define GICD_TYPER_SECURITYEXT 0x400 #define GIC_SUPPORT_SECEXT(_sc) \ ((_sc->typer & GICD_TYPER_SECURITYEXT) == GICD_TYPER_SECURITYEXT) /* First bit is a polarity bit (0 - low, 1 - high) */ #define GICD_ICFGR_POL_LOW (0 << 0) #define GICD_ICFGR_POL_HIGH (1 << 0) #define GICD_ICFGR_POL_MASK 0x1 /* Second bit is a trigger bit (0 - level, 1 - edge) */ #define GICD_ICFGR_TRIG_LVL (0 << 1) #define GICD_ICFGR_TRIG_EDGE (1 << 1) #define GICD_ICFGR_TRIG_MASK 0x2 #ifndef GIC_DEFAULT_ICFGR_INIT #define GIC_DEFAULT_ICFGR_INIT 0x00000000 #endif #ifdef INTRNG struct gic_irqsrc { struct intr_irqsrc gi_isrc; uint32_t gi_irq; enum intr_polarity gi_pol; enum intr_trigger gi_trig; #define GI_FLAG_EARLY_EOI (1 << 0) #define GI_FLAG_MSI (1 << 1) /* This interrupt source should only */ /* be used for MSI/MSI-X interrupts */ #define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */ /* for a MSI/MSI-X interrupt */ u_int gi_flags; }; static u_int gic_irq_cpu; static int arm_gic_intr(void *); static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc); #ifdef SMP static u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; static u_int sgi_first_unused = GIC_FIRST_SGI; #endif #endif #ifdef INTRNG struct arm_gic_range { uint64_t bus; uint64_t host; uint64_t size; }; struct arm_gic_devinfo { struct ofw_bus_devinfo obdinfo; struct resource_list rl; }; #endif struct arm_gic_softc { device_t gic_dev; #ifdef INTRNG void * gic_intrhand; struct gic_irqsrc * gic_irqs; #endif struct resource * gic_res[3]; bus_space_tag_t gic_c_bst; bus_space_tag_t gic_d_bst; bus_space_handle_t gic_c_bsh; bus_space_handle_t gic_d_bsh; uint8_t ver; struct mtx mutex; uint32_t nirqs; uint32_t typer; #ifdef GIC_DEBUG_SPURIOUS uint32_t last_irq[MAXCPU]; #endif #ifdef INTRNG /* FDT child data */ pcell_t addr_cells; pcell_t size_cells; int nranges; struct arm_gic_range * ranges; #endif }; #ifdef INTRNG #define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc) #endif static struct resource_spec arm_gic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ #ifdef INTRNG { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */ #endif { -1, 0 } }; static u_int arm_gic_map[MAXCPU]; static struct arm_gic_softc *gic_sc = NULL; #define gic_c_read_4(_sc, _reg) \ bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg)) #define gic_c_write_4(_sc, _reg, _val) \ bus_space_write_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg), (_val)) #define gic_d_read_4(_sc, _reg) \ bus_space_read_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg)) #define gic_d_write_1(_sc, _reg, _val) \ bus_space_write_1((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) #define gic_d_write_4(_sc, _reg, _val) \ bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) #ifndef INTRNG static int gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol); static void gic_post_filter(void *); #endif static struct ofw_compat_data compat_data[] = { {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ {"arm,gic-400", true}, {"arm,cortex-a15-gic", true}, {"arm,cortex-a9-gic", true}, {"arm,cortex-a7-gic", true}, {"arm,arm11mp-gic", true}, {"brcm,brahma-b15-gic", true}, {"qcom,msm-qgic2", true}, {NULL, false} }; static int arm_gic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "ARM Generic Interrupt Controller"); return (BUS_PROBE_DEFAULT); } #ifdef INTRNG static inline void gic_irq_unmask(struct arm_gic_softc *sc, u_int irq) { gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F))); } static inline void gic_irq_mask(struct arm_gic_softc *sc, u_int irq) { gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F))); } #endif static uint8_t gic_cpu_mask(struct arm_gic_softc *sc) { uint32_t mask; int i; /* Read the current cpuid mask by reading ITARGETSR{0..7} */ for (i = 0; i < 8; i++) { mask = gic_d_read_4(sc, GICD_ITARGETSR(i)); if (mask != 0) break; } /* No mask found, assume we are on CPU interface 0 */ if (mask == 0) return (1); /* Collect the mask in the lower byte */ mask |= mask >> 16; mask |= mask >> 8; return (mask); } #ifdef SMP #ifdef INTRNG static void arm_gic_init_secondary(device_t dev) { struct arm_gic_softc *sc = device_get_softc(dev); u_int irq, cpu; /* Set the mask so we can find this CPU to send it IPIs */ cpu = PCPU_GET(cpuid); arm_gic_map[cpu] = gic_cpu_mask(sc); for (irq = 0; irq < sc->nirqs; irq += 4) gic_d_write_4(sc, GICD_IPRIORITYR(irq >> 2), 0); /* Set all the interrupts to be in Group 0 (secure) */ for (irq = 0; GIC_SUPPORT_SECEXT(sc) && irq < sc->nirqs; irq += 32) { gic_d_write_4(sc, GICD_IGROUPR(irq >> 5), 0); } /* Enable CPU interface */ gic_c_write_4(sc, GICC_CTLR, 1); /* Set priority mask register. */ gic_c_write_4(sc, GICC_PMR, 0xff); /* Enable interrupt distribution */ gic_d_write_4(sc, GICD_CTLR, 0x01); /* Unmask attached SGI interrupts. */ for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) if (intr_isrc_init_on_cpu(GIC_INTR_ISRC(sc, irq), cpu)) gic_irq_unmask(sc, irq); /* Unmask attached PPI interrupts. */ for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) if (intr_isrc_init_on_cpu(GIC_INTR_ISRC(sc, irq), cpu)) gic_irq_unmask(sc, irq); } #else static void arm_gic_init_secondary(device_t dev) { struct arm_gic_softc *sc = device_get_softc(dev); int i; /* Set the mask so we can find this CPU to send it IPIs */ arm_gic_map[PCPU_GET(cpuid)] = gic_cpu_mask(sc); for (i = 0; i < sc->nirqs; i += 4) gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); /* Set all the interrupts to be in Group 0 (secure) */ for (i = 0; GIC_SUPPORT_SECEXT(sc) && i < sc->nirqs; i += 32) { gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); } /* Enable CPU interface */ gic_c_write_4(sc, GICC_CTLR, 1); /* Set priority mask register. */ gic_c_write_4(sc, GICC_PMR, 0xff); /* Enable interrupt distribution */ gic_d_write_4(sc, GICD_CTLR, 0x01); /* * Activate the timer interrupts: virtual, secure, and non-secure. */ gic_d_write_4(sc, GICD_ISENABLER(27 >> 5), (1UL << (27 & 0x1F))); gic_d_write_4(sc, GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); gic_d_write_4(sc, GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F))); } #endif /* INTRNG */ #endif /* SMP */ #ifndef INTRNG int gic_decode_fdt(phandle_t iparent, pcell_t *intr, int *interrupt, int *trig, int *pol) { static u_int num_intr_cells; static phandle_t self; struct ofw_compat_data *ocd; if (self == 0) { for (ocd = compat_data; ocd->ocd_str != NULL; ocd++) { if (fdt_is_compatible(iparent, ocd->ocd_str)) { self = iparent; break; } } } if (self != iparent) return (ENXIO); if (num_intr_cells == 0) { if (OF_searchencprop(OF_node_from_xref(iparent), "#interrupt-cells", &num_intr_cells, sizeof(num_intr_cells)) == -1) { num_intr_cells = 1; } } if (num_intr_cells == 1) { *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } else { if (fdt32_to_cpu(intr[0]) == 0) *interrupt = fdt32_to_cpu(intr[1]) + GIC_FIRST_SPI; else *interrupt = fdt32_to_cpu(intr[1]) + GIC_FIRST_PPI; /* * In intr[2], bits[3:0] are trigger type and level flags. * 1 = low-to-high edge triggered * 2 = high-to-low edge triggered * 4 = active high level-sensitive * 8 = active low level-sensitive * The hardware only supports active-high-level or rising-edge * for SPIs */ if (*interrupt >= GIC_FIRST_SPI && fdt32_to_cpu(intr[2]) & 0x0a) { printf("unsupported trigger/polarity configuration " "0x%02x\n", fdt32_to_cpu(intr[2]) & 0x0f); } *pol = INTR_POLARITY_CONFORM; if (fdt32_to_cpu(intr[2]) & 0x03) *trig = INTR_TRIGGER_EDGE; else *trig = INTR_TRIGGER_LEVEL; } return (0); } #endif #ifdef INTRNG static inline intptr_t gic_xref(device_t dev) { #ifdef FDT return (OF_xref_from_node(ofw_bus_get_node(dev))); #else return (0); #endif } static int arm_gic_register_isrcs(struct arm_gic_softc *sc, uint32_t num) { int error; uint32_t irq; struct gic_irqsrc *irqs; struct intr_irqsrc *isrc; const char *name; irqs = malloc(num * sizeof(struct gic_irqsrc), M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->gic_dev); for (irq = 0; irq < num; irq++) { irqs[irq].gi_irq = irq; irqs[irq].gi_pol = INTR_POLARITY_CONFORM; irqs[irq].gi_trig = INTR_TRIGGER_CONFORM; isrc = &irqs[irq].gi_isrc; if (irq <= GIC_LAST_SGI) { error = intr_isrc_register(isrc, sc->gic_dev, INTR_ISRCF_IPI, "%s,i%u", name, irq - GIC_FIRST_SGI); } else if (irq <= GIC_LAST_PPI) { error = intr_isrc_register(isrc, sc->gic_dev, INTR_ISRCF_PPI, "%s,p%u", name, irq - GIC_FIRST_PPI); } else { error = intr_isrc_register(isrc, sc->gic_dev, 0, "%s,s%u", name, irq - GIC_FIRST_SPI); } if (error != 0) { /* XXX call intr_isrc_deregister() */ free(irqs, M_DEVBUF); return (error); } } sc->gic_irqs = irqs; sc->nirqs = num; return (0); } static int arm_gic_fill_ranges(phandle_t node, struct arm_gic_softc *sc) { pcell_t host_cells; cell_t *base_ranges; ssize_t nbase_ranges; int i, j, k; host_cells = 1; OF_getencprop(OF_parent(node), "#address-cells", &host_cells, sizeof(host_cells)); sc->addr_cells = 2; OF_getencprop(node, "#address-cells", &sc->addr_cells, sizeof(sc->addr_cells)); sc->size_cells = 2; OF_getencprop(node, "#size-cells", &sc->size_cells, sizeof(sc->size_cells)); nbase_ranges = OF_getproplen(node, "ranges"); if (nbase_ranges < 0) return (-1); sc->nranges = nbase_ranges / sizeof(cell_t) / (sc->addr_cells + host_cells + sc->size_cells); if (sc->nranges == 0) return (0); sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), M_DEVBUF, M_WAITOK); base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); OF_getencprop(node, "ranges", base_ranges, nbase_ranges); for (i = 0, j = 0; i < sc->nranges; i++) { sc->ranges[i].bus = 0; for (k = 0; k < sc->addr_cells; k++) { sc->ranges[i].bus <<= 32; sc->ranges[i].bus |= base_ranges[j++]; } sc->ranges[i].host = 0; for (k = 0; k < host_cells; k++) { sc->ranges[i].host <<= 32; sc->ranges[i].host |= base_ranges[j++]; } sc->ranges[i].size = 0; for (k = 0; k < sc->size_cells; k++) { sc->ranges[i].size <<= 32; sc->ranges[i].size |= base_ranges[j++]; } } free(base_ranges, M_DEVBUF); return (sc->nranges); } static bool arm_gic_add_children(device_t dev) { struct arm_gic_softc *sc; struct arm_gic_devinfo *dinfo; phandle_t child, node; device_t cdev; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); /* If we have no children don't probe for them */ child = OF_child(node); if (child == 0) return (false); if (arm_gic_fill_ranges(node, sc) < 0) { device_printf(dev, "Have a child, but no ranges\n"); return (false); } for (; child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->obdinfo, child) != 0) { free(dinfo, M_DEVBUF); continue; } resource_list_init(&dinfo->rl); ofw_bus_reg_to_rl(dev, child, sc->addr_cells, sc->size_cells, &dinfo->rl); cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->obdinfo.obd_name); resource_list_free(&dinfo->rl); ofw_bus_gen_destroy_devinfo(&dinfo->obdinfo); free(dinfo, M_DEVBUF); continue; } device_set_ivars(cdev, dinfo); } return (true); } static void arm_gic_reserve_msi_range(device_t dev, u_int start, u_int count) { struct arm_gic_softc *sc; int i; sc = device_get_softc(dev); KASSERT((start + count) < sc->nirqs, ("%s: Trying to allocate too many MSI IRQs: %d + %d > %d", __func__, start, count, sc->nirqs)); for (i = 0; i < count; i++) { KASSERT(sc->gic_irqs[start + i].gi_isrc.isrc_handlers == 0, ("%s: MSI interrupt %d already has a handler", __func__, count + i)); KASSERT(sc->gic_irqs[start + i].gi_pol == INTR_POLARITY_CONFORM, ("%s: MSI interrupt %d already has a polarity", __func__, count + i)); KASSERT(sc->gic_irqs[start + i].gi_trig == INTR_TRIGGER_CONFORM, ("%s: MSI interrupt %d already has a trigger", __func__, count + i)); sc->gic_irqs[start + i].gi_pol = INTR_POLARITY_HIGH; sc->gic_irqs[start + i].gi_trig = INTR_TRIGGER_EDGE; sc->gic_irqs[start + i].gi_flags |= GI_FLAG_MSI; } } #endif static int arm_gic_attach(device_t dev) { struct arm_gic_softc *sc; int i; uint32_t icciidr, mask, nirqs; #ifdef INTRNG phandle_t pxref; intptr_t xref = gic_xref(dev); #endif if (gic_sc) return (ENXIO); sc = device_get_softc(dev); if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->gic_dev = dev; gic_sc = sc; /* Initialize mutex */ mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); /* Distributor Interface */ sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]); sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]); /* CPU Interface */ sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); /* Disable interrupt forwarding to the CPU interface */ gic_d_write_4(sc, GICD_CTLR, 0x00); /* Get the number of interrupts */ sc->typer = gic_d_read_4(sc, GICD_TYPER); nirqs = 32 * ((sc->typer & 0x1f) + 1); #ifdef INTRNG if (arm_gic_register_isrcs(sc, nirqs)) { device_printf(dev, "could not register irqs\n"); goto cleanup; } #else sc->nirqs = nirqs; /* Set up function pointers */ arm_post_filter = gic_post_filter; arm_config_irq = gic_config_irq; #endif icciidr = gic_c_read_4(sc, GICC_IIDR); device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n", icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, (icciidr & 0xfff), sc->nirqs); /* Set all global interrupts to be level triggered, active low. */ for (i = 32; i < sc->nirqs; i += 16) { gic_d_write_4(sc, GICD_ICFGR(i >> 4), GIC_DEFAULT_ICFGR_INIT); } /* Disable all interrupts. */ for (i = 32; i < sc->nirqs; i += 32) { gic_d_write_4(sc, GICD_ICENABLER(i >> 5), 0xFFFFFFFF); } /* Find the current cpu mask */ mask = gic_cpu_mask(sc); /* Set the mask so we can find this CPU to send it IPIs */ arm_gic_map[PCPU_GET(cpuid)] = mask; /* Set all four targets to this cpu */ mask |= mask << 8; mask |= mask << 16; for (i = 0; i < sc->nirqs; i += 4) { gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); if (i > 32) { gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), mask); } } /* Set all the interrupts to be in Group 0 (secure) */ for (i = 0; GIC_SUPPORT_SECEXT(sc) && i < sc->nirqs; i += 32) { gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); } /* Enable CPU interface */ gic_c_write_4(sc, GICC_CTLR, 1); /* Set priority mask register. */ gic_c_write_4(sc, GICC_PMR, 0xff); /* Enable interrupt distribution */ gic_d_write_4(sc, GICD_CTLR, 0x01); #ifndef INTRNG return (0); #else /* * Now, when everything is initialized, it's right time to * register interrupt controller to interrupt framefork. */ - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto cleanup; } /* * Controller is root if: * - doesn't have interrupt parent * - his interrupt parent is this controller */ pxref = ofw_bus_find_iparent(ofw_bus_get_node(dev)); if (pxref == 0 || xref == pxref) { if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc, GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { device_printf(dev, "could not set PIC as a root\n"); intr_pic_deregister(dev, xref); goto cleanup; } } else { if (sc->gic_res[2] == NULL) { device_printf(dev, "not root PIC must have defined interrupt\n"); intr_pic_deregister(dev, xref); goto cleanup; } if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_CLK, arm_gic_intr, NULL, sc, &sc->gic_intrhand)) { device_printf(dev, "could not setup irq handler\n"); intr_pic_deregister(dev, xref); goto cleanup; } } OF_device_register_xref(xref, dev); /* If we have children probe and attach them */ if (arm_gic_add_children(dev)) { bus_generic_probe(dev); return (bus_generic_attach(dev)); } return (0); cleanup: /* * XXX - not implemented arm_gic_detach() should be called ! */ if (sc->gic_irqs != NULL) free(sc->gic_irqs, M_DEVBUF); bus_release_resources(dev, arm_gic_spec, sc->gic_res); return(ENXIO); #endif } #ifdef INTRNG static struct resource * arm_gic_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct arm_gic_softc *sc; struct arm_gic_devinfo *di; struct resource_list_entry *rle; int j; KASSERT(type == SYS_RES_MEMORY, ("Invalid resoure type %x", type)); sc = device_get_softc(bus); /* * Request for the default allocation with a given rid: use resource * list stored in the local device info. */ if (RMAN_IS_DEFAULT_RANGE(start, end)) { if ((di = device_get_ivars(child)) == NULL) return (NULL); if (type == SYS_RES_IOPORT) type = SYS_RES_MEMORY; rle = resource_list_find(&di->rl, type, *rid); if (rle == NULL) { if (bootverbose) device_printf(bus, "no default resources for " "rid = %d, type = %d\n", *rid, type); return (NULL); } start = rle->start; end = rle->end; count = rle->count; } /* Remap through ranges property */ for (j = 0; j < sc->nranges; j++) { if (start >= sc->ranges[j].bus && end < sc->ranges[j].bus + sc->ranges[j].size) { start -= sc->ranges[j].bus; start += sc->ranges[j].host; end -= sc->ranges[j].bus; end += sc->ranges[j].host; break; } } if (j == sc->nranges && sc->nranges != 0) { if (bootverbose) device_printf(bus, "Could not map resource " "%#jx-%#jx\n", (uintmax_t)start, (uintmax_t)end); return (NULL); } return (bus_generic_alloc_resource(bus, child, type, rid, start, end, count, flags)); } static const struct ofw_bus_devinfo * arm_gic_ofw_get_devinfo(device_t bus __unused, device_t child) { struct arm_gic_devinfo *di; di = device_get_ivars(child); return (&di->obdinfo); } static int arm_gic_intr(void *arg) { struct arm_gic_softc *sc = arg; struct gic_irqsrc *gi; uint32_t irq_active_reg, irq; struct trapframe *tf; irq_active_reg = gic_c_read_4(sc, GICC_IAR); irq = irq_active_reg & 0x3FF; /* * 1. We do EOI here because recent read value from active interrupt * register must be used for it. Another approach is to save this * value into associated interrupt source. * 2. EOI must be done on same CPU where interrupt has fired. Thus * we must ensure that interrupted thread does not migrate to * another CPU. * 3. EOI cannot be delayed by any preemption which could happen on * critical_exit() used in MI intr code, when interrupt thread is * scheduled. See next point. * 4. IPI_RENDEZVOUS assumes that no preemption is permitted during * an action and any use of critical_exit() could break this * assumption. See comments within smp_rendezvous_action(). * 5. We always return FILTER_HANDLED as this is an interrupt * controller dispatch function. Otherwise, in cascaded interrupt * case, the whole interrupt subtree would be masked. */ if (irq >= sc->nirqs) { #ifdef GIC_DEBUG_SPURIOUS device_printf(sc->gic_dev, "Spurious interrupt detected: last irq: %d on CPU%d\n", sc->last_irq[PCPU_GET(cpuid)], PCPU_GET(cpuid)); #endif return (FILTER_HANDLED); } tf = curthread->td_intr_frame; dispatch_irq: gi = sc->gic_irqs + irq; /* * Note that GIC_FIRST_SGI is zero and is not used in 'if' statement * as compiler complains that comparing u_int >= 0 is always true. */ if (irq <= GIC_LAST_SGI) { #ifdef SMP /* Call EOI for all IPI before dispatch. */ gic_c_write_4(sc, GICC_EOIR, irq_active_reg); intr_ipi_dispatch(sgi_to_ipi[gi->gi_irq], tf); goto next_irq; #else device_printf(sc->gic_dev, "SGI %u on UP system detected\n", irq - GIC_FIRST_SGI); gic_c_write_4(sc, GICC_EOIR, irq_active_reg); goto next_irq; #endif } #ifdef GIC_DEBUG_SPURIOUS sc->last_irq[PCPU_GET(cpuid)] = irq; #endif if ((gi->gi_flags & GI_FLAG_EARLY_EOI) == GI_FLAG_EARLY_EOI) gic_c_write_4(sc, GICC_EOIR, irq_active_reg); if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) { gic_irq_mask(sc, irq); if ((gi->gi_flags & GI_FLAG_EARLY_EOI) != GI_FLAG_EARLY_EOI) gic_c_write_4(sc, GICC_EOIR, irq_active_reg); device_printf(sc->gic_dev, "Stray irq %u disabled\n", irq); } next_irq: arm_irq_memory_barrier(irq); irq_active_reg = gic_c_read_4(sc, GICC_IAR); irq = irq_active_reg & 0x3FF; if (irq < sc->nirqs) goto dispatch_irq; return (FILTER_HANDLED); } static void gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { uint32_t reg; uint32_t mask; if (irq < GIC_FIRST_SPI) return; mtx_lock_spin(&sc->mutex); reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4)); mask = (reg >> 2*(irq % 16)) & 0x3; if (pol == INTR_POLARITY_LOW) { mask &= ~GICD_ICFGR_POL_MASK; mask |= GICD_ICFGR_POL_LOW; } else if (pol == INTR_POLARITY_HIGH) { mask &= ~GICD_ICFGR_POL_MASK; mask |= GICD_ICFGR_POL_HIGH; } if (trig == INTR_TRIGGER_LEVEL) { mask &= ~GICD_ICFGR_TRIG_MASK; mask |= GICD_ICFGR_TRIG_LVL; } else if (trig == INTR_TRIGGER_EDGE) { mask &= ~GICD_ICFGR_TRIG_MASK; mask |= GICD_ICFGR_TRIG_EDGE; } /* Set mask */ reg = reg & ~(0x3 << 2*(irq % 16)); reg = reg | (mask << 2*(irq % 16)); gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg); mtx_unlock_spin(&sc->mutex); } static int gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus) { uint32_t cpu, end, mask; end = min(mp_ncpus, 8); for (cpu = end; cpu < MAXCPU; cpu++) if (CPU_ISSET(cpu, cpus)) return (EINVAL); for (mask = 0, cpu = 0; cpu < end; cpu++) if (CPU_ISSET(cpu, cpus)) mask |= 1 << cpu; gic_d_write_1(sc, GICD_ITARGETSR(0) + irq, mask); return (0); } #ifdef FDT static int gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, enum intr_polarity *polp, enum intr_trigger *trigp) { if (ncells == 1) { *irqp = cells[0]; *polp = INTR_POLARITY_CONFORM; *trigp = INTR_TRIGGER_CONFORM; return (0); } if (ncells == 3) { u_int irq, tripol; /* * The 1st cell is the interrupt type: * 0 = SPI * 1 = PPI * The 2nd cell contains the interrupt number: * [0 - 987] for SPI * [0 - 15] for PPI * The 3rd cell is the flags, encoded as follows: * bits[3:0] trigger type and level flags * 1 = low-to-high edge triggered * 2 = high-to-low edge triggered * 4 = active high level-sensitive * 8 = active low level-sensitive * bits[15:8] PPI interrupt cpu mask * Each bit corresponds to each of the 8 possible cpus * attached to the GIC. A bit set to '1' indicated * the interrupt is wired to that CPU. */ switch (cells[0]) { case 0: irq = GIC_FIRST_SPI + cells[1]; /* SPI irq is checked later. */ break; case 1: irq = GIC_FIRST_PPI + cells[1]; if (irq > GIC_LAST_PPI) { device_printf(dev, "unsupported PPI interrupt " "number %u\n", cells[1]); return (EINVAL); } break; default: device_printf(dev, "unsupported interrupt type " "configuration %u\n", cells[0]); return (EINVAL); } tripol = cells[2] & 0xff; if (tripol & 0xf0 || (tripol & 0x0a && cells[0] == 0)) device_printf(dev, "unsupported trigger/polarity " "configuration 0x%02x\n", tripol); *irqp = irq; *polp = INTR_POLARITY_CONFORM; *trigp = tripol & 0x03 ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL; return (0); } return (EINVAL); } #endif static int gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, enum intr_polarity *polp, enum intr_trigger *trigp) { u_int irq; enum intr_polarity pol; enum intr_trigger trig; struct arm_gic_softc *sc; #ifdef FDT struct intr_map_data_fdt *daf; #endif sc = device_get_softc(dev); switch (data->type) { #ifdef FDT case INTR_MAP_DATA_FDT: daf = (struct intr_map_data_fdt *)data; if (gic_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig) != 0) return (EINVAL); KASSERT(irq >= sc->nirqs || (sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) == 0, ("%s: Attempting to map a MSI interrupt from FDT", __func__)); break; #endif default: return (ENOTSUP); } if (irq >= sc->nirqs) return (EINVAL); if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW && pol != INTR_POLARITY_HIGH) return (EINVAL); if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE && trig != INTR_TRIGGER_LEVEL) return (EINVAL); *irqp = irq; if (polp != NULL) *polp = pol; if (trigp != NULL) *trigp = trig; return (0); } static int arm_gic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { int error; u_int irq; struct arm_gic_softc *sc; error = gic_map_intr(dev, data, &irq, NULL, NULL); if (error == 0) { sc = device_get_softc(dev); *isrcp = GIC_INTR_ISRC(sc, irq); } return (error); } static int arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; u_int irq; enum intr_trigger trig; enum intr_polarity pol; if ((gi->gi_flags & GI_FLAG_MSI) == GI_FLAG_MSI) { irq = gi->gi_irq; pol = gi->gi_pol; trig = gi->gi_trig; KASSERT(pol == INTR_POLARITY_HIGH, ("%s: MSI interrupts must be active-high", __func__)); KASSERT(trig == INTR_TRIGGER_EDGE, ("%s: MSI interrupts must be edge triggered", __func__)); } else if (data != NULL) { /* Get config for resource. */ if (gic_map_intr(dev, data, &irq, &pol, &trig)) return (EINVAL); if (gi->gi_irq != irq) return (EINVAL); } else return (ENOTSUP); /* Compare config if this is not first setup. */ if (isrc->isrc_handlers != 0) { if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) || (trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig)) return (EINVAL); else return (0); } /* For MSI/MSI-X we should have already configured these */ if ((gi->gi_flags & GI_FLAG_MSI) == 0) { if (pol == INTR_POLARITY_CONFORM) pol = INTR_POLARITY_LOW; /* just pick some */ if (trig == INTR_TRIGGER_CONFORM) trig = INTR_TRIGGER_EDGE; /* just pick some */ gi->gi_pol = pol; gi->gi_trig = trig; /* Edge triggered interrupts need an early EOI sent */ if (gi->gi_pol == INTR_TRIGGER_EDGE) gi->gi_flags |= GI_FLAG_EARLY_EOI; } /* * XXX - In case that per CPU interrupt is going to be enabled in time * when SMP is already started, we need some IPI call which * enables it on others CPUs. Further, it's more complicated as * pic_enable_source() and pic_disable_source() should act on * per CPU basis only. Thus, it should be solved here somehow. */ if (isrc->isrc_flags & INTR_ISRCF_PPI) CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); gic_config(sc, gi->gi_irq, gi->gi_trig, gi->gi_pol); arm_gic_bind_intr(dev, isrc); return (0); } static int arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; if (isrc->isrc_handlers == 0 && (gi->gi_flags & GI_FLAG_MSI) == 0) { gi->gi_pol = INTR_POLARITY_CONFORM; gi->gi_trig = INTR_TRIGGER_CONFORM; } return (0); } static void arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; arm_irq_memory_barrier(gi->gi_irq); gic_irq_unmask(sc, gi->gi_irq); } static void arm_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; gic_irq_mask(sc, gi->gi_irq); } static void arm_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; arm_gic_disable_intr(dev, isrc); gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); } static void arm_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { arm_irq_memory_barrier(0); arm_gic_enable_intr(dev, isrc); } static void arm_gic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; /* EOI for edge-triggered done earlier. */ if ((gi->gi_flags & GI_FLAG_EARLY_EOI) == GI_FLAG_EARLY_EOI) return; arm_irq_memory_barrier(0); gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); } static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; if (gi->gi_irq < GIC_FIRST_SPI) return (EINVAL); if (CPU_EMPTY(&isrc->isrc_cpu)) { gic_irq_cpu = intr_irq_next_cpu(gic_irq_cpu, &all_cpus); CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu); } return (gic_bind(sc, gi->gi_irq, &isrc->isrc_cpu)); } #ifdef SMP static void arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, u_int ipi) { struct arm_gic_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; uint32_t val = 0, i; for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; gic_d_write_4(sc, GICD_SGIR(0), val | gi->gi_irq); } static int arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) { struct intr_irqsrc *isrc; struct arm_gic_softc *sc = device_get_softc(dev); if (sgi_first_unused > GIC_LAST_SGI) return (ENOSPC); isrc = GIC_INTR_ISRC(sc, sgi_first_unused); sgi_to_ipi[sgi_first_unused++] = ipi; CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); *isrcp = isrc; return (0); } #endif #else static int arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq) { uint32_t active_irq; active_irq = gic_c_read_4(sc, GICC_IAR); /* * Immediately EOIR the SGIs, because doing so requires the other * bits (ie CPU number), not just the IRQ number, and we do not * have this information later. */ if ((active_irq & 0x3ff) <= GIC_LAST_SGI) gic_c_write_4(sc, GICC_EOIR, active_irq); active_irq &= 0x3FF; if (active_irq == 0x3FF) { if (last_irq == -1) device_printf(sc->gic_dev, "Spurious interrupt detected\n"); return -1; } return active_irq; } static int arm_gic_config(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { struct arm_gic_softc *sc = device_get_softc(dev); uint32_t reg; uint32_t mask; /* Function is public-accessible, so validate input arguments */ if ((irq < 0) || (irq >= sc->nirqs)) goto invalid_args; if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) && (trig != INTR_TRIGGER_CONFORM)) goto invalid_args; if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) && (pol != INTR_POLARITY_CONFORM)) goto invalid_args; mtx_lock_spin(&sc->mutex); reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4)); mask = (reg >> 2*(irq % 16)) & 0x3; if (pol == INTR_POLARITY_LOW) { mask &= ~GICD_ICFGR_POL_MASK; mask |= GICD_ICFGR_POL_LOW; } else if (pol == INTR_POLARITY_HIGH) { mask &= ~GICD_ICFGR_POL_MASK; mask |= GICD_ICFGR_POL_HIGH; } if (trig == INTR_TRIGGER_LEVEL) { mask &= ~GICD_ICFGR_TRIG_MASK; mask |= GICD_ICFGR_TRIG_LVL; } else if (trig == INTR_TRIGGER_EDGE) { mask &= ~GICD_ICFGR_TRIG_MASK; mask |= GICD_ICFGR_TRIG_EDGE; } /* Set mask */ reg = reg & ~(0x3 << 2*(irq % 16)); reg = reg | (mask << 2*(irq % 16)); gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg); mtx_unlock_spin(&sc->mutex); return (0); invalid_args: device_printf(dev, "gic_config_irg, invalid parameters\n"); return (EINVAL); } static void arm_gic_mask(device_t dev, int irq) { struct arm_gic_softc *sc = device_get_softc(dev); gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F))); gic_c_write_4(sc, GICC_EOIR, irq); /* XXX - not allowed */ } static void arm_gic_unmask(device_t dev, int irq) { struct arm_gic_softc *sc = device_get_softc(dev); if (irq > GIC_LAST_SGI) arm_irq_memory_barrier(irq); gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F))); } #ifdef SMP static void arm_gic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) { struct arm_gic_softc *sc = device_get_softc(dev); uint32_t val = 0, i; for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; gic_d_write_4(sc, GICD_SGIR(0), val | ipi); } static int arm_gic_ipi_read(device_t dev, int i) { if (i != -1) { /* * The intr code will automagically give the frame pointer * if the interrupt argument is 0. */ if ((unsigned int)i > 16) return (0); return (i); } return (0x3ff); } static void arm_gic_ipi_clear(device_t dev, int ipi) { /* no-op */ } #endif static void gic_post_filter(void *arg) { struct arm_gic_softc *sc = gic_sc; uintptr_t irq = (uintptr_t) arg; if (irq > GIC_LAST_SGI) arm_irq_memory_barrier(irq); gic_c_write_4(sc, GICC_EOIR, irq); } static int gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol) { return (arm_gic_config(gic_sc->gic_dev, irq, trig, pol)); } void arm_mask_irq(uintptr_t nb) { arm_gic_mask(gic_sc->gic_dev, nb); } void arm_unmask_irq(uintptr_t nb) { arm_gic_unmask(gic_sc->gic_dev, nb); } int arm_get_next_irq(int last_irq) { return (arm_gic_next_irq(gic_sc, last_irq)); } #ifdef SMP void intr_pic_init_secondary(void) { arm_gic_init_secondary(gic_sc->gic_dev); } void pic_ipi_send(cpuset_t cpus, u_int ipi) { arm_gic_ipi_send(gic_sc->gic_dev, cpus, ipi); } int pic_ipi_read(int i) { return (arm_gic_ipi_read(gic_sc->gic_dev, i)); } void pic_ipi_clear(int ipi) { arm_gic_ipi_clear(gic_sc->gic_dev, ipi); } #endif #endif /* INTRNG */ static device_method_t arm_gic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arm_gic_probe), DEVMETHOD(device_attach, arm_gic_attach), #ifdef INTRNG /* Bus interface */ 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), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, arm_gic_ofw_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), /* 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 #endif { 0, 0 } }; static driver_t arm_gic_driver = { "gic", arm_gic_methods, sizeof(struct arm_gic_softc), }; static devclass_t arm_gic_devclass; EARLY_DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); #ifdef INTRNG /* * 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 struct arm_gicv2m_softc { struct resource *sc_mem; struct mtx sc_mutex; u_int sc_spi_start; u_int sc_spi_end; u_int sc_spi_count; }; static struct ofw_compat_data gicv2m_compat_data[] = { {"arm,gic-v2m-frame", true}, {NULL, false} }; static int arm_gicv2m_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, gicv2m_compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX"); return (BUS_PROBE_DEFAULT); } static int arm_gicv2m_attach(device_t dev) { struct arm_gicv2m_softc *sc; struct arm_gic_softc *psc; uint32_t typer; int rid; psc = device_get_softc(device_get_parent(dev)); 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", "", MTX_DEF); intr_msi_register(dev, gic_xref(dev)); 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, device_t *pic, struct intr_irqsrc **srcs) { struct arm_gic_softc *psc; struct arm_gicv2m_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); found = false; for (irq = sc->sc_spi_start; irq < sc->sc_spi_end && !found; irq++) { /* Start on an aligned interrupt */ if ((irq & (maxcount - 1)) != 0) continue; /* Assume we found a valid range until shown otherwise */ found = true; /* Check this range is valid */ for (end_irq = irq; end_irq != irq + count - 1; end_irq++) { /* No free interrupts */ if (end_irq == sc->sc_spi_end) { found = false; break; } KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI)!= 0, ("%s: Non-MSI interrupt found", __func__)); /* This is already used */ if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED) { found = false; break; } } } /* Not enough interrupts were found */ if (!found || irq == sc->sc_spi_end) { mtx_unlock(&sc->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; } mtx_unlock(&sc->sc_mutex); for (i = 0; i < count; i++) srcs[i] = (struct intr_irqsrc *)&psc->gic_irqs[irq + i]; *pic = device_get_parent(dev); return (0); } static int arm_gicv2m_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **isrc) { struct arm_gicv2m_softc *sc; struct gic_irqsrc *gi; int i; sc = device_get_softc(dev); mtx_lock(&sc->sc_mutex); for (i = 0; i < count; i++) { gi = (struct gic_irqsrc *)isrc; KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED, ("%s: Trying to release an unused MSI-X interrupt", __func__)); gi->gi_flags &= ~GI_FLAG_MSI_USED; mtx_unlock(&sc->sc_mutex); } return (0); } static int arm_gicv2m_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; int irq; psc = device_get_softc(device_get_parent(dev)); sc = device_get_softc(dev); mtx_lock(&sc->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, ("%s: Non-MSI interrupt found", __func__)); if ((psc->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); return (ENXIO); } /* Mark the interrupt as used */ psc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED; mtx_unlock(&sc->sc_mutex); *isrcp = (struct intr_irqsrc *)&psc->gic_irqs[irq]; *pic = device_get_parent(dev); return (0); } static int arm_gicv2m_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) { struct arm_gicv2m_softc *sc; struct gic_irqsrc *gi; sc = device_get_softc(dev); gi = (struct gic_irqsrc *)isrc; 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); gi->gi_flags &= ~GI_FLAG_MSI_USED; mtx_unlock(&sc->sc_mutex); return (0); } static int arm_gicv2m_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, uint64_t *addr, uint32_t *data) { struct arm_gicv2m_softc *sc = device_get_softc(dev); struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; *addr = vtophys(rman_get_virtual(sc->sc_mem)) + GICv2M_MSI_SETSPI_NS; *data = gi->gi_irq; return (0); } static device_method_t arm_gicv2m_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arm_gicv2m_probe), DEVMETHOD(device_attach, arm_gicv2m_attach), /* MSI/MSI-X */ DEVMETHOD(msi_alloc_msi, arm_gicv2m_alloc_msi), DEVMETHOD(msi_release_msi, arm_gicv2m_release_msi), DEVMETHOD(msi_alloc_msix, arm_gicv2m_alloc_msix), DEVMETHOD(msi_release_msix, arm_gicv2m_release_msix), DEVMETHOD(msi_map_msi, arm_gicv2m_map_msi), /* End */ DEVMETHOD_END }; DEFINE_CLASS_0(gicv2m, arm_gicv2m_driver, arm_gicv2m_methods, sizeof(struct arm_gicv2m_softc)); static devclass_t arm_gicv2m_devclass; EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_driver, arm_gicv2m_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); #endif Index: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 300148) +++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 300149) @@ -1,1501 +1,1504 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012-2015 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #ifdef INTRNG #include "pic_if.h" #endif #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define dprintf(fmt, args...) #endif #define BCM_GPIO_IRQS 4 #define BCM_GPIO_PINS 54 #define BCM_GPIO_PINS_PER_BANK 32 #ifdef INTRNG #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) #else #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) #endif static struct resource_spec bcm_gpio_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ { SYS_RES_IRQ, 2, RF_ACTIVE }, /* bank 1 interrupt (mirrored) */ { SYS_RES_IRQ, 3, RF_ACTIVE }, /* bank 0-1 interrupt (united) */ { -1, 0, 0 } }; struct bcm_gpio_sysctl { struct bcm_gpio_softc *sc; uint32_t pin; }; #ifdef INTRNG struct bcm_gpio_irqsrc { struct intr_irqsrc bgi_isrc; uint32_t bgi_irq; uint32_t bgi_mode; uint32_t bgi_mask; }; #endif struct bcm_gpio_softc { device_t sc_dev; device_t sc_busdev; struct mtx sc_mtx; struct resource * sc_res[BCM_GPIO_IRQS + 1]; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand[BCM_GPIO_IRQS]; int sc_gpio_npins; int sc_ro_npins; int sc_ro_pins[BCM_GPIO_PINS]; struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; #ifndef INTRNG struct intr_event * sc_events[BCM_GPIO_PINS]; #endif struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; #ifdef INTRNG struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; #else enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS]; enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS]; #endif }; enum bcm_gpio_pud { BCM_GPIO_NONE, BCM_GPIO_PULLDOWN, BCM_GPIO_PULLUP, }; #define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define BCM_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) #define BCM_GPIO_READ(_sc, _off) \ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) #define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits)) #define BCM_GPIO_SET_BITS(_sc, _off, _bits) \ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits) #define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK) #define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK)) #define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */ #define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */ #define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */ #define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */ #define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */ #define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */ #define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */ #define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */ #define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */ #define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */ #define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */ #define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */ #define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */ static struct bcm_gpio_softc *bcm_gpio_sc = NULL; #ifdef INTRNG static int bcm_gpio_intr_bank0(void *arg); static int bcm_gpio_intr_bank1(void *arg); static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); #endif static int bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) { int i; for (i = 0; i < sc->sc_ro_npins; i++) if (pin == sc->sc_ro_pins[i]) return (1); return (0); } static uint32_t bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) { uint32_t bank, func, offset; /* Five banks, 10 pins per bank, 3 bits per pin. */ bank = pin / 10; offset = (pin - bank * 10) * 3; BCM_GPIO_LOCK(sc); func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; BCM_GPIO_UNLOCK(sc); return (func); } static void bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) { switch (nfunc) { case BCM_GPIO_INPUT: strncpy(buf, "input", bufsize); break; case BCM_GPIO_OUTPUT: strncpy(buf, "output", bufsize); break; case BCM_GPIO_ALT0: strncpy(buf, "alt0", bufsize); break; case BCM_GPIO_ALT1: strncpy(buf, "alt1", bufsize); break; case BCM_GPIO_ALT2: strncpy(buf, "alt2", bufsize); break; case BCM_GPIO_ALT3: strncpy(buf, "alt3", bufsize); break; case BCM_GPIO_ALT4: strncpy(buf, "alt4", bufsize); break; case BCM_GPIO_ALT5: strncpy(buf, "alt5", bufsize); break; default: strncpy(buf, "invalid", bufsize); } } static int bcm_gpio_str_func(char *func, uint32_t *nfunc) { if (strcasecmp(func, "input") == 0) *nfunc = BCM_GPIO_INPUT; else if (strcasecmp(func, "output") == 0) *nfunc = BCM_GPIO_OUTPUT; else if (strcasecmp(func, "alt0") == 0) *nfunc = BCM_GPIO_ALT0; else if (strcasecmp(func, "alt1") == 0) *nfunc = BCM_GPIO_ALT1; else if (strcasecmp(func, "alt2") == 0) *nfunc = BCM_GPIO_ALT2; else if (strcasecmp(func, "alt3") == 0) *nfunc = BCM_GPIO_ALT3; else if (strcasecmp(func, "alt4") == 0) *nfunc = BCM_GPIO_ALT4; else if (strcasecmp(func, "alt5") == 0) *nfunc = BCM_GPIO_ALT5; else return (-1); return (0); } static uint32_t bcm_gpio_func_flag(uint32_t nfunc) { switch (nfunc) { case BCM_GPIO_INPUT: return (GPIO_PIN_INPUT); case BCM_GPIO_OUTPUT: return (GPIO_PIN_OUTPUT); } return (0); } static void bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Must be called with lock held. */ BCM_GPIO_LOCK_ASSERT(sc); /* Five banks, 10 pins per bank, 3 bits per pin. */ bank = pin / 10; offset = (pin - bank * 10) * 3; data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); data &= ~(7 << offset); data |= (f << offset); BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); } static void bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank; /* Must be called with lock held. */ BCM_GPIO_LOCK_ASSERT(sc); bank = BCM_GPIO_BANK(pin); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); } void bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) { struct bcm_gpio_softc *sc; int i; sc = device_get_softc(dev); BCM_GPIO_LOCK(sc); /* Disable pull-up or pull-down on pin. */ bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); /* And now set the pin function. */ bcm_gpio_set_function(sc, pin, nfunc); /* Update the pin flags. */ for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i < sc->sc_gpio_npins) sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); BCM_GPIO_UNLOCK(sc); } static void bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { BCM_GPIO_LOCK(sc); /* * Manage input/output. */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; bcm_gpio_set_function(sc, pin->gp_pin, BCM_GPIO_OUTPUT); } else { pin->gp_flags |= GPIO_PIN_INPUT; bcm_gpio_set_function(sc, pin->gp_pin, BCM_GPIO_INPUT); } } /* Manage Pull-up/pull-down. */ pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) { pin->gp_flags |= GPIO_PIN_PULLUP; bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); } else { pin->gp_flags |= GPIO_PIN_PULLDOWN; bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); } } else bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); BCM_GPIO_UNLOCK(sc); } static device_t bcm_gpio_get_bus(device_t dev) { struct bcm_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int bcm_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = BCM_GPIO_PINS - 1; return (0); } static int bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never touch on read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, reg; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never write to read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); BCM_GPIO_LOCK(sc); bank = BCM_GPIO_BANK(pin); if (value) reg = BCM_GPIO_GPSET(bank); else reg = BCM_GPIO_GPCLR(bank); BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, reg_data; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); bank = BCM_GPIO_BANK(pin); BCM_GPIO_LOCK(sc); reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); BCM_GPIO_UNLOCK(sc); *val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0; return (0); } static int bcm_gpio_pin_toggle(device_t dev, uint32_t pin) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, data, reg; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never write to read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); BCM_GPIO_LOCK(sc); bank = BCM_GPIO_BANK(pin); data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); if (data & BCM_GPIO_MASK(pin)) reg = BCM_GPIO_GPCLR(bank); else reg = BCM_GPIO_GPSET(bank); BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) { char buf[16]; struct bcm_gpio_softc *sc; struct bcm_gpio_sysctl *sc_sysctl; uint32_t nfunc; int error; sc_sysctl = arg1; sc = sc_sysctl->sc; /* Get the current pin function. */ nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); bcm_gpio_func_str(nfunc, buf, sizeof(buf)); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error != 0 || req->newptr == NULL) return (error); /* Ignore changes on read-only pins. */ if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) return (0); /* Parse the user supplied string and check for a valid pin function. */ if (bcm_gpio_str_func(buf, &nfunc) != 0) return (EINVAL); /* Update the pin alternate function. */ bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); return (0); } static void bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) { char pinbuf[3]; struct bcm_gpio_sysctl *sc_sysctl; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node, *pin_node, *pinN_node; struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; int i; /* * Add per-pin sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", CTLFLAG_RD, NULL, "GPIO Pins"); pin_tree = SYSCTL_CHILDREN(pin_node); for (i = 0; i < sc->sc_gpio_npins; i++) { snprintf(pinbuf, sizeof(pinbuf), "%d", i); pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, CTLFLAG_RD, NULL, "GPIO Pin"); pinN_tree = SYSCTL_CHILDREN(pinN_node); sc->sc_sysctl[i].sc = sc; sc_sysctl = &sc->sc_sysctl[i]; sc_sysctl->sc = sc; sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, "A", "Pin Function"); } } static int bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, const char *propname, const char *label) { int i, need_comma, npins, range_start, range_stop; pcell_t *pins; /* Get the property data. */ npins = OF_getencprop_alloc(node, propname, sizeof(*pins), (void **)&pins); if (npins < 0) return (-1); if (npins == 0) { OF_prop_free(pins); return (0); } for (i = 0; i < npins; i++) sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; sc->sc_ro_npins += npins; need_comma = 0; device_printf(sc->sc_dev, "%s pins: ", label); range_start = range_stop = pins[0]; for (i = 1; i < npins; i++) { if (pins[i] != range_stop + 1) { if (need_comma) printf(","); if (range_start != range_stop) printf("%d-%d", range_start, range_stop); else printf("%d", range_start); range_start = range_stop = pins[i]; need_comma = 1; } else range_stop++; } if (need_comma) printf(","); if (range_start != range_stop) printf("%d-%d.\n", range_start, range_stop); else printf("%d.\n", range_start); OF_prop_free(pins); return (0); } static int bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) { char *name; phandle_t gpio, node, reserved; ssize_t len; /* Get read-only pins. */ gpio = ofw_bus_get_node(sc->sc_dev); if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", "read-only") != 0) return (-1); /* Traverse the GPIO subnodes to find the reserved pins node. */ reserved = 0; node = OF_child(gpio); while ((node != 0) && (reserved == 0)) { len = OF_getprop_alloc(node, "name", 1, (void **)&name); if (len == -1) return (-1); if (strcmp(name, "reserved") == 0) reserved = node; OF_prop_free(name); node = OF_peer(node); } if (reserved == 0) return (-1); /* Get the reserved pins. */ if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", "reserved") != 0) return (-1); return (0); } #ifndef INTRNG static int bcm_gpio_intr(void *arg) { int bank_last, irq; struct bcm_gpio_softc *sc; struct intr_event *event; uint32_t bank, mask, reg; sc = (struct bcm_gpio_softc *)arg; reg = 0; bank_last = -1; for (irq = 0; irq < BCM_GPIO_PINS; irq++) { bank = BCM_GPIO_BANK(irq); mask = BCM_GPIO_MASK(irq); if (bank != bank_last) { reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); bank_last = bank; } if (reg & mask) { event = sc->sc_events[irq]; if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers)) intr_event_handle(event, NULL); else { device_printf(sc->sc_dev, "Stray IRQ %d\n", irq); } /* Clear the Status bit by writing '1' to it. */ BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask); } } return (FILTER_HANDLED); } #endif static int bcm_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio")) return (ENXIO); device_set_desc(dev, "BCM2708/2835 GPIO controller"); return (BUS_PROBE_DEFAULT); } #ifdef INTRNG static int bcm_gpio_intr_attach(device_t dev) { struct bcm_gpio_softc *sc; /* * Only first two interrupt lines are used. Third line is * mirrored second line and forth line is common for all banks. */ sc = device_get_softc(dev); if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) return (-1); if (bcm_gpio_pic_attach(sc) != 0) { device_printf(dev, "unable to attach PIC\n"); return (-1); } if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) return (-1); if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) return (-1); return (0); } static void bcm_gpio_intr_detach(device_t dev) { struct bcm_gpio_softc *sc; sc = device_get_softc(dev); if (sc->sc_intrhand[0] != NULL) bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); if (sc->sc_intrhand[1] != NULL) bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); bcm_gpio_pic_detach(sc); } #else static int bcm_gpio_intr_attach(device_t dev) { struct bcm_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < BCM_GPIO_IRQS; i++) { if (bus_setup_intr(dev, sc->sc_res[i + 1], INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr, NULL, sc, &sc->sc_intrhand[i]) != 0) { return (-1); } } return (0); } static void bcm_gpio_intr_detach(device_t dev) { struct bcm_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < BCM_GPIO_IRQS; i++) { if (sc->sc_intrhand[i]) { bus_teardown_intr(dev, sc->sc_res[i + 1], sc->sc_intrhand[i]); } } } #endif static int bcm_gpio_attach(device_t dev) { int i, j; phandle_t gpio; struct bcm_gpio_softc *sc; uint32_t func; if (bcm_gpio_sc != NULL) return (ENXIO); bcm_gpio_sc = sc = device_get_softc(dev); sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN); if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) { device_printf(dev, "cannot allocate resources\n"); goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_res[0]); sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); /* Setup the GPIO interrupt handler. */ if (bcm_gpio_intr_attach(dev)) { device_printf(dev, "unable to setup the gpio irq handler\n"); goto fail; } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; /* * Find the read-only pins. These are pins we never touch or bad * things could happen. */ if (bcm_gpio_get_reserved_pins(sc) == -1) goto fail; /* Initialize the software controlled pins. */ for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", j); func = bcm_gpio_get_function(sc, j); sc->sc_gpio_pins[i].gp_pin = j; sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); #ifndef INTRNG /* The default is active-low interrupts. */ sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; #endif i++; } sc->sc_gpio_npins = i; bcm_gpio_sysctl_init(sc); sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; return (0); fail: bcm_gpio_intr_detach(dev); bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int bcm_gpio_detach(device_t dev) { return (EBUSY); } #ifdef INTRNG static inline void bcm_gpio_modify(struct bcm_gpio_softc *sc, uint32_t reg, uint32_t mask, bool set_bits) { if (set_bits) BCM_GPIO_SET_BITS(sc, reg, mask); else BCM_GPIO_CLEAR_BITS(sc, reg, mask); } static inline void bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) { uint32_t bank; /* Write 1 to clear. */ bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); } static inline bool bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) { return (bgi->bgi_mode == GPIO_INTR_LEVEL_LOW || bgi->bgi_mode == GPIO_INTR_LEVEL_HIGH); } static inline void bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) { uint32_t bank; bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_LOCK(sc); switch (bgi->bgi_mode) { case GPIO_INTR_LEVEL_LOW: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); break; case GPIO_INTR_LEVEL_HIGH: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_RISING: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_FALLING: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_BOTH: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; } BCM_GPIO_UNLOCK(sc); } static inline void bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) { uint32_t bank; bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_LOCK(sc); switch (bgi->bgi_mode) { case GPIO_INTR_LEVEL_LOW: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); break; case GPIO_INTR_LEVEL_HIGH: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_RISING: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_FALLING: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_BOTH: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; } BCM_GPIO_UNLOCK(sc); } static int bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) { u_int irq; struct bcm_gpio_irqsrc *bgi; uint32_t reg; /* Do not care of spurious interrupt on GPIO. */ reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); while (reg != 0) { irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; bgi = sc->sc_isrcs + irq; if (!bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); if (intr_isrc_dispatch(&bgi->bgi_isrc, curthread->td_intr_frame) != 0) { bcm_gpio_isrc_mask(sc, bgi); if (bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } reg &= ~bgi->bgi_mask; } return (FILTER_HANDLED); } static int bcm_gpio_intr_bank0(void *arg) { return (bcm_gpio_intr_internal(arg, 0)); } static int bcm_gpio_intr_bank1(void *arg) { return (bcm_gpio_intr_internal(arg, 1)); } static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) { int error; uint32_t irq; const char *name; name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < BCM_GPIO_PINS; irq++) { sc->sc_isrcs[irq].bgi_irq = irq; sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); sc->sc_isrcs[irq].bgi_mode = GPIO_INTR_CONFORM; error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); /* XXX deregister ISRCs */ } - return (intr_pic_register(sc->sc_dev, - OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)))); + if (intr_pic_register(sc->sc_dev, + OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) + return (ENXIO); + + return (0); } static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) { /* * There has not been established any procedure yet * how to detach PIC from living system correctly. */ device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); return (EBUSY); } static void bcm_gpio_pic_config_intr(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi, uint32_t mode) { uint32_t bank; bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_LOCK(sc); bcm_gpio_modify(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask, mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); bcm_gpio_modify(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask, mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); bcm_gpio_modify(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask, mode == GPIO_INTR_LEVEL_HIGH); bcm_gpio_modify(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask, mode == GPIO_INTR_LEVEL_LOW); bgi->bgi_mode = mode; BCM_GPIO_UNLOCK(sc); } static void bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; bcm_gpio_isrc_mask(sc, bgi); } static void bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; arm_irq_memory_barrier(bgi->bgi_irq); bcm_gpio_isrc_unmask(sc, bgi); } static int bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, struct intr_map_data_fdt *daf, u_int *irqp, uint32_t *modep) { u_int irq; uint32_t mode, bank; /* * The first cell is the interrupt number. * The second cell is used to specify flags: * bits[3:0] trigger type and level flags: * 1 = low-to-high edge triggered. * 2 = high-to-low edge triggered. * 4 = active high level-sensitive. * 8 = active low level-sensitive. */ if (daf->ncells != 2) return (EINVAL); irq = daf->cells[0]; if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) return (EINVAL); /* Only reasonable modes are supported. */ bank = BCM_GPIO_BANK(irq); if (daf->cells[1] == 1) mode = GPIO_INTR_EDGE_RISING; else if (daf->cells[1] == 2) mode = GPIO_INTR_EDGE_FALLING; else if (daf->cells[1] == 3) mode = GPIO_INTR_EDGE_BOTH; else if (daf->cells[1] == 4) mode = GPIO_INTR_LEVEL_HIGH; else if (daf->cells[1] == 8) mode = GPIO_INTR_LEVEL_LOW; else return (EINVAL); *irqp = irq; if (modep != NULL) *modep = mode; return (0); } static int bcm_gpio_pic_map_gpio(struct bcm_gpio_softc *sc, struct intr_map_data_gpio *dag, u_int *irqp, uint32_t *modep) { u_int irq; uint32_t mode; irq = dag->gpio_pin_num; if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) return (EINVAL); mode = dag->gpio_intr_mode; if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && mode != GPIO_INTR_EDGE_BOTH) return (EINVAL); *irqp = irq; if (modep != NULL) *modep = mode; return (0); } static int bcm_gpio_pic_map(struct bcm_gpio_softc *sc, struct intr_map_data *data, u_int *irqp, uint32_t *modep) { switch (data->type) { case INTR_MAP_DATA_FDT: return (bcm_gpio_pic_map_fdt(sc, (struct intr_map_data_fdt *)data, irqp, modep)); case INTR_MAP_DATA_GPIO: return (bcm_gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data, irqp, modep)); default: return (ENOTSUP); } } static int bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { int error; u_int irq; struct bcm_gpio_softc *sc = device_get_softc(dev); error = bcm_gpio_pic_map(sc, data, &irq, NULL); if (error == 0) *isrcp = &sc->sc_isrcs[irq].bgi_isrc; return (error); } static void bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; if (bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); } static void bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { bcm_gpio_pic_enable_intr(dev, isrc); } static void bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; bcm_gpio_isrc_mask(sc, bgi); if (bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); } static int bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { u_int irq; uint32_t mode; struct bcm_gpio_softc *sc; struct bcm_gpio_irqsrc *bgi; if (data == NULL) return (ENOTSUP); sc = device_get_softc(dev); bgi = (struct bcm_gpio_irqsrc *)isrc; /* Get and check config for an interrupt. */ if (bcm_gpio_pic_map(sc, data, &irq, &mode) != 0 || bgi->bgi_irq != irq) return (EINVAL); /* * If this is a setup for another handler, * only check that its configuration match. */ if (isrc->isrc_handlers != 0) return (bgi->bgi_mode == mode ? 0 : EINVAL); bcm_gpio_pic_config_intr(sc, bgi, mode); return (0); } static int bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; if (isrc->isrc_handlers == 0) bcm_gpio_pic_config_intr(sc, bgi, GPIO_INTR_CONFORM); return (0); } #else static uint32_t bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank) { if (irq > BCM_GPIO_PINS) return (0); if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) { if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) return (BCM_GPIO_GPLEN(bank)); else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) return (BCM_GPIO_GPHEN(bank)); } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) { if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) return (BCM_GPIO_GPFEN(bank)); else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) return (BCM_GPIO_GPREN(bank)); } return (0); } static void bcm_gpio_mask_irq(void *source) { uint32_t bank, mask, reg; unsigned int irq; irq = (unsigned int)source; if (irq > BCM_GPIO_PINS) return; if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq)) return; bank = BCM_GPIO_BANK(irq); mask = BCM_GPIO_MASK(irq); BCM_GPIO_LOCK(bcm_gpio_sc); reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank); if (reg != 0) BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask); BCM_GPIO_UNLOCK(bcm_gpio_sc); } static void bcm_gpio_unmask_irq(void *source) { uint32_t bank, mask, reg; unsigned int irq; irq = (unsigned int)source; if (irq > BCM_GPIO_PINS) return; if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq)) return; bank = BCM_GPIO_BANK(irq); mask = BCM_GPIO_MASK(irq); BCM_GPIO_LOCK(bcm_gpio_sc); reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank); if (reg != 0) BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask); BCM_GPIO_UNLOCK(bcm_gpio_sc); } static int bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { int pin; if (type != SYS_RES_IRQ) return (ENXIO); /* Unmask the interrupt. */ pin = rman_get_start(res); bcm_gpio_unmask_irq((void *)pin); return (0); } static int bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { int pin; if (type != SYS_RES_IRQ) return (ENXIO); /* Mask the interrupt. */ pin = rman_get_start(res); bcm_gpio_mask_irq((void *)pin); return (0); } static int bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { int bank; struct bcm_gpio_softc *sc; uint32_t mask, oldreg, reg; if (irq > BCM_GPIO_PINS) return (EINVAL); /* There is no standard trigger or polarity. */ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM) return (EINVAL); sc = device_get_softc(dev); if (bcm_gpio_pin_is_ro(sc, irq)) return (EINVAL); bank = BCM_GPIO_BANK(irq); mask = BCM_GPIO_MASK(irq); BCM_GPIO_LOCK(sc); oldreg = bcm_gpio_intr_reg(sc, irq, bank); sc->sc_irq_trigger[irq] = trig; sc->sc_irq_polarity[irq] = pol; reg = bcm_gpio_intr_reg(sc, irq, bank); if (reg != 0) BCM_GPIO_SET_BITS(sc, reg, mask); if (reg != oldreg && oldreg != 0) BCM_GPIO_CLEAR_BITS(sc, oldreg, mask); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct bcm_gpio_softc *sc; struct intr_event *event; int pin, error; sc = device_get_softc(bus); pin = rman_get_start(ires); if (pin > BCM_GPIO_PINS) panic("%s: bad pin %d", __func__, pin); event = sc->sc_events[pin]; if (event == NULL) { error = intr_event_create(&event, (void *)pin, 0, pin, bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL, "gpio%d pin%d:", device_get_unit(bus), pin); if (error != 0) return (error); sc->sc_events[pin] = event; } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); return (0); } static int bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct bcm_gpio_softc *sc; int pin, err; sc = device_get_softc(dev); pin = rman_get_start(ires); if (pin > BCM_GPIO_PINS) panic("%s: bad pin %d", __func__, pin); if (sc->sc_events[pin] == NULL) panic("Trying to teardown unoccupied IRQ"); err = intr_event_remove_handler(cookie); if (!err) sc->sc_events[pin] = NULL; return (err); } #endif static phandle_t bcm_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_gpio_probe), DEVMETHOD(device_attach, bcm_gpio_attach), DEVMETHOD(device_detach, bcm_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), #ifdef INTRNG /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), #else /* Bus interface */ DEVMETHOD(bus_activate_resource, bcm_gpio_activate_resource), DEVMETHOD(bus_deactivate_resource, bcm_gpio_deactivate_resource), DEVMETHOD(bus_config_intr, bcm_gpio_config_intr), DEVMETHOD(bus_setup_intr, bcm_gpio_setup_intr), DEVMETHOD(bus_teardown_intr, bcm_gpio_teardown_intr), #endif /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), DEVMETHOD_END }; static devclass_t bcm_gpio_devclass; static driver_t bcm_gpio_driver = { "gpio", bcm_gpio_methods, sizeof(struct bcm_gpio_softc), }; DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); Index: head/sys/arm/broadcom/bcm2835/bcm2835_intr.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_intr.c (revision 300148) +++ head/sys/arm/broadcom/bcm2835/bcm2835_intr.c (revision 300149) @@ -1,540 +1,543 @@ /*- * Copyright (c) 2012 Damjan Marion * All rights reserved. * * Based on OMAP3 INTC code by Ben Gray * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SOC_BCM2836 #include #endif #ifdef INTRNG #include "pic_if.h" #endif #define INTC_PENDING_BASIC 0x00 #define INTC_PENDING_BANK1 0x04 #define INTC_PENDING_BANK2 0x08 #define INTC_FIQ_CONTROL 0x0C #define INTC_ENABLE_BANK1 0x10 #define INTC_ENABLE_BANK2 0x14 #define INTC_ENABLE_BASIC 0x18 #define INTC_DISABLE_BANK1 0x1C #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 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 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) #ifdef DEBUG #define dprintf(fmt, args...) printf(fmt, ##args) #else #define dprintf(fmt, args...) #endif #ifdef 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 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; #define intc_read_4(_sc, reg) \ bus_space_read_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg)) #define intc_write_4(_sc, reg, val) \ bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val)) #ifdef 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 intr_map_data_fdt *daf; struct bcm_intc_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); daf = (struct intr_map_data_fdt *)data; if (daf->ncells == 1) irq = daf->cells[0]; else if (daf->ncells == 2) irq = daf->cells[0] * 32 + daf->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)); + if (intr_pic_register(sc->sc_dev, xref) == NULL) + return (ENXIO); + + return (0); } #endif static int bcm_intc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-armctrl-ic")) return (ENXIO); device_set_desc(dev, "BCM2835 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_intc_attach(device_t dev) { struct bcm_intc_softc *sc = device_get_softc(dev); int rid = 0; #ifdef INTRNG intptr_t xref; #endif sc->sc_dev = dev; if (bcm_intc_sc) return (ENXIO); sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->intc_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } #ifdef 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); bcm_intc_sc = sc; return (0); } static device_method_t bcm_intc_methods[] = { DEVMETHOD(device_probe, bcm_intc_probe), DEVMETHOD(device_attach, bcm_intc_attach), #ifdef 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 } }; static driver_t bcm_intc_driver = { "intc", bcm_intc_methods, sizeof(struct bcm_intc_softc), }; static devclass_t bcm_intc_devclass; DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, 0, 0); #ifndef INTRNG int arm_get_next_irq(int last_irq) { struct bcm_intc_softc *sc = bcm_intc_sc; uint32_t pending; int32_t irq = last_irq + 1; #ifdef SOC_BCM2836 int ret; #endif /* Sanity check */ if (irq < 0) irq = 0; #ifdef SOC_BCM2836 if ((ret = bcm2836_get_next_irq(irq)) < 0) return (-1); if (ret != BCM2836_GPU_IRQ) return (ret + BANK3_START); #endif /* TODO: should we mask last_irq? */ if (irq < BANK1_START) { pending = intc_read_4(sc, INTC_PENDING_BASIC); if ((pending & 0xFF) == 0) { irq = BANK1_START; /* skip to next bank */ } else do { if (pending & (1 << irq)) return irq; irq++; } while (irq < BANK1_START); } if (irq < BANK2_START) { pending = intc_read_4(sc, INTC_PENDING_BANK1); if (pending == 0) { irq = BANK2_START; /* skip to next bank */ } else do { if (pending & (1 << IRQ_BANK1(irq))) return irq; irq++; } while (irq < BANK2_START); } if (irq < BANK3_START) { pending = intc_read_4(sc, INTC_PENDING_BANK2); if (pending != 0) do { if (pending & (1 << IRQ_BANK2(irq))) return irq; irq++; } while (irq < BANK3_START); } return (-1); } void arm_mask_irq(uintptr_t nb) { struct bcm_intc_softc *sc = bcm_intc_sc; dprintf("%s: %d\n", __func__, nb); if (IS_IRQ_BASIC(nb)) intc_write_4(sc, INTC_DISABLE_BASIC, (1 << nb)); else if (IS_IRQ_BANK1(nb)) intc_write_4(sc, INTC_DISABLE_BANK1, (1 << IRQ_BANK1(nb))); else if (IS_IRQ_BANK2(nb)) intc_write_4(sc, INTC_DISABLE_BANK2, (1 << IRQ_BANK2(nb))); #ifdef SOC_BCM2836 else if (ID_IRQ_BCM2836(nb)) bcm2836_mask_irq(nb - BANK3_START); #endif else printf("arm_mask_irq: Invalid IRQ number: %d\n", nb); } void arm_unmask_irq(uintptr_t nb) { struct bcm_intc_softc *sc = bcm_intc_sc; dprintf("%s: %d\n", __func__, nb); if (IS_IRQ_BASIC(nb)) intc_write_4(sc, INTC_ENABLE_BASIC, (1 << nb)); else if (IS_IRQ_BANK1(nb)) intc_write_4(sc, INTC_ENABLE_BANK1, (1 << IRQ_BANK1(nb))); else if (IS_IRQ_BANK2(nb)) intc_write_4(sc, INTC_ENABLE_BANK2, (1 << IRQ_BANK2(nb))); #ifdef SOC_BCM2836 else if (ID_IRQ_BCM2836(nb)) bcm2836_unmask_irq(nb - BANK3_START); #endif else printf("arm_mask_irq: Invalid IRQ number: %d\n", nb); } #ifdef SMP void intr_pic_init_secondary(void) { } #endif #endif Index: head/sys/arm/broadcom/bcm2835/bcm2836.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2836.c (revision 300148) +++ head/sys/arm/broadcom/bcm2835/bcm2836.c (revision 300149) @@ -1,912 +1,913 @@ /* * Copyright 2015 Andrew Turner. * Copyright 2016 Svatopluk Kraus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #ifdef SMP #include #endif #include #include #include #ifdef SMP #include #endif #include #include #ifdef INTRNG #include "pic_if.h" #else #include #define ARM_LOCAL_BASE 0x40000000 #define ARM_LOCAL_SIZE 0x00001000 #define ARM_LOCAL_CONTROL 0x00 #define ARM_LOCAL_PRESCALER 0x08 #define PRESCALER_19_2 0x80000000 /* 19.2 MHz */ #define ARM_LOCAL_INT_TIMER(n) (0x40 + (n) * 4) #define ARM_LOCAL_INT_MAILBOX(n) (0x50 + (n) * 4) #define ARM_LOCAL_INT_PENDING(n) (0x60 + (n) * 4) #define INT_PENDING_MASK 0x011f #define MAILBOX0_IRQ 4 #define MAILBOX0_IRQEN (1 << 0) #endif #ifdef INTRNG #define BCM_LINTC_CONTROL_REG 0x00 #define BCM_LINTC_PRESCALER_REG 0x08 #define BCM_LINTC_GPU_ROUTING_REG 0x0c #define BCM_LINTC_PMU_ROUTING_SET_REG 0x10 #define BCM_LINTC_PMU_ROUTING_CLR_REG 0x14 #define BCM_LINTC_TIMER_CFG_REG(n) (0x40 + (n) * 4) #define BCM_LINTC_MBOX_CFG_REG(n) (0x50 + (n) * 4) #define BCM_LINTC_PENDING_REG(n) (0x60 + (n) * 4) #define BCM_LINTC_MBOX0_SET_REG(n) (0x80 + (n) * 16) #define BCM_LINTC_MBOX1_SET_REG(n) (0x84 + (n) * 16) #define BCM_LINTC_MBOX2_SET_REG(n) (0x88 + (n) * 16) #define BCM_LINTC_MBOX3_SET_REG(n) (0x8C + (n) * 16) #define BCM_LINTC_MBOX0_CLR_REG(n) (0xC0 + (n) * 16) #define BCM_LINTC_MBOX1_CLR_REG(n) (0xC4 + (n) * 16) #define BCM_LINTC_MBOX2_CLR_REG(n) (0xC8 + (n) * 16) #define BCM_LINTC_MBOX3_CLR_REG(n) (0xCC + (n) * 16) /* Prescaler Register */ #define BCM_LINTC_PSR_19_2 0x80000000 /* 19.2 MHz */ /* GPU Interrupt Routing Register */ #define BCM_LINTC_GIRR_IRQ_CORE(n) (n) #define BCM_LINTC_GIRR_FIQ_CORE(n) ((n) << 2) /* PMU Interrupt Routing Register */ #define BCM_LINTC_PIRR_IRQ_EN_CORE(n) (1 << (n)) #define BCM_LINTC_PIRR_FIQ_EN_CORE(n) (1 << ((n) + 4)) /* Timer Config Register */ #define BCM_LINTC_TCR_IRQ_EN_TIMER(n) (1 << (n)) #define BCM_LINTC_TCR_FIQ_EN_TIMER(n) (1 << ((n) + 4)) /* MBOX Config Register */ #define BCM_LINTC_MCR_IRQ_EN_MBOX(n) (1 << (n)) #define BCM_LINTC_MCR_FIQ_EN_MBOX(n) (1 << ((n) + 4)) #define BCM_LINTC_CNTPSIRQ_IRQ 0 #define BCM_LINTC_CNTPNSIRQ_IRQ 1 #define BCM_LINTC_CNTHPIRQ_IRQ 2 #define BCM_LINTC_CNTVIRQ_IRQ 3 #define BCM_LINTC_MBOX0_IRQ 4 #define BCM_LINTC_MBOX1_IRQ 5 #define BCM_LINTC_MBOX2_IRQ 6 #define BCM_LINTC_MBOX3_IRQ 7 #define BCM_LINTC_GPU_IRQ 8 #define BCM_LINTC_PMU_IRQ 9 #define BCM_LINTC_AXI_IRQ 10 #define BCM_LINTC_LTIMER_IRQ 11 #define BCM_LINTC_NIRQS 12 #define BCM_LINTC_TIMER0_IRQ BCM_LINTC_CNTPSIRQ_IRQ #define BCM_LINTC_TIMER1_IRQ BCM_LINTC_CNTPNSIRQ_IRQ #define BCM_LINTC_TIMER2_IRQ BCM_LINTC_CNTHPIRQ_IRQ #define BCM_LINTC_TIMER3_IRQ BCM_LINTC_CNTVIRQ_IRQ #define BCM_LINTC_TIMER0_IRQ_MASK (1 << BCM_LINTC_TIMER0_IRQ) #define BCM_LINTC_TIMER1_IRQ_MASK (1 << BCM_LINTC_TIMER1_IRQ) #define BCM_LINTC_TIMER2_IRQ_MASK (1 << BCM_LINTC_TIMER2_IRQ) #define BCM_LINTC_TIMER3_IRQ_MASK (1 << BCM_LINTC_TIMER3_IRQ) #define BCM_LINTC_MBOX0_IRQ_MASK (1 << BCM_LINTC_MBOX0_IRQ) #define BCM_LINTC_GPU_IRQ_MASK (1 << BCM_LINTC_GPU_IRQ) #define BCM_LINTC_PMU_IRQ_MASK (1 << BCM_LINTC_PMU_IRQ) #define BCM_LINTC_UP_PENDING_MASK \ (BCM_LINTC_TIMER0_IRQ_MASK | \ BCM_LINTC_TIMER1_IRQ_MASK | \ BCM_LINTC_TIMER2_IRQ_MASK | \ BCM_LINTC_TIMER3_IRQ_MASK | \ BCM_LINTC_GPU_IRQ_MASK | \ BCM_LINTC_PMU_IRQ_MASK) #define BCM_LINTC_SMP_PENDING_MASK \ (BCM_LINTC_UP_PENDING_MASK | \ BCM_LINTC_MBOX0_IRQ_MASK) #ifdef SMP #define BCM_LINTC_PENDING_MASK BCM_LINTC_SMP_PENDING_MASK #else #define BCM_LINTC_PENDING_MASK BCM_LINTC_UP_PENDING_MASK #endif struct bcm_lintc_irqsrc { struct intr_irqsrc bli_isrc; u_int bli_irq; union { u_int bli_mask; /* for timers */ u_int bli_value; /* for GPU */ }; }; struct bcm_lintc_softc { device_t bls_dev; struct mtx bls_mtx; struct resource * bls_mem; bus_space_tag_t bls_bst; bus_space_handle_t bls_bsh; struct bcm_lintc_irqsrc bls_isrcs[BCM_LINTC_NIRQS]; }; static struct bcm_lintc_softc *bcm_lintc_sc; #ifdef SMP #define BCM_LINTC_NIPIS 32 /* only mailbox 0 is used for IPI */ CTASSERT(INTR_IPI_COUNT <= BCM_LINTC_NIPIS); #endif #define BCM_LINTC_LOCK(sc) mtx_lock_spin(&(sc)->bls_mtx) #define BCM_LINTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->bls_mtx) #define BCM_LINTC_LOCK_INIT(sc) mtx_init(&(sc)->bls_mtx, \ device_get_nameunit((sc)->bls_dev), "bmc_local_intc", MTX_SPIN) #define BCM_LINTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->bls_mtx) #define bcm_lintc_read_4(sc, reg) \ bus_space_read_4((sc)->bls_bst, (sc)->bls_bsh, (reg)) #define bcm_lintc_write_4(sc, reg, val) \ bus_space_write_4((sc)->bls_bst, (sc)->bls_bsh, (reg), (val)) static inline void bcm_lintc_rwreg_clr(struct bcm_lintc_softc *sc, uint32_t reg, uint32_t mask) { bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) & ~mask); } static inline void bcm_lintc_rwreg_set(struct bcm_lintc_softc *sc, uint32_t reg, uint32_t mask) { bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) | mask); } static void bcm_lintc_timer_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) bcm_lintc_rwreg_clr(sc, BCM_LINTC_TIMER_CFG_REG(cpu), bli->bli_mask); BCM_LINTC_UNLOCK(sc); } static void bcm_lintc_timer_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) bcm_lintc_rwreg_set(sc, BCM_LINTC_TIMER_CFG_REG(cpu), bli->bli_mask); BCM_LINTC_UNLOCK(sc); } static inline void bcm_lintc_gpu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { /* It's accessed just and only by one core. */ bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, 0); } static inline void bcm_lintc_gpu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { /* It's accessed just and only by one core. */ bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, bli->bli_value); } static inline void bcm_lintc_pmu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu, mask; mask = 0; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu); /* Write-clear register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_CLR_REG, mask); BCM_LINTC_UNLOCK(sc); } static inline void bcm_lintc_pmu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu, mask; mask = 0; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu); /* Write-set register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, mask); BCM_LINTC_UNLOCK(sc); } static void bcm_lintc_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { switch (bli->bli_irq) { case BCM_LINTC_TIMER0_IRQ: case BCM_LINTC_TIMER1_IRQ: case BCM_LINTC_TIMER2_IRQ: case BCM_LINTC_TIMER3_IRQ: bcm_lintc_timer_mask(sc, bli); return; case BCM_LINTC_MBOX0_IRQ: case BCM_LINTC_MBOX1_IRQ: case BCM_LINTC_MBOX2_IRQ: case BCM_LINTC_MBOX3_IRQ: return; case BCM_LINTC_GPU_IRQ: bcm_lintc_gpu_mask(sc, bli); return; case BCM_LINTC_PMU_IRQ: bcm_lintc_pmu_mask(sc, bli); return; default: panic("%s: not implemented for irq %u", __func__, bli->bli_irq); } } static void bcm_lintc_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { switch (bli->bli_irq) { case BCM_LINTC_TIMER0_IRQ: case BCM_LINTC_TIMER1_IRQ: case BCM_LINTC_TIMER2_IRQ: case BCM_LINTC_TIMER3_IRQ: bcm_lintc_timer_unmask(sc, bli); return; case BCM_LINTC_MBOX0_IRQ: case BCM_LINTC_MBOX1_IRQ: case BCM_LINTC_MBOX2_IRQ: case BCM_LINTC_MBOX3_IRQ: return; case BCM_LINTC_GPU_IRQ: bcm_lintc_gpu_unmask(sc, bli); return; case BCM_LINTC_PMU_IRQ: bcm_lintc_pmu_unmask(sc, bli); return; default: panic("%s: not implemented for irq %u", __func__, bli->bli_irq); } } #ifdef SMP static inline void bcm_lintc_ipi_write(struct bcm_lintc_softc *sc, cpuset_t cpus, u_int ipi) { u_int cpu; uint32_t mask; mask = 1 << ipi; for (cpu = 0; cpu < mp_ncpus; cpu++) if (CPU_ISSET(cpu, &cpus)) bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_SET_REG(cpu), mask); } static inline void bcm_lintc_ipi_dispatch(struct bcm_lintc_softc *sc, u_int cpu, struct trapframe *tf) { u_int ipi; uint32_t mask; mask = bcm_lintc_read_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu)); if (mask == 0) { device_printf(sc->bls_dev, "Spurious ipi detected\n"); return; } for (ipi = 0; mask != 0; mask >>= 1, ipi++) { if ((mask & 0x01) == 0) continue; /* * Clear an IPI before dispatching to not miss anyone * and make sure that it's observed by everybody. */ bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu), 1 << ipi); dsb(); intr_ipi_dispatch(ipi, tf); } } #endif static inline void bcm_lintc_irq_dispatch(struct bcm_lintc_softc *sc, u_int irq, struct trapframe *tf) { struct bcm_lintc_irqsrc *bli; bli = &sc->bls_isrcs[irq]; if (intr_isrc_dispatch(&bli->bli_isrc, tf) != 0) device_printf(sc->bls_dev, "Stray irq %u detected\n", irq); } static int bcm_lintc_intr(void *arg) { struct bcm_lintc_softc *sc; u_int cpu; uint32_t num, reg; struct trapframe *tf; sc = arg; cpu = PCPU_GET(cpuid); tf = curthread->td_intr_frame; for (num = 0; ; num++) { reg = bcm_lintc_read_4(sc, BCM_LINTC_PENDING_REG(cpu)); if ((reg & BCM_LINTC_PENDING_MASK) == 0) break; #ifdef SMP if (reg & BCM_LINTC_MBOX0_IRQ_MASK) bcm_lintc_ipi_dispatch(sc, cpu, tf); #endif if (reg & BCM_LINTC_TIMER0_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER0_IRQ, tf); if (reg & BCM_LINTC_TIMER1_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER1_IRQ, tf); if (reg & BCM_LINTC_TIMER2_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER2_IRQ, tf); if (reg & BCM_LINTC_TIMER3_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER3_IRQ, tf); if (reg & BCM_LINTC_GPU_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_GPU_IRQ, tf); if (reg & BCM_LINTC_PMU_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_PMU_IRQ, tf); arm_irq_memory_barrier(0); /* XXX */ } reg &= ~BCM_LINTC_PENDING_MASK; if (reg != 0) device_printf(sc->bls_dev, "Unknown interrupt(s) %x\n", reg); else if (num == 0) device_printf(sc->bls_dev, "Spurious interrupt detected\n"); return (FILTER_HANDLED); } static void bcm_lintc_disable_intr(device_t dev, struct intr_irqsrc *isrc) { bcm_lintc_mask(device_get_softc(dev), (struct bcm_lintc_irqsrc *)isrc); } static void bcm_lintc_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; arm_irq_memory_barrier(bli->bli_irq); bcm_lintc_unmask(device_get_softc(dev), bli); } static int bcm_lintc_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct bcm_lintc_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= BCM_LINTC_NIRQS) return (EINVAL); sc = device_get_softc(dev); *isrcp = &sc->bls_isrcs[daf->cells[0]].bli_isrc; return (0); } static void bcm_lintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; if (bli->bli_irq == BCM_LINTC_GPU_IRQ) bcm_lintc_gpu_mask(device_get_softc(dev), bli); else { /* * Handler for PPI interrupt does not make sense much unless * there is one bound ithread for each core for it. Thus the * interrupt can be masked on current core only while ithread * bounded to this core ensures unmasking on the same core. */ panic ("%s: handlers are not supported", __func__); } } static void bcm_lintc_post_ithread(device_t dev, struct intr_irqsrc *isrc) { struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; if (bli->bli_irq == BCM_LINTC_GPU_IRQ) bcm_lintc_gpu_unmask(device_get_softc(dev), bli); else { /* See comment in bcm_lintc_pre_ithread(). */ panic ("%s: handlers are not supported", __func__); } } static void bcm_lintc_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static int bcm_lintc_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct bcm_lintc_softc *sc; if (isrc->isrc_handlers == 0 && isrc->isrc_flags & INTR_ISRCF_PPI) { sc = device_get_softc(dev); BCM_LINTC_LOCK(sc); CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); BCM_LINTC_UNLOCK(sc); } return (0); } #ifdef SMP static void bcm_lintc_init_rwreg_on_ap(struct bcm_lintc_softc *sc, u_int cpu, u_int irq, uint32_t reg, uint32_t mask) { if (intr_isrc_init_on_cpu(&sc->bls_isrcs[irq].bli_isrc, cpu)) bcm_lintc_rwreg_set(sc, reg, mask); } static void bcm_lintc_init_pmu_on_ap(struct bcm_lintc_softc *sc, u_int cpu) { struct intr_irqsrc *isrc = &sc->bls_isrcs[BCM_LINTC_PMU_IRQ].bli_isrc; if (intr_isrc_init_on_cpu(isrc, cpu)) { /* Write-set register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, BCM_LINTC_PIRR_IRQ_EN_CORE(cpu)); } } static void bcm_lintc_init_secondary(device_t dev) { u_int cpu; struct bcm_lintc_softc *sc; cpu = PCPU_GET(cpuid); sc = device_get_softc(dev); BCM_LINTC_LOCK(sc); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER0_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(0)); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER1_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(1)); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER2_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(2)); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER3_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(3)); bcm_lintc_init_pmu_on_ap(sc, cpu); BCM_LINTC_UNLOCK(sc); } static void bcm_lintc_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, u_int ipi) { struct bcm_lintc_softc *sc = device_get_softc(dev); KASSERT(isrc == &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc, ("%s: bad ISRC %p argument", __func__, isrc)); bcm_lintc_ipi_write(sc, cpus, ipi); } static int bcm_lintc_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) { struct bcm_lintc_softc *sc = device_get_softc(dev); KASSERT(ipi < BCM_LINTC_NIPIS, ("%s: too high ipi %u", __func__, ipi)); *isrcp = &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc; return (0); } #endif static int bcm_lintc_pic_attach(struct bcm_lintc_softc *sc) { struct bcm_lintc_irqsrc *bisrcs; + struct intr_pic *pic; int error; u_int flags; uint32_t irq; const char *name; intptr_t xref; bisrcs = sc->bls_isrcs; name = device_get_nameunit(sc->bls_dev); for (irq = 0; irq < BCM_LINTC_NIRQS; irq++) { bisrcs[irq].bli_irq = irq; switch (irq) { case BCM_LINTC_TIMER0_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(0); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_TIMER1_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(1); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_TIMER2_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(2); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_TIMER3_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(3); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_MBOX0_IRQ: case BCM_LINTC_MBOX1_IRQ: case BCM_LINTC_MBOX2_IRQ: case BCM_LINTC_MBOX3_IRQ: bisrcs[irq].bli_value = 0; /* not used */ flags = INTR_ISRCF_IPI; break; case BCM_LINTC_GPU_IRQ: bisrcs[irq].bli_value = BCM_LINTC_GIRR_IRQ_CORE(0); flags = 0; break; case BCM_LINTC_PMU_IRQ: bisrcs[irq].bli_value = 0; /* not used */ flags = INTR_ISRCF_PPI; break; default: bisrcs[irq].bli_value = 0; /* not used */ flags = 0; break; } error = intr_isrc_register(&bisrcs[irq].bli_isrc, sc->bls_dev, flags, "%s,%u", name, irq); if (error != 0) return (error); } xref = OF_xref_from_node(ofw_bus_get_node(sc->bls_dev)); - error = intr_pic_register(sc->bls_dev, xref); - if (error != 0) - return (error); + pic = intr_pic_register(sc->bls_dev, xref); + if (pic == NULL) + return (ENXIO); return (intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc, 0)); } static int bcm_lintc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "brcm,bcm2836-l1-intc")) return (ENXIO); device_set_desc(dev, "BCM2836 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_lintc_attach(device_t dev) { struct bcm_lintc_softc *sc; int cpu, rid; sc = device_get_softc(dev); sc->bls_dev = dev; if (bcm_lintc_sc != NULL) return (ENXIO); rid = 0; sc->bls_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bls_mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->bls_bst = rman_get_bustag(sc->bls_mem); sc->bls_bsh = rman_get_bushandle(sc->bls_mem); bcm_lintc_write_4(sc, BCM_LINTC_CONTROL_REG, 0); bcm_lintc_write_4(sc, BCM_LINTC_PRESCALER_REG, BCM_LINTC_PSR_19_2); /* Disable all timers on all cores. */ for (cpu = 0; cpu < 4; cpu++) bcm_lintc_write_4(sc, BCM_LINTC_TIMER_CFG_REG(cpu), 0); #ifdef SMP /* Enable mailbox 0 on all cores used for IPI. */ for (cpu = 0; cpu < 4; cpu++) bcm_lintc_write_4(sc, BCM_LINTC_MBOX_CFG_REG(cpu), BCM_LINTC_MCR_IRQ_EN_MBOX(0)); #endif if (bcm_lintc_pic_attach(sc) != 0) { device_printf(dev, "could not attach PIC\n"); return (ENXIO); } BCM_LINTC_LOCK_INIT(sc); bcm_lintc_sc = sc; return (0); } static device_method_t bcm_lintc_methods[] = { DEVMETHOD(device_probe, bcm_lintc_probe), DEVMETHOD(device_attach, bcm_lintc_attach), DEVMETHOD(pic_disable_intr, bcm_lintc_disable_intr), DEVMETHOD(pic_enable_intr, bcm_lintc_enable_intr), DEVMETHOD(pic_map_intr, bcm_lintc_map_intr), DEVMETHOD(pic_post_filter, bcm_lintc_post_filter), DEVMETHOD(pic_post_ithread, bcm_lintc_post_ithread), DEVMETHOD(pic_pre_ithread, bcm_lintc_pre_ithread), DEVMETHOD(pic_setup_intr, bcm_lintc_setup_intr), #ifdef SMP DEVMETHOD(pic_init_secondary, bcm_lintc_init_secondary), DEVMETHOD(pic_ipi_send, bcm_lintc_ipi_send), DEVMETHOD(pic_ipi_setup, bcm_lintc_ipi_setup), #endif DEVMETHOD_END }; static driver_t bcm_lintc_driver = { "local_intc", bcm_lintc_methods, sizeof(struct bcm_lintc_softc), }; static devclass_t bcm_lintc_devclass; EARLY_DRIVER_MODULE(local_intc, simplebus, bcm_lintc_driver, bcm_lintc_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); #else /* * A driver for features of the bcm2836. */ struct bcm2836_softc { device_t sc_dev; struct resource *sc_mem; }; static device_identify_t bcm2836_identify; static device_probe_t bcm2836_probe; static device_attach_t bcm2836_attach; struct bcm2836_softc *softc; static void bcm2836_identify(driver_t *driver, device_t parent) { if (BUS_ADD_CHILD(parent, 0, "bcm2836", -1) == NULL) device_printf(parent, "add child failed\n"); } static int bcm2836_probe(device_t dev) { if (softc != NULL) return (ENXIO); device_set_desc(dev, "Broadcom bcm2836"); return (BUS_PROBE_DEFAULT); } static int bcm2836_attach(device_t dev) { int i, rid; softc = device_get_softc(dev); softc->sc_dev = dev; rid = 0; softc->sc_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, ARM_LOCAL_BASE, ARM_LOCAL_BASE + ARM_LOCAL_SIZE, ARM_LOCAL_SIZE, RF_ACTIVE); if (softc->sc_mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } bus_write_4(softc->sc_mem, ARM_LOCAL_CONTROL, 0); bus_write_4(softc->sc_mem, ARM_LOCAL_PRESCALER, PRESCALER_19_2); for (i = 0; i < 4; i++) bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), 0); for (i = 0; i < 4; i++) bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(i), 1); return (0); } int bcm2836_get_next_irq(int last_irq) { uint32_t reg; int cpu; int irq; cpu = PCPU_GET(cpuid); reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_PENDING(cpu)); reg &= INT_PENDING_MASK; if (reg == 0) return (-1); irq = ffs(reg) - 1; return (irq); } void bcm2836_mask_irq(uintptr_t irq) { uint32_t reg; #ifdef SMP int cpu; #endif int i; if (irq < MAILBOX0_IRQ) { for (i = 0; i < 4; i++) { reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i)); reg &= ~(1 << irq); bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), reg); } #ifdef SMP } else if (irq == MAILBOX0_IRQ) { /* Mailbox 0 for IPI */ cpu = PCPU_GET(cpuid); reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu)); reg &= ~MAILBOX0_IRQEN; bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg); #endif } } void bcm2836_unmask_irq(uintptr_t irq) { uint32_t reg; #ifdef SMP int cpu; #endif int i; if (irq < MAILBOX0_IRQ) { for (i = 0; i < 4; i++) { reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i)); reg |= (1 << irq); bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), reg); } #ifdef SMP } else if (irq == MAILBOX0_IRQ) { /* Mailbox 0 for IPI */ cpu = PCPU_GET(cpuid); reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu)); reg |= MAILBOX0_IRQEN; bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg); #endif } } static device_method_t bcm2836_methods[] = { /* Device interface */ DEVMETHOD(device_identify, bcm2836_identify), DEVMETHOD(device_probe, bcm2836_probe), DEVMETHOD(device_attach, bcm2836_attach), DEVMETHOD_END }; static devclass_t bcm2836_devclass; static driver_t bcm2836_driver = { "bcm2836", bcm2836_methods, sizeof(struct bcm2836_softc), }; EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); #endif Index: head/sys/arm/mv/mpic.c =================================================================== --- head/sys/arm/mv/mpic.c (revision 300148) +++ head/sys/arm/mv/mpic.c (revision 300149) @@ -1,635 +1,635 @@ /*- * Copyright (c) 2006 Benno Rice. * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD. * Copyright (c) 2012 Semihalf. * All rights reserved. * * Developed by Semihalf. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1 * from: FreeBSD: src/sys/arm/mv/ic.c,v 1.5 2011/02/08 01:49:30 */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INTRNG #include "pic_if.h" #endif #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif #define MPIC_INT_ERR 4 #define MPIC_INT_MSI 96 #define MPIC_IRQ_MASK 0x3ff #define MPIC_CTRL 0x0 #define MPIC_SOFT_INT 0x4 #define MPIC_SOFT_INT_DRBL1 (1 << 5) #define MPIC_ERR_CAUSE 0x20 #define MPIC_ISE 0x30 #define MPIC_ICE 0x34 #define MPIC_INT_CTL(irq) (0x100 + (irq)*4) #define MPIC_INT_IRQ_FIQ_MASK(cpuid) (0x101 << (cpuid)) #define MPIC_CTRL_NIRQS(ctrl) (((ctrl) >> 2) & 0x3ff) #define MPIC_IN_DRBL 0x08 #define MPIC_IN_DRBL_MASK 0x0c #define MPIC_PPI_CAUSE 0x10 #define MPIC_CTP 0x40 #define MPIC_IIACK 0x44 #define MPIC_ISM 0x48 #define MPIC_ICM 0x4c #define MPIC_ERR_MASK 0xe50 #define MPIC_PPI 32 #ifdef INTRNG struct mv_mpic_irqsrc { struct intr_irqsrc mmi_isrc; u_int mmi_irq; }; #endif struct mv_mpic_softc { device_t sc_dev; struct resource * mpic_res[4]; bus_space_tag_t mpic_bst; bus_space_handle_t mpic_bsh; bus_space_tag_t cpu_bst; bus_space_handle_t cpu_bsh; bus_space_tag_t drbl_bst; bus_space_handle_t drbl_bsh; struct mtx mtx; #ifdef INTRNG struct mv_mpic_irqsrc * mpic_isrcs; #endif int nirqs; void * intr_hand; }; static struct resource_spec mv_mpic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { {"mrvl,mpic", true}, {"marvell,mpic", true}, {NULL, false} }; static struct mv_mpic_softc *mv_mpic_sc = NULL; void mpic_send_ipi(int cpus, u_int ipi); static int mv_mpic_probe(device_t); static int mv_mpic_attach(device_t); uint32_t mv_mpic_get_cause(void); uint32_t mv_mpic_get_cause_err(void); uint32_t mv_mpic_get_msi(void); static void mpic_unmask_irq(uintptr_t nb); static void mpic_mask_irq(uintptr_t nb); static void mpic_mask_irq_err(uintptr_t nb); static void mpic_unmask_irq_err(uintptr_t nb); static int mpic_intr(void *arg); static void mpic_unmask_msi(void); #ifndef INTRNG static void arm_mask_irq_err(uintptr_t); static void arm_unmask_irq_err(uintptr_t); #endif #define MPIC_WRITE(softc, reg, val) \ bus_space_write_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg), (val)) #define MPIC_READ(softc, reg) \ bus_space_read_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg)) #define MPIC_CPU_WRITE(softc, reg, val) \ bus_space_write_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg), (val)) #define MPIC_CPU_READ(softc, reg) \ bus_space_read_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg)) #define MPIC_DRBL_WRITE(softc, reg, val) \ bus_space_write_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg), (val)) #define MPIC_DRBL_READ(softc, reg) \ bus_space_read_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg)) static int mv_mpic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Marvell Integrated Interrupt Controller"); return (0); } #ifdef INTRNG static int mv_mpic_register_isrcs(struct mv_mpic_softc *sc) { int error; uint32_t irq; struct intr_irqsrc *isrc; const char *name; sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < sc->nirqs; irq++) { sc->mpic_isrcs[irq].mmi_irq = irq; isrc = &sc->mpic_isrcs[irq].mmi_isrc; if (irq < MPIC_PPI) { error = intr_isrc_register(isrc, sc->sc_dev, INTR_ISRCF_PPI, "%s", name); } else { error = intr_isrc_register(isrc, sc->sc_dev, 0, "%s", name); } if (error != 0) { /* XXX call intr_isrc_deregister() */ device_printf(sc->sc_dev, "%s failed", __func__); return (error); } } return (0); } #endif static int mv_mpic_attach(device_t dev) { struct mv_mpic_softc *sc; int error; uint32_t val; sc = (struct mv_mpic_softc *)device_get_softc(dev); if (mv_mpic_sc != NULL) return (ENXIO); mv_mpic_sc = sc; sc->sc_dev = dev; mtx_init(&sc->mtx, "MPIC lock", NULL, MTX_SPIN); error = bus_alloc_resources(dev, mv_mpic_spec, sc->mpic_res); if (error) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } #ifdef INTRNG if (sc->mpic_res[3] == NULL) device_printf(dev, "No interrupt to use.\n"); else bus_setup_intr(dev, sc->mpic_res[3], INTR_TYPE_CLK, mpic_intr, NULL, sc, &sc->intr_hand); #endif sc->mpic_bst = rman_get_bustag(sc->mpic_res[0]); sc->mpic_bsh = rman_get_bushandle(sc->mpic_res[0]); sc->cpu_bst = rman_get_bustag(sc->mpic_res[1]); sc->cpu_bsh = rman_get_bushandle(sc->mpic_res[1]); if (sc->mpic_res[2] != NULL) { /* This is required only if MSIs are used. */ sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]); sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]); } bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, MPIC_CTRL, 1); MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0); val = MPIC_READ(mv_mpic_sc, MPIC_CTRL); sc->nirqs = MPIC_CTRL_NIRQS(val); #ifdef INTRNG if (mv_mpic_register_isrcs(sc) != 0) { device_printf(dev, "could not register PIC ISRCs\n"); bus_release_resources(dev, mv_mpic_spec, sc->mpic_res); return (ENXIO); } - if (intr_pic_register(dev, OF_xref_from_device(dev)) != 0) { + if (intr_pic_register(dev, OF_xref_from_device(dev)) == NULL) { device_printf(dev, "could not register PIC\n"); bus_release_resources(dev, mv_mpic_spec, sc->mpic_res); return (ENXIO); } #endif mpic_unmask_msi(); return (0); } #ifdef INTRNG static int mpic_intr(void *arg) { struct mv_mpic_softc *sc; uint32_t cause, irqsrc; unsigned int irq; u_int cpuid; sc = arg; cpuid = PCPU_GET(cpuid); irq = 0; for (cause = MPIC_CPU_READ(sc, MPIC_PPI_CAUSE); cause > 0; cause >>= 1, irq++) { if (cause & 1) { irqsrc = MPIC_READ(sc, MPIC_INT_CTL(irq)); if ((irqsrc & MPIC_INT_IRQ_FIQ_MASK(cpuid)) == 0) continue; if (intr_isrc_dispatch(&sc->mpic_isrcs[irq].mmi_isrc, curthread->td_intr_frame) != 0) { mpic_mask_irq(irq); device_printf(sc->sc_dev, "Stray irq %u " "disabled\n", irq); } } } return (FILTER_HANDLED); } static void mpic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq; mpic_mask_irq(irq); } static void mpic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq; mpic_unmask_irq(irq); } static int mpic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct mv_mpic_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells !=1 || daf->cells[0] >= sc->nirqs) return (EINVAL); *isrcp = &sc->mpic_isrcs[daf->cells[0]].mmi_isrc; return (0); } static void mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mpic_disable_intr(dev, isrc); } static void mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mpic_enable_intr(dev, isrc); } #endif static device_method_t mv_mpic_methods[] = { DEVMETHOD(device_probe, mv_mpic_probe), DEVMETHOD(device_attach, mv_mpic_attach), #ifdef INTRNG DEVMETHOD(pic_disable_intr, mpic_disable_intr), DEVMETHOD(pic_enable_intr, mpic_enable_intr), DEVMETHOD(pic_map_intr, mpic_map_intr), DEVMETHOD(pic_post_ithread, mpic_post_ithread), DEVMETHOD(pic_pre_ithread, mpic_pre_ithread), #endif { 0, 0 } }; static driver_t mv_mpic_driver = { "mpic", mv_mpic_methods, sizeof(struct mv_mpic_softc), }; static devclass_t mv_mpic_devclass; EARLY_DRIVER_MODULE(mpic, simplebus, mv_mpic_driver, mv_mpic_devclass, 0, 0, BUS_PASS_INTERRUPT); #ifndef INTRNG int arm_get_next_irq(int last) { u_int irq, next = -1; irq = mv_mpic_get_cause() & MPIC_IRQ_MASK; CTR2(KTR_INTR, "%s: irq:%#x", __func__, irq); if (irq != MPIC_IRQ_MASK) { if (irq == MPIC_INT_ERR) irq = mv_mpic_get_cause_err(); if (irq == MPIC_INT_MSI) irq = mv_mpic_get_msi(); next = irq; } CTR3(KTR_INTR, "%s: last=%d, next=%d", __func__, last, next); return (next); } /* * XXX We can make arm_enable_irq to operate on ICE and then mask/unmask only * by ISM/ICM and remove access to ICE in masking operation */ void arm_mask_irq(uintptr_t nb) { mpic_mask_irq(nb); } static void arm_mask_irq_err(uintptr_t nb) { mpic_mask_irq_err(nb); } void arm_unmask_irq(uintptr_t nb) { mpic_unmask_irq(nb); } void arm_unmask_irq_err(uintptr_t nb) { mpic_unmask_irq_err(nb); } #endif static void mpic_unmask_msi(void) { mpic_unmask_irq(MPIC_INT_MSI); } static void mpic_unmask_irq_err(uintptr_t nb) { uint32_t mask; uint8_t bit_off; bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, MPIC_ISE, MPIC_INT_ERR); MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, MPIC_INT_ERR); bit_off = nb - ERR_IRQ; mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK); mask |= (1 << bit_off); MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask); } static void mpic_mask_irq_err(uintptr_t nb) { uint32_t mask; uint8_t bit_off; bit_off = nb - ERR_IRQ; mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK); mask &= ~(1 << bit_off); MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask); } static void mpic_unmask_irq(uintptr_t nb) { if (nb < ERR_IRQ) { bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, MPIC_ISE, nb); MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb); } else if (nb < MSI_IRQ) mpic_unmask_irq_err(nb); if (nb == 0) MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff); } static void mpic_mask_irq(uintptr_t nb) { if (nb < ERR_IRQ) { bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, MPIC_ICE, nb); MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb); } else if (nb < MSI_IRQ) mpic_mask_irq_err(nb); } uint32_t mv_mpic_get_cause(void) { return (MPIC_CPU_READ(mv_mpic_sc, MPIC_IIACK)); } uint32_t mv_mpic_get_cause_err(void) { uint32_t err_cause; uint8_t bit_off; err_cause = bus_space_read_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, MPIC_ERR_CAUSE); if (err_cause) bit_off = ffs(err_cause) - 1; else return (-1); debugf("%s: irq:%x cause:%x\n", __func__, bit_off, err_cause); return (ERR_IRQ + bit_off); } uint32_t mv_mpic_get_msi(void) { uint32_t cause; uint8_t bit_off; KASSERT(mv_mpic_sc->drbl_bst != NULL, ("No doorbell in mv_mpic_get_msi")); cause = MPIC_DRBL_READ(mv_mpic_sc, 0); if (cause) bit_off = ffs(cause) - 1; else return (-1); debugf("%s: irq:%x cause:%x\n", __func__, bit_off, cause); cause &= ~(1 << bit_off); MPIC_DRBL_WRITE(mv_mpic_sc, 0, cause); return (MSI_IRQ + bit_off); } int mv_msi_data(int irq, uint64_t *addr, uint32_t *data) { u_long phys, base, size; phandle_t node; int error; node = ofw_bus_get_node(mv_mpic_sc->sc_dev); /* Get physical address of register space */ error = fdt_get_range(OF_parent(node), 0, &phys, &size); if (error) { printf("%s: Cannot get register physical address, err:%d", __func__, error); return (error); } /* Get offset of MPIC register space */ error = fdt_regsize(node, &base, &size); if (error) { printf("%s: Cannot get MPIC register offset, err:%d", __func__, error); return (error); } *addr = phys + base + MPIC_SOFT_INT; *data = MPIC_SOFT_INT_DRBL1 | irq; return (0); } #if defined(SMP) && defined(SOC_MV_ARMADAXP) void intr_pic_init_secondary(void) { } void pic_ipi_send(cpuset_t cpus, u_int ipi) { uint32_t val, i; val = 0x00000000; for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) val |= (1 << (8 + i)); val |= ipi; bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, MPIC_SOFT_INT, val); } int pic_ipi_read(int i __unused) { uint32_t val; int ipi; val = MPIC_CPU_READ(mv_mpic_sc, MPIC_IN_DRBL); if (val) { ipi = ffs(val) - 1; MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL, ~(1 << ipi)); return (ipi); } return (0x3ff); } void pic_ipi_clear(int ipi) { } #endif Index: head/sys/arm/nvidia/tegra_gpio.c =================================================================== --- head/sys/arm/nvidia/tegra_gpio.c (revision 300148) +++ head/sys/arm/nvidia/tegra_gpio.c (revision 300149) @@ -1,901 +1,904 @@ /*- * Copyright (c) 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Tegra GPIO driver. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ device_get_nameunit(_sc->dev), "tegra_gpio", MTX_DEF) #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx); #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED); #define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED); #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) #define GPIO_BANK_OFFS 0x100 /* Bank offset */ #define GPIO_NUM_BANKS 8 /* Total number per bank */ #define GPIO_REGS_IN_BANK 4 /* Total registers in bank */ #define GPIO_PINS_IN_REG 8 /* Total pin in register */ #define GPIO_BANKNUM(n) ((n) / (GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG)) #define GPIO_PORTNUM(n) (((n) / GPIO_PINS_IN_REG) % GPIO_REGS_IN_BANK) #define GPIO_BIT(n) ((n) % GPIO_PINS_IN_REG) #define GPIO_REGNUM(n) (GPIO_BANKNUM(n) * GPIO_BANK_OFFS + \ GPIO_PORTNUM(n) * 4) #define NGPIO ((GPIO_NUM_BANKS * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG) - 8) /* Register offsets */ #define GPIO_CNF 0x00 #define GPIO_OE 0x10 #define GPIO_OUT 0x20 #define GPIO_IN 0x30 #define GPIO_INT_STA 0x40 #define GPIO_INT_ENB 0x50 #define GPIO_INT_LVL 0x60 #define GPIO_INT_LVL_DELTA (1 << 16) #define GPIO_INT_LVL_EDGE (1 << 8) #define GPIO_INT_LVL_HIGH (1 << 0) #define GPIO_INT_LVL_MASK (GPIO_INT_LVL_DELTA | \ GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH) #define GPIO_INT_CLR 0x70 #define GPIO_MSK_CNF 0x80 #define GPIO_MSK_OE 0x90 #define GPIO_MSK_OUT 0xA0 #define GPIO_MSK_INT_STA 0xC0 #define GPIO_MSK_INT_ENB 0xD0 #define GPIO_MSK_INT_LVL 0xE0 char *tegra_gpio_port_names[] = { "A", "B", "C", "D", /* Bank 0 */ "E", "F", "G", "H", /* Bank 1 */ "I", "J", "K", "L", /* Bank 2 */ "M", "N", "O", "P", /* Bank 3 */ "Q", "R", "S", "T", /* Bank 4 */ "U", "V", "W", "X", /* Bank 5 */ "Y", "Z", "AA", "BB", /* Bank 6 */ "CC", "DD", "EE" /* Bank 7 */ }; struct tegra_gpio_irqsrc { struct intr_irqsrc isrc; u_int irq; uint32_t cfgreg; }; struct tegra_gpio_softc; struct tegra_gpio_irq_cookie { struct tegra_gpio_softc *sc; int bank_num; }; struct tegra_gpio_softc { device_t dev; device_t busdev; struct mtx mtx; struct resource *mem_res; struct resource *irq_res[GPIO_NUM_BANKS]; void *irq_ih[GPIO_NUM_BANKS]; struct tegra_gpio_irq_cookie irq_cookies[GPIO_NUM_BANKS]; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; struct tegra_gpio_irqsrc *isrcs; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-gpio", 1}, {NULL, 0} }; /* -------------------------------------------------------------------------- * * GPIO * */ static inline void gpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin, uint32_t val) { uint32_t tmp; int bit; bit = GPIO_BIT(pin->gp_pin); tmp = 0x100 << bit; /* mask */ tmp |= (val & 1) << bit; /* value */ bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp); } static inline uint32_t gpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin) { int bit; uint32_t val; bit = GPIO_BIT(pin->gp_pin); val = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin)); return (val >> bit) & 1; } static void tegra_gpio_pin_configure(struct tegra_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0) return; /* Manage input/output */ pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; gpio_write_masked(sc, GPIO_MSK_OE, pin, 1); } else { pin->gp_flags |= GPIO_PIN_INPUT; gpio_write_masked(sc, GPIO_MSK_OE, pin, 0); } } static device_t tegra_gpio_get_bus(device_t dev) { struct tegra_gpio_softc *sc; sc = device_get_softc(dev); return (sc->busdev); } static int tegra_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = NGPIO - 1; return (0); } static int tegra_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct tegra_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[pin].gp_caps; GPIO_UNLOCK(sc); return (0); } static int tegra_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct tegra_gpio_softc *sc; int cnf; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]); if (cnf == 0) { GPIO_UNLOCK(sc); return (ENXIO); } *flags = sc->gpio_pins[pin].gp_flags; GPIO_UNLOCK(sc); return (0); } static int tegra_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct tegra_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int tegra_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct tegra_gpio_softc *sc; int cnf; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]); if (cnf == 0) { /* XXX - allow this for while .... GPIO_UNLOCK(sc); return (ENXIO); */ gpio_write_masked(sc, GPIO_MSK_CNF, &sc->gpio_pins[pin], 1); } tegra_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags); GPIO_UNLOCK(sc); return (0); } static int tegra_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct tegra_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); gpio_write_masked(sc, GPIO_MSK_OUT, &sc->gpio_pins[pin], value); GPIO_UNLOCK(sc); return (0); } static int tegra_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct tegra_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]); GPIO_UNLOCK(sc); return (0); } static int tegra_gpio_pin_toggle(device_t dev, uint32_t pin) { struct tegra_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); gpio_write_masked(sc, GPIO_MSK_OE, &sc->gpio_pins[pin], gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]) ^ 1); GPIO_UNLOCK(sc); return (0); } /* -------------------------------------------------------------------------- * * Interrupts * */ static inline void intr_write_masked(struct tegra_gpio_softc *sc, bus_addr_t reg, struct tegra_gpio_irqsrc *tgi, uint32_t val) { uint32_t tmp; int bit; bit = GPIO_BIT(tgi->irq); tmp = 0x100 << bit; /* mask */ tmp |= (val & 1) << bit; /* value */ bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp); } static inline void intr_write_modify(struct tegra_gpio_softc *sc, bus_addr_t reg, struct tegra_gpio_irqsrc *tgi, uint32_t val, uint32_t mask) { uint32_t tmp; int bit; bit = GPIO_BIT(tgi->irq); GPIO_LOCK(sc); tmp = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq)); tmp &= ~(mask << bit); tmp |= val << bit; bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp); GPIO_UNLOCK(sc); } static inline void tegra_gpio_isrc_mask(struct tegra_gpio_softc *sc, struct tegra_gpio_irqsrc *tgi, uint32_t val) { intr_write_masked(sc, GPIO_MSK_INT_ENB, tgi, val); } static inline void tegra_gpio_isrc_eoi(struct tegra_gpio_softc *sc, struct tegra_gpio_irqsrc *tgi) { intr_write_masked(sc, GPIO_INT_CLR, tgi, 1); } static inline bool tegra_gpio_isrc_is_level(struct tegra_gpio_irqsrc *tgi) { return (tgi->cfgreg & GPIO_INT_LVL_EDGE); } static int tegra_gpio_intr(void *arg) { u_int irq, i, j, val, basepin; struct tegra_gpio_softc *sc; struct trapframe *tf; struct tegra_gpio_irqsrc *tgi; struct tegra_gpio_irq_cookie *cookie; cookie = (struct tegra_gpio_irq_cookie *)arg; sc = cookie->sc; tf = curthread->td_intr_frame; for (i = 0; i < GPIO_REGS_IN_BANK; i++) { basepin = cookie->bank_num * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG + i * GPIO_PINS_IN_REG; val = bus_read_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(basepin)); val &= bus_read_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(basepin)); /* Interrupt handling */ for (j = 0; j < GPIO_PINS_IN_REG; j++) { if ((val & (1 << j)) == 0) continue; irq = basepin + j; tgi = &sc->isrcs[irq]; if (!tegra_gpio_isrc_is_level(tgi)) tegra_gpio_isrc_eoi(sc, tgi); if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) { tegra_gpio_isrc_mask(sc, tgi, 0); if (tegra_gpio_isrc_is_level(tgi)) tegra_gpio_isrc_eoi(sc, tgi); device_printf(sc->dev, "Stray irq %u disabled\n", irq); } } } return (FILTER_HANDLED); } static int tegra_gpio_pic_attach(struct tegra_gpio_softc *sc) { int error; uint32_t irq; const char *name; sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->dev); for (irq = 0; irq < sc->gpio_npins; irq++) { sc->isrcs[irq].irq = irq; sc->isrcs[irq].cfgreg = 0; error = intr_isrc_register(&sc->isrcs[irq].isrc, sc->dev, 0, "%s,%u", name, irq); if (error != 0) return (error); /* XXX deregister ISRCs */ } - return (intr_pic_register(sc->dev, - OF_xref_from_node(ofw_bus_get_node(sc->dev)))); + if (intr_pic_register(sc->dev, + OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL) + return (ENXIO); + + return (0); } static int tegra_gpio_pic_detach(struct tegra_gpio_softc *sc) { /* * There has not been established any procedure yet * how to detach PIC from living system correctly. */ device_printf(sc->dev, "%s: not implemented yet\n", __func__); return (EBUSY); } static void tegra_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; tegra_gpio_isrc_mask(sc, tgi, 0); } static void tegra_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; tegra_gpio_isrc_mask(sc, tgi, 1); } static int tegra_gpio_pic_map_fdt(struct tegra_gpio_softc *sc, u_int ncells, pcell_t *cells, u_int *irqp, uint32_t *regp) { uint32_t reg; /* * The first cell is the interrupt number. * The second cell is used to specify flags: * bits[3:0] trigger type and level flags: * 1 = low-to-high edge triggered. * 2 = high-to-low edge triggered. * 4 = active high level-sensitive. * 8 = active low level-sensitive. */ if (ncells != 2 || cells[0] >= sc->gpio_npins) return (EINVAL); /* * All interrupt types could be set for an interrupt at one moment. * At least, the combination of 'low-to-high' and 'high-to-low' edge * triggered interrupt types can make a sense. */ if (cells[1] == 1) reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH; else if (cells[1] == 2) reg = GPIO_INT_LVL_EDGE; else if (cells[1] == 3) reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA; else if (cells[1] == 4) reg = GPIO_INT_LVL_HIGH; else if (cells[1] == 8) reg = 0; else return (EINVAL); *irqp = cells[0]; if (regp != NULL) *regp = reg; return (0); } static int tegra_gpio_pic_map_gpio(struct tegra_gpio_softc *sc, u_int gpio_pin_num, u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, uint32_t *regp) { uint32_t reg; if (gpio_pin_num >= sc->gpio_npins) return (EINVAL); switch (intr_mode) { case GPIO_INTR_CONFORM: case GPIO_INTR_LEVEL_LOW: reg = 0; break; case GPIO_INTR_LEVEL_HIGH: reg = GPIO_INT_LVL_HIGH; break; case GPIO_INTR_EDGE_RISING: reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH; break; case GPIO_INTR_EDGE_FALLING: reg = GPIO_INT_LVL_EDGE; break; case GPIO_INTR_EDGE_BOTH: reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA; break; default: return (EINVAL); } *irqp = gpio_pin_num; if (regp != NULL) *regp = reg; return (0); } static int tegra_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { int rv; u_int irq; struct tegra_gpio_softc *sc; sc = device_get_softc(dev); if (data->type == INTR_MAP_DATA_FDT) { struct intr_map_data_fdt *daf; daf = (struct intr_map_data_fdt *)data; rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq, NULL); } else if (data->type == INTR_MAP_DATA_GPIO) { struct intr_map_data_gpio *dag; dag = (struct intr_map_data_gpio *)data; rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num, dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL); } else return (ENOTSUP); if (rv == 0) *isrcp = &sc->isrcs[irq].isrc; return (rv); } static void tegra_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; if (tegra_gpio_isrc_is_level(tgi)) tegra_gpio_isrc_eoi(sc, tgi); } static void tegra_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; tegra_gpio_isrc_mask(sc, tgi, 1); } static void tegra_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; tegra_gpio_isrc_mask(sc, tgi, 0); if (tegra_gpio_isrc_is_level(tgi)) tegra_gpio_isrc_eoi(sc, tgi); } static int tegra_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { u_int irq; uint32_t cfgreg; int rv; struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; if (data == NULL) return (ENOTSUP); /* Get and check config for an interrupt. */ if (data->type == INTR_MAP_DATA_FDT) { struct intr_map_data_fdt *daf; daf = (struct intr_map_data_fdt *)data; rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq, &cfgreg); } else if (data->type == INTR_MAP_DATA_GPIO) { struct intr_map_data_gpio *dag; dag = (struct intr_map_data_gpio *)data; rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num, dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, &cfgreg); } else return (ENOTSUP); if (rv != 0) return (EINVAL); /* * If this is a setup for another handler, * only check that its configuration match. */ if (isrc->isrc_handlers != 0) return (tgi->cfgreg == cfgreg ? 0 : EINVAL); tgi->cfgreg = cfgreg; intr_write_modify(sc, GPIO_INT_LVL, tgi, cfgreg, GPIO_INT_LVL_MASK); tegra_gpio_pic_enable_intr(dev, isrc); return (0); } static int tegra_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct tegra_gpio_softc *sc; struct tegra_gpio_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_gpio_irqsrc *)isrc; if (isrc->isrc_handlers == 0) tegra_gpio_isrc_mask(sc, tgi, 0); return (0); } static int tegra_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Tegra GPIO Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } /* -------------------------------------------------------------------------- * * Bus * */ static int tegra_gpio_detach(device_t dev) { struct tegra_gpio_softc *sc; int i; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized")); for (i = 0; i < GPIO_NUM_BANKS; i++) { if (sc->irq_ih[i] != NULL) bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]); } if (sc->isrcs != NULL) tegra_gpio_pic_detach(sc); gpiobus_detach_bus(dev); for (i = 0; i < GPIO_NUM_BANKS; i++) { if (sc->irq_res[i] != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res[i]); } if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); GPIO_LOCK_DESTROY(sc); return(0); } static int tegra_gpio_attach(device_t dev) { struct tegra_gpio_softc *sc; int i, rid; sc = device_get_softc(dev); sc->dev = dev; GPIO_LOCK_INIT(sc); /* Allocate bus_space resources. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); tegra_gpio_detach(dev); return (ENXIO); } sc->gpio_npins = NGPIO; for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d", tegra_gpio_port_names[ i / GPIO_PINS_IN_REG], i % GPIO_PINS_IN_REG); sc->gpio_pins[i].gp_flags = gpio_read(sc, GPIO_OE, &sc->gpio_pins[i]) != 0 ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; } /* Init interrupt related registes. */ for (i = 0; i < sc->gpio_npins; i += GPIO_PINS_IN_REG) { bus_write_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i), 0); bus_write_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i), 0xFF); bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), 0xFF); } /* Allocate interrupts. */ for (i = 0; i < GPIO_NUM_BANKS; i++) { sc->irq_cookies[i].sc = sc; sc->irq_cookies[i].bank_num = i; rid = i; sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res[i] == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); tegra_gpio_detach(dev); return (ENXIO); } if ((bus_setup_intr(dev, sc->irq_res[i], INTR_TYPE_MISC | INTR_MPSAFE, tegra_gpio_intr, NULL, &sc->irq_cookies[i], &sc->irq_ih[i]))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); tegra_gpio_detach(dev); return (ENXIO); } } if (tegra_gpio_pic_attach(sc) != 0) { device_printf(dev, "WARNING: unable to attach PIC\n"); tegra_gpio_detach(dev); return (ENXIO); } sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) { tegra_gpio_detach(dev); return (ENXIO); } return (bus_generic_attach(dev)); } static int tegra_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { if (gcells != 2) return (ERANGE); *pin = gpios[0]; *flags= gpios[1]; return (0); } static phandle_t tegra_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t tegra_gpio_methods[] = { DEVMETHOD(device_probe, tegra_gpio_probe), DEVMETHOD(device_attach, tegra_gpio_attach), DEVMETHOD(device_detach, tegra_gpio_detach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, tegra_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, tegra_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, tegra_gpio_pic_map_intr), DEVMETHOD(pic_setup_intr, tegra_gpio_pic_setup_intr), DEVMETHOD(pic_teardown_intr, tegra_gpio_pic_teardown_intr), DEVMETHOD(pic_post_filter, tegra_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, tegra_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, tegra_gpio_pic_pre_ithread), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, tegra_gpio_get_bus), DEVMETHOD(gpio_pin_max, tegra_gpio_pin_max), DEVMETHOD(gpio_pin_getname, tegra_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, tegra_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, tegra_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, tegra_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, tegra_gpio_pin_get), DEVMETHOD(gpio_pin_set, tegra_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, tegra_gpio_pin_toggle), DEVMETHOD(gpio_map_gpios, tegra_map_gpios), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, tegra_gpio_get_node), DEVMETHOD_END }; static driver_t tegra_gpio_driver = { "tegra_gpio", tegra_gpio_methods, sizeof(struct tegra_gpio_softc), }; static devclass_t tegra_gpio_devclass; EARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver, tegra_gpio_devclass, 0, 0, 70); extern devclass_t ofwgpiobus_devclass; extern driver_t ofw_gpiobus_driver; EARLY_DRIVER_MODULE(ofw_gpiobus, tegra_gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0, BUS_PASS_BUS); extern devclass_t gpioc_devclass; extern driver_t gpioc_driver; DRIVER_MODULE(gpioc, tegra_gpio, gpioc_driver, gpioc_devclass, 0, 0); Index: head/sys/arm/nvidia/tegra_lic.c =================================================================== --- head/sys/arm/nvidia/tegra_lic.c (revision 300148) +++ head/sys/arm/nvidia/tegra_lic.c (revision 300149) @@ -1,288 +1,288 @@ /*- * Copyright (c) 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Local interrupt controller driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define LIC_VIRQ_CPU 0x00 #define LIC_VIRQ_COP 0x04 #define LIC_VFRQ_CPU 0x08 #define LIC_VFRQ_COP 0x0c #define LIC_ISR 0x10 #define LIC_FIR 0x14 #define LIC_FIR_SET 0x18 #define LIC_FIR_CLR 0x1c #define LIC_CPU_IER 0x20 #define LIC_CPU_IER_SET 0x24 #define LIC_CPU_IER_CLR 0x28 #define LIC_CPU_IEP_CLASS 0x2C #define LIC_COP_IER 0x30 #define LIC_COP_IER_SET 0x34 #define LIC_COP_IER_CLR 0x38 #define LIC_COP_IEP_CLASS 0x3c #define WR4(_sc, _b, _r, _v) bus_write_4((_sc)->mem_res[_b], (_r), (_v)) #define RD4(_sc, _b, _r) bus_read_4((_sc)->mem_res[_b], (_r)) static struct resource_spec lic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_MEMORY, 4, RF_ACTIVE }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-ictlr", 1}, {NULL, 0} }; struct tegra_lic_sc { device_t dev; struct resource *mem_res[nitems(lic_spec)]; device_t parent; }; static int tegra_lic_alloc_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct tegra_lic_sc *sc = device_get_softc(dev); return (PIC_ALLOC_INTR(sc->parent, isrc, res, data)); } static void tegra_lic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); PIC_DISABLE_INTR(sc->parent, isrc); } static void tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); PIC_ENABLE_INTR(sc->parent, isrc); } static int tegra_lic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct tegra_lic_sc *sc = device_get_softc(dev); return (PIC_MAP_INTR(sc->parent, data, isrcp)); } static int tegra_lic_release_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct tegra_lic_sc *sc = device_get_softc(dev); return (PIC_RELEASE_INTR(sc->parent, isrc, res, data)); } static int tegra_lic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct tegra_lic_sc *sc = device_get_softc(dev); return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); } static int tegra_lic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct tegra_lic_sc *sc = device_get_softc(dev); return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); } static void tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); PIC_PRE_ITHREAD(sc->parent, isrc); } static void tegra_lic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); PIC_POST_ITHREAD(sc->parent, isrc); } static void tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); PIC_POST_FILTER(sc->parent, isrc); } #ifdef SMP static int tegra_lic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); return (PIC_BIND_INTR(sc->parent, isrc)); } #endif static int tegra_lic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); return (BUS_PROBE_DEFAULT); } static int tegra_lic_attach(device_t dev) { struct tegra_lic_sc *sc; phandle_t node; phandle_t parent_xref; int i, rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); rv = OF_getencprop(node, "interrupt-parent", &parent_xref, sizeof(parent_xref)); if (rv <= 0) { device_printf(dev, "Cannot read parent node property\n"); goto fail; } sc->parent = OF_device_from_xref(parent_xref); if (sc->parent == NULL) { device_printf(dev, "Cannott find parent controller\n"); goto fail; } if (bus_alloc_resources(dev, lic_spec, sc->mem_res)) { device_printf(dev, "Cannott allocate resources\n"); goto fail; } /* Disable all interrupts, route all to irq */ for (i = 0; i < nitems(lic_spec); i++) { if (sc->mem_res[i] == NULL) continue; WR4(sc, i, LIC_CPU_IER_CLR, 0xFFFFFFFF); WR4(sc, i, LIC_CPU_IEP_CLASS, 0); } - if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) { + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { device_printf(dev, "Cannot register PIC\n"); goto fail; } return (0); fail: bus_release_resources(dev, lic_spec, sc->mem_res); return (ENXIO); } static int tegra_lic_detach(device_t dev) { struct tegra_lic_sc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < nitems(lic_spec); i++) { if (sc->mem_res[i] == NULL) continue; bus_release_resource(dev, SYS_RES_MEMORY, i, sc->mem_res[i]); } return (0); } static device_method_t tegra_lic_methods[] = { DEVMETHOD(device_probe, tegra_lic_probe), DEVMETHOD(device_attach, tegra_lic_attach), DEVMETHOD(device_detach, tegra_lic_detach), /* Interrupt controller interface */ DEVMETHOD(pic_alloc_intr, tegra_lic_alloc_intr), DEVMETHOD(pic_disable_intr, tegra_lic_disable_intr), DEVMETHOD(pic_enable_intr, tegra_lic_enable_intr), DEVMETHOD(pic_map_intr, tegra_lic_map_intr), DEVMETHOD(pic_release_intr, tegra_lic_release_intr), DEVMETHOD(pic_setup_intr, tegra_lic_setup_intr), DEVMETHOD(pic_teardown_intr, tegra_lic_teardown_intr), DEVMETHOD(pic_pre_ithread, tegra_lic_pre_ithread), DEVMETHOD(pic_post_ithread, tegra_lic_post_ithread), DEVMETHOD(pic_post_filter, tegra_lic_post_filter), #ifdef SMP DEVMETHOD(pic_bind_intr, tegra_lic_bind_intr), #endif DEVMETHOD_END }; devclass_t tegra_lic_devclass; DEFINE_CLASS_0(tegra_lic, tegra_lic_driver, tegra_lic_methods, sizeof(struct tegra_lic_sc)); EARLY_DRIVER_MODULE(tegra_lic, simplebus, tegra_lic_driver, tegra_lic_devclass, NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1); Index: head/sys/arm/ti/aintc.c =================================================================== --- head/sys/arm/ti/aintc.c (revision 300148) +++ head/sys/arm/ti/aintc.c (revision 300149) @@ -1,382 +1,383 @@ /*- * Copyright (c) 2012 Damjan Marion * All rights reserved. * * Based on OMAP3 INTC code by Ben Gray * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INTRNG #include "pic_if.h" #endif #define INTC_REVISION 0x00 #define INTC_SYSCONFIG 0x10 #define INTC_SYSSTATUS 0x14 #define INTC_SIR_IRQ 0x40 #define INTC_CONTROL 0x48 #define INTC_THRESHOLD 0x68 #define INTC_MIR_CLEAR(x) (0x88 + ((x) * 0x20)) #define INTC_MIR_SET(x) (0x8C + ((x) * 0x20)) #define INTC_ISR_SET(x) (0x90 + ((x) * 0x20)) #define INTC_ISR_CLEAR(x) (0x94 + ((x) * 0x20)) #define INTC_SIR_SPURIOUS_MASK 0xffffff80 #define INTC_SIR_ACTIVE_MASK 0x7f #define INTC_NIRQS 128 #ifdef INTRNG struct ti_aintc_irqsrc { struct intr_irqsrc tai_isrc; u_int tai_irq; }; #endif struct ti_aintc_softc { device_t sc_dev; struct resource * aintc_res[3]; bus_space_tag_t aintc_bst; bus_space_handle_t aintc_bsh; uint8_t ver; #ifdef INTRNG struct ti_aintc_irqsrc aintc_isrcs[INTC_NIRQS]; #endif }; static struct resource_spec ti_aintc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static struct ti_aintc_softc *ti_aintc_sc = NULL; #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)) /* List of compatible strings for FDT tree */ static struct ofw_compat_data compat_data[] = { {"ti,am33xx-intc", 1}, {"ti,omap2-intc", 1}, {NULL, 0}, }; #ifdef INTRNG static inline void ti_aintc_irq_eoi(struct ti_aintc_softc *sc) { aintc_write_4(sc, INTC_CONTROL, 1); } static inline void ti_aintc_irq_mask(struct ti_aintc_softc *sc, u_int irq) { aintc_write_4(sc, INTC_MIR_SET(irq >> 5), (1UL << (irq & 0x1F))); } static inline void ti_aintc_irq_unmask(struct ti_aintc_softc *sc, u_int irq) { aintc_write_4(sc, INTC_MIR_CLEAR(irq >> 5), (1UL << (irq & 0x1F))); } static int ti_aintc_intr(void *arg) { uint32_t irq; struct ti_aintc_softc *sc = arg; /* Get active interrupt */ irq = aintc_read_4(sc, INTC_SIR_IRQ); if ((irq & INTC_SIR_SPURIOUS_MASK) != 0) { device_printf(sc->sc_dev, "Spurious interrupt detected (0x%08x)\n", irq); ti_aintc_irq_eoi(sc); return (FILTER_HANDLED); } /* Only level-sensitive interrupts detection is supported. */ irq &= INTC_SIR_ACTIVE_MASK; if (intr_isrc_dispatch(&sc->aintc_isrcs[irq].tai_isrc, curthread->td_intr_frame) != 0) { ti_aintc_irq_mask(sc, irq); ti_aintc_irq_eoi(sc); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } arm_irq_memory_barrier(irq); /* XXX */ return (FILTER_HANDLED); } static void ti_aintc_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq; struct ti_aintc_softc *sc = device_get_softc(dev); arm_irq_memory_barrier(irq); ti_aintc_irq_unmask(sc, irq); } static void ti_aintc_disable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq; struct ti_aintc_softc *sc = device_get_softc(dev); ti_aintc_irq_mask(sc, irq); } static int ti_aintc_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct ti_aintc_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= INTC_NIRQS) return (EINVAL); sc = device_get_softc(dev); *isrcp = &sc->aintc_isrcs[daf->cells[0]].tai_isrc; return (0); } static void ti_aintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq; struct ti_aintc_softc *sc = device_get_softc(dev); ti_aintc_irq_mask(sc, irq); ti_aintc_irq_eoi(sc); } static void ti_aintc_post_ithread(device_t dev, struct intr_irqsrc *isrc) { ti_aintc_enable_intr(dev, isrc); } static void ti_aintc_post_filter(device_t dev, struct intr_irqsrc *isrc) { ti_aintc_irq_eoi(device_get_softc(dev)); } static int ti_aintc_pic_attach(struct ti_aintc_softc *sc) { + struct intr_pic *pic; int error; uint32_t irq; const char *name; intptr_t xref; name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < INTC_NIRQS; irq++) { sc->aintc_isrcs[irq].tai_irq = irq; error = intr_isrc_register(&sc->aintc_isrcs[irq].tai_isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); } xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)); - error = intr_pic_register(sc->sc_dev, xref); - if (error != 0) - return (error); + pic = intr_pic_register(sc->sc_dev, xref); + if (pic == NULL) + return (ENXIO); return (intr_pic_claim_root(sc->sc_dev, xref, ti_aintc_intr, sc, 0)); } #else static void aintc_post_filter(void *arg) { arm_irq_memory_barrier(0); aintc_write_4(ti_aintc_sc, INTC_CONTROL, 1); /* EOI */ } #endif static int ti_aintc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "TI AINTC Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int ti_aintc_attach(device_t dev) { struct ti_aintc_softc *sc = device_get_softc(dev); uint32_t x; sc->sc_dev = dev; if (ti_aintc_sc) return (ENXIO); if (bus_alloc_resources(dev, ti_aintc_spec, sc->aintc_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->aintc_bst = rman_get_bustag(sc->aintc_res[0]); sc->aintc_bsh = rman_get_bushandle(sc->aintc_res[0]); ti_aintc_sc = sc; x = aintc_read_4(sc, INTC_REVISION); device_printf(dev, "Revision %u.%u\n",(x >> 4) & 0xF, x & 0xF); /* SoftReset */ aintc_write_4(sc, INTC_SYSCONFIG, 2); /* Wait for reset to complete */ while(!(aintc_read_4(sc, INTC_SYSSTATUS) & 1)); /*Set Priority Threshold */ aintc_write_4(sc, INTC_THRESHOLD, 0xFF); #ifndef INTRNG arm_post_filter = aintc_post_filter; #else if (ti_aintc_pic_attach(sc) != 0) { device_printf(dev, "could not attach PIC\n"); return (ENXIO); } #endif return (0); } static device_method_t ti_aintc_methods[] = { DEVMETHOD(device_probe, ti_aintc_probe), DEVMETHOD(device_attach, ti_aintc_attach), #ifdef INTRNG DEVMETHOD(pic_disable_intr, ti_aintc_disable_intr), DEVMETHOD(pic_enable_intr, ti_aintc_enable_intr), DEVMETHOD(pic_map_intr, ti_aintc_map_intr), DEVMETHOD(pic_post_filter, ti_aintc_post_filter), DEVMETHOD(pic_post_ithread, ti_aintc_post_ithread), DEVMETHOD(pic_pre_ithread, ti_aintc_pre_ithread), #endif { 0, 0 } }; static driver_t ti_aintc_driver = { "aintc", ti_aintc_methods, sizeof(struct ti_aintc_softc), }; static devclass_t ti_aintc_devclass; EARLY_DRIVER_MODULE(aintc, simplebus, ti_aintc_driver, ti_aintc_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); SIMPLEBUS_PNP_INFO(compat_data); #ifndef INTRNG int arm_get_next_irq(int last_irq) { struct ti_aintc_softc *sc = ti_aintc_sc; uint32_t active_irq; /* Get the next active interrupt */ active_irq = aintc_read_4(sc, INTC_SIR_IRQ); /* Check for spurious interrupt */ if ((active_irq & 0xffffff80)) { device_printf(sc->sc_dev, "Spurious interrupt detected (0x%08x)\n", active_irq); aintc_write_4(sc, INTC_SIR_IRQ, 0); return -1; } if (active_irq != last_irq) return active_irq; else return -1; } void arm_mask_irq(uintptr_t nb) { struct ti_aintc_softc *sc = ti_aintc_sc; aintc_write_4(sc, INTC_MIR_SET(nb >> 5), (1UL << (nb & 0x1F))); aintc_write_4(sc, INTC_CONTROL, 1); /* EOI */ } void arm_unmask_irq(uintptr_t nb) { struct ti_aintc_softc *sc = ti_aintc_sc; arm_irq_memory_barrier(nb); aintc_write_4(sc, INTC_MIR_CLEAR(nb >> 5), (1UL << (nb & 0x1F))); } #endif Index: head/sys/arm/ti/omap4/omap4_wugen.c =================================================================== --- head/sys/arm/ti/omap4/omap4_wugen.c (revision 300148) +++ head/sys/arm/ti/omap4/omap4_wugen.c (revision 300149) @@ -1,250 +1,250 @@ /*- * Copyright (c) 2016 Svatopluk Kraus * Copyright (c) 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" static struct ofw_compat_data compat_data[] = { {"ti,omap4-wugen-mpu", 1}, {NULL, 0} }; struct omap4_wugen_sc { device_t sc_dev; struct resource *sc_mem_res; device_t sc_parent; }; static int omap4_wugen_alloc_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct omap4_wugen_sc *sc = device_get_softc(dev); return (PIC_ALLOC_INTR(sc->sc_parent, isrc, res, data)); } static void omap4_wugen_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); PIC_DISABLE_INTR(sc->sc_parent, isrc); } static void omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); PIC_ENABLE_INTR(sc->sc_parent, isrc); } static int omap4_wugen_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct omap4_wugen_sc *sc = device_get_softc(dev); return (PIC_MAP_INTR(sc->sc_parent, data, isrcp)); } static int omap4_wugen_release_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct omap4_wugen_sc *sc = device_get_softc(dev); return (PIC_RELEASE_INTR(sc->sc_parent, isrc, res, data)); } static int omap4_wugen_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct omap4_wugen_sc *sc = device_get_softc(dev); return (PIC_SETUP_INTR(sc->sc_parent, isrc, res, data)); } static int omap4_wugen_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct omap4_wugen_sc *sc = device_get_softc(dev); return (PIC_TEARDOWN_INTR(sc->sc_parent, isrc, res, data)); } static void omap4_wugen_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); PIC_PRE_ITHREAD(sc->sc_parent, isrc); } static void omap4_wugen_post_ithread(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); PIC_POST_ITHREAD(sc->sc_parent, isrc); } static void omap4_wugen_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); PIC_POST_FILTER(sc->sc_parent, isrc); } #ifdef SMP static int omap4_wugen_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); return (PIC_BIND_INTR(sc->sc_parent, isrc)); } #endif static int omap4_wugen_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); return (BUS_PROBE_DEFAULT); } static int omap4_wugen_detach(device_t dev) { struct omap4_wugen_sc *sc; sc = device_get_softc(dev); if (sc->sc_mem_res != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); sc->sc_mem_res = NULL; } return (0); } static int omap4_wugen_attach(device_t dev) { struct omap4_wugen_sc *sc; phandle_t node; phandle_t parent_xref; int rid, rv; sc = device_get_softc(dev); sc->sc_dev = dev; node = ofw_bus_get_node(dev); rv = OF_getencprop(node, "interrupt-parent", &parent_xref, sizeof(parent_xref)); if (rv <= 0) { device_printf(dev, "can't read parent node property\n"); goto fail; } sc->sc_parent = OF_device_from_xref(parent_xref); if (sc->sc_parent == NULL) { device_printf(dev, "can't find parent controller\n"); goto fail; } rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "can't allocate resources\n"); return (ENXIO); } - if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) { + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { device_printf(dev, "can't register PIC\n"); goto fail; } return (0); fail: omap4_wugen_detach(dev); return (ENXIO); } static device_method_t omap4_wugen_methods[] = { DEVMETHOD(device_probe, omap4_wugen_probe), DEVMETHOD(device_attach, omap4_wugen_attach), DEVMETHOD(device_detach, omap4_wugen_detach), /* Interrupt controller interface */ DEVMETHOD(pic_alloc_intr, omap4_wugen_alloc_intr), DEVMETHOD(pic_disable_intr, omap4_wugen_disable_intr), DEVMETHOD(pic_enable_intr, omap4_wugen_enable_intr), DEVMETHOD(pic_map_intr, omap4_wugen_map_intr), DEVMETHOD(pic_release_intr, omap4_wugen_release_intr), DEVMETHOD(pic_setup_intr, omap4_wugen_setup_intr), DEVMETHOD(pic_teardown_intr, omap4_wugen_teardown_intr), DEVMETHOD(pic_pre_ithread, omap4_wugen_pre_ithread), DEVMETHOD(pic_post_ithread, omap4_wugen_post_ithread), DEVMETHOD(pic_post_filter, omap4_wugen_post_filter), #ifdef SMP DEVMETHOD(pic_bind_intr, omap4_wugen_bind_intr), #endif DEVMETHOD_END }; devclass_t omap4_wugen_devclass; DEFINE_CLASS_0(omap4_wugen, omap4_wugen_driver, omap4_wugen_methods, sizeof(struct omap4_wugen_sc)); EARLY_DRIVER_MODULE(omap4_wugen, simplebus, omap4_wugen_driver, omap4_wugen_devclass, NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1); Index: head/sys/arm/ti/ti_gpio.c =================================================================== --- head/sys/arm/ti/ti_gpio.c (revision 300148) +++ head/sys/arm/ti/ti_gpio.c (revision 300149) @@ -1,1376 +1,1379 @@ /*- * Copyright (c) 2011 Ben Gray . * Copyright (c) 2014 Luiz Otavio O Souza . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /** * Beware that the OMAP4 datasheet(s) lists GPIO banks 1-6, whereas the code * here uses 0-5. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "ti_gpio_if.h" #ifdef INTRNG #include "pic_if.h" #endif #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X) #error "Unknown SoC" #endif /* Register definitions */ #define TI_GPIO_REVISION 0x0000 #define TI_GPIO_SYSCONFIG 0x0010 #define TI_GPIO_IRQSTATUS_RAW_0 0x0024 #define TI_GPIO_IRQSTATUS_RAW_1 0x0028 #define TI_GPIO_IRQSTATUS_0 0x002C /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_1 0x0030 /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_SET_0 0x0034 /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_SET_1 0x0038 /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_CLR_0 0x003C /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_CLR_1 0x0040 /* writing a 0 has no effect */ #define TI_GPIO_IRQWAKEN_0 0x0044 #define TI_GPIO_IRQWAKEN_1 0x0048 #define TI_GPIO_SYSSTATUS 0x0114 #define TI_GPIO_IRQSTATUS1 0x0118 #define TI_GPIO_IRQENABLE1 0x011C #define TI_GPIO_WAKEUPENABLE 0x0120 #define TI_GPIO_IRQSTATUS2 0x0128 #define TI_GPIO_IRQENABLE2 0x012C #define TI_GPIO_CTRL 0x0130 #define TI_GPIO_OE 0x0134 #define TI_GPIO_DATAIN 0x0138 #define TI_GPIO_DATAOUT 0x013C #define TI_GPIO_LEVELDETECT0 0x0140 /* RW register */ #define TI_GPIO_LEVELDETECT1 0x0144 /* RW register */ #define TI_GPIO_RISINGDETECT 0x0148 /* RW register */ #define TI_GPIO_FALLINGDETECT 0x014C /* RW register */ #define TI_GPIO_DEBOUNCENABLE 0x0150 #define TI_GPIO_DEBOUNCINGTIME 0x0154 #define TI_GPIO_CLEARWKUPENA 0x0180 #define TI_GPIO_SETWKUENA 0x0184 #define TI_GPIO_CLEARDATAOUT 0x0190 #define TI_GPIO_SETDATAOUT 0x0194 /* Other SoC Specific definitions */ #define OMAP4_FIRST_GPIO_BANK 1 #define OMAP4_INTR_PER_BANK 1 #define OMAP4_GPIO_REV 0x50600801 #define AM335X_FIRST_GPIO_BANK 0 #define AM335X_INTR_PER_BANK 2 #define AM335X_GPIO_REV 0x50600801 #define PINS_PER_BANK 32 #define TI_GPIO_MASK(p) (1U << ((p) % PINS_PER_BANK)) static int ti_gpio_intr(void *arg); static int ti_gpio_detach(device_t); #ifdef INTRNG static int ti_gpio_pic_attach(struct ti_gpio_softc *sc); static int ti_gpio_pic_detach(struct ti_gpio_softc *sc); #endif static u_int ti_first_gpio_bank(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_FIRST_GPIO_BANK); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_FIRST_GPIO_BANK); #endif } return (0); } static uint32_t ti_gpio_rev(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_GPIO_REV); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_GPIO_REV); #endif } return (0); } /** * Macros for driver mutex locking */ #define TI_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define TI_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define TI_GPIO_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ "ti_gpio", MTX_SPIN) #define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) /** * ti_gpio_read_4 - reads a 32-bit value from one of the GPIO registers * @sc: GPIO device context * @bank: The bank to read from * @off: The offset of a register from the GPIO register address range * * * RETURNS: * 32-bit value read from the register. */ static inline uint32_t ti_gpio_read_4(struct ti_gpio_softc *sc, bus_size_t off) { return (bus_read_4(sc->sc_mem_res, off)); } /** * ti_gpio_write_4 - writes a 32-bit value to one of the GPIO registers * @sc: GPIO device context * @bank: The bank to write to * @off: The offset of a register from the GPIO register address range * @val: The value to write into the register * * RETURNS: * nothing */ static inline void ti_gpio_write_4(struct ti_gpio_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res, off, val); } static inline void ti_gpio_intr_clr(struct ti_gpio_softc *sc, uint32_t mask) { /* We clear both set of registers. */ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_0, mask); ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_1, mask); } static inline void ti_gpio_intr_set(struct ti_gpio_softc *sc, uint32_t mask) { /* * On OMAP4 we unmask only the MPU interrupt and on AM335x we * also activate only the first interrupt. */ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_SET_0, mask); } static inline void ti_gpio_intr_ack(struct ti_gpio_softc *sc, uint32_t mask) { /* * Acknowledge the interrupt on both registers even if we use only * the first one. */ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_0, mask); ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_1, mask); } static inline uint32_t ti_gpio_intr_status(struct ti_gpio_softc *sc) { uint32_t reg; /* Get the status from both registers. */ reg = ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_0); reg |= ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_1); return (reg); } static device_t ti_gpio_get_bus(device_t dev) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } /** * ti_gpio_pin_max - Returns the maximum number of GPIO pins * @dev: gpio device handle * @maxpin: pointer to a value that upon return will contain the maximum number * of pins in the device. * * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = PINS_PER_BANK - 1; return (0); } static int ti_gpio_valid_pin(struct ti_gpio_softc *sc, int pin) { if (pin >= sc->sc_maxpin || sc->sc_mem_res == NULL) return (EINVAL); return (0); } /** * ti_gpio_pin_getcaps - Gets the capabilities of a given pin * @dev: gpio device handle * @pin: the number of the pin * @caps: pointer to a value that upon return will contain the capabilities * * Currently all pins have the same capability, notably: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * - GPIO_INTR_LEVEL_LOW * - GPIO_INTR_LEVEL_HIGH * - GPIO_INTR_EDGE_RISING * - GPIO_INTR_EDGE_FALLING * - GPIO_INTR_EDGE_BOTH * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); #ifdef INTRNG *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH); #else *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); #endif return (0); } /** * ti_gpio_pin_getflags - Gets the current flags of a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: upon return will contain the current flags of the pin * * Reads the current flags of a given pin, here we actually read the H/W * registers to determine the flags, rather than storing the value in the * setflags call. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Get the current pin state */ TI_GPIO_LOCK(sc); TI_GPIO_GET_FLAGS(dev, pin, flags); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_getname - Gets the name of a given pin * @dev: gpio device handle * @pin: the number of the pin * @name: buffer to put the name in * * The driver simply calls the pins gpio_n, where 'n' is obviously the number * of the pin. * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Set a very simple name */ snprintf(name, GPIOMAXNAME, "gpio_%u", pin); name[GPIOMAXNAME - 1] = '\0'; return (0); } /** * ti_gpio_pin_setflags - Sets the flags for a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: the flags to set * * The flags of the pin correspond to things like input/output mode, pull-ups, * pull-downs, etc. This driver doesn't support all flags, only the following: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct ti_gpio_softc *sc; uint32_t oe; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Set the GPIO mode and state */ TI_GPIO_LOCK(sc); if (TI_GPIO_SET_FLAGS(dev, pin, flags) != 0) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* If configuring as an output set the "output enable" bit */ oe = ti_gpio_read_4(sc, TI_GPIO_OE); if (flags & GPIO_PIN_INPUT) oe |= TI_GPIO_MASK(pin); else oe &= ~TI_GPIO_MASK(pin); ti_gpio_write_4(sc, TI_GPIO_OE, oe); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_set - Sets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: non-zero value will drive the pin high, otherwise the pin is * driven low. * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct ti_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); TI_GPIO_LOCK(sc); if (value == GPIO_PIN_LOW) reg = TI_GPIO_CLEARDATAOUT; else reg = TI_GPIO_SETDATAOUT; ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin)); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_get - Gets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: pointer to a value that upond return will contain the pin value * * The pin must be configured as an input pin beforehand, otherwise this * function will fail. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) { struct ti_gpio_softc *sc; uint32_t oe, reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* * Return data from output latch when set as output and from the * input register otherwise. */ TI_GPIO_LOCK(sc); oe = ti_gpio_read_4(sc, TI_GPIO_OE); if (oe & TI_GPIO_MASK(pin)) reg = TI_GPIO_DATAIN; else reg = TI_GPIO_DATAOUT; val = ti_gpio_read_4(sc, reg); *value = (val & TI_GPIO_MASK(pin)) ? 1 : 0; TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_toggle - Toggles a given GPIO pin * @dev: gpio device handle * @pin: the number of the pin * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_toggle(device_t dev, uint32_t pin) { struct ti_gpio_softc *sc; uint32_t reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Toggle the pin */ TI_GPIO_LOCK(sc); val = ti_gpio_read_4(sc, TI_GPIO_DATAOUT); if (val & TI_GPIO_MASK(pin)) reg = TI_GPIO_CLEARDATAOUT; else reg = TI_GPIO_SETDATAOUT; ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin)); TI_GPIO_UNLOCK(sc); return (0); } #ifndef INTRNG /** * ti_gpio_intr - ISR for all GPIO modules * @arg: the soft context pointer * * LOCKING: * Internally locks the context * */ static int ti_gpio_intr(void *arg) { int bank_last, irq; struct intr_event *event; struct ti_gpio_softc *sc; uint32_t reg; sc = (struct ti_gpio_softc *)arg; bank_last = -1; reg = 0; /* squelch bogus gcc warning */ reg = ti_gpio_intr_status(sc); for (irq = 0; irq < sc->sc_maxpin; irq++) { if ((reg & TI_GPIO_MASK(irq)) == 0) continue; event = sc->sc_events[irq]; if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers)) intr_event_handle(event, NULL); else device_printf(sc->sc_dev, "Stray IRQ %d\n", irq); /* Ack the IRQ Status bit. */ ti_gpio_intr_ack(sc, TI_GPIO_MASK(irq)); } return (FILTER_HANDLED); } #endif static int ti_gpio_bank_init(device_t dev) { int pin; struct ti_gpio_softc *sc; uint32_t flags, reg_oe, reg_set, rev; clk_ident_t clk; sc = device_get_softc(dev); /* Enable the interface and functional clocks for the module. */ clk = ti_hwmods_get_clock(dev); if (clk == INVALID_CLK_IDENT) { device_printf(dev, "failed to get device id based on ti,hwmods\n"); return (EINVAL); } sc->sc_bank = clk - GPIO1_CLK + ti_first_gpio_bank(); ti_prcm_clk_enable(clk); /* * Read the revision number of the module. TI don't publish the * actual revision numbers, so instead the values have been * determined by experimentation. */ rev = ti_gpio_read_4(sc, TI_GPIO_REVISION); /* Check the revision. */ if (rev != ti_gpio_rev()) { device_printf(dev, "Warning: could not determine the revision " "of GPIO module (revision:0x%08x)\n", rev); return (EINVAL); } /* Disable interrupts for all pins. */ ti_gpio_intr_clr(sc, 0xffffffff); /* Init OE register based on pads configuration. */ reg_oe = 0xffffffff; reg_set = 0; for (pin = 0; pin < PINS_PER_BANK; pin++) { TI_GPIO_GET_FLAGS(dev, pin, &flags); if (flags & GPIO_PIN_OUTPUT) { reg_oe &= ~(1UL << pin); if (flags & GPIO_PIN_PULLUP) reg_set |= (1UL << pin); } } ti_gpio_write_4(sc, TI_GPIO_OE, reg_oe); if (reg_set) ti_gpio_write_4(sc, TI_GPIO_SETDATAOUT, reg_set); return (0); } /** * ti_gpio_attach - attach function for the driver * @dev: gpio device handle * * Allocates and sets up the driver context for all GPIO banks. This function * expects the memory ranges and IRQs to already be allocated to the driver. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_attach(device_t dev) { struct ti_gpio_softc *sc; #ifndef INTRNG unsigned int i; #endif int err; sc = device_get_softc(dev); sc->sc_dev = dev; TI_GPIO_LOCK_INIT(sc); ti_gpio_pin_max(dev, &sc->sc_maxpin); sc->sc_maxpin++; sc->sc_mem_rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_mem_rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "Error: could not allocate mem resources\n"); ti_gpio_detach(dev); return (ENXIO); } sc->sc_irq_rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE); if (!sc->sc_irq_res) { device_printf(dev, "Error: could not allocate irq resources\n"); ti_gpio_detach(dev); return (ENXIO); } /* * Register our interrupt filter for each of the IRQ resources. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc, &sc->sc_irq_hdl) != 0) { device_printf(dev, "WARNING: unable to register interrupt filter\n"); ti_gpio_detach(dev); return (ENXIO); } #ifdef INTRNG if (ti_gpio_pic_attach(sc) != 0) { device_printf(dev, "WARNING: unable to attach PIC\n"); ti_gpio_detach(dev); return (ENXIO); } #else /* * Initialize the interrupt settings. The default is active-low * interrupts. */ sc->sc_irq_trigger = malloc( sizeof(*sc->sc_irq_trigger) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); sc->sc_irq_polarity = malloc( sizeof(*sc->sc_irq_polarity) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0; i < sc->sc_maxpin; i++) { sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; } sc->sc_events = malloc(sizeof(struct intr_event *) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); sc->sc_mask_args = malloc(sizeof(struct ti_gpio_mask_arg) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); #endif /* We need to go through each block and ensure the clocks are running and * the module is enabled. It might be better to do this only when the * pins are configured which would result in less power used if the GPIO * pins weren't used ... */ if (sc->sc_mem_res != NULL) { /* Initialize the GPIO module. */ err = ti_gpio_bank_init(dev); if (err != 0) { ti_gpio_detach(dev); return (err); } } sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { ti_gpio_detach(dev); return (ENXIO); } return (0); } /** * ti_gpio_detach - detach function for the driver * @dev: scm device handle * * Allocates and sets up the driver context, this simply entails creating a * bus mappings for the SCM register set. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_detach(device_t dev) { struct ti_gpio_softc *sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); /* Disable all interrupts */ if (sc->sc_mem_res != NULL) ti_gpio_intr_clr(sc, 0xffffffff); gpiobus_detach_bus(dev); #ifdef INTRNG if (sc->sc_isrcs != NULL) ti_gpio_pic_detach(sc); #else if (sc->sc_events) free(sc->sc_events, M_DEVBUF); if (sc->sc_mask_args) free(sc->sc_mask_args, M_DEVBUF); if (sc->sc_irq_polarity) free(sc->sc_irq_polarity, M_DEVBUF); if (sc->sc_irq_trigger) free(sc->sc_irq_trigger, M_DEVBUF); #endif /* Release the memory and IRQ resources. */ if (sc->sc_irq_hdl) { bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdl); } bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, sc->sc_mem_res); TI_GPIO_LOCK_DESTROY(sc); return (0); } #ifdef INTRNG static inline void ti_gpio_rwreg_modify(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask, bool set_bits) { uint32_t value; value = ti_gpio_read_4(sc, reg); ti_gpio_write_4(sc, reg, set_bits ? value | mask : value & ~mask); } static inline void ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) { /* Writing a 0 has no effect. */ ti_gpio_intr_clr(sc, tgi->tgi_mask); } static inline void ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) { /* Writing a 0 has no effect. */ ti_gpio_intr_set(sc, tgi->tgi_mask); } static inline void ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) { /* Writing a 0 has no effect. */ ti_gpio_intr_ack(sc, tgi->tgi_mask); } static inline bool ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi) { return (tgi->tgi_mode == GPIO_INTR_LEVEL_LOW || tgi->tgi_mode == GPIO_INTR_LEVEL_HIGH); } static int ti_gpio_intr(void *arg) { u_int irq; uint32_t reg; struct ti_gpio_softc *sc; struct trapframe *tf; struct ti_gpio_irqsrc *tgi; sc = (struct ti_gpio_softc *)arg; tf = curthread->td_intr_frame; reg = ti_gpio_intr_status(sc); for (irq = 0; irq < sc->sc_maxpin; irq++) { tgi = &sc->sc_isrcs[irq]; if ((reg & tgi->tgi_mask) == 0) continue; if (!ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) { ti_gpio_isrc_mask(sc, tgi); if (ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } } return (FILTER_HANDLED); } static int ti_gpio_pic_attach(struct ti_gpio_softc *sc) { int error; uint32_t irq; const char *name; sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < sc->sc_maxpin; irq++) { sc->sc_isrcs[irq].tgi_irq = irq; sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq); sc->sc_isrcs[irq].tgi_mode = GPIO_INTR_CONFORM; error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); /* XXX deregister ISRCs */ } - return (intr_pic_register(sc->sc_dev, - OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)))); + if (intr_pic_register(sc->sc_dev, + OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) + return (ENXIO); + + return (0); } static int ti_gpio_pic_detach(struct ti_gpio_softc *sc) { /* * There has not been established any procedure yet * how to detach PIC from living system correctly. */ device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); return (EBUSY); } static void ti_gpio_pic_config_intr(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi, uint32_t mode) { TI_GPIO_LOCK(sc); ti_gpio_rwreg_modify(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask, mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); ti_gpio_rwreg_modify(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask, mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask, mode == GPIO_INTR_LEVEL_HIGH); ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask, mode == GPIO_INTR_LEVEL_LOW); tgi->tgi_mode = mode; TI_GPIO_UNLOCK(sc); } static void ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; ti_gpio_isrc_mask(sc, tgi); } static void ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; arm_irq_memory_barrier(tgi->tgi_irq); ti_gpio_isrc_unmask(sc, tgi); } static int ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, struct intr_map_data_fdt *daf, u_int *irqp, uint32_t *modep) { uint32_t mode; /* * The first cell is the interrupt number. * The second cell is used to specify flags: * bits[3:0] trigger type and level flags: * 1 = low-to-high edge triggered. * 2 = high-to-low edge triggered. * 4 = active high level-sensitive. * 8 = active low level-sensitive. */ if (daf->ncells != 2 || daf->cells[0] >= sc->sc_maxpin) return (EINVAL); /* Only reasonable modes are supported. */ if (daf->cells[1] == 1) mode = GPIO_INTR_EDGE_RISING; else if (daf->cells[1] == 2) mode = GPIO_INTR_EDGE_FALLING; else if (daf->cells[1] == 3) mode = GPIO_INTR_EDGE_BOTH; else if (daf->cells[1] == 4) mode = GPIO_INTR_LEVEL_HIGH; else if (daf->cells[1] == 8) mode = GPIO_INTR_LEVEL_LOW; else return (EINVAL); *irqp = daf->cells[0]; if (modep != NULL) *modep = mode; return (0); } static int ti_gpio_pic_map_gpio(struct ti_gpio_softc *sc, struct intr_map_data_gpio *dag, u_int *irqp, uint32_t *modep) { uint32_t mode; if (dag->gpio_pin_num >= sc->sc_maxpin) return (EINVAL); mode = dag->gpio_intr_mode; if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && mode != GPIO_INTR_EDGE_BOTH) return (EINVAL); *irqp = dag->gpio_pin_num; if (modep != NULL) *modep = mode; return (0); } static int ti_gpio_pic_map(struct ti_gpio_softc *sc, struct intr_map_data *data, u_int *irqp, uint32_t *modep) { switch (data->type) { case INTR_MAP_DATA_FDT: return (ti_gpio_pic_map_fdt(sc, (struct intr_map_data_fdt *)data, irqp, modep)); case INTR_MAP_DATA_GPIO: return (ti_gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data, irqp, modep)); default: return (ENOTSUP); } } static int ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { int error; u_int irq; struct ti_gpio_softc *sc = device_get_softc(dev); error = ti_gpio_pic_map(sc, data, &irq, NULL); if (error == 0) *isrcp = &sc->sc_isrcs[irq].tgi_isrc; return (error); } static void ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; if (ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); } static void ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { ti_gpio_pic_enable_intr(dev, isrc); } static void ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; ti_gpio_isrc_mask(sc, tgi); if (ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); } static int ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { u_int irq; uint32_t mode; struct ti_gpio_softc *sc; struct ti_gpio_irqsrc *tgi; if (data == NULL) return (ENOTSUP); sc = device_get_softc(dev); tgi = (struct ti_gpio_irqsrc *)isrc; /* Get and check config for an interrupt. */ if (ti_gpio_pic_map(sc, data, &irq, &mode) != 0 || tgi->tgi_irq != irq) return (EINVAL); /* * If this is a setup for another handler, * only check that its configuration match. */ if (isrc->isrc_handlers != 0) return (tgi->tgi_mode == mode ? 0 : EINVAL); ti_gpio_pic_config_intr(sc, tgi, mode); return (0); } static int ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; if (isrc->isrc_handlers == 0) ti_gpio_pic_config_intr(sc, tgi, GPIO_INTR_CONFORM); return (0); } #else static uint32_t ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq) { if (ti_gpio_valid_pin(sc, irq) != 0) return (0); if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) { if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) return (TI_GPIO_LEVELDETECT0); else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) return (TI_GPIO_LEVELDETECT1); } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) { if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) return (TI_GPIO_FALLINGDETECT); else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) return (TI_GPIO_RISINGDETECT); } return (0); } static void ti_gpio_mask_irq_internal(struct ti_gpio_softc *sc, int irq) { uint32_t reg, val; if (ti_gpio_valid_pin(sc, irq) != 0) return; TI_GPIO_LOCK(sc); ti_gpio_intr_clr(sc, TI_GPIO_MASK(irq)); reg = ti_gpio_intr_reg(sc, irq); if (reg != 0) { val = ti_gpio_read_4(sc, reg); val &= ~TI_GPIO_MASK(irq); ti_gpio_write_4(sc, reg, val); } TI_GPIO_UNLOCK(sc); } static void ti_gpio_unmask_irq_internal(struct ti_gpio_softc *sc, int irq) { uint32_t reg, val; if (ti_gpio_valid_pin(sc, irq) != 0) return; TI_GPIO_LOCK(sc); reg = ti_gpio_intr_reg(sc, irq); if (reg != 0) { val = ti_gpio_read_4(sc, reg); val |= TI_GPIO_MASK(irq); ti_gpio_write_4(sc, reg, val); ti_gpio_intr_set(sc, TI_GPIO_MASK(irq)); } TI_GPIO_UNLOCK(sc); } static void ti_gpio_mask_irq(void *source) { struct ti_gpio_mask_arg *arg = source; ti_gpio_mask_irq_internal(arg->softc, arg->pin); } static void ti_gpio_unmask_irq(void *source) { struct ti_gpio_mask_arg *arg = source; ti_gpio_unmask_irq_internal(arg->softc, arg->pin); } static int ti_gpio_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { struct ti_gpio_mask_arg mask_arg; if (type != SYS_RES_IRQ) return (ENXIO); /* Unmask the interrupt. */ mask_arg.pin = rman_get_start(res); mask_arg.softc = device_get_softc(dev); ti_gpio_unmask_irq((void *)&mask_arg); return (0); } static int ti_gpio_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { int pin; if (type != SYS_RES_IRQ) return (ENXIO); /* Mask the interrupt. */ pin = rman_get_start(res); ti_gpio_mask_irq((void *)(uintptr_t)pin); return (0); } static int ti_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { struct ti_gpio_softc *sc; uint32_t oldreg, reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, irq) != 0) return (EINVAL); /* There is no standard trigger or polarity. */ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM) return (EINVAL); TI_GPIO_LOCK(sc); /* * TRM recommends add the new event before remove the old one to * avoid losing interrupts. */ oldreg = ti_gpio_intr_reg(sc, irq); sc->sc_irq_trigger[irq] = trig; sc->sc_irq_polarity[irq] = pol; reg = ti_gpio_intr_reg(sc, irq); if (reg != 0) { /* Apply the new settings. */ val = ti_gpio_read_4(sc, reg); val |= TI_GPIO_MASK(irq); ti_gpio_write_4(sc, reg, val); } if (reg != oldreg && oldreg != 0) { /* Remove the old settings. */ val = ti_gpio_read_4(sc, oldreg); val &= ~TI_GPIO_MASK(irq); ti_gpio_write_4(sc, oldreg, val); } TI_GPIO_UNLOCK(sc); return (0); } static int ti_gpio_setup_intr(device_t dev, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct ti_gpio_softc *sc; struct intr_event *event; int pin, error; sc = device_get_softc(dev); pin = rman_get_start(ires); if (ti_gpio_valid_pin(sc, pin) != 0) panic("%s: bad pin %d", __func__, pin); event = sc->sc_events[pin]; if (event == NULL) { sc->sc_mask_args[pin].softc = sc; sc->sc_mask_args[pin].pin = pin; error = intr_event_create(&event, (void *)&sc->sc_mask_args[pin], 0, pin, ti_gpio_mask_irq, ti_gpio_unmask_irq, NULL, NULL, "gpio%d pin%d:", device_get_unit(dev), pin); if (error != 0) return (error); sc->sc_events[pin] = event; } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); return (0); } static int ti_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct ti_gpio_softc *sc; int pin, err; sc = device_get_softc(dev); pin = rman_get_start(ires); if (ti_gpio_valid_pin(sc, pin) != 0) panic("%s: bad pin %d", __func__, pin); if (sc->sc_events[pin] == NULL) panic("Trying to teardown unoccupied IRQ"); err = intr_event_remove_handler(cookie); if (!err) sc->sc_events[pin] = NULL; return (err); } #endif static phandle_t ti_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_gpio_methods[] = { DEVMETHOD(device_attach, ti_gpio_attach), DEVMETHOD(device_detach, ti_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, ti_gpio_get_bus), DEVMETHOD(gpio_pin_max, ti_gpio_pin_max), DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, ti_gpio_pin_get), DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), #ifdef INTRNG /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, ti_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, ti_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, ti_gpio_pic_map_intr), DEVMETHOD(pic_setup_intr, ti_gpio_pic_setup_intr), DEVMETHOD(pic_teardown_intr, ti_gpio_pic_teardown_intr), DEVMETHOD(pic_post_filter, ti_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, ti_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, ti_gpio_pic_pre_ithread), #else /* Bus interface */ DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource), DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource), DEVMETHOD(bus_config_intr, ti_gpio_config_intr), DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr), DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr), #endif /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node), {0, 0}, }; driver_t ti_gpio_driver = { "gpio", ti_gpio_methods, sizeof(struct ti_gpio_softc), }; Index: head/sys/arm64/arm64/gic_v3_fdt.c =================================================================== --- head/sys/arm64/arm64/gic_v3_fdt.c (revision 300148) +++ head/sys/arm64/arm64/gic_v3_fdt.c (revision 300149) @@ -1,330 +1,330 @@ /*- * Copyright (c) 2015 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under * the sponsorship of the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "gic_v3_reg.h" #include "gic_v3_var.h" /* * FDT glue. */ static int gic_v3_fdt_probe(device_t); static int gic_v3_fdt_attach(device_t); static struct resource *gic_v3_ofw_bus_alloc_res(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static const struct ofw_bus_devinfo *gic_v3_ofw_get_devinfo(device_t, device_t); static device_method_t gic_v3_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gic_v3_fdt_probe), DEVMETHOD(device_attach, gic_v3_fdt_attach), /* Bus interface */ DEVMETHOD(bus_alloc_resource, gic_v3_ofw_bus_alloc_res), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, gic_v3_ofw_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), /* End */ DEVMETHOD_END }; DEFINE_CLASS_1(gic, gic_v3_fdt_driver, gic_v3_fdt_methods, sizeof(struct gic_v3_softc), gic_v3_driver); static devclass_t gic_v3_fdt_devclass; EARLY_DRIVER_MODULE(gic_v3, simplebus, gic_v3_fdt_driver, gic_v3_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(gic_v3, ofwbus, gic_v3_fdt_driver, gic_v3_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); /* * Helper functions declarations. */ static int gic_v3_ofw_bus_attach(device_t); /* * Device interface. */ static int gic_v3_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "arm,gic-v3")) return (ENXIO); device_set_desc(dev, GIC_V3_DEVSTR); return (BUS_PROBE_DEFAULT); } static int gic_v3_fdt_attach(device_t dev) { struct gic_v3_softc *sc; pcell_t redist_regions; #ifdef INTRNG intptr_t xref; #endif int err; sc = device_get_softc(dev); sc->dev = dev; /* * Recover number of the Re-Distributor regions. */ if (OF_getencprop(ofw_bus_get_node(dev), "#redistributor-regions", &redist_regions, sizeof(redist_regions)) <= 0) sc->gic_redists.nregions = 1; else sc->gic_redists.nregions = redist_regions; err = gic_v3_attach(dev); if (err != 0) goto error; #ifdef INTRNG xref = OF_xref_from_node(ofw_bus_get_node(dev)); - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto error; } if (intr_pic_claim_root(dev, xref, arm_gic_v3_intr, sc, GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { goto error; } #endif /* * Try to register ITS to this GIC. * GIC will act as a bus in that case. * Failure here will not affect main GIC functionality. */ if (gic_v3_ofw_bus_attach(dev) != 0) { if (bootverbose) { device_printf(dev, "Failed to attach ITS to this GIC\n"); } } return (err); error: if (bootverbose) { device_printf(dev, "Failed to attach. Error %d\n", err); } /* Failure so free resources */ gic_v3_detach(dev); - return (err); + return (ENXIO); } /* OFW bus interface */ struct gic_v3_ofw_devinfo { struct ofw_bus_devinfo di_dinfo; struct resource_list di_rl; }; static const struct ofw_bus_devinfo * gic_v3_ofw_get_devinfo(device_t bus __unused, device_t child) { struct gic_v3_ofw_devinfo *di; di = device_get_ivars(child); return (&di->di_dinfo); } static struct resource * gic_v3_ofw_bus_alloc_res(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct gic_v3_ofw_devinfo *di; struct resource_list_entry *rle; int ranges_len; if (RMAN_IS_DEFAULT_RANGE(start, end)) { if ((di = device_get_ivars(child)) == NULL) return (NULL); if (type != SYS_RES_MEMORY) return (NULL); /* Find defaults for this rid */ rle = resource_list_find(&di->di_rl, type, *rid); if (rle == NULL) return (NULL); start = rle->start; end = rle->end; count = rle->count; } /* * XXX: No ranges remap! * Absolute address is expected. */ if (ofw_bus_has_prop(bus, "ranges")) { ranges_len = OF_getproplen(ofw_bus_get_node(bus), "ranges"); if (ranges_len != 0) { if (bootverbose) { device_printf(child, "Ranges remap not supported\n"); } return (NULL); } } return (bus_generic_alloc_resource(bus, child, type, rid, start, end, count, flags)); } /* Helper functions */ /* * Bus capability support for GICv3. * Collects and configures device informations and finally * adds ITS device as a child of GICv3 in Newbus hierarchy. */ static int gic_v3_ofw_bus_attach(device_t dev) { struct gic_v3_ofw_devinfo *di; device_t child; phandle_t parent, node; pcell_t addr_cells, size_cells; parent = ofw_bus_get_node(dev); if (parent > 0) { addr_cells = 2; OF_getencprop(parent, "#address-cells", &addr_cells, sizeof(addr_cells)); size_cells = 2; OF_getencprop(parent, "#size-cells", &size_cells, sizeof(size_cells)); /* Iterate through all GIC subordinates */ for (node = OF_child(parent); node > 0; node = OF_peer(node)) { /* Allocate and populate devinfo. */ di = malloc(sizeof(*di), M_GIC_V3, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node)) { if (bootverbose) { device_printf(dev, "Could not set up devinfo for ITS\n"); } free(di, M_GIC_V3); continue; } /* Initialize and populate resource list. */ resource_list_init(&di->di_rl); ofw_bus_reg_to_rl(dev, node, addr_cells, size_cells, &di->di_rl); /* Should not have any interrupts, so don't add any */ /* Add newbus device for this FDT node */ child = device_add_child(dev, NULL, -1); if (!child) { if (bootverbose) { device_printf(dev, "Could not add child: %s\n", di->di_dinfo.obd_name); } resource_list_free(&di->di_rl); ofw_bus_gen_destroy_devinfo(&di->di_dinfo); free(di, M_GIC_V3); continue; } device_set_ivars(child, di); } } return (bus_generic_attach(dev)); } #ifndef INTRNG static int gic_v3_its_fdt_probe(device_t dev); static device_method_t gic_v3_its_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gic_v3_its_fdt_probe), /* End */ DEVMETHOD_END }; DEFINE_CLASS_1(its, gic_v3_its_fdt_driver, gic_v3_its_fdt_methods, sizeof(struct gic_v3_its_softc), gic_v3_its_driver); static devclass_t gic_v3_its_fdt_devclass; EARLY_DRIVER_MODULE(its, gic, gic_v3_its_fdt_driver, gic_v3_its_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); static int gic_v3_its_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, GIC_V3_ITS_COMPSTR)) return (ENXIO); device_set_desc(dev, GIC_V3_ITS_DEVSTR); return (BUS_PROBE_DEFAULT); } #endif Index: head/sys/kern/subr_intr.c =================================================================== --- head/sys/kern/subr_intr.c (revision 300148) +++ head/sys/kern/subr_intr.c (revision 300149) @@ -1,1479 +1,1479 @@ /*- * Copyright (c) 2015-2016 Svatopluk Kraus * Copyright (c) 2015-2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * New-style Interrupt Framework * * TODO: - to support IPI (PPI) enabling on other CPUs if already started * - to complete things for removable PICs */ #include "opt_acpi.h" #include "opt_ddb.h" #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #ifdef DDB #include #endif #include "pic_if.h" #include "msi_if.h" #define INTRNAME_LEN (2*MAXCOMLEN + 1) #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif MALLOC_DECLARE(M_INTRNG); MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); /* Main interrupt handler called from assembler -> 'hidden' for C code. */ void intr_irq_handler(struct trapframe *tf); /* Root interrupt controller stuff. */ device_t intr_irq_root_dev; static intr_irq_filter_t *irq_root_filter; static void *irq_root_arg; static u_int irq_root_ipicount; /* Interrupt controller definition. */ struct intr_pic { SLIST_ENTRY(intr_pic) pic_next; intptr_t pic_xref; /* hardware identification */ device_t pic_dev; #define FLAG_PIC (1 << 0) #define FLAG_MSI (1 << 1) u_int pic_flags; }; static struct mtx pic_list_lock; static SLIST_HEAD(, intr_pic) pic_list; static struct intr_pic *pic_lookup(device_t dev, intptr_t xref); /* Interrupt source definition. */ static struct mtx isrc_table_lock; static struct intr_irqsrc *irq_sources[NIRQ]; u_int irq_next_free; /* * XXX - All stuff around struct intr_dev_data is considered as temporary * until better place for storing struct intr_map_data will be find. * * For now, there are two global interrupt numbers spaces: * <0, NIRQ) ... interrupts without config data * managed in irq_sources[] * IRQ_DDATA_BASE + <0, 2 * NIRQ) ... interrupts with config data * managed in intr_ddata_tab[] * * Read intr_ddata_lookup() to see how these spaces are worked with. * Note that each interrupt number from second space duplicates some number * from first space at this moment. An interrupt number from first space can * be duplicated even multiple times in second space. */ struct intr_dev_data { device_t idd_dev; intptr_t idd_xref; u_int idd_irq; struct intr_map_data * idd_data; struct intr_irqsrc * idd_isrc; }; static struct intr_dev_data *intr_ddata_tab[2 * NIRQ]; static u_int intr_ddata_first_unused; #define IRQ_DDATA_BASE 10000 CTASSERT(IRQ_DDATA_BASE > nitems(irq_sources)); #ifdef SMP static boolean_t irq_assign_cpu = FALSE; #endif /* * - 2 counters for each I/O interrupt. * - MAXCPU counters for each IPI counters for SMP. */ #ifdef SMP #define INTRCNT_COUNT (NIRQ * 2 + INTR_IPI_COUNT * MAXCPU) #else #define INTRCNT_COUNT (NIRQ * 2) #endif /* Data for MI statistics reporting. */ u_long intrcnt[INTRCNT_COUNT]; char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; size_t sintrcnt = sizeof(intrcnt); size_t sintrnames = sizeof(intrnames); static u_int intrcnt_index; /* * Interrupt framework initialization routine. */ static void intr_irq_init(void *dummy __unused) { SLIST_INIT(&pic_list); mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF); mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF); } SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL); static void intrcnt_setname(const char *name, int index) { snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", INTRNAME_LEN - 1, name); } /* * Update name for interrupt source with interrupt event. */ static void intrcnt_updatename(struct intr_irqsrc *isrc) { /* QQQ: What about stray counter name? */ mtx_assert(&isrc_table_lock, MA_OWNED); intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); } /* * Virtualization for interrupt source interrupt counter increment. */ static inline void isrc_increment_count(struct intr_irqsrc *isrc) { if (isrc->isrc_flags & INTR_ISRCF_PPI) atomic_add_long(&isrc->isrc_count[0], 1); else isrc->isrc_count[0]++; } /* * Virtualization for interrupt source interrupt stray counter increment. */ static inline void isrc_increment_straycount(struct intr_irqsrc *isrc) { isrc->isrc_count[1]++; } /* * Virtualization for interrupt source interrupt name update. */ static void isrc_update_name(struct intr_irqsrc *isrc, const char *name) { char str[INTRNAME_LEN]; mtx_assert(&isrc_table_lock, MA_OWNED); if (name != NULL) { snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); intrcnt_setname(str, isrc->isrc_index); snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, name); intrcnt_setname(str, isrc->isrc_index + 1); } else { snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); intrcnt_setname(str, isrc->isrc_index); snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); intrcnt_setname(str, isrc->isrc_index + 1); } } /* * Virtualization for interrupt source interrupt counters setup. */ static void isrc_setup_counters(struct intr_irqsrc *isrc) { u_int index; /* * XXX - it does not work well with removable controllers and * interrupt sources !!! */ index = atomic_fetchadd_int(&intrcnt_index, 2); isrc->isrc_index = index; isrc->isrc_count = &intrcnt[index]; isrc_update_name(isrc, NULL); } /* * Virtualization for interrupt source interrupt counters release. */ static void isrc_release_counters(struct intr_irqsrc *isrc) { panic("%s: not implemented", __func__); } #ifdef SMP /* * Virtualization for interrupt source IPI counters setup. */ u_long * intr_ipi_setup_counters(const char *name) { u_int index, i; char str[INTRNAME_LEN]; index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); for (i = 0; i < MAXCPU; i++) { snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); intrcnt_setname(str, index + i); } return (&intrcnt[index]); } #endif /* * Main interrupt dispatch handler. It's called straight * from the assembler, where CPU interrupt is served. */ void intr_irq_handler(struct trapframe *tf) { struct trapframe * oldframe; struct thread * td; KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); PCPU_INC(cnt.v_intr); critical_enter(); td = curthread; oldframe = td->td_intr_frame; td->td_intr_frame = tf; irq_root_filter(irq_root_arg); td->td_intr_frame = oldframe; critical_exit(); } /* * interrupt controller dispatch function for interrupts. It should * be called straight from the interrupt controller, when associated interrupt * source is learned. */ int intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) { KASSERT(isrc != NULL, ("%s: no source", __func__)); isrc_increment_count(isrc); #ifdef INTR_SOLO if (isrc->isrc_filter != NULL) { int error; error = isrc->isrc_filter(isrc->isrc_arg, tf); PIC_POST_FILTER(isrc->isrc_dev, isrc); if (error == FILTER_HANDLED) return (0); } else #endif if (isrc->isrc_event != NULL) { if (intr_event_handle(isrc->isrc_event, tf) == 0) return (0); } isrc_increment_straycount(isrc); return (EINVAL); } /* * Alloc unique interrupt number (resource handle) for interrupt source. * * There could be various strategies how to allocate free interrupt number * (resource handle) for new interrupt source. * * 1. Handles are always allocated forward, so handles are not recycled * immediately. However, if only one free handle left which is reused * constantly... */ static inline int isrc_alloc_irq(struct intr_irqsrc *isrc) { u_int maxirqs, irq; mtx_assert(&isrc_table_lock, MA_OWNED); maxirqs = nitems(irq_sources); if (irq_next_free >= maxirqs) return (ENOSPC); for (irq = irq_next_free; irq < maxirqs; irq++) { if (irq_sources[irq] == NULL) goto found; } for (irq = 0; irq < irq_next_free; irq++) { if (irq_sources[irq] == NULL) goto found; } irq_next_free = maxirqs; return (ENOSPC); found: isrc->isrc_irq = irq; irq_sources[irq] = isrc; irq_next_free = irq + 1; if (irq_next_free >= maxirqs) irq_next_free = 0; return (0); } /* * Free unique interrupt number (resource handle) from interrupt source. */ static inline int isrc_free_irq(struct intr_irqsrc *isrc) { mtx_assert(&isrc_table_lock, MA_OWNED); if (isrc->isrc_irq >= nitems(irq_sources)) return (EINVAL); if (irq_sources[isrc->isrc_irq] != isrc) return (EINVAL); irq_sources[isrc->isrc_irq] = NULL; isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ return (0); } /* * Lookup interrupt source by interrupt number (resource handle). */ static inline struct intr_irqsrc * isrc_lookup(u_int irq) { if (irq < nitems(irq_sources)) return (irq_sources[irq]); return (NULL); } /* * Initialize interrupt source and register it into global interrupt table. */ int intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, const char *fmt, ...) { int error; va_list ap; bzero(isrc, sizeof(struct intr_irqsrc)); isrc->isrc_dev = dev; isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ isrc->isrc_flags = flags; va_start(ap, fmt); vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); va_end(ap); mtx_lock(&isrc_table_lock); error = isrc_alloc_irq(isrc); if (error != 0) { mtx_unlock(&isrc_table_lock); return (error); } /* * Setup interrupt counters, but not for IPI sources. Those are setup * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust * our counter pool. */ if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) isrc_setup_counters(isrc); mtx_unlock(&isrc_table_lock); return (0); } /* * Deregister interrupt source from global interrupt table. */ int intr_isrc_deregister(struct intr_irqsrc *isrc) { int error; mtx_lock(&isrc_table_lock); if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) isrc_release_counters(isrc); error = isrc_free_irq(isrc); mtx_unlock(&isrc_table_lock); return (error); } #ifdef SMP /* * A support function for a PIC to decide if provided ISRC should be inited * on given cpu. The logic of INTR_ISRCF_BOUND flag and isrc_cpu member of * struct intr_irqsrc is the following: * * If INTR_ISRCF_BOUND is set, the ISRC should be inited only on cpus * set in isrc_cpu. If not, the ISRC should be inited on every cpu and * isrc_cpu is kept consistent with it. Thus isrc_cpu is always correct. */ bool intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) { if (isrc->isrc_handlers == 0) return (false); if ((isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) == 0) return (false); if (isrc->isrc_flags & INTR_ISRCF_BOUND) return (CPU_ISSET(cpu, &isrc->isrc_cpu)); CPU_SET(cpu, &isrc->isrc_cpu); return (true); } #endif static struct intr_dev_data * intr_ddata_alloc(u_int extsize) { struct intr_dev_data *ddata; size_t size; size = sizeof(*ddata); ddata = malloc(size + extsize, M_INTRNG, M_WAITOK | M_ZERO); mtx_lock(&isrc_table_lock); if (intr_ddata_first_unused >= nitems(intr_ddata_tab)) { mtx_unlock(&isrc_table_lock); free(ddata, M_INTRNG); return (NULL); } intr_ddata_tab[intr_ddata_first_unused] = ddata; ddata->idd_irq = IRQ_DDATA_BASE + intr_ddata_first_unused++; mtx_unlock(&isrc_table_lock); ddata->idd_data = (struct intr_map_data *)((uintptr_t)ddata + size); ddata->idd_data->size = extsize; return (ddata); } static struct intr_irqsrc * intr_ddata_lookup(u_int irq, struct intr_map_data **datap) { int error; struct intr_irqsrc *isrc; struct intr_dev_data *ddata; isrc = isrc_lookup(irq); if (isrc != NULL) { if (datap != NULL) *datap = NULL; return (isrc); } if (irq < IRQ_DDATA_BASE) return (NULL); irq -= IRQ_DDATA_BASE; if (irq >= nitems(intr_ddata_tab)) return (NULL); ddata = intr_ddata_tab[irq]; if (ddata->idd_isrc == NULL) { error = intr_map_irq(ddata->idd_dev, ddata->idd_xref, ddata->idd_data, &irq); if (error != 0) return (NULL); ddata->idd_isrc = isrc_lookup(irq); } if (datap != NULL) *datap = ddata->idd_data; return (ddata->idd_isrc); } #ifdef DEV_ACPI /* * Map interrupt source according to ACPI info into framework. If such mapping * does not exist, create it. Return unique interrupt number (resource handle) * associated with mapped interrupt source. */ u_int intr_acpi_map_irq(device_t dev, u_int irq, enum intr_polarity pol, enum intr_trigger trig) { struct intr_map_data_acpi *daa; struct intr_dev_data *ddata; ddata = intr_ddata_alloc(sizeof(struct intr_map_data_acpi)); if (ddata == NULL) return (INTR_IRQ_INVALID); /* no space left */ ddata->idd_dev = dev; ddata->idd_data->type = INTR_MAP_DATA_ACPI; daa = (struct intr_map_data_acpi *)ddata->idd_data; daa->irq = irq; daa->pol = pol; daa->trig = trig; return (ddata->idd_irq); } #endif #ifdef FDT /* * Map interrupt source according to FDT data into framework. If such mapping * does not exist, create it. Return unique interrupt number (resource handle) * associated with mapped interrupt source. */ u_int intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) { size_t cellsize; struct intr_dev_data *ddata; struct intr_map_data_fdt *daf; cellsize = ncells * sizeof(*cells); ddata = intr_ddata_alloc(sizeof(struct intr_map_data_fdt) + cellsize); if (ddata == NULL) return (INTR_IRQ_INVALID); /* no space left */ ddata->idd_xref = (intptr_t)node; ddata->idd_data->type = INTR_MAP_DATA_FDT; daf = (struct intr_map_data_fdt *)ddata->idd_data; daf->ncells = ncells; memcpy(daf->cells, cells, cellsize); return (ddata->idd_irq); } #endif /* * Store GPIO interrupt decription in framework and return unique interrupt * number (resource handle) associated with it. */ u_int intr_gpio_map_irq(device_t dev, u_int pin_num, u_int pin_flags, u_int intr_mode) { struct intr_dev_data *ddata; struct intr_map_data_gpio *dag; ddata = intr_ddata_alloc(sizeof(struct intr_map_data_gpio)); if (ddata == NULL) return (INTR_IRQ_INVALID); /* no space left */ ddata->idd_dev = dev; ddata->idd_data->type = INTR_MAP_DATA_GPIO; dag = (struct intr_map_data_gpio *)ddata->idd_data; dag->gpio_pin_num = pin_num; dag->gpio_pin_flags = pin_flags; dag->gpio_intr_mode = intr_mode; return (ddata->idd_irq); } #ifdef INTR_SOLO /* * Setup filter into interrupt source. */ static int iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, intr_irq_filter_t *filter, void *arg, void **cookiep) { if (filter == NULL) return (EINVAL); mtx_lock(&isrc_table_lock); /* * Make sure that we do not mix the two ways * how we handle interrupt sources. */ if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { mtx_unlock(&isrc_table_lock); return (EBUSY); } isrc->isrc_filter = filter; isrc->isrc_arg = arg; isrc_update_name(isrc, name); mtx_unlock(&isrc_table_lock); *cookiep = isrc; return (0); } #endif /* * Interrupt source pre_ithread method for MI interrupt framework. */ static void intr_isrc_pre_ithread(void *arg) { struct intr_irqsrc *isrc = arg; PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); } /* * Interrupt source post_ithread method for MI interrupt framework. */ static void intr_isrc_post_ithread(void *arg) { struct intr_irqsrc *isrc = arg; PIC_POST_ITHREAD(isrc->isrc_dev, isrc); } /* * Interrupt source post_filter method for MI interrupt framework. */ static void intr_isrc_post_filter(void *arg) { struct intr_irqsrc *isrc = arg; PIC_POST_FILTER(isrc->isrc_dev, isrc); } /* * Interrupt source assign_cpu method for MI interrupt framework. */ static int intr_isrc_assign_cpu(void *arg, int cpu) { #ifdef SMP struct intr_irqsrc *isrc = arg; int error; if (isrc->isrc_dev != intr_irq_root_dev) return (EINVAL); mtx_lock(&isrc_table_lock); if (cpu == NOCPU) { CPU_ZERO(&isrc->isrc_cpu); isrc->isrc_flags &= ~INTR_ISRCF_BOUND; } else { CPU_SETOF(cpu, &isrc->isrc_cpu); isrc->isrc_flags |= INTR_ISRCF_BOUND; } /* * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or * re-balance it to another CPU or enable it on more CPUs. However, * PIC is expected to change isrc_cpu appropriately to keep us well * informed if the call is successful. */ if (irq_assign_cpu) { error = PIC_BIND_INTR(isrc->isrc_dev, isrc); if (error) { CPU_ZERO(&isrc->isrc_cpu); mtx_unlock(&isrc_table_lock); return (error); } } mtx_unlock(&isrc_table_lock); return (0); #else return (EOPNOTSUPP); #endif } /* * Create interrupt event for interrupt source. */ static int isrc_event_create(struct intr_irqsrc *isrc) { struct intr_event *ie; int error; error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, intr_isrc_pre_ithread, intr_isrc_post_ithread, intr_isrc_post_filter, intr_isrc_assign_cpu, "%s:", isrc->isrc_name); if (error) return (error); mtx_lock(&isrc_table_lock); /* * Make sure that we do not mix the two ways * how we handle interrupt sources. Let contested event wins. */ #ifdef INTR_SOLO if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { #else if (isrc->isrc_event != NULL) { #endif mtx_unlock(&isrc_table_lock); intr_event_destroy(ie); return (isrc->isrc_event != NULL ? EBUSY : 0); } isrc->isrc_event = ie; mtx_unlock(&isrc_table_lock); return (0); } #ifdef notyet /* * Destroy interrupt event for interrupt source. */ static void isrc_event_destroy(struct intr_irqsrc *isrc) { struct intr_event *ie; mtx_lock(&isrc_table_lock); ie = isrc->isrc_event; isrc->isrc_event = NULL; mtx_unlock(&isrc_table_lock); if (ie != NULL) intr_event_destroy(ie); } #endif /* * Add handler to interrupt source. */ static int isrc_add_handler(struct intr_irqsrc *isrc, const char *name, driver_filter_t filter, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) { int error; if (isrc->isrc_event == NULL) { error = isrc_event_create(isrc); if (error) return (error); } error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, arg, intr_priority(flags), flags, cookiep); if (error == 0) { mtx_lock(&isrc_table_lock); intrcnt_updatename(isrc); mtx_unlock(&isrc_table_lock); } return (error); } /* * Lookup interrupt controller locked. */ static inline struct intr_pic * pic_lookup_locked(device_t dev, intptr_t xref) { struct intr_pic *pic; mtx_assert(&pic_list_lock, MA_OWNED); if (dev == NULL && xref == 0) return (NULL); /* Note that pic->pic_dev is never NULL on registered PIC. */ SLIST_FOREACH(pic, &pic_list, pic_next) { if (dev == NULL) { if (xref == pic->pic_xref) return (pic); } else if (xref == 0 || pic->pic_xref == 0) { if (dev == pic->pic_dev) return (pic); } else if (xref == pic->pic_xref && dev == pic->pic_dev) return (pic); } return (NULL); } /* * Lookup interrupt controller. */ static struct intr_pic * pic_lookup(device_t dev, intptr_t xref) { struct intr_pic *pic; mtx_lock(&pic_list_lock); pic = pic_lookup_locked(dev, xref); mtx_unlock(&pic_list_lock); return (pic); } /* * Create interrupt controller. */ static struct intr_pic * pic_create(device_t dev, intptr_t xref) { struct intr_pic *pic; mtx_lock(&pic_list_lock); pic = pic_lookup_locked(dev, xref); if (pic != NULL) { mtx_unlock(&pic_list_lock); return (pic); } pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); if (pic == NULL) { mtx_unlock(&pic_list_lock); return (NULL); } pic->pic_xref = xref; pic->pic_dev = dev; SLIST_INSERT_HEAD(&pic_list, pic, pic_next); mtx_unlock(&pic_list_lock); return (pic); } #ifdef notyet /* * Destroy interrupt controller. */ static void pic_destroy(device_t dev, intptr_t xref) { struct intr_pic *pic; mtx_lock(&pic_list_lock); pic = pic_lookup_locked(dev, xref); if (pic == NULL) { mtx_unlock(&pic_list_lock); return; } SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next); mtx_unlock(&pic_list_lock); free(pic, M_INTRNG); } #endif /* * Register interrupt controller. */ -int +struct intr_pic * intr_pic_register(device_t dev, intptr_t xref) { struct intr_pic *pic; if (dev == NULL) - return (EINVAL); + return (NULL); pic = pic_create(dev, xref); if (pic == NULL) - return (ENOMEM); + return (NULL); pic->pic_flags |= FLAG_PIC; debugf("PIC %p registered for %s \n", pic, device_get_nameunit(dev), dev, xref); - return (0); + return (pic); } /* * Unregister interrupt controller. */ int intr_pic_deregister(device_t dev, intptr_t xref) { panic("%s: not implemented", __func__); } /* * Mark interrupt controller (itself) as a root one. * * Note that only an interrupt controller can really know its position * in interrupt controller's tree. So root PIC must claim itself as a root. * * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, * page 30: * "The root of the interrupt tree is determined when traversal * of the interrupt tree reaches an interrupt controller node without * an interrupts property and thus no explicit interrupt parent." */ int intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, void *arg, u_int ipicount) { struct intr_pic *pic; pic = pic_lookup(dev, xref); if (pic == NULL) { device_printf(dev, "not registered\n"); return (EINVAL); } KASSERT((pic->pic_flags & FLAG_PIC) != 0, ("%s: Found a non-PIC controller: %s", __func__, device_get_name(pic->pic_dev))); if (filter == NULL) { device_printf(dev, "filter missing\n"); return (EINVAL); } /* * Only one interrupt controllers could be on the root for now. * Note that we further suppose that there is not threaded interrupt * routine (handler) on the root. See intr_irq_handler(). */ if (intr_irq_root_dev != NULL) { device_printf(dev, "another root already set\n"); return (EBUSY); } intr_irq_root_dev = dev; irq_root_filter = filter; irq_root_arg = arg; irq_root_ipicount = ipicount; debugf("irq root set to %s\n", device_get_nameunit(dev)); return (0); } int intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, u_int *irqp) { int error; struct intr_irqsrc *isrc; struct intr_pic *pic; if (data == NULL) return (EINVAL); pic = pic_lookup(dev, xref); if (pic == NULL) return (ESRCH); KASSERT((pic->pic_flags & FLAG_PIC) != 0, ("%s: Found a non-PIC controller: %s", __func__, device_get_name(pic->pic_dev))); error = PIC_MAP_INTR(pic->pic_dev, data, &isrc); if (error == 0) *irqp = isrc->isrc_irq; return (error); } int intr_alloc_irq(device_t dev, struct resource *res) { struct intr_map_data *data; struct intr_irqsrc *isrc; KASSERT(rman_get_start(res) == rman_get_end(res), ("%s: more interrupts in resource", __func__)); isrc = intr_ddata_lookup(rman_get_start(res), &data); if (isrc == NULL) return (EINVAL); return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data)); } int intr_release_irq(device_t dev, struct resource *res) { struct intr_map_data *data; struct intr_irqsrc *isrc; KASSERT(rman_get_start(res) == rman_get_end(res), ("%s: more interrupts in resource", __func__)); isrc = intr_ddata_lookup(rman_get_start(res), &data); if (isrc == NULL) return (EINVAL); return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data)); } int intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, driver_intr_t hand, void *arg, int flags, void **cookiep) { int error; struct intr_map_data *data; struct intr_irqsrc *isrc; const char *name; KASSERT(rman_get_start(res) == rman_get_end(res), ("%s: more interrupts in resource", __func__)); isrc = intr_ddata_lookup(rman_get_start(res), &data); if (isrc == NULL) return (EINVAL); name = device_get_nameunit(dev); #ifdef INTR_SOLO /* * Standard handling is done through MI interrupt framework. However, * some interrupts could request solely own special handling. This * non standard handling can be used for interrupt controllers without * handler (filter only), so in case that interrupt controllers are * chained, MI interrupt framework is called only in leaf controller. * * Note that root interrupt controller routine is served as well, * however in intr_irq_handler(), i.e. main system dispatch routine. */ if (flags & INTR_SOLO && hand != NULL) { debugf("irq %u cannot solo on %s\n", irq, name); return (EINVAL); } if (flags & INTR_SOLO) { error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, arg, cookiep); debugf("irq %u setup filter error %d on %s\n", irq, error, name); } else #endif { error = isrc_add_handler(isrc, name, filt, hand, arg, flags, cookiep); debugf("irq %u add handler error %d on %s\n", irq, error, name); } if (error != 0) return (error); mtx_lock(&isrc_table_lock); error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); if (error == 0) { isrc->isrc_handlers++; if (isrc->isrc_handlers == 1) PIC_ENABLE_INTR(isrc->isrc_dev, isrc); } mtx_unlock(&isrc_table_lock); if (error != 0) intr_event_remove_handler(*cookiep); return (error); } int intr_teardown_irq(device_t dev, struct resource *res, void *cookie) { int error; struct intr_map_data *data; struct intr_irqsrc *isrc; KASSERT(rman_get_start(res) == rman_get_end(res), ("%s: more interrupts in resource", __func__)); isrc = intr_ddata_lookup(rman_get_start(res), &data); if (isrc == NULL || isrc->isrc_handlers == 0) return (EINVAL); #ifdef INTR_SOLO if (isrc->isrc_filter != NULL) { if (isrc != cookie) return (EINVAL); mtx_lock(&isrc_table_lock); isrc->isrc_filter = NULL; isrc->isrc_arg = NULL; isrc->isrc_handlers = 0; PIC_DISABLE_INTR(isrc->isrc_dev, isrc); PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); isrc_update_name(isrc, NULL); mtx_unlock(&isrc_table_lock); return (0); } #endif if (isrc != intr_handler_source(cookie)) return (EINVAL); error = intr_event_remove_handler(cookie); if (error == 0) { mtx_lock(&isrc_table_lock); isrc->isrc_handlers--; if (isrc->isrc_handlers == 0) PIC_DISABLE_INTR(isrc->isrc_dev, isrc); PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); intrcnt_updatename(isrc); mtx_unlock(&isrc_table_lock); } return (error); } int intr_describe_irq(device_t dev, struct resource *res, void *cookie, const char *descr) { int error; struct intr_irqsrc *isrc; KASSERT(rman_get_start(res) == rman_get_end(res), ("%s: more interrupts in resource", __func__)); isrc = intr_ddata_lookup(rman_get_start(res), NULL); if (isrc == NULL || isrc->isrc_handlers == 0) return (EINVAL); #ifdef INTR_SOLO if (isrc->isrc_filter != NULL) { if (isrc != cookie) return (EINVAL); mtx_lock(&isrc_table_lock); isrc_update_name(isrc, descr); mtx_unlock(&isrc_table_lock); return (0); } #endif error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); if (error == 0) { mtx_lock(&isrc_table_lock); intrcnt_updatename(isrc); mtx_unlock(&isrc_table_lock); } return (error); } #ifdef SMP int intr_bind_irq(device_t dev, struct resource *res, int cpu) { struct intr_irqsrc *isrc; KASSERT(rman_get_start(res) == rman_get_end(res), ("%s: more interrupts in resource", __func__)); isrc = intr_ddata_lookup(rman_get_start(res), NULL); if (isrc == NULL || isrc->isrc_handlers == 0) return (EINVAL); #ifdef INTR_SOLO if (isrc->isrc_filter != NULL) return (intr_isrc_assign_cpu(isrc, cpu)); #endif return (intr_event_bind(isrc->isrc_event, cpu)); } /* * Return the CPU that the next interrupt source should use. * For now just returns the next CPU according to round-robin. */ u_int intr_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) { if (!irq_assign_cpu || mp_ncpus == 1) return (PCPU_GET(cpuid)); do { last_cpu++; if (last_cpu > mp_maxid) last_cpu = 0; } while (!CPU_ISSET(last_cpu, cpumask)); return (last_cpu); } /* * Distribute all the interrupt sources among the available * CPUs once the AP's have been launched. */ static void intr_irq_shuffle(void *arg __unused) { struct intr_irqsrc *isrc; u_int i; if (mp_ncpus == 1) return; mtx_lock(&isrc_table_lock); irq_assign_cpu = TRUE; for (i = 0; i < NIRQ; i++) { isrc = irq_sources[i]; if (isrc == NULL || isrc->isrc_handlers == 0 || isrc->isrc_flags & (INTR_ISRCF_PPI | INTR_ISRCF_IPI)) continue; if (isrc->isrc_event != NULL && isrc->isrc_flags & INTR_ISRCF_BOUND && isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) panic("%s: CPU inconsistency", __func__); if ((isrc->isrc_flags & INTR_ISRCF_BOUND) == 0) CPU_ZERO(&isrc->isrc_cpu); /* start again */ /* * We are in wicked position here if the following call fails * for bound ISRC. The best thing we can do is to clear * isrc_cpu so inconsistency with ie_cpu will be detectable. */ if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) CPU_ZERO(&isrc->isrc_cpu); } mtx_unlock(&isrc_table_lock); } SYSINIT(intr_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, intr_irq_shuffle, NULL); #else u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) { return (PCPU_GET(cpuid)); } #endif /* * Register a MSI/MSI-X interrupt controller */ int intr_msi_register(device_t dev, intptr_t xref) { struct intr_pic *pic; if (dev == NULL) return (EINVAL); pic = pic_create(dev, xref); if (pic == NULL) return (ENOMEM); pic->pic_flags |= FLAG_MSI; debugf("PIC %p registered for %s \n", pic, device_get_nameunit(dev), dev, (uintmax_t)xref); return (0); } int intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, int maxcount, int *irqs) { struct intr_irqsrc **isrc; struct intr_pic *pic; device_t pdev; int err, i; pic = pic_lookup(NULL, xref); if (pic == NULL) return (ESRCH); KASSERT((pic->pic_flags & FLAG_MSI) != 0, ("%s: Found a non-MSI controller: %s", __func__, device_get_name(pic->pic_dev))); isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); if (err == 0) { for (i = 0; i < count; i++) { irqs[i] = isrc[i]->isrc_irq; } } free(isrc, M_INTRNG); return (err); } int intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, int *irqs) { struct intr_irqsrc **isrc; struct intr_pic *pic; int i, err; pic = pic_lookup(NULL, xref); if (pic == NULL) return (ESRCH); KASSERT((pic->pic_flags & FLAG_MSI) != 0, ("%s: Found a non-MSI controller: %s", __func__, device_get_name(pic->pic_dev))); isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); for (i = 0; i < count; i++) { isrc[i] = isrc_lookup(irqs[i]); if (isrc == NULL) { free(isrc, M_INTRNG); return (EINVAL); } } err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); free(isrc, M_INTRNG); return (err); } int intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) { struct intr_irqsrc *isrc; struct intr_pic *pic; device_t pdev; int err; pic = pic_lookup(NULL, xref); if (pic == NULL) return (ESRCH); KASSERT((pic->pic_flags & FLAG_MSI) != 0, ("%s: Found a non-MSI controller: %s", __func__, device_get_name(pic->pic_dev))); err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); if (err != 0) return (err); *irq = isrc->isrc_irq; return (0); } int intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) { struct intr_irqsrc *isrc; struct intr_pic *pic; int err; pic = pic_lookup(NULL, xref); if (pic == NULL) return (ESRCH); KASSERT((pic->pic_flags & FLAG_MSI) != 0, ("%s: Found a non-MSI controller: %s", __func__, device_get_name(pic->pic_dev))); isrc = isrc_lookup(irq); if (isrc == NULL) return (EINVAL); err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); return (err); } int intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, uint64_t *addr, uint32_t *data) { struct intr_irqsrc *isrc; struct intr_pic *pic; int err; pic = pic_lookup(NULL, xref); if (pic == NULL) return (ESRCH); KASSERT((pic->pic_flags & FLAG_MSI) != 0, ("%s: Found a non-MSI controller: %s", __func__, device_get_name(pic->pic_dev))); isrc = isrc_lookup(irq); if (isrc == NULL) return (EINVAL); err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); return (err); } void dosoftints(void); void dosoftints(void) { } #ifdef SMP /* * Init interrupt controller on another CPU. */ void intr_pic_init_secondary(void) { /* * QQQ: Only root PIC is aware of other CPUs ??? */ KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); //mtx_lock(&isrc_table_lock); PIC_INIT_SECONDARY(intr_irq_root_dev); //mtx_unlock(&isrc_table_lock); } #endif #ifdef DDB DB_SHOW_COMMAND(irqs, db_show_irqs) { u_int i, irqsum; u_long num; struct intr_irqsrc *isrc; for (irqsum = 0, i = 0; i < NIRQ; i++) { isrc = irq_sources[i]; if (isrc == NULL) continue; num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, isrc->isrc_name, isrc->isrc_cpu.__bits[0], isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); irqsum += num; } db_printf("irq total %u\n", irqsum); } #endif Index: head/sys/mips/mediatek/mtk_gpio_v1.c =================================================================== --- head/sys/mips/mediatek/mtk_gpio_v1.c (revision 300148) +++ head/sys/mips/mediatek/mtk_gpio_v1.c (revision 300149) @@ -1,671 +1,671 @@ /*- * Copyright 2016 Stanislav Galabov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "pic_if.h" #define MTK_GPIO_PINS 32 enum mtk_gpio_regs { GPIO_PIOINT = 0, GPIO_PIOEDGE, GPIO_PIORENA, GPIO_PIOFENA, GPIO_PIODATA, GPIO_PIODIR, GPIO_PIOPOL, GPIO_PIOSET, GPIO_PIORESET, GPIO_PIOTOG, GPIO_PIOMAX }; struct mtk_gpio_pin_irqsrc { struct intr_irqsrc isrc; u_int irq; }; struct mtk_gpio_pin { uint32_t pin_caps; uint32_t pin_flags; enum intr_trigger intr_trigger; enum intr_polarity intr_polarity; char pin_name[GPIOMAXNAME]; struct mtk_gpio_pin_irqsrc pin_irqsrc; }; struct mtk_gpio_softc { device_t dev; device_t busdev; struct resource *res[2]; struct mtx mtx; struct mtk_gpio_pin pins[MTK_GPIO_PINS]; void *intrhand; uint8_t regs[GPIO_PIOMAX]; uint32_t num_pins; uint8_t do_remap; }; #define PIC_INTR_ISRC(sc, irq) (&(sc)->pins[(irq)].pin_irqsrc.isrc) static struct resource_spec mtk_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static int mtk_gpio_probe(device_t dev); static int mtk_gpio_attach(device_t dev); static int mtk_gpio_detach(device_t dev); static int mtk_gpio_intr(void *arg); #define MTK_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx) #define MTK_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) #define MTK_GPIO_LOCK_INIT(sc) \ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ "mtk_gpio", MTX_SPIN) #define MTK_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) #define MTK_WRITE_4(sc, reg, val) \ bus_write_4((sc)->res[0], (sc)->regs[(reg)], (val)) #define MTK_READ_4(sc, reg) \ bus_read_4((sc)->res[0], (sc)->regs[(reg)]) static struct ofw_compat_data compat_data[] = { { "ralink,rt2880-gpio", 1 }, { "ralink,rt3050-gpio", 1 }, { "ralink,rt3352-gpio", 1 }, { "ralink,rt3883-gpio", 1 }, { "ralink,rt5350-gpio", 1 }, { "ralink,mt7620a-gpio", 1 }, { NULL, 0 } }; static int mtk_gpio_probe(device_t dev) { phandle_t node; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); node = ofw_bus_get_node(dev); if (!OF_hasprop(node, "gpio-controller")) return (ENXIO); device_set_desc(dev, "MTK GPIO Controller (v1)"); return (BUS_PROBE_DEFAULT); } static int mtk_pic_register_isrcs(struct mtk_gpio_softc *sc) { int error; uint32_t irq; struct intr_irqsrc *isrc; const char *name; name = device_get_nameunit(sc->dev); for (irq = 0; irq < sc->num_pins; irq++) { sc->pins[irq].pin_irqsrc.irq = irq; isrc = PIC_INTR_ISRC(sc, irq); error = intr_isrc_register(isrc, sc->dev, 0, "%s", name); if (error != 0) { /* XXX call intr_isrc_deregister */ device_printf(sc->dev, "%s failed", __func__); return (error); } } return (0); } static int mtk_gpio_pin_set_direction(struct mtk_gpio_softc *sc, uint32_t pin, uint32_t dir) { uint32_t regval, mask = (1u << pin); if (!(sc->pins[pin].pin_caps & dir)) return (EINVAL); regval = MTK_READ_4(sc, GPIO_PIODIR); if (dir == GPIO_PIN_INPUT) regval &= ~mask; else regval |= mask; MTK_WRITE_4(sc, GPIO_PIODIR, regval); sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); sc->pins[pin].pin_flags |= dir; return (0); } static int mtk_gpio_pin_set_invert(struct mtk_gpio_softc *sc, uint32_t pin, uint32_t val) { uint32_t regval, mask = (1u << pin); regval = MTK_READ_4(sc, GPIO_PIOPOL); if (val) regval |= mask; else regval &= ~mask; MTK_WRITE_4(sc, GPIO_PIOPOL, regval); sc->pins[pin].pin_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT); sc->pins[pin].pin_flags |= val; return (0); } static void mtk_gpio_pin_probe(struct mtk_gpio_softc *sc, uint32_t pin) { uint32_t mask = (1u << pin); uint32_t val; /* Clear cached gpio config */ sc->pins[pin].pin_flags = 0; val = MTK_READ_4(sc, GPIO_PIORENA) | MTK_READ_4(sc, GPIO_PIOFENA); if (val & mask) { /* Pin is in interrupt mode */ sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE; val = MTK_READ_4(sc, GPIO_PIORENA); if (val & mask) sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH; else sc->pins[pin].intr_polarity = INTR_POLARITY_LOW; } val = MTK_READ_4(sc, GPIO_PIODIR); if (val & mask) sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT; else sc->pins[pin].pin_flags |= GPIO_PIN_INPUT; val = MTK_READ_4(sc, GPIO_PIOPOL); if (val & mask) { if (sc->pins[pin].pin_flags & GPIO_PIN_INPUT) { sc->pins[pin].pin_flags |= GPIO_PIN_INVIN; } else { sc->pins[pin].pin_flags |= GPIO_PIN_INVOUT; } } } static int mtk_gpio_attach(device_t dev) { struct mtk_gpio_softc *sc; phandle_t node; uint32_t i, num_pins; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, mtk_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources for device\n"); return (ENXIO); } MTK_GPIO_LOCK_INIT(sc); node = ofw_bus_get_node(dev); if (OF_hasprop(node, "clocks")) mtk_soc_start_clock(dev); if (OF_hasprop(node, "resets")) mtk_soc_reset_device(dev); if (OF_getprop(node, "ralink,register-map", sc->regs, GPIO_PIOMAX) <= 0) { device_printf(dev, "Failed to read register map\n"); return (ENXIO); } if (OF_hasprop(node, "ralink,num-gpios") && (OF_getencprop(node, "ralink,num-gpios", &num_pins, sizeof(num_pins)) >= 0)) sc->num_pins = num_pins; else sc->num_pins = MTK_GPIO_PINS; for (i = 0; i < sc->num_pins; i++) { sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_INVIN | GPIO_PIN_INVOUT; sc->pins[i].intr_polarity = INTR_POLARITY_HIGH; sc->pins[i].intr_trigger = INTR_TRIGGER_EDGE; snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d", device_get_unit(dev) + 'a', i); sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0'; mtk_gpio_pin_probe(sc, i); } if (mtk_pic_register_isrcs(sc) != 0) { device_printf(dev, "could not register PIC ISRCs\n"); goto fail; } - if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) { + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { device_printf(dev, "could not register PIC\n"); goto fail; } if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, mtk_gpio_intr, NULL, sc, &sc->intrhand) != 0) goto fail_pic; sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) goto fail_pic; return (0); fail_pic: intr_pic_deregister(dev, OF_xref_from_node(node)); fail: if(sc->intrhand != NULL) bus_teardown_intr(dev, sc->res[1], sc->intrhand); bus_release_resources(dev, mtk_gpio_spec, sc->res); MTK_GPIO_LOCK_DESTROY(sc); return (ENXIO); } static int mtk_gpio_detach(device_t dev) { struct mtk_gpio_softc *sc = device_get_softc(dev); phandle_t node; node = ofw_bus_get_node(dev); intr_pic_deregister(dev, OF_xref_from_node(node)); if (sc->intrhand != NULL) bus_teardown_intr(dev, sc->res[1], sc->intrhand); bus_release_resources(dev, mtk_gpio_spec, sc->res); MTK_GPIO_LOCK_DESTROY(sc); return (0); } static device_t mtk_gpio_get_bus(device_t dev) { struct mtk_gpio_softc *sc = device_get_softc(dev); return (sc->busdev); } static int mtk_gpio_pin_max(device_t dev, int *maxpin) { struct mtk_gpio_softc *sc = device_get_softc(dev); *maxpin = sc->num_pins - 1; return (0); } static int mtk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct mtk_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); *caps = sc->pins[pin].pin_caps; MTK_GPIO_UNLOCK(sc); return (0); } static int mtk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct mtk_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); *flags = sc->pins[pin].pin_flags; MTK_GPIO_UNLOCK(sc); return (0); } static int mtk_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct mtk_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1); name[GPIOMAXNAME - 1] = '\0'; return (0); } static int mtk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct mtk_gpio_softc *sc; int retval; sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); retval = mtk_gpio_pin_set_direction(sc, pin, flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)); if (retval == 0) retval = mtk_gpio_pin_set_invert(sc, pin, flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)); MTK_GPIO_UNLOCK(sc); return (retval); } static int mtk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct mtk_gpio_softc *sc; int ret; sc = device_get_softc(dev); ret = 0; if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); if (value) MTK_WRITE_4(sc, GPIO_PIOSET, (1u << pin)); else MTK_WRITE_4(sc, GPIO_PIORESET, (1u << pin)); MTK_GPIO_UNLOCK(sc); return (ret); } static int mtk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct mtk_gpio_softc *sc; uint32_t data; int ret; sc = device_get_softc(dev); ret = 0; if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); data = MTK_READ_4(sc, GPIO_PIODATA); *val = (data & (1u << pin)) ? 1 : 0; MTK_GPIO_UNLOCK(sc); return (ret); } static int mtk_gpio_pin_toggle(device_t dev, uint32_t pin) { struct mtk_gpio_softc *sc; int ret; sc = device_get_softc(dev); ret = 0; if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); if (!(sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT)) { ret = EINVAL; goto out; } MTK_WRITE_4(sc, GPIO_PIOTOG, (1u << pin)); out: MTK_GPIO_UNLOCK(sc); return (ret); } static int mtk_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct mtk_gpio_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= sc->num_pins) return (EINVAL); *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); return (0); } static void mtk_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct mtk_gpio_softc *sc; struct mtk_gpio_pin_irqsrc *pisrc; uint32_t pin, mask, val; sc = device_get_softc(dev); pisrc = (struct mtk_gpio_pin_irqsrc *)isrc; pin = pisrc->irq; mask = 1u << pin; MTK_GPIO_LOCK(sc); if (sc->pins[pin].intr_polarity == INTR_POLARITY_LOW) { val = MTK_READ_4(sc, GPIO_PIORENA) & ~mask; MTK_WRITE_4(sc, GPIO_PIORENA, val); val = MTK_READ_4(sc, GPIO_PIOFENA) | mask; MTK_WRITE_4(sc, GPIO_PIOFENA, val); } else { val = MTK_READ_4(sc, GPIO_PIOFENA) & ~mask; MTK_WRITE_4(sc, GPIO_PIOFENA, val); val = MTK_READ_4(sc, GPIO_PIORENA) | mask; MTK_WRITE_4(sc, GPIO_PIORENA, val); } MTK_GPIO_UNLOCK(sc); } static void mtk_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct mtk_gpio_softc *sc; struct mtk_gpio_pin_irqsrc *pisrc; uint32_t pin, mask, val; sc = device_get_softc(dev); pisrc = (struct mtk_gpio_pin_irqsrc *)isrc; pin = pisrc->irq; mask = 1u << pin; MTK_GPIO_LOCK(sc); val = MTK_READ_4(sc, GPIO_PIORENA) & ~mask; MTK_WRITE_4(sc, GPIO_PIORENA, val); val = MTK_READ_4(sc, GPIO_PIOFENA) & ~mask; MTK_WRITE_4(sc, GPIO_PIOFENA, val); MTK_GPIO_UNLOCK(sc); } static void mtk_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_gpio_pic_disable_intr(dev, isrc); } static void mtk_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_gpio_pic_enable_intr(dev, isrc); } static void mtk_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct mtk_gpio_softc *sc; struct mtk_gpio_pin_irqsrc *pisrc; pisrc = (struct mtk_gpio_pin_irqsrc *)isrc; sc = device_get_softc(dev); MTK_GPIO_LOCK(sc); MTK_WRITE_4(sc, GPIO_PIOINT, 1u << pisrc->irq); MTK_GPIO_UNLOCK(sc); } static int mtk_gpio_intr(void *arg) { struct mtk_gpio_softc *sc; uint32_t i, interrupts; sc = arg; interrupts = MTK_READ_4(sc, GPIO_PIOINT); for (i = 0; interrupts != 0; i++, interrupts >>= 1) { if ((interrupts & 0x1) == 0) continue; if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->dev, "spurious interrupt %d\n", i); } } return (FILTER_HANDLED); } static phandle_t mtk_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t mtk_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_gpio_probe), DEVMETHOD(device_attach, mtk_gpio_attach), DEVMETHOD(device_detach, mtk_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, mtk_gpio_get_bus), DEVMETHOD(gpio_pin_max, mtk_gpio_pin_max), DEVMETHOD(gpio_pin_getname, mtk_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, mtk_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, mtk_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, mtk_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, mtk_gpio_pin_get), DEVMETHOD(gpio_pin_set, mtk_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, mtk_gpio_pin_toggle), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, mtk_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, mtk_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, mtk_gpio_pic_map_intr), DEVMETHOD(pic_post_filter, mtk_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, mtk_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, mtk_gpio_pic_pre_ithread), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, mtk_gpio_get_node), DEVMETHOD_END }; static driver_t mtk_gpio_driver = { "gpio", mtk_gpio_methods, sizeof(struct mtk_gpio_softc), }; static devclass_t mtk_gpio_devclass; EARLY_DRIVER_MODULE(mtk_gpio_v1, simplebus, mtk_gpio_driver, mtk_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); Index: head/sys/mips/mediatek/mtk_gpio_v2.c =================================================================== --- head/sys/mips/mediatek/mtk_gpio_v2.c (revision 300148) +++ head/sys/mips/mediatek/mtk_gpio_v2.c (revision 300149) @@ -1,668 +1,668 @@ /*- * Copyright 2016 Stanislav Galabov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "pic_if.h" #define MTK_GPIO_PINS 32 struct mtk_gpio_pin_irqsrc { struct intr_irqsrc isrc; u_int irq; }; struct mtk_gpio_pin { uint32_t pin_caps; uint32_t pin_flags; enum intr_trigger intr_trigger; enum intr_polarity intr_polarity; char pin_name[GPIOMAXNAME]; struct mtk_gpio_pin_irqsrc pin_irqsrc; }; struct mtk_gpio_softc { device_t dev; device_t busdev; struct resource *res[2]; struct mtx mtx; struct mtk_gpio_pin pins[MTK_GPIO_PINS]; void *intrhand; uint32_t num_pins; uint32_t bank_id; }; #define PIC_INTR_ISRC(sc, irq) (&(sc)->pins[(irq)].pin_irqsrc.isrc) static struct resource_spec mtk_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static int mtk_gpio_probe(device_t dev); static int mtk_gpio_attach(device_t dev); static int mtk_gpio_detach(device_t dev); static int mtk_gpio_intr(void *arg); #define MTK_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx) #define MTK_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) #define MTK_GPIO_LOCK_INIT(sc) \ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ "mtk_gpio", MTX_SPIN) #define MTK_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) #define MTK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) #define MTK_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) /* Register definitions */ #define GPIO_REG(_sc, _reg) ((_reg) + (_sc)->bank_id * 0x4) #define GPIO_PIOINT(_sc) GPIO_REG((_sc), 0x0090) #define GPIO_PIOEDGE(_sc) GPIO_REG((_sc), 0x00A0) #define GPIO_PIORENA(_sc) GPIO_REG((_sc), 0x0050) #define GPIO_PIOFENA(_sc) GPIO_REG((_sc), 0x0060) #define GPIO_PIODATA(_sc) GPIO_REG((_sc), 0x0020) #define GPIO_PIODIR(_sc) GPIO_REG((_sc), 0x0000) #define GPIO_PIOPOL(_sc) GPIO_REG((_sc), 0x0010) #define GPIO_PIOSET(_sc) GPIO_REG((_sc), 0x0030) #define GPIO_PIORESET(_sc) GPIO_REG((_sc), 0x0040) static struct ofw_compat_data compat_data[] = { { "mtk,mt7621-gpio-bank", 1 }, { "mtk,mt7628-gpio-bank", 1 }, { NULL, 0 } }; static int mtk_gpio_probe(device_t dev) { phandle_t node; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); node = ofw_bus_get_node(dev); if (!OF_hasprop(node, "gpio-controller")) return (ENXIO); device_set_desc(dev, "MTK GPIO Controller (v2)"); return (BUS_PROBE_DEFAULT); } static int mtk_pic_register_isrcs(struct mtk_gpio_softc *sc) { int error; uint32_t irq; struct intr_irqsrc *isrc; const char *name; name = device_get_nameunit(sc->dev); for (irq = 0; irq < sc->num_pins; irq++) { sc->pins[irq].pin_irqsrc.irq = irq; isrc = PIC_INTR_ISRC(sc, irq); error = intr_isrc_register(isrc, sc->dev, 0, "%s", name); if (error != 0) { /* XXX call intr_isrc_deregister */ device_printf(sc->dev, "%s failed", __func__); return (error); } } return (0); } static int mtk_gpio_pin_set_direction(struct mtk_gpio_softc *sc, uint32_t pin, uint32_t dir) { uint32_t regval, mask = (1u << pin); if (!(sc->pins[pin].pin_caps & dir)) return (EINVAL); regval = MTK_READ_4(sc, GPIO_PIODIR(sc)); if (dir == GPIO_PIN_INPUT) regval &= ~mask; else regval |= mask; MTK_WRITE_4(sc, GPIO_PIODIR(sc), regval); sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); sc->pins[pin].pin_flags |= dir; return (0); } static int mtk_gpio_pin_set_invert(struct mtk_gpio_softc *sc, uint32_t pin, uint32_t val) { uint32_t regval, mask = (1u << pin); regval = MTK_READ_4(sc, GPIO_PIOPOL(sc)); if (val) regval |= mask; else regval &= ~mask; MTK_WRITE_4(sc, GPIO_PIOPOL(sc), regval); sc->pins[pin].pin_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT); sc->pins[pin].pin_flags |= val; return (0); } static void mtk_gpio_pin_probe(struct mtk_gpio_softc *sc, uint32_t pin) { uint32_t mask = (1u << pin); uint32_t val; /* Clear cached gpio config */ sc->pins[pin].pin_flags = 0; val = MTK_READ_4(sc, GPIO_PIORENA(sc)) | MTK_READ_4(sc, GPIO_PIOFENA(sc)); if (val & mask) { /* Pin is in interrupt mode */ sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE; val = MTK_READ_4(sc, GPIO_PIORENA(sc)); if (val & mask) sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH; else sc->pins[pin].intr_polarity = INTR_POLARITY_LOW; } val = MTK_READ_4(sc, GPIO_PIODIR(sc)); if (val & mask) sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT; else sc->pins[pin].pin_flags |= GPIO_PIN_INPUT; val = MTK_READ_4(sc, GPIO_PIOPOL(sc)); if (val & mask) { if (sc->pins[pin].pin_flags & GPIO_PIN_INPUT) { sc->pins[pin].pin_flags |= GPIO_PIN_INVIN; } else { sc->pins[pin].pin_flags |= GPIO_PIN_INVOUT; } } } static int mtk_gpio_attach(device_t dev) { struct mtk_gpio_softc *sc; phandle_t node; uint32_t i, num_pins, bank_id; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, mtk_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources for device\n"); return (ENXIO); } MTK_GPIO_LOCK_INIT(sc); node = ofw_bus_get_node(dev); if (OF_hasprop(node, "clocks")) mtk_soc_start_clock(dev); if (OF_hasprop(node, "resets")) mtk_soc_reset_device(dev); if (OF_hasprop(node, "mtk,bank-id") && (OF_getencprop(node, "mtk,bank-id", &bank_id, sizeof(bank_id)) >= 0)) sc->bank_id = bank_id; else sc->bank_id = device_get_unit(dev); if (OF_hasprop(node, "mtk,num-pins") && (OF_getencprop(node, "mtk,num-pins", &num_pins, sizeof(num_pins)) >= 0)) sc->num_pins = num_pins; else sc->num_pins = MTK_GPIO_PINS; for (i = 0; i < sc->num_pins; i++) { sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_INVIN | GPIO_PIN_INVOUT; sc->pins[i].intr_polarity = INTR_POLARITY_HIGH; sc->pins[i].intr_trigger = INTR_TRIGGER_EDGE; snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d", device_get_unit(dev) + 'a', i); sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0'; mtk_gpio_pin_probe(sc, i); } if (mtk_pic_register_isrcs(sc) != 0) { device_printf(dev, "could not register PIC ISRCs\n"); goto fail; } - if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) { + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { device_printf(dev, "could not register PIC\n"); goto fail; } if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, mtk_gpio_intr, NULL, sc, &sc->intrhand) != 0) goto fail_pic; sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) goto fail_pic; return (0); fail_pic: intr_pic_deregister(dev, OF_xref_from_node(node)); fail: if(sc->intrhand != NULL) bus_teardown_intr(dev, sc->res[1], sc->intrhand); bus_release_resources(dev, mtk_gpio_spec, sc->res); MTK_GPIO_LOCK_DESTROY(sc); return (ENXIO); } static int mtk_gpio_detach(device_t dev) { struct mtk_gpio_softc *sc = device_get_softc(dev); phandle_t node; node = ofw_bus_get_node(dev); intr_pic_deregister(dev, OF_xref_from_node(node)); if (sc->intrhand != NULL) bus_teardown_intr(dev, sc->res[1], sc->intrhand); bus_release_resources(dev, mtk_gpio_spec, sc->res); MTK_GPIO_LOCK_DESTROY(sc); return (0); } static device_t mtk_gpio_get_bus(device_t dev) { struct mtk_gpio_softc *sc = device_get_softc(dev); return (sc->busdev); } static int mtk_gpio_pin_max(device_t dev, int *maxpin) { struct mtk_gpio_softc *sc = device_get_softc(dev); *maxpin = sc->num_pins - 1; return (0); } static int mtk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct mtk_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); *caps = sc->pins[pin].pin_caps; MTK_GPIO_UNLOCK(sc); return (0); } static int mtk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct mtk_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); *flags = sc->pins[pin].pin_flags; MTK_GPIO_UNLOCK(sc); return (0); } static int mtk_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct mtk_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1); name[GPIOMAXNAME - 1] = '\0'; return (0); } static int mtk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct mtk_gpio_softc *sc; int retval; sc = device_get_softc(dev); if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); retval = mtk_gpio_pin_set_direction(sc, pin, flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)); if (retval == 0) retval = mtk_gpio_pin_set_invert(sc, pin, flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)); MTK_GPIO_UNLOCK(sc); return (retval); } static int mtk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct mtk_gpio_softc *sc; int ret; sc = device_get_softc(dev); ret = 0; if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); if (value) MTK_WRITE_4(sc, GPIO_PIOSET(sc), (1u << pin)); else MTK_WRITE_4(sc, GPIO_PIORESET(sc), (1u << pin)); MTK_GPIO_UNLOCK(sc); return (ret); } static int mtk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct mtk_gpio_softc *sc; uint32_t data; int ret; sc = device_get_softc(dev); ret = 0; if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); data = MTK_READ_4(sc, GPIO_PIODATA(sc)); *val = (data & (1u << pin)) ? 1 : 0; MTK_GPIO_UNLOCK(sc); return (ret); } static int mtk_gpio_pin_toggle(device_t dev, uint32_t pin) { struct mtk_gpio_softc *sc; uint32_t val; int ret; sc = device_get_softc(dev); ret = 0; if (pin >= sc->num_pins) return (EINVAL); MTK_GPIO_LOCK(sc); if(!(sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT)) { ret = EINVAL; goto out; } val = MTK_READ_4(sc, GPIO_PIODATA(sc)); val &= (1u << pin); if (val) MTK_WRITE_4(sc, GPIO_PIORESET(sc), (1u << pin)); else MTK_WRITE_4(sc, GPIO_PIOSET(sc), (1u << pin)); out: MTK_GPIO_UNLOCK(sc); return (ret); } static int mtk_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct mtk_gpio_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= sc->num_pins) return (EINVAL); *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); return (0); } static void mtk_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct mtk_gpio_softc *sc; struct mtk_gpio_pin_irqsrc *pisrc; uint32_t pin, mask, val; sc = device_get_softc(dev); pisrc = (struct mtk_gpio_pin_irqsrc *)isrc; pin = pisrc->irq; mask = 1u << pin; MTK_GPIO_LOCK(sc); if (sc->pins[pin].intr_polarity == INTR_POLARITY_LOW) { val = MTK_READ_4(sc, GPIO_PIORENA(sc)) & ~mask; MTK_WRITE_4(sc, GPIO_PIORENA(sc), val); val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) | mask; MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val); } else { val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) & ~mask; MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val); val = MTK_READ_4(sc, GPIO_PIORENA(sc)) | mask; MTK_WRITE_4(sc, GPIO_PIORENA(sc), val); } MTK_GPIO_UNLOCK(sc); } static void mtk_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct mtk_gpio_softc *sc; struct mtk_gpio_pin_irqsrc *pisrc; uint32_t pin, mask, val; sc = device_get_softc(dev); pisrc = (struct mtk_gpio_pin_irqsrc *)isrc; pin = pisrc->irq; mask = 1u << pin; MTK_GPIO_LOCK(sc); val = MTK_READ_4(sc, GPIO_PIORENA(sc)) & ~mask; MTK_WRITE_4(sc, GPIO_PIORENA(sc), val); val = MTK_READ_4(sc, GPIO_PIOFENA(sc)) & ~mask; MTK_WRITE_4(sc, GPIO_PIOFENA(sc), val); MTK_GPIO_UNLOCK(sc); } static void mtk_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_gpio_pic_disable_intr(dev, isrc); } static void mtk_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_gpio_pic_enable_intr(dev, isrc); } static void mtk_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct mtk_gpio_softc *sc; struct mtk_gpio_pin_irqsrc *pisrc; pisrc = (struct mtk_gpio_pin_irqsrc *)isrc; sc = device_get_softc(dev); MTK_GPIO_LOCK(sc); MTK_WRITE_4(sc, GPIO_PIOINT(sc), 1u << pisrc->irq); MTK_GPIO_UNLOCK(sc); } static int mtk_gpio_intr(void *arg) { struct mtk_gpio_softc *sc; uint32_t i, interrupts; sc = arg; interrupts = MTK_READ_4(sc, GPIO_PIOINT(sc)); for (i = 0; interrupts != 0; i++, interrupts >>= 1) { if ((interrupts & 0x1) == 0) continue; if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->dev, "spurious interrupt %d\n", i); } } return (FILTER_HANDLED); } static phandle_t mtk_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t mtk_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_gpio_probe), DEVMETHOD(device_attach, mtk_gpio_attach), DEVMETHOD(device_detach, mtk_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, mtk_gpio_get_bus), DEVMETHOD(gpio_pin_max, mtk_gpio_pin_max), DEVMETHOD(gpio_pin_getname, mtk_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, mtk_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, mtk_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, mtk_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, mtk_gpio_pin_get), DEVMETHOD(gpio_pin_set, mtk_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, mtk_gpio_pin_toggle), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, mtk_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, mtk_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, mtk_gpio_pic_map_intr), DEVMETHOD(pic_post_filter, mtk_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, mtk_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, mtk_gpio_pic_pre_ithread), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, mtk_gpio_get_node), DEVMETHOD_END }; static driver_t mtk_gpio_driver = { "gpio", mtk_gpio_methods, sizeof(struct mtk_gpio_softc), }; static devclass_t mtk_gpio_devclass; EARLY_DRIVER_MODULE(mtk_gpio_v2, simplebus, mtk_gpio_driver, mtk_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); Index: head/sys/mips/mediatek/mtk_intr_gic.c =================================================================== --- head/sys/mips/mediatek/mtk_intr_gic.c (revision 300148) +++ head/sys/mips/mediatek/mtk_intr_gic.c (revision 300149) @@ -1,370 +1,370 @@ /*- * Copyright (c) 2016 Stanislav Galabov * Copyright (c) 2015 Alexander Kabaev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define MTK_NIRQS 64 /* We'll only use 64 for now */ #define MTK_INTPOL 0x0100 #define MTK_INTTRIG 0x0180 #define MTK_INTDIS 0x0300 #define MTK_INTENA 0x0380 #define MTK_INTMASK 0x0400 #define MTK_INTSTAT 0x0480 #define MTK_MAPPIN(_i) (0x0500 + (4 * (_i))) #define MTK_MAPVPE(_i, _v) (0x2000 + (32 * (_i)) + (((_v) / 32) * 4)) #define MTK_INTPOL_POS 1 #define MTK_INTPOL_NEG 0 #define MTK_INTTRIG_EDGE 1 #define MTK_INTTRIG_LEVEL 0 #define MTK_PIN_BITS(_i) ((1 << 31) | (_i)) #define MTK_VPE_BITS(_v) (1 << ((_v) % 32)) static int mtk_gic_intr(void *); struct mtk_gic_irqsrc { struct intr_irqsrc isrc; u_int irq; }; struct mtk_gic_softc { device_t gic_dev; void * gic_intrhand; struct resource * gic_res[2]; struct mtk_gic_irqsrc gic_irqs[MTK_NIRQS]; struct mtx mutex; uint32_t nirqs; }; #define GIC_INTR_ISRC(sc, irq) (&(sc)->gic_irqs[(irq)].isrc) static struct resource_spec mtk_gic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */ { -1, 0 } }; static struct ofw_compat_data compat_data[] = { { "mti,gic", 1 }, { NULL, 0 } }; #define READ4(_sc, _reg) bus_read_4((_sc)->gic_res[0], (_reg)) #define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->gic_res[0], (_reg), (_val)) static int mtk_gic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "MTK Interrupt Controller (GIC)"); return (BUS_PROBE_DEFAULT); } static inline void gic_irq_unmask(struct mtk_gic_softc *sc, u_int irq) { WRITE4(sc, MTK_INTENA, (1u << (irq))); } static inline void gic_irq_mask(struct mtk_gic_softc *sc, u_int irq) { WRITE4(sc, MTK_INTDIS, (1u << (irq))); } static inline intptr_t gic_xref(device_t dev) { return (OF_xref_from_node(ofw_bus_get_node(dev))); } static int mtk_gic_register_isrcs(struct mtk_gic_softc *sc) { int error; uint32_t irq; struct intr_irqsrc *isrc; const char *name; name = device_get_nameunit(sc->gic_dev); for (irq = 0; irq < sc->nirqs; irq++) { sc->gic_irqs[irq].irq = irq; isrc = GIC_INTR_ISRC(sc, irq); error = intr_isrc_register(isrc, sc->gic_dev, 0, "%s", name); if (error != 0) { /* XXX call intr_isrc_deregister */ device_printf(sc->gic_dev, "%s failed", __func__); return (error); } } return (0); } static int mtk_gic_attach(device_t dev) { struct mtk_gic_softc *sc; intptr_t xref = gic_xref(dev); int i; sc = device_get_softc(dev); if (bus_alloc_resources(dev, mtk_gic_spec, sc->gic_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->gic_dev = dev; /* Initialize mutex */ mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN); /* Set the number of interrupts */ sc->nirqs = nitems(sc->gic_irqs); /* Mask all interrupts */ WRITE4(sc, MTK_INTDIS, 0xFFFFFFFF); /* All interrupts are of type level */ WRITE4(sc, MTK_INTTRIG, 0x00000000); /* All interrupts are of positive polarity */ WRITE4(sc, MTK_INTPOL, 0xFFFFFFFF); /* * Route all interrupts to pin 0 on VPE 0; */ for (i = 0; i < 32; i++) { WRITE4(sc, MTK_MAPPIN(i), MTK_PIN_BITS(0)); WRITE4(sc, MTK_MAPVPE(i, 0), MTK_VPE_BITS(0)); } /* Register the interrupts */ if (mtk_gic_register_isrcs(sc) != 0) { device_printf(dev, "could not register GIC ISRCs\n"); goto cleanup; } /* * Now, when everything is initialized, it's right time to * register interrupt controller to interrupt framefork. */ - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto cleanup; } cpu_establish_hardintr("gic", mtk_gic_intr, NULL, sc, 0, INTR_TYPE_CLK, NULL); return (0); cleanup: bus_release_resources(dev, mtk_gic_spec, sc->gic_res); return(ENXIO); } static int mtk_gic_intr(void *arg) { struct mtk_gic_softc *sc = arg; struct thread *td; uint32_t i, intr; td = curthread; /* Workaround: do not inflate intr nesting level */ td->td_intr_nesting_level--; intr = READ4(sc, MTK_INTSTAT) & READ4(sc, MTK_INTMASK); while ((i = fls(intr)) != 0) { i--; intr &= ~(1u << i); if (intr_isrc_dispatch(GIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->gic_dev, "Stray interrupt %u detected\n", i); gic_irq_mask(sc, i); continue; } } KASSERT(i == 0, ("all interrupts handled")); td->td_intr_nesting_level++; return (FILTER_HANDLED); } static int mtk_gic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { #ifdef FDT struct intr_map_data_fdt *daf; struct mtk_gic_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 3 || daf->cells[1] >= sc->nirqs) return (EINVAL); *isrcp = GIC_INTR_ISRC(sc, daf->cells[1]); return (0); #else return (ENOTSUP); #endif } static void mtk_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mtk_gic_irqsrc *)isrc)->irq; gic_irq_unmask(device_get_softc(dev), irq); } static void mtk_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mtk_gic_irqsrc *)isrc)->irq; gic_irq_mask(device_get_softc(dev), irq); } static void mtk_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_gic_disable_intr(dev, isrc); } static void mtk_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_gic_enable_intr(dev, isrc); } static void mtk_gic_post_filter(device_t dev, struct intr_irqsrc *isrc) { } #ifdef SMP static int mtk_gic_bind(device_t dev, struct intr_irqsrc *isrc) { return (EOPNOTSUPP); } static void mtk_gic_init_secondary(device_t dev) { } static void mtk_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus) { } #endif static device_method_t mtk_gic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_gic_probe), DEVMETHOD(device_attach, mtk_gic_attach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, mtk_gic_disable_intr), DEVMETHOD(pic_enable_intr, mtk_gic_enable_intr), DEVMETHOD(pic_map_intr, mtk_gic_map_intr), DEVMETHOD(pic_post_filter, mtk_gic_post_filter), DEVMETHOD(pic_post_ithread, mtk_gic_post_ithread), DEVMETHOD(pic_pre_ithread, mtk_gic_pre_ithread), #ifdef SMP DEVMETHOD(pic_bind, mtk_gic_bind), DEVMETHOD(pic_init_secondary, mtk_gic_init_secondary), DEVMETHOD(pic_ipi_send, mtk_gic_ipi_send), #endif { 0, 0 } }; static driver_t mtk_gic_driver = { "intc", mtk_gic_methods, sizeof(struct mtk_gic_softc), }; static devclass_t mtk_gic_devclass; EARLY_DRIVER_MODULE(intc_gic, simplebus, mtk_gic_driver, mtk_gic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: head/sys/mips/mediatek/mtk_intr_v1.c =================================================================== --- head/sys/mips/mediatek/mtk_intr_v1.c (revision 300148) +++ head/sys/mips/mediatek/mtk_intr_v1.c (revision 300149) @@ -1,357 +1,357 @@ /*- * Copyright (c) 2015 Stanislav Galabov * Copyright (c) 2015 Alexander Kabaev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define MTK_NIRQS 32 #define MTK_IRQ0STAT 0x0000 #define MTK_IRQ1STAT 0x0004 #define MTK_INTTYPE 0x0020 #define MTK_INTRAW 0x0030 #define MTK_INTENA 0x0034 #define MTK_INTDIS 0x0038 static int mtk_pic_intr(void *); struct mtk_pic_irqsrc { struct intr_irqsrc isrc; u_int irq; }; struct mtk_pic_softc { device_t pic_dev; void * pic_intrhand; struct resource * pic_res[2]; struct mtk_pic_irqsrc pic_irqs[MTK_NIRQS]; struct mtx mutex; uint32_t nirqs; }; #define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc) static struct resource_spec mtk_pic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt 1 */ // { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Parent interrupt 2 */ { -1, 0 } }; static struct ofw_compat_data compat_data[] = { { "ralink,rt2880-intc", 1 }, { "ralink,rt3050-intc", 1 }, { "ralink,rt3352-intc", 1 }, { "ralink,rt3883-intc", 1 }, { "ralink,rt5350-intc", 1 }, { "ralink,mt7620a-intc", 1 }, { NULL, 0 } }; #define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg) #define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val) static int mtk_pic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "MTK Interrupt Controller (v2)"); return (BUS_PROBE_DEFAULT); } static inline void pic_irq_unmask(struct mtk_pic_softc *sc, u_int irq) { WRITE4(sc, MTK_INTENA, (1u << (irq))); } static inline void pic_irq_mask(struct mtk_pic_softc *sc, u_int irq) { WRITE4(sc, MTK_INTDIS, (1u << (irq))); } static inline intptr_t pic_xref(device_t dev) { return (OF_xref_from_node(ofw_bus_get_node(dev))); } static int mtk_pic_register_isrcs(struct mtk_pic_softc *sc) { int error; uint32_t irq; struct intr_irqsrc *isrc; const char *name; name = device_get_nameunit(sc->pic_dev); for (irq = 0; irq < sc->nirqs; irq++) { sc->pic_irqs[irq].irq = irq; isrc = PIC_INTR_ISRC(sc, irq); error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s", name); if (error != 0) { /* XXX call intr_isrc_deregister */ device_printf(sc->pic_dev, "%s failed", __func__); return (error); } } return (0); } static int mtk_pic_attach(device_t dev) { struct mtk_pic_softc *sc; intptr_t xref = pic_xref(dev); sc = device_get_softc(dev); if (bus_alloc_resources(dev, mtk_pic_spec, sc->pic_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->pic_dev = dev; /* Initialize mutex */ mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN); /* Set the number of interrupts */ sc->nirqs = nitems(sc->pic_irqs); /* Mask all interrupts */ WRITE4(sc, MTK_INTDIS, 0x7FFFFFFF); /* But enable interrupt generation/masking */ WRITE4(sc, MTK_INTENA, 0x80000000); /* Set all interrupts to type 0 */ WRITE4(sc, MTK_INTTYPE, 0x00000000); /* Register the interrupts */ if (mtk_pic_register_isrcs(sc) != 0) { device_printf(dev, "could not register PIC ISRCs\n"); goto cleanup; } /* * Now, when everything is initialized, it's right time to * register interrupt controller to interrupt framefork. */ - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto cleanup; } if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK, mtk_pic_intr, NULL, sc, &sc->pic_intrhand)) { device_printf(dev, "could not setup irq handler\n"); intr_pic_deregister(dev, xref); goto cleanup; } return (0); cleanup: bus_release_resources(dev, mtk_pic_spec, sc->pic_res); return(ENXIO); } static int mtk_pic_intr(void *arg) { struct mtk_pic_softc *sc = arg; struct thread *td; uint32_t i, intr; td = curthread; /* Workaround: do not inflate intr nesting level */ td->td_intr_nesting_level--; #ifdef _notyet_ intr = READ4(sc, MTK_IRQ1STAT); while ((i = fls(intr)) != 0) { i--; intr &= ~(1u << i); if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); pic_irq_mask(sc, i); continue; } } KASSERT(i == 0, ("all interrupts handled")); #endif intr = READ4(sc, MTK_IRQ0STAT); while ((i = fls(intr)) != 0) { i--; intr &= ~(1u << i); if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); pic_irq_mask(sc, i); continue; } } KASSERT(i == 0, ("all interrupts handled")); td->td_intr_nesting_level++; return (FILTER_HANDLED); } static int mtk_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { #ifdef FDT struct intr_map_data_fdt *daf; struct mtk_pic_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= sc->nirqs) return (EINVAL); *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); return (0); #else return (ENOTSUP); #endif } static void mtk_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mtk_pic_irqsrc *)isrc)->irq; pic_irq_unmask(device_get_softc(dev), irq); } static void mtk_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mtk_pic_irqsrc *)isrc)->irq; pic_irq_mask(device_get_softc(dev), irq); } static void mtk_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_pic_disable_intr(dev, isrc); } static void mtk_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_pic_enable_intr(dev, isrc); } static void mtk_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static device_method_t mtk_pic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_pic_probe), DEVMETHOD(device_attach, mtk_pic_attach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, mtk_pic_disable_intr), DEVMETHOD(pic_enable_intr, mtk_pic_enable_intr), DEVMETHOD(pic_map_intr, mtk_pic_map_intr), DEVMETHOD(pic_post_filter, mtk_pic_post_filter), DEVMETHOD(pic_post_ithread, mtk_pic_post_ithread), DEVMETHOD(pic_pre_ithread, mtk_pic_pre_ithread), { 0, 0 } }; static driver_t mtk_pic_driver = { "intc", mtk_pic_methods, sizeof(struct mtk_pic_softc), }; static devclass_t mtk_pic_devclass; EARLY_DRIVER_MODULE(intc_v1, simplebus, mtk_pic_driver, mtk_pic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: head/sys/mips/mediatek/mtk_intr_v2.c =================================================================== --- head/sys/mips/mediatek/mtk_intr_v2.c (revision 300148) +++ head/sys/mips/mediatek/mtk_intr_v2.c (revision 300149) @@ -1,352 +1,352 @@ /*- * Copyright (c) 2015 Stanislav Galabov * Copyright (c) 2015 Alexander Kabaev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define MTK_NIRQS 32 #define MTK_IRQ0STAT 0x009c #define MTK_IRQ1STAT 0x00a0 #define MTK_INTTYPE 0x0000 #define MTK_INTRAW 0x00a4 #define MTK_INTENA 0x0080 #define MTK_INTDIS 0x0078 static int mtk_pic_intr(void *); struct mtk_pic_irqsrc { struct intr_irqsrc isrc; u_int irq; }; struct mtk_pic_softc { device_t pic_dev; void * pic_intrhand; struct resource * pic_res[2]; struct mtk_pic_irqsrc pic_irqs[MTK_NIRQS]; struct mtx mutex; uint32_t nirqs; }; #define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc) static struct resource_spec mtk_pic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt 1 */ // { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Parent interrupt 2 */ { -1, 0 } }; static struct ofw_compat_data compat_data[] = { { "ralink,mt7628an-intc", 1 }, { NULL, 0 } }; #define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg) #define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val) static int mtk_pic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "MTK Interrupt Controller (v2)"); return (BUS_PROBE_DEFAULT); } static inline void pic_irq_unmask(struct mtk_pic_softc *sc, u_int irq) { WRITE4(sc, MTK_INTENA, (1u << (irq))); } static inline void pic_irq_mask(struct mtk_pic_softc *sc, u_int irq) { WRITE4(sc, MTK_INTDIS, (1u << (irq))); } static inline intptr_t pic_xref(device_t dev) { return (OF_xref_from_node(ofw_bus_get_node(dev))); } static int mtk_pic_register_isrcs(struct mtk_pic_softc *sc) { int error; uint32_t irq; struct intr_irqsrc *isrc; const char *name; name = device_get_nameunit(sc->pic_dev); for (irq = 0; irq < sc->nirqs; irq++) { sc->pic_irqs[irq].irq = irq; isrc = PIC_INTR_ISRC(sc, irq); error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s", name); if (error != 0) { /* XXX call intr_isrc_deregister */ device_printf(sc->pic_dev, "%s failed", __func__); return (error); } } return (0); } static int mtk_pic_attach(device_t dev) { struct mtk_pic_softc *sc; intptr_t xref = pic_xref(dev); sc = device_get_softc(dev); if (bus_alloc_resources(dev, mtk_pic_spec, sc->pic_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->pic_dev = dev; /* Initialize mutex */ mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN); /* Set the number of interrupts */ sc->nirqs = nitems(sc->pic_irqs); /* Mask all interrupts */ WRITE4(sc, MTK_INTDIS, 0xFFFFFFFF); /* But enable interrupt generation/masking */ WRITE4(sc, MTK_INTENA, 0x00000000); /* Set all interrupts to type 0 */ WRITE4(sc, MTK_INTTYPE, 0xFFFFFFFF); /* Register the interrupts */ if (mtk_pic_register_isrcs(sc) != 0) { device_printf(dev, "could not register PIC ISRCs\n"); goto cleanup; } /* * Now, when everything is initialized, it's right time to * register interrupt controller to interrupt framefork. */ - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto cleanup; } if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK, mtk_pic_intr, NULL, sc, &sc->pic_intrhand)) { device_printf(dev, "could not setup irq handler\n"); intr_pic_deregister(dev, xref); goto cleanup; } return (0); cleanup: bus_release_resources(dev, mtk_pic_spec, sc->pic_res); return(ENXIO); } static int mtk_pic_intr(void *arg) { struct mtk_pic_softc *sc = arg; struct thread *td; uint32_t i, intr; td = curthread; /* Workaround: do not inflate intr nesting level */ td->td_intr_nesting_level--; #ifdef _notyet_ intr = READ4(sc, MTK_IRQ1STAT); while ((i = fls(intr)) != 0) { i--; intr &= ~(1u << i); if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); pic_irq_mask(sc, i); continue; } } KASSERT(i == 0, ("all interrupts handled")); #endif intr = READ4(sc, MTK_IRQ0STAT); while ((i = fls(intr)) != 0) { i--; intr &= ~(1u << i); if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); pic_irq_mask(sc, i); continue; } } KASSERT(i == 0, ("all interrupts handled")); td->td_intr_nesting_level++; return (FILTER_HANDLED); } static int mtk_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { #ifdef FDT struct intr_map_data_fdt *daf; struct mtk_pic_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= sc->nirqs) return (EINVAL); *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); return (0); #else return (ENOTSUP); #endif } static void mtk_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mtk_pic_irqsrc *)isrc)->irq; pic_irq_unmask(device_get_softc(dev), irq); } static void mtk_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mtk_pic_irqsrc *)isrc)->irq; pic_irq_mask(device_get_softc(dev), irq); } static void mtk_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_pic_disable_intr(dev, isrc); } static void mtk_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mtk_pic_enable_intr(dev, isrc); } static void mtk_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static device_method_t mtk_pic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_pic_probe), DEVMETHOD(device_attach, mtk_pic_attach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, mtk_pic_disable_intr), DEVMETHOD(pic_enable_intr, mtk_pic_enable_intr), DEVMETHOD(pic_map_intr, mtk_pic_map_intr), DEVMETHOD(pic_post_filter, mtk_pic_post_filter), DEVMETHOD(pic_post_ithread, mtk_pic_post_ithread), DEVMETHOD(pic_pre_ithread, mtk_pic_pre_ithread), { 0, 0 } }; static driver_t mtk_pic_driver = { "intc", mtk_pic_methods, sizeof(struct mtk_pic_softc), }; static devclass_t mtk_pic_devclass; EARLY_DRIVER_MODULE(intc_v2, simplebus, mtk_pic_driver, mtk_pic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: head/sys/mips/mediatek/mtk_pcie.c =================================================================== --- head/sys/mips/mediatek/mtk_pcie.c (revision 300148) +++ head/sys/mips/mediatek/mtk_pcie.c (revision 300149) @@ -1,1267 +1,1267 @@ /*- * Copyright (c) 2016 Stanislav Galabov. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_bus_if.h" #include "pcib_if.h" #include "pic_if.h" /* * Note: We only support PCIe at the moment. * Most SoCs in the Ralink/Mediatek family that we target actually don't * support PCI anyway, with the notable exceptions being RT3662/RT3883, which * support both PCI and PCIe. If there exists a board based on one of them * which is of interest in the future it shouldn't be too hard to enable PCI * support for it. */ /* Chip specific function declarations */ static int mtk_pcie_phy_init(device_t); static int mtk_pcie_phy_start(device_t); static int mtk_pcie_phy_stop(device_t); static int mtk_pcie_phy_mt7621_init(device_t); static int mtk_pcie_phy_mt7628_init(device_t); static int mtk_pcie_phy_mt7620_init(device_t); static int mtk_pcie_phy_rt3883_init(device_t); static void mtk_pcie_phy_setup_slots(device_t); /* Generic declarations */ struct mtx mtk_pci_mtx; MTX_SYSINIT(mtk_pci_mtx, &mtk_pci_mtx, "MTK PCIe mutex", MTX_SPIN); static int mtk_pci_intr(void *); static struct mtk_pci_softc *mt_sc = NULL; struct mtk_pci_range { u_long base; u_long len; }; #define FDT_RANGES_CELLS ((1 + 2 + 3) * 2) static void mtk_pci_range_dump(struct mtk_pci_range *range) { #ifdef DEBUG printf("\n"); printf(" base = 0x%08lx\n", range->base); printf(" len = 0x%08lx\n", range->len); #endif } static int mtk_pci_ranges_decode(phandle_t node, struct mtk_pci_range *io_space, struct mtk_pci_range *mem_space) { struct mtk_pci_range *pci_space; pcell_t ranges[FDT_RANGES_CELLS]; pcell_t addr_cells, size_cells, par_addr_cells; pcell_t *rangesptr; pcell_t cell0, cell1, cell2; int tuple_size, tuples, i, rv, len; /* * Retrieve 'ranges' property. */ if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) return (EINVAL); if (addr_cells != 3 || size_cells != 2) return (ERANGE); par_addr_cells = fdt_parent_addr_cells(node); if (par_addr_cells != 1) return (ERANGE); len = OF_getproplen(node, "ranges"); if (len > sizeof(ranges)) return (ENOMEM); if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) return (EINVAL); tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells + size_cells); tuples = len / tuple_size; /* * Initialize the ranges so that we don't have to worry about * having them all defined in the FDT. In particular, it is * perfectly fine not to want I/O space on PCI busses. */ bzero(io_space, sizeof(*io_space)); bzero(mem_space, sizeof(*mem_space)); rangesptr = &ranges[0]; for (i = 0; i < tuples; i++) { cell0 = fdt_data_get((void *)rangesptr, 1); rangesptr++; cell1 = fdt_data_get((void *)rangesptr, 1); rangesptr++; cell2 = fdt_data_get((void *)rangesptr, 1); rangesptr++; if (cell0 & 0x02000000) { pci_space = mem_space; } else if (cell0 & 0x01000000) { pci_space = io_space; } else { rv = ERANGE; goto out; } pci_space->base = fdt_data_get((void *)rangesptr, par_addr_cells); rangesptr += par_addr_cells; pci_space->len = fdt_data_get((void *)rangesptr, size_cells); rangesptr += size_cells; } rv = 0; out: return (rv); } static int mtk_pci_ranges(phandle_t node, struct mtk_pci_range *io_space, struct mtk_pci_range *mem_space) { int err; if ((err = mtk_pci_ranges_decode(node, io_space, mem_space)) != 0) { return (err); } mtk_pci_range_dump(io_space); mtk_pci_range_dump(mem_space); return (0); } static struct ofw_compat_data compat_data[] = { { "ralink,rt3883-pci", MTK_SOC_RT3883 }, { "mediatek,mt7620-pci", MTK_SOC_MT7620A }, { "mediatek,mt7628-pci", MTK_SOC_MT7628 }, { "mediatek,mt7621-pci", MTK_SOC_MT7621 }, { NULL, MTK_SOC_UNKNOWN } }; static int mtk_pci_probe(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); sc->socid = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (sc->socid == MTK_SOC_UNKNOWN) return (ENXIO); device_set_desc(dev, "MTK PCIe Controller"); return (0); } static int mtk_pci_attach(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); struct mtk_pci_range io_space, mem_space; phandle_t node; intptr_t xref; int i, rid; sc->sc_dev = dev; mt_sc = sc; sc->addr_mask = 0xffffffff; /* Request our memory */ rid = 0; sc->pci_res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->pci_res[0] == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } /* See how many interrupts we need */ if (sc->socid == MTK_SOC_MT7621) sc->sc_num_irq = 3; else { sc->sc_num_irq = 1; sc->pci_res[2] = sc->pci_res[3] = NULL; sc->pci_intrhand[1] = sc->pci_intrhand[2] = NULL; } /* Request our interrupts */ for (i = 1; i <= sc->sc_num_irq ; i++) { rid = i - 1; sc->pci_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->pci_res[i] == NULL) { device_printf(dev, "could not allocate interrupt " "resource %d\n", rid); goto cleanup_res; } } /* Parse our PCI 'ranges' property */ node = ofw_bus_get_node(dev); xref = OF_xref_from_node(node); if (mtk_pci_ranges(node, &io_space, &mem_space)) { device_printf(dev, "could not retrieve 'ranges' data\n"); goto cleanup_res; } /* Memory, I/O and IRQ resource limits */ sc->sc_io_base = io_space.base; sc->sc_io_size = io_space.len; sc->sc_mem_base = mem_space.base; sc->sc_mem_size = mem_space.len; sc->sc_irq_start = MTK_PCIE0_IRQ; sc->sc_irq_end = MTK_PCIE2_IRQ; /* Init resource managers for memory, I/O and IRQ */ sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "mtk pcie memory window"; if (rman_init(&sc->sc_mem_rman) != 0 || rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base, sc->sc_mem_base + sc->sc_mem_size - 1) != 0) { device_printf(dev, "failed to setup memory rman\n"); goto cleanup_res; } sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "mtk pcie io window"; if (rman_init(&sc->sc_io_rman) != 0 || rman_manage_region(&sc->sc_io_rman, sc->sc_io_base, sc->sc_io_base + sc->sc_io_size - 1) != 0) { device_printf(dev, "failed to setup io rman\n"); goto cleanup_res; } sc->sc_irq_rman.rm_type = RMAN_ARRAY; sc->sc_irq_rman.rm_descr = "mtk pcie irqs"; if (rman_init(&sc->sc_irq_rman) != 0 || rman_manage_region(&sc->sc_irq_rman, sc->sc_irq_start, sc->sc_irq_end) != 0) { device_printf(dev, "failed to setup irq rman\n"); goto cleanup_res; } /* Do SoC-specific PCIe initialization */ if (mtk_pcie_phy_init(dev)) { device_printf(dev, "pcie phy init failed\n"); goto cleanup_rman; } /* Register ourselves as an interrupt controller */ - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto cleanup_rman; } /* Set up our interrupt handler */ for (i = 1; i <= sc->sc_num_irq; i++) { sc->pci_intrhand[i - 1] = NULL; if (bus_setup_intr(dev, sc->pci_res[i], INTR_TYPE_MISC, mtk_pci_intr, NULL, sc, &sc->pci_intrhand[i - 1])) { device_printf(dev, "could not setup intr handler %d\n", i); goto cleanup; } } /* Attach our PCI child so bus enumeration can start */ if (device_add_child(dev, "pci", -1) == NULL) { device_printf(dev, "could not attach pci bus\n"); goto cleanup; } /* And finally, attach ourselves to the bus */ if (bus_generic_attach(dev)) { device_printf(dev, "could not attach to bus\n"); goto cleanup; } return (0); cleanup: #ifdef notyet intr_pic_unregister(dev, xref); #endif for (i = 1; i <= sc->sc_num_irq; i++) { if (sc->pci_intrhand[i - 1] != NULL) bus_teardown_intr(dev, sc->pci_res[i], sc->pci_intrhand[i - 1]); } cleanup_rman: mtk_pcie_phy_stop(dev); rman_fini(&sc->sc_irq_rman); rman_fini(&sc->sc_io_rman); rman_fini(&sc->sc_mem_rman); cleanup_res: mt_sc = NULL; if (sc->pci_res[0] != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->pci_res[0]); if (sc->pci_res[1] != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pci_res[1]); if (sc->pci_res[2] != NULL) bus_release_resource(dev, SYS_RES_IRQ, 1, sc->pci_res[2]); if (sc->pci_res[3] != NULL) bus_release_resource(dev, SYS_RES_IRQ, 2, sc->pci_res[3]); return (ENXIO); } static int mtk_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct mtk_pci_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = device_get_unit(dev); return (0); case PCIB_IVAR_BUS: *result = sc->sc_busno; return (0); } return (ENOENT); } static int mtk_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t result) { struct mtk_pci_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_BUS: sc->sc_busno = result; return (0); } return (ENOENT); } static struct resource * mtk_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct mtk_pci_softc *sc = device_get_softc(bus); struct resource *rv; struct rman *rm; switch (type) { case PCI_RES_BUS: return pci_domain_alloc_bus(0, child, rid, start, end, count, flags); case SYS_RES_IRQ: rm = &sc->sc_irq_rman; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if ((flags & RF_ACTIVE) && type != SYS_RES_IRQ) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int mtk_pci_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (type == PCI_RES_BUS) return (pci_domain_release_bus(0, child, rid, res)); return (bus_generic_release_resource(bus, child, type, rid, res)); } static int mtk_pci_adjust_resource(device_t bus, device_t child, int type, struct resource *res, rman_res_t start, rman_res_t end) { struct mtk_pci_softc *sc = device_get_softc(bus); struct rman *rm; switch (type) { case PCI_RES_BUS: return pci_domain_adjust_bus(0, child, res, start, end); case SYS_RES_IRQ: rm = &sc->sc_irq_rman; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: rm = NULL; break; } if (rm != NULL) return (rman_adjust_resource(res, start, end)); return (bus_generic_adjust_resource(bus, child, type, res, start, end)); } static inline int mtk_idx_to_irq(int idx) { return ((idx == 0) ? MTK_PCIE0_IRQ : (idx == 1) ? MTK_PCIE1_IRQ : (idx == 2) ? MTK_PCIE2_IRQ : -1); } static inline int mtk_irq_to_idx(int irq) { return ((irq == MTK_PCIE0_IRQ) ? 0 : (irq == MTK_PCIE1_IRQ) ? 1 : (irq == MTK_PCIE2_IRQ) ? 2 : -1); } static void mtk_pci_mask_irq(void *source) { MT_WRITE32(mt_sc, MTK_PCI_PCIENA, MT_READ32(mt_sc, MTK_PCI_PCIENA) & ~(1<<((int)source))); } static void mtk_pci_unmask_irq(void *source) { MT_WRITE32(mt_sc, MTK_PCI_PCIENA, MT_READ32(mt_sc, MTK_PCI_PCIENA) | (1<<((int)source))); } static int mtk_pci_setup_intr(device_t bus, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct mtk_pci_softc *sc = device_get_softc(bus); struct intr_event *event; int irq, error, irqidx; irq = rman_get_start(ires); if (irq < sc->sc_irq_start || irq > sc->sc_irq_end) return (EINVAL); irqidx = irq - sc->sc_irq_start; event = sc->sc_eventstab[irqidx]; if (event == NULL) { error = intr_event_create(&event, (void *)irq, 0, irq, mtk_pci_mask_irq, mtk_pci_unmask_irq, NULL, NULL, "pci intr%d:", irq); if (error == 0) { sc->sc_eventstab[irqidx] = event; } else { return (error); } } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); mtk_pci_unmask_irq((void*)irq); return (0); } static int mtk_pci_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct mtk_pci_softc *sc = device_get_softc(dev); int irq, result, irqidx; irq = rman_get_start(ires); if (irq < sc->sc_irq_start || irq > sc->sc_irq_end) return (EINVAL); irqidx = irq - sc->sc_irq_start; if (sc->sc_eventstab[irqidx] == NULL) panic("Trying to teardown unoccupied IRQ"); mtk_pci_mask_irq((void*)irq); result = intr_event_remove_handler(cookie); if (!result) sc->sc_eventstab[irqidx] = NULL; return (result); } static inline uint32_t mtk_pci_make_addr(int bus, int slot, int func, int reg) { uint32_t addr; addr = ((((reg & 0xf00) >> 8) << 24) | (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xfc) | (1 << 31)); return (addr); } static int mtk_pci_maxslots(device_t dev) { return (PCI_SLOTMAX); } static inline int mtk_pci_slot_has_link(device_t dev, int slot) { struct mtk_pci_softc *sc = device_get_softc(dev); return !!(sc->pcie_link_status & (1<addr_mask; MT_WRITE32(sc, MTK_PCI_CFGADDR, addr); switch (bytes % 4) { case 0: data = MT_READ32(sc, MTK_PCI_CFGDATA); break; case 1: data = MT_READ8(sc, MTK_PCI_CFGDATA + (reg & 0x3)); break; case 2: data = MT_READ16(sc, MTK_PCI_CFGDATA + (reg & 0x3)); break; default: panic("%s(): Wrong number of bytes (%d) requested!\n", __FUNCTION__, bytes % 4); } mtx_unlock_spin(&mtk_pci_mtx); return (data); } static void mtk_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct mtk_pci_softc *sc = device_get_softc(dev); uint32_t addr = 0, data = val; /* Do not write if slot has no link */ if (bus == 0 && mtk_pci_slot_has_link(dev, slot) == 0) return; mtx_lock_spin(&mtk_pci_mtx); addr = mtk_pci_make_addr(bus, slot, func, (reg & ~3)) & sc->addr_mask; MT_WRITE32(sc, MTK_PCI_CFGADDR, addr); switch (bytes % 4) { case 0: MT_WRITE32(sc, MTK_PCI_CFGDATA, data); break; case 1: MT_WRITE8(sc, MTK_PCI_CFGDATA + (reg & 0x3), data); break; case 2: MT_WRITE16(sc, MTK_PCI_CFGDATA + (reg & 0x3), data); break; default: panic("%s(): Wrong number of bytes (%d) requested!\n", __FUNCTION__, bytes % 4); } mtx_unlock_spin(&mtk_pci_mtx); } static int mtk_pci_route_interrupt(device_t pcib, device_t device, int pin) { int bus, sl, dev; bus = pci_get_bus(device); sl = pci_get_slot(device); dev = pci_get_device(device); if (bus != 0) panic("Unexpected bus number %d\n", bus); /* PCIe only */ switch (sl) { case 0: return MTK_PCIE0_IRQ; case 1: return MTK_PCIE0_IRQ + 1; case 2: return MTK_PCIE0_IRQ + 2; default: return (-1); } return (-1); } static device_method_t mtk_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_pci_probe), DEVMETHOD(device_attach, mtk_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, mtk_pci_read_ivar), DEVMETHOD(bus_write_ivar, mtk_pci_write_ivar), DEVMETHOD(bus_alloc_resource, mtk_pci_alloc_resource), DEVMETHOD(bus_release_resource, mtk_pci_release_resource), DEVMETHOD(bus_adjust_resource, mtk_pci_adjust_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, mtk_pci_setup_intr), DEVMETHOD(bus_teardown_intr, mtk_pci_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, mtk_pci_maxslots), DEVMETHOD(pcib_read_config, mtk_pci_read_config), DEVMETHOD(pcib_write_config, mtk_pci_write_config), DEVMETHOD(pcib_route_interrupt, mtk_pci_route_interrupt), /* OFW bus interface */ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; static driver_t mtk_pci_driver = { "pcib", mtk_pci_methods, sizeof(struct mtk_pci_softc), }; static devclass_t mtk_pci_devclass; DRIVER_MODULE(mtk_pci, simplebus, mtk_pci_driver, mtk_pci_devclass, 0, 0); /* Our interrupt handler */ static int mtk_pci_intr(void *arg) { struct mtk_pci_softc *sc = arg; struct intr_event *event; uint32_t reg, irq, irqidx; reg = MT_READ32(sc, MTK_PCI_PCIINT); for (irq = sc->sc_irq_start; irq <= sc->sc_irq_end; irq++) { if (reg & (1u<sc_irq_start; event = sc->sc_eventstab[irqidx]; if (!event || TAILQ_EMPTY(&event->ie_handlers)) { if (irq != 0) printf("Stray PCI IRQ %d\n", irq); continue; } intr_event_handle(event, NULL); } } return (FILTER_HANDLED); } /* PCIe SoC-specific initialization */ static int mtk_pcie_phy_init(device_t dev) { struct mtk_pci_softc *sc; /* Get our softc */ sc = device_get_softc(dev); /* We don't know how many slots we have yet */ sc->num_slots = 0; /* Handle SoC specific PCIe init */ switch (sc->socid) { case MTK_SOC_MT7628: /* Fallthrough */ case MTK_SOC_MT7688: if (mtk_pcie_phy_mt7628_init(dev)) return (ENXIO); break; case MTK_SOC_MT7621: if (mtk_pcie_phy_mt7621_init(dev)) return (ENXIO); break; case MTK_SOC_MT7620A: if (mtk_pcie_phy_mt7620_init(dev)) return (ENXIO); break; case MTK_SOC_RT3662: /* Fallthrough */ case MTK_SOC_RT3883: if (mtk_pcie_phy_rt3883_init(dev)) return (ENXIO); break; default: device_printf(dev, "unsupported device %x\n", sc->socid); return (ENXIO); } /* * If we were successful so far go and set up the PCIe slots, so we * may allocate mem/io/irq resources and enumerate busses later. */ mtk_pcie_phy_setup_slots(dev); return (0); } static int mtk_pcie_phy_start(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); if (sc->socid == MTK_SOC_MT7621 && (mtk_sysctl_get(SYSCTL_REVID) & SYSCTL_REVID_MASK) != SYSCTL_MT7621_REV_E) { if (fdt_reset_assert_all(dev)) return (ENXIO); } else { if (fdt_reset_deassert_all(dev)) return (ENXIO); } if (fdt_clock_enable_all(dev)) return (ENXIO); return (0); } static int mtk_pcie_phy_stop(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); if (sc->socid == MTK_SOC_MT7621 && (mtk_sysctl_get(SYSCTL_REVID) & SYSCTL_REVID_MASK) != SYSCTL_MT7621_REV_E) { if (fdt_reset_deassert_all(dev)) return (ENXIO); } else { if (fdt_reset_assert_all(dev)) return (ENXIO); } if (fdt_clock_disable_all(dev)) return (ENXIO); return (0); } #define mtk_pcie_phy_set(_sc, _reg, _s, _n, _v) \ MT_WRITE32((_sc), (_reg), ((MT_READ32((_sc), (_reg)) & \ (~(((1ull << (_n)) - 1) << (_s)))) | ((_v) << (_s)))) static void mtk_pcie_phy_mt7621_bypass_pipe_rst(struct mtk_pci_softc *sc, uint32_t off) { mtk_pcie_phy_set(sc, off + 0x002c, 12, 1, 1); mtk_pcie_phy_set(sc, off + 0x002c, 4, 1, 1); mtk_pcie_phy_set(sc, off + 0x012c, 12, 1, 1); mtk_pcie_phy_set(sc, off + 0x012c, 4, 1, 1); mtk_pcie_phy_set(sc, off + 0x102c, 12, 1, 1); mtk_pcie_phy_set(sc, off + 0x102c, 4, 1, 1); } static void mtk_pcie_phy_mt7621_setup_ssc(struct mtk_pci_softc *sc, uint32_t off) { uint32_t xtal_sel; xtal_sel = mtk_sysctl_get(SYSCTL_SYSCFG) >> 6; xtal_sel &= 0x7; mtk_pcie_phy_set(sc, off + 0x400, 8, 1, 1); mtk_pcie_phy_set(sc, off + 0x400, 9, 2, 0); mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 1); mtk_pcie_phy_set(sc, off + 0x100, 4, 1, 1); mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 0); mtk_pcie_phy_set(sc, off + 0x100, 5, 1, 0); if (xtal_sel <= 5 && xtal_sel >= 3) { mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 1); mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x1a); mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x1a); } else { mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 0); if (xtal_sel >= 6) { mtk_pcie_phy_set(sc, off + 0x4bc, 4, 2, 0x01); mtk_pcie_phy_set(sc, off + 0x49c, 0, 31, 0x18000000); mtk_pcie_phy_set(sc, off + 0x4a4, 0, 16, 0x18d); mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x4a); mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x4a); mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x11); mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x11); } else { mtk_pcie_phy_set(sc, off + 0x4a8, 0, 12, 0x1a); mtk_pcie_phy_set(sc, off + 0x4a8, 16, 12, 0x1a); } } mtk_pcie_phy_set(sc, off + 0x4a0, 5, 1, 1); mtk_pcie_phy_set(sc, off + 0x490, 22, 2, 2); mtk_pcie_phy_set(sc, off + 0x490, 18, 4, 6); mtk_pcie_phy_set(sc, off + 0x490, 12, 4, 2); mtk_pcie_phy_set(sc, off + 0x490, 8, 4, 1); mtk_pcie_phy_set(sc, off + 0x4ac, 16, 3, 0); mtk_pcie_phy_set(sc, off + 0x490, 1, 3, 2); if (xtal_sel <= 5 && xtal_sel >= 3) { mtk_pcie_phy_set(sc, off + 0x414, 6, 2, 1); mtk_pcie_phy_set(sc, off + 0x414, 5, 1, 1); } mtk_pcie_phy_set(sc, off + 0x414, 28, 2, 1); mtk_pcie_phy_set(sc, off + 0x040, 17, 4, 7); mtk_pcie_phy_set(sc, off + 0x040, 16, 1, 1); mtk_pcie_phy_set(sc, off + 0x140, 17, 4, 7); mtk_pcie_phy_set(sc, off + 0x140, 16, 1, 1); mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 1); mtk_pcie_phy_set(sc, off + 0x100, 5, 1, 1); mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 0); mtk_pcie_phy_set(sc, off + 0x100, 4, 1, 0); } /* XXX: ugly, we need to fix this at some point */ #define MT7621_GPIO_CTRL0 *((volatile uint32_t *)0xbe000600) #define MT7621_GPIO_DATA0 *((volatile uint32_t *)0xbe000620) #define mtk_gpio_clr_set(_reg, _clr, _set) \ do { \ (_reg) = ((_reg) & (_clr)) | (_set); \ } while (0) static int mtk_pcie_phy_mt7621_init(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); /* First off, stop the PHY */ if (mtk_pcie_phy_stop(dev)) return (ENXIO); /* PCIe resets are GPIO pins */ mtk_sysctl_clr_set(SYSCTL_GPIOMODE, MT7621_PERST_GPIO_MODE | MT7621_UARTL3_GPIO_MODE, MT7621_PERST_GPIO | MT7621_UARTL3_GPIO); /* Set GPIO pins as outputs */ mtk_gpio_clr_set(MT7621_GPIO_CTRL0, 0, MT7621_PCIE_RST); /* Assert resets to PCIe devices */ mtk_gpio_clr_set(MT7621_GPIO_DATA0, MT7621_PCIE_RST, 0); /* Give everything a chance to sink in */ DELAY(100000); /* Now start the PHY again */ if (mtk_pcie_phy_start(dev)) return (ENXIO); /* Wait for things to settle */ DELAY(100000); /* Only apply below to REV-E hardware */ if ((mtk_sysctl_get(SYSCTL_REVID) & SYSCTL_REVID_MASK) == SYSCTL_MT7621_REV_E) mtk_pcie_phy_mt7621_bypass_pipe_rst(sc, 0x9000); /* Setup PCIe ports 0 and 1 */ mtk_pcie_phy_mt7621_setup_ssc(sc, 0x9000); /* Setup PCIe port 2 */ mtk_pcie_phy_mt7621_setup_ssc(sc, 0xa000); /* Deassert resets to PCIe devices */ mtk_gpio_clr_set(MT7621_GPIO_DATA0, 0, MT7621_PCIE_RST); /* Set number of slots supported */ sc->num_slots = 3; /* Give it a chance to sink in */ DELAY(100000); return (0); } static void mtk_pcie_phy_mt7628_setup(struct mtk_pci_softc *sc, uint32_t off) { uint32_t xtal_sel; xtal_sel = mtk_sysctl_get(SYSCTL_SYSCFG) >> 6; xtal_sel &= 0x1; mtk_pcie_phy_set(sc, off + 0x400, 8, 1, 1); mtk_pcie_phy_set(sc, off + 0x400, 9, 2, 0); mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 1); mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 0); mtk_pcie_phy_set(sc, off + 0x4ac, 16, 3, 3); if (xtal_sel == 1) { mtk_pcie_phy_set(sc, off + 0x4bc, 24, 8, 0x7d); mtk_pcie_phy_set(sc, off + 0x490, 12, 4, 0x08); mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 0x01); mtk_pcie_phy_set(sc, off + 0x4c0, 0, 32, 0x1f400000); mtk_pcie_phy_set(sc, off + 0x4a4, 0, 16, 0x013d); mtk_pcie_phy_set(sc, off + 0x4a8, 16, 16, 0x74); mtk_pcie_phy_set(sc, off + 0x4a8, 0, 16, 0x74); } else { mtk_pcie_phy_set(sc, off + 0x4bc, 24, 8, 0x64); mtk_pcie_phy_set(sc, off + 0x490, 12, 4, 0x0a); mtk_pcie_phy_set(sc, off + 0x490, 6, 2, 0x00); mtk_pcie_phy_set(sc, off + 0x4c0, 0, 32, 0x19000000); mtk_pcie_phy_set(sc, off + 0x4a4, 0, 16, 0x018d); mtk_pcie_phy_set(sc, off + 0x4a8, 16, 16, 0x4a); mtk_pcie_phy_set(sc, off + 0x4a8, 0, 16, 0x4a); } mtk_pcie_phy_set(sc, off + 0x498, 0, 8, 5); mtk_pcie_phy_set(sc, off + 0x000, 5, 1, 1); mtk_pcie_phy_set(sc, off + 0x000, 4, 1, 0); } static int mtk_pcie_phy_mt7628_init(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); /* Set PCIe reset to normal mode */ mtk_sysctl_clr_set(SYSCTL_GPIOMODE, MT7628_PERST_GPIO_MODE, MT7628_PERST); /* Start the PHY */ if (mtk_pcie_phy_start(dev)) return (ENXIO); /* Give it a chance to sink in */ DELAY(100000); /* Setup the PHY */ mtk_pcie_phy_mt7628_setup(sc, 0x9000); /* Deassert PCIe device reset */ MT_CLR_SET32(sc, MTK_PCI_PCICFG, MTK_PCI_RESET, 0); /* Set number of slots supported */ sc->num_slots = 1; return (0); } static int mtk_pcie_phy_mt7620_wait_busy(struct mtk_pci_softc *sc) { uint32_t reg_value, retry; reg_value = retry = 0; while (retry++ < MT7620_MAX_RETRIES) { reg_value = MT_READ32(sc, MT7620_PCIE_PHY_CFG); if (reg_value & PHY_BUSY) DELAY(100000); else break; } if (retry >= MT7620_MAX_RETRIES) return (ENXIO); return (0); } static int mtk_pcie_phy_mt7620_set(struct mtk_pci_softc *sc, uint32_t reg, uint32_t val) { uint32_t reg_val; if (mtk_pcie_phy_mt7620_wait_busy(sc)) return (ENXIO); reg_val = PHY_MODE_WRITE | ((reg & 0xff) << PHY_ADDR_OFFSET) | (val & 0xff); MT_WRITE32(sc, MT7620_PCIE_PHY_CFG, reg_val); DELAY(1000); if (mtk_pcie_phy_mt7620_wait_busy(sc)) return (ENXIO); return (0); } static int mtk_pcie_phy_mt7620_init(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); /* * The below sets the PCIe PHY to bypass the PCIe DLL and enables * "elastic buffer control", whatever that may be... */ if (mtk_pcie_phy_mt7620_set(sc, 0x00, 0x80) || mtk_pcie_phy_mt7620_set(sc, 0x01, 0x04) || mtk_pcie_phy_mt7620_set(sc, 0x68, 0x84)) return (ENXIO); /* Stop PCIe */ if (mtk_pcie_phy_stop(dev)) return (ENXIO); /* Restore PPLL to a sane state before going on */ mtk_sysctl_clr_set(MT7620_PPLL_DRV, LC_CKDRVPD, PDRV_SW_SET); /* No PCIe on the MT7620N */ if (!(mtk_sysctl_get(SYSCTL_REVID) & MT7620_PKG_BGA)) { device_printf(dev, "PCIe disabled for MT7620N\n"); mtk_sysctl_clr_set(MT7620_PPLL_CFG0, 0, PPLL_SW_SET); mtk_sysctl_clr_set(MT7620_PPLL_CFG1, 0, PPLL_PD); return (ENXIO); } /* PCIe device reset pin is in normal mode */ mtk_sysctl_clr_set(SYSCTL_GPIOMODE, MT7620_PERST_GPIO_MODE, MT7620_PERST); /* Enable PCIe now */ if (mtk_pcie_phy_start(dev)) return (ENXIO); /* Give it a chance to sink in */ DELAY(100000); /* If PLL is not locked - bail */ if (!(mtk_sysctl_get(MT7620_PPLL_CFG1) & PPLL_LOCKED)) { device_printf(dev, "no PPLL not lock\n"); mtk_pcie_phy_stop(dev); return (ENXIO); } /* Configure PCIe PLL */ mtk_sysctl_clr_set(MT7620_PPLL_DRV, LC_CKDRVOHZ | LC_CKDRVHZ, LC_CKDRVPD | PDRV_SW_SET); /* and give it a chance to settle */ DELAY(100000); /* Deassert PCIe device reset */ MT_CLR_SET32(sc, MTK_PCI_PCICFG, MTK_PCI_RESET, 0); /* MT7620 supports one PCIe slot */ sc->num_slots = 1; return (0); } static int mtk_pcie_phy_rt3883_init(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); /* Enable PCI host mode and PCIe RC mode */ mtk_sysctl_clr_set(SYSCTL_SYSCFG1, 0, RT3883_PCI_HOST_MODE | RT3883_PCIE_RC_MODE); /* Enable PCIe PHY */ if (mtk_pcie_phy_start(dev)) return (ENXIO); /* Disable PCI, we only support PCIe for now */ mtk_sysctl_clr_set(SYSCTL_RSTCTRL, 0, RT3883_PCI_RST); mtk_sysctl_clr_set(SYSCTL_CLKCFG1, RT3883_PCI_CLK, 0); /* Give things a chance to sink in */ DELAY(500000); /* Set PCIe port number to 0 and lift PCIe reset */ MT_WRITE32(sc, MTK_PCI_PCICFG, 0); /* Configure PCI Arbiter */ MT_WRITE32(sc, MTK_PCI_ARBCTL, 0x79); /* We have a single PCIe slot */ sc->num_slots = 1; return (0); } static void mtk_pcie_phy_setup_slots(device_t dev) { struct mtk_pci_softc *sc = device_get_softc(dev); uint32_t bar0_val, val; int i; /* Disable all PCIe interrupts */ MT_WRITE32(sc, MTK_PCI_PCIENA, 0); /* Default bar0_val is 64M, enabled */ bar0_val = 0x03FF0001; /* But we override it to 2G, enabled for some SoCs */ if (sc->socid == MTK_SOC_MT7620A || sc->socid == MTK_SOC_MT7628 || sc->socid == MTK_SOC_MT7688 || sc->socid == MTK_SOC_MT7621) bar0_val = 0x7FFF0001; /* We still don't know which slots have linked up */ sc->pcie_link_status = 0; /* XXX: I am not sure if this delay is really necessary */ DELAY(500000); /* * See which slots have links and mark them. * Set up all slots' BARs and make them look like PCIe bridges. */ for (i = 0; i < sc->num_slots; i++) { /* If slot has link - mark it */ if (MT_READ32(sc, MTK_PCIE_STATUS(i)) & 1) sc->pcie_link_status |= (1< * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include "opt_hwpmc_hooks.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #include #endif #include "pic_if.h" #define NHARD_IRQS 6 #define NSOFT_IRQS 2 #define NREAL_IRQS (NHARD_IRQS + NSOFT_IRQS) static int mips_pic_intr(void *); struct mips_pic_irqsrc { struct intr_irqsrc isrc; struct resource *res; u_int irq; }; struct mips_pic_softc { device_t pic_dev; struct mips_pic_irqsrc pic_irqs[NREAL_IRQS]; struct rman pic_irq_rman; struct mtx mutex; uint32_t nirqs; }; static struct mips_pic_softc *pic_sc; #define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc) #ifdef FDT static struct ofw_compat_data compat_data[] = { {"mti,cpu-interrupt-controller", true}, {NULL, false} }; #endif #ifndef FDT static void mips_pic_identify(driver_t *drv, device_t parent) { BUS_ADD_CHILD(parent, 0, "cpupic", 0); } #endif static int mips_pic_probe(device_t dev) { #ifdef FDT if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); #endif device_set_desc(dev, "MIPS32 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static inline void pic_irq_unmask(struct mips_pic_softc *sc, u_int irq) { mips_wr_status(mips_rd_status() | ((1 << irq) << 8)); } static inline void pic_irq_mask(struct mips_pic_softc *sc, u_int irq) { mips_wr_status(mips_rd_status() & ~((1 << irq) << 8)); } static inline intptr_t pic_xref(device_t dev) { #ifdef FDT return (OF_xref_from_node(ofw_bus_get_node(dev))); #else return (0); #endif } static int mips_pic_register_isrcs(struct mips_pic_softc *sc) { int error; uint32_t irq, i, tmpirq; struct intr_irqsrc *isrc; char *name; for (irq = 0; irq < sc->nirqs; irq++) { sc->pic_irqs[irq].irq = irq; sc->pic_irqs[irq].res = rman_reserve_resource(&sc->pic_irq_rman, irq, irq, 1, RF_ACTIVE, sc->pic_dev); if (sc->pic_irqs[irq].res == NULL) { device_printf(sc->pic_dev, "%s failed to alloc resource for irq %u", __func__, irq); return (ENOMEM); } isrc = PIC_INTR_ISRC(sc, irq); if (irq < NSOFT_IRQS) { name = "sint"; tmpirq = irq; } else { name = "int"; tmpirq = irq - NSOFT_IRQS; } error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s%u", name, tmpirq); if (error != 0) { for (i = 0; i < irq; i++) { intr_isrc_deregister(PIC_INTR_ISRC(sc, i)); } device_printf(sc->pic_dev, "%s failed", __func__); return (error); } } return (0); } static int mips_pic_attach(device_t dev) { struct mips_pic_softc *sc; intptr_t xref = pic_xref(dev); if (pic_sc) return (ENXIO); sc = device_get_softc(dev); sc->pic_dev = dev; pic_sc = sc; /* Initialize mutex */ mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN); /* Set the number of interrupts */ sc->nirqs = nitems(sc->pic_irqs); /* Init the IRQ rman */ sc->pic_irq_rman.rm_type = RMAN_ARRAY; sc->pic_irq_rman.rm_descr = "MIPS PIC IRQs"; if (rman_init(&sc->pic_irq_rman) != 0 || rman_manage_region(&sc->pic_irq_rman, 0, sc->nirqs - 1) != 0) { device_printf(dev, "failed to setup IRQ rman\n"); goto cleanup; } /* Register the interrupts */ if (mips_pic_register_isrcs(sc) != 0) { device_printf(dev, "could not register PIC ISRCs\n"); goto cleanup; } /* * Now, when everything is initialized, it's right time to * register interrupt controller to interrupt framefork. */ - if (intr_pic_register(dev, xref) != 0) { + if (intr_pic_register(dev, xref) == NULL) { device_printf(dev, "could not register PIC\n"); goto cleanup; } /* Claim our root controller role */ if (intr_pic_claim_root(dev, xref, mips_pic_intr, sc, 0) != 0) { device_printf(dev, "could not set PIC as a root\n"); intr_pic_deregister(dev, xref); goto cleanup; } return (0); cleanup: return(ENXIO); } int mips_pic_intr(void *arg) { struct mips_pic_softc *sc = arg; register_t cause, status; int i, intr; cause = mips_rd_cause(); status = mips_rd_status(); intr = (cause & MIPS_INT_MASK) >> 8; /* * Do not handle masked interrupts. They were masked by * pre_ithread function (mips_mask_XXX_intr) and will be * unmasked once ithread is through with handler */ intr &= (status & MIPS_INT_MASK) >> 8; while ((i = fls(intr)) != 0) { i--; /* Get a 0-offset interrupt. */ intr &= ~(1 << i); if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), curthread->td_intr_frame) != 0) { device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); pic_irq_mask(sc, i); continue; } } KASSERT(i == 0, ("all interrupts handled")); #ifdef HWPMC_HOOKS if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) { struct trapframe *tf = PCPU_GET(curthread)->td_intr_frame; pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); } #endif return (FILTER_HANDLED); } static void mips_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mips_pic_irqsrc *)isrc)->irq; pic_irq_mask(device_get_softc(dev), irq); } static void mips_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; irq = ((struct mips_pic_irqsrc *)isrc)->irq; pic_irq_unmask(device_get_softc(dev), irq); } static int mips_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { #ifdef FDT struct intr_map_data_fdt *daf; struct mips_pic_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); sc = device_get_softc(dev); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= sc->nirqs) return (EINVAL); *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); return (0); #else return (ENOTSUP); #endif } static void mips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { mips_pic_disable_intr(dev, isrc); } static void mips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { mips_pic_enable_intr(dev, isrc); } static void mips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static device_method_t mips_pic_methods[] = { /* Device interface */ #ifndef FDT DEVMETHOD(device_identify, mips_pic_identify), #endif DEVMETHOD(device_probe, mips_pic_probe), DEVMETHOD(device_attach, mips_pic_attach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, mips_pic_disable_intr), DEVMETHOD(pic_enable_intr, mips_pic_enable_intr), DEVMETHOD(pic_map_intr, mips_pic_map_intr), DEVMETHOD(pic_pre_ithread, mips_pic_pre_ithread), DEVMETHOD(pic_post_ithread, mips_pic_post_ithread), DEVMETHOD(pic_post_filter, mips_pic_post_filter), { 0, 0 } }; static driver_t mips_pic_driver = { "cpupic", mips_pic_methods, sizeof(struct mips_pic_softc), }; static devclass_t mips_pic_devclass; #ifdef FDT EARLY_DRIVER_MODULE(cpupic, ofwbus, mips_pic_driver, mips_pic_devclass, 0, 0, BUS_PASS_INTERRUPT); #else EARLY_DRIVER_MODULE(cpupic, nexus, mips_pic_driver, mips_pic_devclass, 0, 0, BUS_PASS_INTERRUPT); #endif void cpu_init_interrupts(void) { } void cpu_establish_hardintr(const char *name, driver_filter_t *filt, void (*handler)(void*), void *arg, int irq, int flags, void **cookiep) { int res; /* * We have 6 levels, but thats 0 - 5 (not including 6) */ if (irq < 0 || irq >= NHARD_IRQS) panic("%s called for unknown hard intr %d", __func__, irq); KASSERT(pic_sc != NULL, ("%s: no pic", __func__)); irq += NSOFT_IRQS; res = intr_setup_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res, filt, handler, arg, flags, cookiep); if (res != 0) panic("Unable to add hard IRQ %d handler", irq); } void cpu_establish_softintr(const char *name, driver_filter_t *filt, void (*handler)(void*), void *arg, int irq, int flags, void **cookiep) { int res; if (irq < 0 || irq > NSOFT_IRQS) panic("%s called for unknown soft intr %d", __func__, irq); KASSERT(pic_sc != NULL, ("%s: no pic", __func__)); res = intr_setup_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res, filt, handler, arg, flags, cookiep); if (res != 0) panic("Unable to add soft IRQ %d handler", irq); } Index: head/sys/sys/intr.h =================================================================== --- head/sys/sys/intr.h (revision 300148) +++ head/sys/sys/intr.h (revision 300149) @@ -1,167 +1,167 @@ /*- * Copyright (c) 2015-2016 Svatopluk Kraus * Copyright (c) 2015-2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_INTR_H_ #define _SYS_INTR_H_ #include #define INTR_IRQ_INVALID 0xFFFFFFFF enum intr_map_data_type { INTR_MAP_DATA_ACPI, INTR_MAP_DATA_FDT, INTR_MAP_DATA_GPIO, }; struct intr_map_data { enum intr_map_data_type type; size_t size; }; #ifdef DEV_ACPI struct intr_map_data_acpi { struct intr_map_data hdr; u_int irq; enum intr_polarity pol; enum intr_trigger trig; }; #endif #ifdef FDT struct intr_map_data_fdt { struct intr_map_data hdr; u_int ncells; pcell_t cells[0]; }; #endif struct intr_map_data_gpio { struct intr_map_data hdr; u_int gpio_pin_num; u_int gpio_pin_flags; u_int gpio_intr_mode; }; #ifdef notyet #define INTR_SOLO INTR_MD1 typedef int intr_irq_filter_t(void *arg, struct trapframe *tf); #else typedef int intr_irq_filter_t(void *arg); #endif #define INTR_ISRC_NAMELEN (MAXCOMLEN + 1) #define INTR_ISRCF_IPI 0x01 /* IPI interrupt */ #define INTR_ISRCF_PPI 0x02 /* PPI interrupt */ #define INTR_ISRCF_BOUND 0x04 /* bound to a CPU */ /* Interrupt source definition. */ struct intr_irqsrc { device_t isrc_dev; /* where isrc is mapped */ u_int isrc_irq; /* unique identificator */ u_int isrc_flags; char isrc_name[INTR_ISRC_NAMELEN]; cpuset_t isrc_cpu; /* on which CPUs is enabled */ u_int isrc_index; u_long * isrc_count; u_int isrc_handlers; struct intr_event * isrc_event; #ifdef INTR_SOLO intr_irq_filter_t * isrc_filter; void * isrc_arg; #endif }; /* Intr interface for PIC. */ int intr_isrc_deregister(struct intr_irqsrc *); int intr_isrc_register(struct intr_irqsrc *, device_t, u_int, const char *, ...) __printflike(4, 5); #ifdef SMP bool intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu); #endif int intr_isrc_dispatch(struct intr_irqsrc *, struct trapframe *); u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask); -int intr_pic_register(device_t, intptr_t); +struct intr_pic *intr_pic_register(device_t, intptr_t); int intr_pic_deregister(device_t, intptr_t); int intr_pic_claim_root(device_t, intptr_t, intr_irq_filter_t *, void *, u_int); extern device_t intr_irq_root_dev; /* Intr interface for BUS. */ int intr_map_irq(device_t, intptr_t, struct intr_map_data *, u_int *); int intr_alloc_irq(device_t, struct resource *); int intr_release_irq(device_t, struct resource *); int intr_setup_irq(device_t, struct resource *, driver_filter_t, driver_intr_t, void *, int, void **); int intr_teardown_irq(device_t, struct resource *, void *); int intr_describe_irq(device_t, struct resource *, void *, const char *); /* MSI/MSI-X handling */ int intr_msi_register(device_t, intptr_t); int intr_alloc_msi(device_t, device_t, intptr_t, int, int, int *); int intr_release_msi(device_t, device_t, intptr_t, int, int *); int intr_map_msi(device_t, device_t, intptr_t, int, uint64_t *, uint32_t *); int intr_alloc_msix(device_t, device_t, intptr_t, int *); int intr_release_msix(device_t, device_t, intptr_t, int); #ifdef DEV_ACPI u_int intr_acpi_map_irq(device_t, u_int, enum intr_polarity, enum intr_trigger); #endif #ifdef FDT u_int intr_fdt_map_irq(phandle_t, pcell_t *, u_int); #endif u_int intr_gpio_map_irq(device_t dev, u_int pin_num, u_int pin_flags, u_int intr_mode); #ifdef SMP int intr_bind_irq(device_t, struct resource *, int); void intr_pic_init_secondary(void); /* Virtualization for interrupt source IPI counter increment. */ static inline void intr_ipi_increment_count(u_long *counter, u_int cpu) { KASSERT(cpu < MAXCPU, ("%s: too big cpu %u", __func__, cpu)); counter[cpu]++; } /* Virtualization for interrupt source IPI counters setup. */ u_long * intr_ipi_setup_counters(const char *name); #endif #endif /* _SYS_INTR_H */