Index: head/sys/arm/arm/gic.c =================================================================== --- head/sys/arm/arm/gic.c (revision 297538) +++ head/sys/arm/arm/gic.c (revision 297539) @@ -1,1203 +1,1250 @@ /*- * 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 "opt_platform.h" - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ARM_INTRNG #include #endif #include #include #include #include #include #include #include #ifdef ARM_INTRNG #include "pic_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 */ /* 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. */ /* 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 ARM_INTRNG +struct gic_irqsrc { + struct intr_irqsrc gi_isrc; + uint32_t gi_irq; + enum intr_polarity gi_pol; + enum intr_trigger gi_trig; +}; + static u_int gic_irq_cpu; static int arm_gic_intr(void *); -static int arm_gic_bind(device_t dev, struct intr_irqsrc *isrc); +static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc); #ifdef SMP u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; -#define ISRC_IPI(isrc) sgi_to_ipi[isrc->isrc_data - GIC_FIRST_SGI] +u_int sgi_first_unused = GIC_FIRST_SGI; #endif #endif struct arm_gic_softc { device_t gic_dev; #ifdef ARM_INTRNG void * gic_intrhand; - struct intr_irqsrc ** gic_irqs; + 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; #ifdef GIC_DEBUG_SPURIOUS uint32_t last_irq[MAXCPU]; #endif }; +#ifdef ARM_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 ARM_INTRNG { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */ #endif { -1, 0 } }; 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 ARM_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}, {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 ARM_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 #ifdef SMP #ifdef ARM_INTRNG static void arm_gic_init_secondary(device_t dev) { struct arm_gic_softc *sc = device_get_softc(dev); struct intr_irqsrc *isrc; u_int irq; 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; 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++) { - isrc = sc->gic_irqs[irq]; + isrc = GIC_INTR_ISRC(sc, irq); if (isrc != NULL && isrc->isrc_handlers != 0) { CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); gic_irq_unmask(sc, irq); } } /* Unmask attached PPI interrupts. */ for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) { - isrc = sc->gic_irqs[irq]; + isrc = GIC_INTR_ISRC(sc, irq); if (isrc == NULL || isrc->isrc_handlers == 0) continue; if (isrc->isrc_flags & INTR_ISRCF_BOUND) { if (CPU_ISSET(PCPU_GET(cpuid), &isrc->isrc_cpu)) gic_irq_unmask(sc, irq); } else { CPU_SET(PCPU_GET(cpuid), &isrc->isrc_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; 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; 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 /* ARM_INTRNG */ #endif /* SMP */ #ifndef ARM_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 ARM_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); +} #endif static int arm_gic_attach(device_t dev) { struct arm_gic_softc *sc; int i; - uint32_t icciidr, mask; + uint32_t icciidr, mask, nirqs; #ifdef ARM_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->nirqs = gic_d_read_4(sc, GICD_TYPER); - sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); + nirqs = gic_d_read_4(sc, GICD_TYPER); + nirqs = 32 * ((nirqs & 0x1f) + 1); #ifdef ARM_INTRNG - sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF, - M_WAITOK | M_ZERO); + 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); } /* 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) mask = 1; /* Collect the mask in the lower byte */ mask |= mask >> 16; mask |= mask >> 8; /* Distribute this back to the upper bytes */ 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; 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 ARM_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) { 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_unregister(dev, xref); + 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_unregister(dev, xref); + 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_unregister(dev, xref); + intr_pic_deregister(dev, xref); goto cleanup; } } OF_device_register_xref(xref, 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 ARM_INTRNG static int arm_gic_intr(void *arg) { struct arm_gic_softc *sc = arg; - struct intr_irqsrc *isrc; + 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: - isrc = sc->gic_irqs[irq]; - if (isrc == NULL) { - device_printf(sc->gic_dev, "Stray interrupt %u detected\n", irq); - gic_irq_mask(sc, irq); - gic_c_write_4(sc, GICC_EOIR, irq_active_reg); - goto next_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(ISRC_IPI(isrc), tf); + 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 (isrc->isrc_trig == INTR_TRIGGER_EDGE) + if (gi->gi_trig == INTR_TRIGGER_EDGE) gic_c_write_4(sc, GICC_EOIR, irq_active_reg); - intr_irq_dispatch(isrc, tf); + if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) { + gic_irq_mask(sc, irq); + if (gi->gi_trig != INTR_TRIGGER_EDGE) + 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 int -gic_attach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq) -{ - const char *name; - - /* - * 1. The link between ISRC and controller must be set atomically. - * 2. Just do things only once in rare case when consumers - * of shared interrupt came here at the same moment. - */ - mtx_lock_spin(&sc->mutex); - if (sc->gic_irqs[irq] != NULL) { - mtx_unlock_spin(&sc->mutex); - return (sc->gic_irqs[irq] == isrc ? 0 : EEXIST); - } - sc->gic_irqs[irq] = isrc; - isrc->isrc_data = irq; - mtx_unlock_spin(&sc->mutex); - - name = device_get_nameunit(sc->gic_dev); - if (irq <= GIC_LAST_SGI) - intr_irq_set_name(isrc, "%s,i%u", name, irq - GIC_FIRST_SGI); - else if (irq <= GIC_LAST_PPI) - intr_irq_set_name(isrc, "%s,p%u", name, irq - GIC_FIRST_PPI); - else - intr_irq_set_name(isrc, "%s,s%u", name, irq - GIC_FIRST_SPI); - return (0); -} - -static int -gic_detach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq) -{ - - mtx_lock_spin(&sc->mutex); - if (sc->gic_irqs[irq] != isrc) { - mtx_unlock_spin(&sc->mutex); - return (sc->gic_irqs[irq] == NULL ? 0 : EINVAL); - } - sc->gic_irqs[irq] = NULL; - isrc->isrc_data = 0; - mtx_unlock_spin(&sc->mutex); - - intr_irq_set_name(isrc, ""); - return (0); -} - 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_irq_from_nspc(struct arm_gic_softc *sc, u_int type, u_int num, u_int *irqp) +gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) { - switch (type) { - case INTR_IRQ_NSPC_PLAIN: - *irqp = num; - return (*irqp < sc->nirqs ? 0 : EINVAL); + if (ncells == 1) { + *irqp = cells[0]; + *polp = INTR_POLARITY_CONFORM; + *trigp = INTR_TRIGGER_CONFORM; + return (0); + } + if (ncells == 3) { + u_int irq, tripol; - case INTR_IRQ_NSPC_IRQ: - *irqp = num + GIC_FIRST_PPI; - return (*irqp < sc->nirqs ? 0 : EINVAL); + /* + * 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); + } - case INTR_IRQ_NSPC_IPI: - *irqp = num + GIC_FIRST_SGI; - return (*irqp < GIC_LAST_SGI ? 0 : EINVAL); + tripol = cells[2] & 0xff; + if (tripol & 0xf0 || (tripol & 0x0a && cells[0] == 0)) + device_printf(dev, "unsupported trigger/polarity " + "configuration 0x%02x\n", tripol); - default: - return (EINVAL); + *irqp = irq; + *polp = INTR_POLARITY_CONFORM; + *trigp = tripol & 0x03 ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL; + return (0); } + return (EINVAL); } +#endif static int -gic_map_nspc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) +gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) { - int error; - - error = gic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num, - irqp); - if (error != 0) - return (error); - return (gic_attach_isrc(sc, isrc, *irqp)); -} - -#ifdef FDT -static int -gic_map_fdt(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) -{ - u_int irq, tripol; - enum intr_trigger trig; + u_int irq; enum intr_polarity pol; - int error; + enum intr_trigger trig; + struct arm_gic_softc *sc; - if (isrc->isrc_ncells == 1) { - irq = isrc->isrc_cells[0]; - pol = INTR_POLARITY_CONFORM; - trig = INTR_TRIGGER_CONFORM; - } else if (isrc->isrc_ncells == 3) { - if (isrc->isrc_cells[0] == 0) - irq = isrc->isrc_cells[1] + GIC_FIRST_SPI; - else - irq = isrc->isrc_cells[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. - */ - tripol = isrc->isrc_cells[2]; - if (tripol & 0x0a && irq >= GIC_FIRST_SPI) { - device_printf(sc->gic_dev, - "unsupported trigger/polarity configuration " - "0x%02x\n", tripol & 0x0f); - } - pol = INTR_POLARITY_CONFORM; - if (tripol & 0x03) - trig = INTR_TRIGGER_EDGE; - else - trig = INTR_TRIGGER_LEVEL; - } else + sc = device_get_softc(dev); + switch (data->type) { +#ifdef FDT + case INTR_MAP_DATA_FDT: + if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, + &pol, &trig) != 0) + return (EINVAL); + break; +#endif + default: return (EINVAL); + } 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); - error = gic_attach_isrc(sc, isrc, irq); - if (error != 0) - return (error); - - isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; - isrc->isrc_nspc_num = irq; - isrc->isrc_trig = trig; - isrc->isrc_pol = pol; - *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; return (0); } -#endif static int -arm_gic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +arm_gic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { - struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq; int error; + u_int irq; + struct arm_gic_softc *sc; - if (isrc->isrc_type == INTR_ISRCT_NAMESPACE) - error = gic_map_nspc(sc, isrc, &irq); -#ifdef FDT - else if (isrc->isrc_type == INTR_ISRCT_FDT) - error = gic_map_fdt(sc, isrc, &irq); -#endif - else - return (EINVAL); - - if (error == 0) - *is_percpu = irq < GIC_FIRST_SPI ? TRUE : FALSE; + 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 void -arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +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); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; + u_int irq; + enum intr_trigger trig; + enum intr_polarity pol; - if (isrc->isrc_trig == INTR_TRIGGER_CONFORM) - isrc->isrc_trig = INTR_TRIGGER_LEVEL; + if (data == NULL) + return (ENOTSUP); + /* Get config for resource. */ + if (gic_map_intr(dev, data, &irq, &pol, &trig)) + return (EINVAL); + + if (gi->gi_irq != irq) + return (EINVAL); + + /* 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); + } + + 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; + /* * 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_PERCPU) + if (isrc->isrc_flags & INTR_ISRCF_PPI) CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); - gic_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol); - arm_gic_bind(dev, isrc); + gic_config(sc, gi->gi_irq, trig, pol); + arm_gic_bind_intr(dev, isrc); + return (0); } -static void -arm_gic_enable_source(device_t dev, struct intr_irqsrc *isrc) +static int +arm_gic_teardown_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); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - arm_irq_memory_barrier(irq); - gic_irq_unmask(sc, irq); + if (isrc->isrc_handlers == 0) { + gi->gi_pol = INTR_POLARITY_CONFORM; + gi->gi_trig = INTR_TRIGGER_CONFORM; + } + return (0); } static void -arm_gic_disable_source(device_t dev, struct intr_irqsrc *isrc) +arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - gic_irq_mask(sc, irq); + arm_irq_memory_barrier(gi->gi_irq); + gic_irq_unmask(sc, gi->gi_irq); } -static int -arm_gic_unregister(device_t dev, struct intr_irqsrc *isrc) +static void +arm_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - return (gic_detach_isrc(sc, isrc, irq)); + 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_source(dev, isrc); - gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); + 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_source(dev, isrc); + 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 (isrc->isrc_trig == INTR_TRIGGER_EDGE) + if (gi->gi_trig == INTR_TRIGGER_EDGE) return; arm_irq_memory_barrier(0); - gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); + gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); } static int -arm_gic_bind(device_t dev, struct intr_irqsrc *isrc) +arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); - uint32_t irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - if (irq < GIC_FIRST_SPI) + 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, irq, &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) +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); - uint32_t irq, val = 0, i; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; + uint32_t val = 0, i; - irq = isrc->isrc_data; - for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) val |= 1 << (16 + i); - gic_d_write_4(sc, GICD_SGIR(0), val | irq); + 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 *isrc) +arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) { struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq; - int error; - error = gic_map_nspc(sc, isrc, &irq); - if (error != 0) - return (error); - sgi_to_ipi[irq - GIC_FIRST_SGI] = ipi; + if (sgi_first_unused > GIC_LAST_SGI) + return (ENOSPC); + + *isrcp = GIC_INTR_ISRC(sc, sgi_first_unused); + sgi_to_ipi[sgi_first_unused++] = ipi; 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); /* * Immediatly 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 |= 1 << (16 + i); 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 /* ARM_INTRNG */ static device_method_t arm_gic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arm_gic_probe), DEVMETHOD(device_attach, arm_gic_attach), #ifdef ARM_INTRNG /* Interrupt controller interface */ - DEVMETHOD(pic_disable_source, arm_gic_disable_source), + DEVMETHOD(pic_disable_intr, arm_gic_disable_intr), DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), - DEVMETHOD(pic_enable_source, arm_gic_enable_source), + 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), - DEVMETHOD(pic_register, arm_gic_register), - DEVMETHOD(pic_unregister, arm_gic_unregister), #ifdef SMP - DEVMETHOD(pic_bind, arm_gic_bind), + 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); Index: head/sys/arm/arm/machdep_intr.c =================================================================== --- head/sys/arm/arm/machdep_intr.c (revision 297538) +++ head/sys/arm/arm/machdep_intr.c (revision 297539) @@ -1,251 +1,232 @@ -/* $NetBSD: intr.c,v 1.12 2003/07/15 00:24:41 lukem Exp $ */ - /*- - * Copyright (c) 2004 Olivier Houchard. - * Copyright (c) 1994-1998 Mark Brinicombe. + * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Mark Brinicombe - * for the NetBSD Project. - * 4. 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 ``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) + * 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. - * - * Soft interrupt and other generic interrupt functions. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ARM_INTRNG #include "pic_if.h" #ifdef SMP #define INTR_IPI_NAMELEN (MAXCOMLEN + 1) struct intr_ipi { intr_ipi_handler_t * ii_handler; void * ii_handler_arg; intr_ipi_send_t * ii_send; void * ii_send_arg; char ii_name[INTR_IPI_NAMELEN]; u_long * ii_count; }; static struct intr_ipi ipi_sources[INTR_IPI_COUNT]; -u_int ipi_next_num; #endif #endif /* * arm_irq_memory_barrier() * * Ensure all writes to device memory have reached devices before proceeding. * * This is intended to be called from the post-filter and post-thread routines * of an interrupt controller implementation. A peripheral device driver should * use bus_space_barrier() if it needs to ensure a write has reached the * hardware for some reason other than clearing interrupt conditions. * * The need for this function arises from the ARM weak memory ordering model. * Writes to locations mapped with the Device attribute bypass any caches, but * are buffered. Multiple writes to the same device will be observed by that * device in the order issued by the cpu. Writes to different devices may * appear at those devices in a different order than issued by the cpu. That * is, if the cpu writes to device A then device B, the write to device B could * complete before the write to device A. * * Consider a typical device interrupt handler which services the interrupt and * writes to a device status-acknowledge register to clear the interrupt before * returning. That write is posted to the L2 controller which "immediately" * places it in a store buffer and automatically drains that buffer. This can * be less immediate than you'd think... There may be no free slots in the store * buffers, so an existing buffer has to be drained first to make room. The * target bus may be busy with other traffic (such as DMA for various devices), * delaying the drain of the store buffer for some indeterminate time. While * all this delay is happening, execution proceeds on the CPU, unwinding its way * out of the interrupt call stack to the point where the interrupt driver code * is ready to EOI and unmask the interrupt. The interrupt controller may be * accessed via a faster bus than the hardware whose handler just ran; the write * to unmask and EOI the interrupt may complete quickly while the device write * to ack and clear the interrupt source is still lingering in a store buffer * waiting for access to a slower bus. With the interrupt unmasked at the * interrupt controller but still active at the device, as soon as interrupts * are enabled on the core the device re-interrupts immediately: now you've got * a spurious interrupt on your hands. * * The right way to fix this problem is for every device driver to use the * proper bus_space_barrier() calls in its interrupt handler. For ARM a single * barrier call at the end of the handler would work. This would have to be * done to every driver in the system, not just arm-specific drivers. * * Another potential fix is to map all device memory as Strongly-Ordered rather * than Device memory, which takes the store buffers out of the picture. This * has a pretty big impact on overall system performance, because each strongly * ordered memory access causes all L2 store buffers to be drained. * * A compromise solution is to have the interrupt controller implementation call * this function to establish a barrier between writes to the interrupt-source * device and writes to the interrupt controller device. * * This takes the interrupt number as an argument, and currently doesn't use it. * The plan is that maybe some day there is a way to flag certain interrupts as * "memory barrier safe" and we can avoid this overhead with them. */ void arm_irq_memory_barrier(uintptr_t irq) { dsb(); cpu_l2cache_drain_writebuf(); } #ifdef ARM_INTRNG #ifdef SMP static inline struct intr_ipi * intr_ipi_lookup(u_int ipi) { if (ipi >= INTR_IPI_COUNT) panic("%s: no such IPI %u", __func__, ipi); return (&ipi_sources[ipi]); } void intr_ipi_dispatch(u_int ipi, struct trapframe *tf) { void *arg; struct intr_ipi *ii; ii = intr_ipi_lookup(ipi); if (ii->ii_count == NULL) panic("%s: not setup IPI %u", __func__, ipi); intr_ipi_increment_count(ii->ii_count, PCPU_GET(cpuid)); /* * Supply ipi filter with trapframe argument * if none is registered. */ arg = ii->ii_handler_arg != NULL ? ii->ii_handler_arg : tf; ii->ii_handler(arg); } void intr_ipi_send(cpuset_t cpus, u_int ipi) { struct intr_ipi *ii; ii = intr_ipi_lookup(ipi); if (ii->ii_count == NULL) panic("%s: not setup IPI %u", __func__, ipi); - ii->ii_send(ii->ii_send_arg, cpus); + ii->ii_send(ii->ii_send_arg, cpus, ipi); } void intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand, void *h_arg, intr_ipi_send_t *send, void *s_arg) { struct intr_ipi *ii; ii = intr_ipi_lookup(ipi); KASSERT(hand != NULL, ("%s: ipi %u no handler", __func__, ipi)); KASSERT(send != NULL, ("%s: ipi %u no sender", __func__, ipi)); KASSERT(ii->ii_count == NULL, ("%s: ipi %u reused", __func__, ipi)); ii->ii_handler = hand; ii->ii_handler_arg = h_arg; ii->ii_send = send; ii->ii_send_arg = s_arg; strlcpy(ii->ii_name, name, INTR_IPI_NAMELEN); ii->ii_count = intr_ipi_setup_counters(name); } /* * Send IPI thru interrupt controller. */ static void -pic_ipi_send(void *arg, cpuset_t cpus) +pic_ipi_send(void *arg, cpuset_t cpus, u_int ipi) { KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); - PIC_IPI_SEND(intr_irq_root_dev, arg, cpus); + PIC_IPI_SEND(intr_irq_root_dev, arg, cpus, ipi); } /* * Setup IPI handler on interrupt controller. * * Not SMP coherent. */ int intr_pic_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand, void *arg) { int error; struct intr_irqsrc *isrc; KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); - isrc = intr_isrc_alloc(INTR_ISRCT_NAMESPACE, 0); - isrc->isrc_nspc_type = INTR_IRQ_NSPC_IPI; - isrc->isrc_nspc_num = ipi_next_num; - - error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, isrc); + error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, &isrc); if (error != 0) return (error); - ipi_next_num++; - - isrc->isrc_dev = intr_irq_root_dev; - isrc->isrc_handlers = 1; + isrc->isrc_handlers++; intr_ipi_setup(ipi, name, hand, arg, pic_ipi_send, isrc); return (0); } #endif #endif Index: head/sys/arm/arm/nexus.c =================================================================== --- head/sys/arm/arm/nexus.c (revision 297538) +++ head/sys/arm/arm/nexus.c (revision 297539) @@ -1,415 +1,417 @@ /*- * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * */ /* * This code implements a `root nexus' for Arm Architecture * machines. The function of the root nexus is to serve as an * attachment point for both processors and buses, and to manage * resources which are common to all of them. In particular, * this code implements the core resource managers for interrupt * requests, DMA requests (which rightfully should be a part of the * ISA code but it's easier to do it here for now), I/O port addresses, * and I/O memory address space. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include "ofw_bus_if.h" #endif static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device"); struct nexus_device { struct resource_list nx_resources; }; #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev)) static struct rman mem_rman; static int nexus_probe(device_t); static int nexus_attach(device_t); static int nexus_print_child(device_t, device_t); static device_t nexus_add_child(device_t, u_int, const char *, int); static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); static bus_space_tag_t nexus_get_bus_tag(device_t, device_t); #ifdef ARM_INTRNG #ifdef SMP static int nexus_bind_intr(device_t, device_t, struct resource *, int); #endif #endif static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol); #ifdef ARM_INTRNG static int nexus_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, const char *descr); #endif static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, struct resource *); static int nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep); static int nexus_teardown_intr(device_t, device_t, struct resource *, void *); #ifdef FDT static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells, pcell_t *intr); #endif static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_config_intr, nexus_config_intr), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), DEVMETHOD(bus_get_bus_tag, nexus_get_bus_tag), #ifdef ARM_INTRNG DEVMETHOD(bus_describe_intr, nexus_describe_intr), #ifdef SMP DEVMETHOD(bus_bind_intr, nexus_bind_intr), #endif #endif #ifdef FDT DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr), #endif { 0, 0 } }; static devclass_t nexus_devclass; static driver_t nexus_driver = { "nexus", nexus_methods, 1 /* no softc */ }; EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY); static int nexus_probe(device_t dev) { device_quiet(dev); /* suppress attach message for neatness */ return (BUS_PROBE_DEFAULT); } static int nexus_attach(device_t dev) { mem_rman.rm_start = 0; mem_rman.rm_end = BUS_SPACE_MAXADDR; mem_rman.rm_type = RMAN_ARRAY; mem_rman.rm_descr = "I/O memory addresses"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, BUS_SPACE_MAXADDR)) panic("nexus_probe mem_rman"); /* * First, deal with the children we know about already */ bus_generic_probe(dev); bus_generic_attach(dev); return (0); } static int nexus_print_child(device_t bus, device_t child) { int retval = 0; retval += bus_print_child_header(bus, child); retval += printf("\n"); return (retval); } static device_t nexus_add_child(device_t bus, u_int order, const char *name, int unit) { device_t child; struct nexus_device *ndev; ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO); if (!ndev) return (0); resource_list_init(&ndev->nx_resources); child = device_add_child_ordered(bus, order, name, unit); /* should we free this in nexus_child_detached? */ device_set_ivars(child, ndev); return (child); } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of nexus0. * (Exceptions include footbridge.) */ static struct resource * nexus_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 resource *rv; struct rman *rm; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rm = &mem_rman; break; default: return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) return (NULL); rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (0); } } return (rv); } static int nexus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { int error; if (rman_get_flags(res) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, res); if (error) return (error); } return (rman_release_resource(res)); } static bus_space_tag_t nexus_get_bus_tag(device_t bus __unused, device_t child __unused) { #ifdef FDT return(fdtbus_bs_tag); #else return((void *)1); #endif } static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { int ret = ENODEV; #ifdef ARM_INTRNG - ret = intr_irq_config(irq, trig, pol); + device_printf(dev, "bus_config_intr is obsolete and not supported!\n"); + ret = EOPNOTSUPP; #else if (arm_config_irq) ret = (*arm_config_irq)(irq, trig, pol); #endif return (ret); } static int nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { +#ifndef ARM_INTRNG int irq; +#endif if ((rman_get_flags(res) & RF_SHAREABLE) == 0) flags |= INTR_EXCL; - for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) { #ifdef ARM_INTRNG - intr_irq_add_handler(child, filt, intr, arg, irq, flags, - cookiep); + return(intr_setup_irq(child, res, filt, intr, arg, flags, cookiep)); #else + for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) { arm_setup_irqhandler(device_get_nameunit(child), filt, intr, arg, irq, flags, cookiep); arm_unmask_irq(irq); -#endif } return (0); +#endif } static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { #ifdef ARM_INTRNG - return (intr_irq_remove_handler(child, rman_get_start(r), ih)); + return (intr_teardown_irq(child, r, ih)); #else return (arm_remove_irqhandler(rman_get_start(r), ih)); #endif } #ifdef ARM_INTRNG static int nexus_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, const char *descr) { - return (intr_irq_describe(rman_get_start(irq), cookie, descr)); + return (intr_describe_irq(child, irq, cookie, descr)); } #ifdef SMP static int nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu) { - return (intr_irq_bind(rman_get_start(irq), cpu)); + return (intr_bind_irq(child, irq, cpu)); } #endif #endif static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int err; bus_addr_t paddr; bus_size_t psize; bus_space_handle_t vaddr; if ((err = rman_activate_resource(r)) != 0) return (err); /* * If this is a memory resource, map it into the kernel. */ if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { paddr = (bus_addr_t)rman_get_start(r); psize = (bus_size_t)rman_get_size(r); #ifdef FDT err = bus_space_map(fdtbus_bs_tag, paddr, psize, 0, &vaddr); if (err != 0) { rman_deactivate_resource(r); return (err); } rman_set_bustag(r, fdtbus_bs_tag); #else vaddr = (bus_space_handle_t)pmap_mapdev((vm_offset_t)paddr, (vm_size_t)psize); if (vaddr == 0) { rman_deactivate_resource(r); return (ENOMEM); } rman_set_bustag(r, (void *)1); #endif rman_set_virtual(r, (void *)vaddr); rman_set_bushandle(r, vaddr); } return (0); } static int nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { bus_size_t psize; bus_space_handle_t vaddr; psize = (bus_size_t)rman_get_size(r); vaddr = rman_get_bushandle(r); if (vaddr != 0) { #ifdef FDT bus_space_unmap(fdtbus_bs_tag, vaddr, psize); #else pmap_unmapdev((vm_offset_t)vaddr, (vm_size_t)psize); #endif rman_set_virtual(r, NULL); rman_set_bushandle(r, 0); } return (rman_deactivate_resource(r)); } #ifdef FDT static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells, pcell_t *intr) { return (intr_fdt_map_irq(iparent, intr, icells)); } #endif Index: head/sys/arm/freescale/imx/imx_gpio.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpio.c (revision 297538) +++ head/sys/arm/freescale/imx/imx_gpio.c (revision 297539) @@ -1,729 +1,786 @@ /*- * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Oleksandr Rybalko under sponsorship * from 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. */ /* * Freescale i.MX515 GPIO driver. */ #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 "gpio_if.h" #ifdef ARM_INTRNG #include "pic_if.h" #endif #define WRITE4(_sc, _r, _v) \ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) #define READ4(_sc, _r) \ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r)) #define SET4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m)) #define CLEAR4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m)) /* Registers definition for Freescale i.MX515 GPIO controller */ #define IMX_GPIO_DR_REG 0x000 /* Pin Data */ #define IMX_GPIO_OE_REG 0x004 /* Set Pin Output */ #define IMX_GPIO_PSR_REG 0x008 /* Pad Status */ #define IMX_GPIO_ICR1_REG 0x00C /* Interrupt Configuration */ #define IMX_GPIO_ICR2_REG 0x010 /* Interrupt Configuration */ #define GPIO_ICR_COND_LOW 0 #define GPIO_ICR_COND_HIGH 1 #define GPIO_ICR_COND_RISE 2 #define GPIO_ICR_COND_FALL 3 #define IMX_GPIO_IMR_REG 0x014 /* Interrupt Mask Register */ #define IMX_GPIO_ISR_REG 0x018 /* Interrupt Status Register */ #define IMX_GPIO_EDGE_REG 0x01C /* Edge Detect Register */ #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) #define NGPIO 32 +#ifdef ARM_INTRNG +struct gpio_irqsrc { + struct intr_irqsrc gi_isrc; + u_int gi_irq; + enum intr_polarity gi_pol; + enum intr_trigger gi_trig; +}; +#endif + struct imx51_gpio_softc { device_t dev; device_t sc_busdev; struct mtx sc_mtx; struct resource *sc_res[3]; /* 1 x mem, 2 x IRQ */ void *gpio_ih[2]; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; - struct intr_irqsrc *gpio_pic_irqsrc[NGPIO]; +#ifdef ARM_INTRNG + struct gpio_irqsrc gpio_pic_irqsrc[NGPIO]; +#endif }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-gpio", 1}, {"fsl,imx53-gpio", 1}, {"fsl,imx51-gpio", 1}, {NULL, 0} }; static struct resource_spec imx_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0 } }; /* * Helpers */ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *, struct gpio_pin *, uint32_t); /* * Driver stuff */ static int imx51_gpio_probe(device_t); static int imx51_gpio_attach(device_t); static int imx51_gpio_detach(device_t); /* * GPIO interface */ static device_t imx51_gpio_get_bus(device_t); static int imx51_gpio_pin_max(device_t, int *); static int imx51_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int imx51_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int imx51_gpio_pin_getname(device_t, uint32_t, char *); static int imx51_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int imx51_gpio_pin_set(device_t, uint32_t, unsigned int); static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *); static int imx51_gpio_pin_toggle(device_t, uint32_t pin); #ifdef ARM_INTRNG +static int +gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx51_gpio_softc *sc; + struct gpio_irqsrc *gi; + + sc = device_get_softc(dev); + if (isrc->isrc_handlers == 0) { + gi = (struct gpio_irqsrc *)isrc; + gi->gi_pol = INTR_POLARITY_CONFORM; + gi->gi_trig = INTR_TRIGGER_CONFORM; + + // XXX Not sure this is necessary + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << gi->gi_irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << gi->gi_irq)); + mtx_unlock_spin(&sc->sc_mtx); + } + return (0); +} + /* - * this is teardown_intr + * this is mask_intr */ static void gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct imx51_gpio_softc *sc; u_int irq; sc = device_get_softc(dev); - irq = isrc->isrc_data; + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; - // XXX Not sure this is necessary mtx_lock_spin(&sc->sc_mtx); CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); - WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); mtx_unlock_spin(&sc->sc_mtx); } -/* - * this is mask_intr - */ -static void -gpio_pic_disable_source(device_t dev, struct intr_irqsrc *isrc) +static int +gpio_pic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) { struct imx51_gpio_softc *sc; + u_int irq, tripol; + enum intr_polarity pol; + enum intr_trigger trig; sc = device_get_softc(dev); - mtx_lock_spin(&sc->sc_mtx); - CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); - mtx_unlock_spin(&sc->sc_mtx); + /* + * From devicetree/bindings/gpio/fsl-imx-gpio.txt: + * #interrupt-cells: 2. The first cell is the GPIO number. The second + * cell bits[3:0] is used to specify 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. + * We can do any single one of these modes, but nothing in combo. + */ + + if (ncells != 2) { + device_printf(sc->dev, "Invalid #interrupt-cells"); + return (EINVAL); + } + + irq = cells[0]; + tripol = cells[1]; + if (irq >= sc->gpio_npins) { + device_printf(sc->dev, "Invalid interrupt number %d", irq); + return (EINVAL); + } + switch (tripol) { + case 1: + trig = INTR_TRIGGER_EDGE; + pol = INTR_POLARITY_HIGH; + break; + case 2: + trig = INTR_TRIGGER_EDGE; + pol = INTR_POLARITY_LOW; + break; + case 4: + trig = INTR_TRIGGER_LEVEL; + pol = INTR_POLARITY_HIGH; + break; + case 8: + trig = INTR_TRIGGER_LEVEL; + pol = INTR_POLARITY_LOW; + break; + default: + device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", + tripol); + return (ENOTSUP); + } + *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; + return (0); } -/* - * this is setup_intr - */ -static void -gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +static int +gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { + int error; + u_int irq; struct imx51_gpio_softc *sc; - int icfg; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + error = gpio_pic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, + NULL, NULL); + if (error == 0) { + sc = device_get_softc(dev); + *isrcp = &sc->gpio_pic_irqsrc[irq].gi_isrc; + } + return (error); +} + +static int +gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx51_gpio_softc *sc; + struct gpio_irqsrc *gi; + int error, icfg; u_int irq, reg, shift, wrk; + enum intr_trigger trig; + enum intr_polarity pol; sc = device_get_softc(dev); + gi = (struct gpio_irqsrc *)isrc; - if (isrc->isrc_trig == INTR_TRIGGER_LEVEL) { - if (isrc->isrc_pol == INTR_POLARITY_LOW) + /* Get config for interrupt. */ + if (data == NULL || data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + error = gpio_pic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, + &pol, &trig); + if (error != 0) + return (error); + if (gi->gi_irq != irq) + return (EINVAL); + + /* Compare config if this is not first setup. */ + if (isrc->isrc_handlers != 0) { + if (pol != gi->gi_pol || trig != gi->gi_trig) + return (EINVAL); + else + return (0); + } + + gi->gi_pol = pol; + gi->gi_trig = trig; + + if (trig == INTR_TRIGGER_LEVEL) { + if (pol == INTR_POLARITY_LOW) icfg = GPIO_ICR_COND_LOW; else icfg = GPIO_ICR_COND_HIGH; } else { - if (isrc->isrc_pol == INTR_POLARITY_HIGH) + if (pol == INTR_POLARITY_HIGH) icfg = GPIO_ICR_COND_FALL; else icfg = GPIO_ICR_COND_RISE; } - irq = isrc->isrc_data; if (irq < 16) { reg = IMX_GPIO_ICR1_REG; shift = 2 * irq; } else { reg = IMX_GPIO_ICR2_REG; shift = 2 * (irq - 16); } mtx_lock_spin(&sc->sc_mtx); CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); wrk = READ4(sc, reg); wrk &= ~(0x03 << shift); wrk |= icfg << shift; WRITE4(sc, reg, wrk); mtx_unlock_spin(&sc->sc_mtx); + return (0); } /* * this is unmask_intr */ static void -gpio_pic_enable_source(device_t dev, struct intr_irqsrc *isrc) +gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct imx51_gpio_softc *sc; + u_int irq; sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; mtx_lock_spin(&sc->sc_mtx); - SET4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); + SET4(sc, IMX_GPIO_IMR_REG, (1U << irq)); mtx_unlock_spin(&sc->sc_mtx); } static void gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct imx51_gpio_softc *sc; + u_int irq; sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; arm_irq_memory_barrier(0); /* EOI. W1C reg so no r-m-w, no locking needed. */ - WRITE4(sc, IMX_GPIO_ISR_REG, (1U << isrc->isrc_data)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); } static void gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { arm_irq_memory_barrier(0); - gpio_pic_enable_source(dev, isrc); + gpio_pic_enable_intr(dev, isrc); } static void gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { - gpio_pic_disable_source(dev, isrc); + gpio_pic_disable_intr(dev, isrc); } -/* - * intrng calls this to make a new isrc known to us. - */ static int -gpio_pic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) -{ - struct imx51_gpio_softc *sc; - u_int irq, tripol; - - sc = device_get_softc(dev); - - /* - * From devicetree/bindings/gpio/fsl-imx-gpio.txt: - * #interrupt-cells: 2. The first cell is the GPIO number. The second - * cell bits[3:0] is used to specify 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. - * We can do any single one of these modes, but nothing in combo. - */ - - if (isrc->isrc_ncells != 2) { - device_printf(sc->dev, "Invalid #interrupt-cells"); - return (EINVAL); - } - - irq = isrc->isrc_cells[0]; - tripol = isrc->isrc_cells[1]; - if (irq >= sc->gpio_npins) { - device_printf(sc->dev, "Invalid interrupt number %d", irq); - return (EINVAL); - } - switch (tripol) - { - case 1: - isrc->isrc_trig = INTR_TRIGGER_EDGE; - isrc->isrc_pol = INTR_POLARITY_HIGH; - break; - case 2: - isrc->isrc_trig = INTR_TRIGGER_EDGE; - isrc->isrc_pol = INTR_POLARITY_LOW; - break; - case 4: - isrc->isrc_trig = INTR_TRIGGER_LEVEL; - isrc->isrc_pol = INTR_POLARITY_HIGH; - break; - case 8: - isrc->isrc_trig = INTR_TRIGGER_LEVEL; - isrc->isrc_pol = INTR_POLARITY_LOW; - break; - default: - device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", - tripol); - return (ENOTSUP); - } - isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; - isrc->isrc_nspc_num = irq; - - /* - * 1. The link between ISRC and controller must be set atomically. - * 2. Just do things only once in rare case when consumers - * of shared interrupt came here at the same moment. - */ - mtx_lock_spin(&sc->sc_mtx); - if (sc->gpio_pic_irqsrc[irq] != NULL) { - mtx_unlock_spin(&sc->sc_mtx); - return (sc->gpio_pic_irqsrc[irq] == isrc ? 0 : EEXIST); - } - sc->gpio_pic_irqsrc[irq] = isrc; - isrc->isrc_data = irq; - mtx_unlock_spin(&sc->sc_mtx); - - intr_irq_set_name(isrc, "%s,%u", device_get_nameunit(sc->dev), irq); - return (0); -} - -static int -gpio_pic_unregister(device_t dev, struct intr_irqsrc *isrc) -{ - struct imx51_gpio_softc *sc; - u_int irq; - - sc = device_get_softc(dev); - - mtx_lock_spin(&sc->sc_mtx); - irq = isrc->isrc_data; - if (sc->gpio_pic_irqsrc[irq] != isrc) { - mtx_unlock_spin(&sc->sc_mtx); - return (sc->gpio_pic_irqsrc[irq] == NULL ? 0 : EINVAL); - } - sc->gpio_pic_irqsrc[irq] = NULL; - isrc->isrc_data = 0; - mtx_unlock_spin(&sc->sc_mtx); - - intr_irq_set_name(isrc, ""); - return (0); -} - -static int gpio_pic_filter(void *arg) { struct imx51_gpio_softc *sc; + struct intr_irqsrc *isrc; uint32_t i, interrupts; sc = arg; mtx_lock_spin(&sc->sc_mtx); interrupts = READ4(sc, IMX_GPIO_ISR_REG) & READ4(sc, IMX_GPIO_IMR_REG); mtx_unlock_spin(&sc->sc_mtx); for (i = 0; interrupts != 0; i++, interrupts >>= 1) { if ((interrupts & 0x1) == 0) continue; - if (sc->gpio_pic_irqsrc[i]) - intr_irq_dispatch(sc->gpio_pic_irqsrc[i], curthread->td_intr_frame); - else - device_printf(sc->dev, "spurious interrupt %d\n", i); + isrc = &sc->gpio_pic_irqsrc[i].gi_isrc; + if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { + gpio_pic_disable_intr(sc->dev, isrc); + gpio_pic_post_filter(sc->dev, isrc); + device_printf(sc->dev, "Stray irq %u disabled\n", i); + } } return (FILTER_HANDLED); } + +/* + * register our isrcs into intrng to make it known about them. + */ +static int +gpio_pic_register_isrcs(struct imx51_gpio_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < NGPIO; irq++) { + sc->gpio_pic_irqsrc[irq].gi_irq = irq; + sc->gpio_pic_irqsrc[irq].gi_pol = INTR_POLARITY_CONFORM; + sc->gpio_pic_irqsrc[irq].gi_trig = INTR_TRIGGER_CONFORM; + + error = intr_isrc_register(&sc->gpio_pic_irqsrc[irq].gi_isrc, + sc->dev, 0, "%s,%u", name, irq); + if (error != 0) { + /* XXX call intr_isrc_deregister() */ + device_printf(sc->dev, "%s failed", __func__); + return (error); + } + } + return (0); +} #endif /* * */ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { mtx_lock_spin(&sc->sc_mtx); /* * 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; SET4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } else { pin->gp_flags |= GPIO_PIN_INPUT; CLEAR4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } } mtx_unlock_spin(&sc->sc_mtx); } static device_t imx51_gpio_get_bus(device_t dev) { struct imx51_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int imx51_gpio_pin_max(device_t dev, int *maxpin) { struct imx51_gpio_softc *sc; sc = device_get_softc(dev); *maxpin = sc->gpio_npins - 1; return (0); } static int imx51_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); *caps = sc->gpio_pins[i].gp_caps; mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); *flags = sc->gpio_pins[i].gp_flags; mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); imx51_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int imx51_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); if (value) SET4(sc, IMX_GPIO_DR_REG, (1U << i)); else CLEAR4(sc, IMX_GPIO_DR_REG, (1U << i)); mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); *val = (READ4(sc, IMX_GPIO_DR_REG) >> i) & 1; mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_toggle(device_t dev, uint32_t pin) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); WRITE4(sc, IMX_GPIO_DR_REG, (READ4(sc, IMX_GPIO_DR_REG) ^ (1U << i))); mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_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, "Freescale i.MX GPIO Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int imx51_gpio_attach(device_t dev) { struct imx51_gpio_softc *sc; int i, irq, unit; sc = device_get_softc(dev); sc->dev = dev; sc->gpio_npins = NGPIO; mtx_init(&sc->sc_mtx, device_get_nameunit(sc->dev), NULL, MTX_SPIN); if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); bus_release_resources(dev, imx_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } sc->sc_iot = rman_get_bustag(sc->sc_res[0]); sc->sc_ioh = rman_get_bushandle(sc->sc_res[0]); /* * Mask off all interrupts in hardware, then set up interrupt handling. */ WRITE4(sc, IMX_GPIO_IMR_REG, 0); for (irq = 0; irq < 2; irq++) { #ifdef ARM_INTRNG if ((bus_setup_intr(dev, sc->sc_res[1 + irq], INTR_TYPE_CLK, gpio_pic_filter, NULL, sc, &sc->gpio_ih[irq]))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); imx51_gpio_detach(dev); return (ENXIO); } #endif } unit = device_get_unit(dev); for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = (READ4(sc, IMX_GPIO_OE_REG) & (1U << i)) ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "imx_gpio%d.%d", unit, i); } #ifdef ARM_INTRNG + gpio_pic_register_isrcs(sc); intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); #endif sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { imx51_gpio_detach(dev); return (ENXIO); } return (0); } static int imx51_gpio_detach(device_t dev) { int irq; struct imx51_gpio_softc *sc; sc = device_get_softc(dev); gpiobus_detach_bus(dev); for (irq = 1; irq <= 2; irq++) { if (sc->gpio_ih[irq]) bus_teardown_intr(dev, sc->sc_res[irq], sc->gpio_ih[irq]); } bus_release_resources(dev, imx_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return(0); } static device_method_t imx51_gpio_methods[] = { DEVMETHOD(device_probe, imx51_gpio_probe), DEVMETHOD(device_attach, imx51_gpio_attach), DEVMETHOD(device_detach, imx51_gpio_detach), #ifdef ARM_INTRNG /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, gpio_pic_disable_intr), - DEVMETHOD(pic_disable_source, gpio_pic_disable_source), DEVMETHOD(pic_enable_intr, gpio_pic_enable_intr), - DEVMETHOD(pic_enable_source, gpio_pic_enable_source), + DEVMETHOD(pic_map_intr, gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, gpio_pic_teardown_intr), DEVMETHOD(pic_post_filter, gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, gpio_pic_pre_ithread), - DEVMETHOD(pic_register, gpio_pic_register), - DEVMETHOD(pic_unregister, gpio_pic_unregister), #endif /* GPIO protocol */ DEVMETHOD(gpio_get_bus, imx51_gpio_get_bus), DEVMETHOD(gpio_pin_max, imx51_gpio_pin_max), DEVMETHOD(gpio_pin_getname, imx51_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, imx51_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, imx51_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, imx51_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, imx51_gpio_pin_get), DEVMETHOD(gpio_pin_set, imx51_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, imx51_gpio_pin_toggle), {0, 0}, }; static driver_t imx51_gpio_driver = { "gpio", imx51_gpio_methods, sizeof(struct imx51_gpio_softc), }; static devclass_t imx51_gpio_devclass; DRIVER_MODULE(imx51_gpio, simplebus, imx51_gpio_driver, imx51_gpio_devclass, 0, 0); Index: head/sys/arm/include/intr.h =================================================================== --- head/sys/arm/include/intr.h (revision 297538) +++ head/sys/arm/include/intr.h (revision 297539) @@ -1,118 +1,118 @@ /* $NetBSD: intr.h,v 1.7 2003/06/16 20:01:00 thorpej Exp $ */ /*- * Copyright (c) 1997 Mark Brinicombe. * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Mark Brinicombe * for the NetBSD Project. * 4. 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 ``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 _MACHINE_INTR_H_ #define _MACHINE_INTR_H_ #ifdef FDT #include #endif #ifdef ARM_INTRNG #ifndef NIRQ #define NIRQ 1024 /* XXX - It should be an option. */ #endif #include #ifdef SMP -typedef void intr_ipi_send_t(void *, cpuset_t); +typedef void intr_ipi_send_t(void *, cpuset_t, u_int); typedef void intr_ipi_handler_t(void *); void intr_ipi_dispatch(u_int, struct trapframe *); void intr_ipi_send(cpuset_t, u_int); void intr_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *, intr_ipi_send_t *, void *); int intr_pic_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *); #endif #else /* ARM_INTRNG */ /* XXX move to std.* files? */ #ifdef CPU_XSCALE_81342 #define NIRQ 128 #elif defined(CPU_XSCALE_PXA2X0) #include #define NIRQ IRQ_GPIO_MAX #elif defined(SOC_MV_DISCOVERY) #define NIRQ 96 #elif defined(CPU_ARM9) || defined(SOC_MV_KIRKWOOD) || \ defined(CPU_XSCALE_IXP435) #define NIRQ 64 #elif defined(CPU_CORTEXA) #define NIRQ 1020 #elif defined(CPU_KRAIT) #define NIRQ 288 #elif defined(CPU_ARM1176) #define NIRQ 128 #elif defined(SOC_MV_ARMADAXP) #define MAIN_IRQ_NUM 116 #define ERR_IRQ_NUM 32 #define ERR_IRQ (MAIN_IRQ_NUM) #define MSI_IRQ_NUM 32 #define MSI_IRQ (ERR_IRQ + ERR_IRQ_NUM) #define NIRQ (MAIN_IRQ_NUM + ERR_IRQ_NUM + MSI_IRQ_NUM) #else #define NIRQ 32 #endif int arm_get_next_irq(int); void arm_mask_irq(uintptr_t); void arm_unmask_irq(uintptr_t); void arm_intrnames_init(void); void arm_setup_irqhandler(const char *, int (*)(void*), void (*)(void*), void *, int, int, void **); int arm_remove_irqhandler(int, void *); extern void (*arm_post_filter)(void *); extern int (*arm_config_irq)(int irq, enum intr_trigger trig, enum intr_polarity pol); void intr_pic_init_secondary(void); #ifdef FDT int gic_decode_fdt(phandle_t, pcell_t *, int *, int *, int *); int intr_fdt_map_irq(phandle_t, pcell_t *, int); #endif #endif /* ARM_INTRNG */ void arm_irq_memory_barrier(uintptr_t); #endif /* _MACHINE_INTR_H */ Index: head/sys/arm/mv/mpic.c =================================================================== --- head/sys/arm/mv/mpic.c (revision 297538) +++ head/sys/arm/mv/mpic.c (revision 297539) @@ -1,643 +1,631 @@ /*- * 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 ARM_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 ARM_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; - - struct intr_irqsrc ** mpic_isrcs; +#ifdef ARM_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 ARM_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 ARM_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 ARM_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 ARM_INTRNG - sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF, - M_WAITOK | M_ZERO); - + 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) { 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 ARM_INTRNG static int mpic_intr(void *arg) { struct mv_mpic_softc *sc; - struct trapframe *tf; - struct intr_irqsrc *isrc; uint32_t cause, irqsrc; unsigned int irq; u_int cpuid; sc = arg; - tf = curthread->td_intr_frame; 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; - isrc = sc->mpic_isrcs[irq]; - if (isrc == NULL) { - device_printf(sc->sc_dev, "Stray interrupt %u detected\n", irq); + if (intr_isrc_dispatch(&sc->mpic_isrcs[irq].mmi_isrc, + curthread->td_intr_frame) != 0) { mpic_mask_irq(irq); - continue; + device_printf(sc->sc_dev, "Stray irq %u " + "disabled\n", irq); } - intr_irq_dispatch(isrc, tf); } } return (FILTER_HANDLED); } -static int -mpic_attach_isrc(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int irq) +static void +mpic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { - const char *name; + u_int irq; - mtx_lock_spin(&sc->mtx); - if (sc->mpic_isrcs[irq] != NULL) { - mtx_unlock_spin(&sc->mtx); - return (sc->mpic_isrcs[irq] == isrc ? 0 : EEXIST); - } - sc->mpic_isrcs[irq] = isrc; - isrc->isrc_data = irq; - mtx_unlock_spin(&sc->mtx); - - name = device_get_nameunit(sc->sc_dev); - intr_irq_set_name(isrc, "%s", name); - - return (0); + irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq; + mpic_mask_irq(irq); } -#ifdef FDT -static int -mpic_map_fdt(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) +static void +mpic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; - int error; - if (isrc->isrc_ncells != 1) - return (EINVAL); - - irq = isrc->isrc_cells[0]; - - error = mpic_attach_isrc(sc, isrc, irq); - if (error != 0) - return (error); - - isrc->isrc_nspc_num = irq; - isrc->isrc_trig = INTR_TRIGGER_CONFORM; - isrc->isrc_pol = INTR_POLARITY_CONFORM; - isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; - - *irqp = irq; - - return (0); + irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq; + mpic_unmask_irq(irq); } -#endif static int -mpic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +mpic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { struct mv_mpic_softc *sc; - int error; - u_int irq = 0; sc = device_get_softc(dev); -#ifdef FDT - if (isrc->isrc_type == INTR_ISRCT_FDT) - error = mpic_map_fdt(sc, isrc, &irq); - else -#endif - error = EINVAL; + if (data->type != INTR_MAP_DATA_FDT || data->fdt.ncells !=1 || + data->fdt.cells[0] >= sc->nirqs) + return (EINVAL); - if (error == 0) - *is_percpu = irq < MPIC_PPI; - - return (error); + *isrcp = &sc->mpic_isrcs[data->fdt.cells[0]].mmi_isrc; + return (0); } static void -mpic_disable_source(device_t dev, struct intr_irqsrc *isrc) -{ - u_int irq; - - irq = isrc->isrc_data; - mpic_mask_irq(irq); -} - -static void -mpic_enable_source(device_t dev, struct intr_irqsrc *isrc) -{ - u_int irq; - - irq = isrc->isrc_data; - mpic_unmask_irq(irq); -} -static void mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { - mpic_disable_source(dev, isrc); + mpic_disable_intr(dev, isrc); } static void mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { - mpic_enable_source(dev, 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 ARM_INTRNG - DEVMETHOD(pic_register, mpic_register), - DEVMETHOD(pic_disable_source, mpic_disable_source), - DEVMETHOD(pic_enable_source, mpic_enable_source), + 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 ARM_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 addres 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_lic.c =================================================================== --- head/sys/arm/nvidia/tegra_lic.c (revision 297538) +++ head/sys/arm/nvidia/tegra_lic.c (revision 297539) @@ -1,265 +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_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +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_REGISTER(sc->parent, isrc, is_percpu)); + return (PIC_ALLOC_INTR(sc->parent, isrc, res, data)); } -static int -tegra_lic_unregister(device_t dev, struct intr_irqsrc *isrc) +static void +tegra_lic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); - return (PIC_UNREGISTER(sc->parent, isrc)); + PIC_DISABLE_INTR(sc->parent, isrc); } static void -tegra_lic_enable_source(device_t dev, struct intr_irqsrc *isrc) +tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); - PIC_ENABLE_SOURCE(sc->parent, isrc); + PIC_ENABLE_INTR(sc->parent, isrc); } -static void -tegra_lic_disable_source(device_t dev, struct intr_irqsrc *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); - PIC_DISABLE_SOURCE(sc->parent, isrc); + return (PIC_MAP_INTR(sc->parent, data, isrcp)); } -static void -tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +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); - PIC_ENABLE_INTR(sc->parent, isrc); + 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(device_t dev, struct intr_irqsrc *isrc) +tegra_lic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_lic_sc *sc = device_get_softc(dev); - return (PIC_BIND(sc->parent, isrc)); + 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) { 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_register, tegra_lic_register), - DEVMETHOD(pic_unregister, tegra_lic_unregister), - DEVMETHOD(pic_enable_source, tegra_lic_enable_source), - DEVMETHOD(pic_disable_source, tegra_lic_disable_source), + 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, tegra_lic_bind), + 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/omap4/omap4_wugen.c =================================================================== --- head/sys/arm/ti/omap4/omap4_wugen.c (revision 297538) +++ head/sys/arm/ti/omap4/omap4_wugen.c (revision 297539) @@ -1,228 +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_register(device_t dev, struct intr_irqsrc *isrc, - boolean_t *is_percpu) +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_REGISTER(sc->sc_parent, isrc, is_percpu)); + return (PIC_ALLOC_INTR(sc->sc_parent, isrc, res, data)); } -static int -omap4_wugen_unregister(device_t dev, struct intr_irqsrc *isrc) +static void +omap4_wugen_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); - return (PIC_UNREGISTER(sc->sc_parent, isrc)); + PIC_DISABLE_INTR(sc->sc_parent, isrc); } static void -omap4_wugen_enable_source(device_t dev, struct intr_irqsrc *isrc) +omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); - PIC_ENABLE_SOURCE(sc->sc_parent, isrc); + PIC_ENABLE_INTR(sc->sc_parent, isrc); } -static void -omap4_wugen_disable_source(device_t dev, struct intr_irqsrc *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); - PIC_DISABLE_SOURCE(sc->sc_parent, isrc); + return (PIC_MAP_INTR(sc->sc_parent, data, isrcp)); } -static void -omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc) +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); - PIC_ENABLE_INTR(sc->sc_parent, isrc); + 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(device_t dev, struct intr_irqsrc *isrc) +omap4_wugen_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); - return (PIC_BIND(sc->sc_parent, isrc)); + 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) { 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_register, omap4_wugen_register), - DEVMETHOD(pic_unregister, omap4_wugen_unregister), - DEVMETHOD(pic_enable_source, omap4_wugen_enable_source), - DEVMETHOD(pic_disable_source, omap4_wugen_disable_source), + 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, omap4_wugen_bind), + 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/kern/pic_if.m =================================================================== --- head/sys/kern/pic_if.m (revision 297538) +++ head/sys/kern/pic_if.m (revision 297539) @@ -1,137 +1,174 @@ #- -# Copyright (c) 2012 Jakub Wojciech Klama -# Copyright (c) 2015 Svatopluk Kraus -# Copyright (c) 2015 Michal Meloun +# 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$ # #include #include -#include -#include +#include +#include INTERFACE pic; CODE { - static int null_pic_bind(device_t dev, struct intr_irqsrc *isrc) + static int + dflt_pic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { + return (EOPNOTSUPP); } - static void null_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) + static int + null_pic_alloc_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - return; + + return (0); } - static void null_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) + static int + null_pic_release_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - return; + + return (0); } - static void null_pic_init_secondary(device_t dev) + static int + null_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - return; + + return (0); } - static void null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) + static int + null_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - return; + + return (0); } + static void + null_pic_init_secondary(device_t dev) + { + } + + static void + null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) + { + } + static int dflt_pic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc *isrc) { return (EOPNOTSUPP); } }; -METHOD int register { +METHOD int alloc_intr { device_t dev; struct intr_irqsrc *isrc; - boolean_t *is_percpu; -}; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_alloc_intr; -METHOD int unregister { +METHOD int bind_intr { device_t dev; struct intr_irqsrc *isrc; -}; +} DEFAULT dflt_pic_bind_intr; METHOD void disable_intr { device_t dev; struct intr_irqsrc *isrc; -} DEFAULT null_pic_disable_intr; +}; -METHOD void disable_source { +METHOD void enable_intr { device_t dev; struct intr_irqsrc *isrc; }; -METHOD void enable_source { +METHOD int map_intr { device_t dev; - struct intr_irqsrc *isrc; + struct intr_map_data *data; + struct intr_irqsrc **isrcp; }; -METHOD void enable_intr { +METHOD int release_intr { device_t dev; struct intr_irqsrc *isrc; -} DEFAULT null_pic_enable_intr; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_release_intr; -METHOD void pre_ithread { +METHOD int setup_intr { device_t dev; struct intr_irqsrc *isrc; -}; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_setup_intr; -METHOD void post_ithread { +METHOD int teardown_intr { device_t dev; struct intr_irqsrc *isrc; -}; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_teardown_intr; METHOD void post_filter { device_t dev; struct intr_irqsrc *isrc; }; -METHOD int bind { +METHOD void post_ithread { device_t dev; struct intr_irqsrc *isrc; -} DEFAULT null_pic_bind; +}; +METHOD void pre_ithread { + device_t dev; + struct intr_irqsrc *isrc; +}; + METHOD void init_secondary { device_t dev; } DEFAULT null_pic_init_secondary; METHOD void ipi_send { device_t dev; struct intr_irqsrc *isrc; cpuset_t cpus; + u_int ipi; } DEFAULT null_pic_ipi_send; METHOD int ipi_setup { device_t dev; u_int ipi; - struct intr_irqsrc *isrc; + struct intr_irqsrc **isrcp; } DEFAULT dflt_pic_ipi_setup; Index: head/sys/kern/subr_intr.c =================================================================== --- head/sys/kern/subr_intr.c (revision 297538) +++ head/sys/kern/subr_intr.c (revision 297539) @@ -1,1214 +1,1235 @@ /*- - * Copyright (c) 2012-2014 Jakub Wojciech Klama . - * Copyright (c) 2015 Svatopluk Kraus - * Copyright (c) 2015 Michal Meloun + * 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$ */ #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 #ifdef FDT #include #include #include #endif #ifdef DDB #include #endif #include "pic_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; }; 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; #define IRQ_INVALID nitems(irq_sources) +/* + * 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 > IRQ_INVALID); + #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) { - /* - * XXX - It should be atomic for PPI interrupts. It was proven that - * the lost is measurable easily for timer PPI interrupts. - */ - isrc->isrc_count[0]++; - /*atomic_add_long(&isrc->isrc_count[0], 1);*/ + 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. */ -void -intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) +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; - } else + return (0); + } else #endif if (isrc->isrc_event != NULL) { if (intr_event_handle(isrc->isrc_event, tf) == 0) - return; + return (0); } isrc_increment_straycount(isrc); - PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); - - device_printf(isrc->isrc_dev, "stray irq <%s> disabled", - isrc->isrc_name); + return (EINVAL); } /* - * Allocate interrupt source. - */ -struct intr_irqsrc * -intr_isrc_alloc(u_int type, u_int extsize) -{ - struct intr_irqsrc *isrc; - - isrc = malloc(sizeof(*isrc) + extsize, M_INTRNG, M_WAITOK | M_ZERO); - isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ - isrc->isrc_type = type; - isrc->isrc_nspc_type = INTR_IRQ_NSPC_NONE; - isrc->isrc_trig = INTR_TRIGGER_CONFORM; - isrc->isrc_pol = INTR_POLARITY_CONFORM; - CPU_ZERO(&isrc->isrc_cpu); - return (isrc); -} - -/* - * Free interrupt source. - */ -void -intr_isrc_free(struct intr_irqsrc *isrc) -{ - - free(isrc, M_INTRNG); -} - -void -intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); - va_end(ap); -} - -/* * 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 int -isrc_alloc_irq_locked(struct intr_irqsrc *isrc) +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; - intr_irq_set_name(isrc, "irq%u", irq); - isrc_setup_counters(isrc); - irq_next_free = irq + 1; if (irq_next_free >= maxirqs) irq_next_free = 0; return (0); } -#ifdef notyet + /* * Free unique interrupt number (resource handle) from interrupt source. */ -static int +static inline int isrc_free_irq(struct intr_irqsrc *isrc) { - u_int maxirqs; - mtx_assert(&isrc_table_lock, MA_NOTOWNED); + mtx_assert(&isrc_table_lock, MA_OWNED); - maxirqs = nitems(irq_sources); - if (isrc->isrc_irq >= maxirqs) + if (isrc->isrc_irq >= nitems(irq_sources)) return (EINVAL); - - mtx_lock(&isrc_table_lock); - if (irq_sources[isrc->isrc_irq] != isrc) { - mtx_unlock(&isrc_table_lock); + if (irq_sources[isrc->isrc_irq] != isrc) return (EINVAL); - } irq_sources[isrc->isrc_irq] = NULL; isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ - mtx_unlock(&isrc_table_lock); - return (0); } -#endif + /* * Lookup interrupt source by interrupt number (resource handle). */ -static struct intr_irqsrc * +static inline struct intr_irqsrc * isrc_lookup(u_int irq) { if (irq < nitems(irq_sources)) return (irq_sources[irq]); return (NULL); } /* - * Lookup interrupt source by namespace description. + * Initialize interrupt source and register it into global interrupt table. */ -static struct intr_irqsrc * -isrc_namespace_lookup(device_t dev, uint16_t type, uint16_t num) +int +intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, + const char *fmt, ...) { - u_int irq; - struct intr_irqsrc *isrc; + int error; + va_list ap; - mtx_assert(&isrc_table_lock, MA_OWNED); + bzero(isrc, sizeof(struct intr_irqsrc)); + isrc->isrc_dev = dev; + isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ + isrc->isrc_flags = flags; - for (irq = 0; irq < nitems(irq_sources); irq++) { - isrc = irq_sources[irq]; - if (isrc != NULL && isrc->isrc_dev == dev && - isrc->isrc_nspc_type == type && isrc->isrc_nspc_num == num) - return (isrc); + 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); } - return (NULL); + /* + * 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); } /* - * Map interrupt source according to namespace into framework. If such mapping - * does not exist, create it. Return unique interrupt number (resource handle) - * associated with mapped interrupt source. + * Deregister interrupt source from global interrupt table. */ -u_int -intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num) +int +intr_isrc_deregister(struct intr_irqsrc *isrc) { - struct intr_irqsrc *isrc, *new_isrc; int error; - new_isrc = intr_isrc_alloc(INTR_ISRCT_NAMESPACE, 0); + 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); +} +static struct intr_dev_data * +intr_ddata_alloc(u_int extsize) +{ + struct intr_dev_data *ddata; + + ddata = malloc(sizeof(*ddata) + extsize, M_INTRNG, M_WAITOK | M_ZERO); + mtx_lock(&isrc_table_lock); - isrc = isrc_namespace_lookup(dev, type, num); - if (isrc != NULL) { + if (intr_ddata_first_unused >= nitems(intr_ddata_tab)) { mtx_unlock(&isrc_table_lock); - intr_isrc_free(new_isrc); - return (isrc->isrc_irq); /* already mapped */ + free(ddata, M_INTRNG); + return (NULL); } - - error = isrc_alloc_irq_locked(new_isrc); - if (error != 0) { - mtx_unlock(&isrc_table_lock); - intr_isrc_free(new_isrc); - return (IRQ_INVALID); /* no space left */ - } - - new_isrc->isrc_dev = dev; - new_isrc->isrc_nspc_type = type; - new_isrc->isrc_nspc_num = num; + intr_ddata_tab[intr_ddata_first_unused] = ddata; + ddata->idd_irq = IRQ_DDATA_BASE + intr_ddata_first_unused++; mtx_unlock(&isrc_table_lock); - - return (new_isrc->isrc_irq); + return (ddata); } -#ifdef FDT -/* - * Lookup interrupt source by FDT description. - */ static struct intr_irqsrc * -isrc_fdt_lookup(intptr_t xref, pcell_t *cells, u_int ncells) +intr_ddata_lookup(u_int irq, struct intr_map_data **datap) { - u_int irq, cellsize; + int error; struct intr_irqsrc *isrc; + struct intr_dev_data *ddata; - mtx_assert(&isrc_table_lock, MA_OWNED); + isrc = isrc_lookup(irq); + if (isrc != NULL) { + if (datap != NULL) + *datap = NULL; + return (isrc); + } - cellsize = ncells * sizeof(*cells); - for (irq = 0; irq < nitems(irq_sources); irq++) { - isrc = irq_sources[irq]; - if (isrc != NULL && isrc->isrc_type == INTR_ISRCT_FDT && - isrc->isrc_xref == xref && isrc->isrc_ncells == ncells && - memcmp(isrc->isrc_cells, cells, cellsize) == 0) - 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); } - return (NULL); + if (datap != NULL) + *datap = &ddata->idd_data; + return (ddata->idd_isrc); } +#ifdef DEV_ACPI /* - * Map interrupt source according to FDT data into framework. If such mapping + * 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_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) +intr_acpi_map_irq(device_t dev, u_int irq, enum intr_polarity pol, + enum intr_trigger trig) { - struct intr_irqsrc *isrc, *new_isrc; - u_int cellsize; - intptr_t xref; - int error; + struct intr_dev_data *ddata; - xref = (intptr_t)node; /* It's so simple for now. */ + ddata = intr_ddata_alloc(0); + if (ddata == NULL) + return (0xFFFFFFFF); /* no space left */ - cellsize = ncells * sizeof(*cells); - new_isrc = intr_isrc_alloc(INTR_ISRCT_FDT, cellsize); - - mtx_lock(&isrc_table_lock); - isrc = isrc_fdt_lookup(xref, cells, ncells); - if (isrc != NULL) { - mtx_unlock(&isrc_table_lock); - intr_isrc_free(new_isrc); - return (isrc->isrc_irq); /* already mapped */ - } - - error = isrc_alloc_irq_locked(new_isrc); - if (error != 0) { - mtx_unlock(&isrc_table_lock); - intr_isrc_free(new_isrc); - return (IRQ_INVALID); /* no space left */ - } - - new_isrc->isrc_xref = xref; - new_isrc->isrc_ncells = ncells; - memcpy(new_isrc->isrc_cells, cells, cellsize); - mtx_unlock(&isrc_table_lock); - - return (new_isrc->isrc_irq); + ddata->idd_dev = dev; + ddata->idd_data.type = INTR_MAP_DATA_ACPI; + ddata->idd_data.acpi.irq = irq; + ddata->idd_data.acpi.pol = pol; + ddata->idd_data.acpi.trig = trig; + return (ddata->idd_irq); } #endif - +#ifdef FDT /* - * Register interrupt source into interrupt controller. + * 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. */ -static int -isrc_register(struct intr_irqsrc *isrc) +u_int +intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) { - struct intr_pic *pic; - boolean_t is_percpu; - int error; + struct intr_dev_data *ddata; + u_int cellsize; - if (isrc->isrc_flags & INTR_ISRCF_REGISTERED) - return (0); + cellsize = ncells * sizeof(*cells); + ddata = intr_ddata_alloc(cellsize); + if (ddata == NULL) + return (0xFFFFFFFF); /* no space left */ - if (isrc->isrc_dev == NULL) { - pic = pic_lookup(NULL, isrc->isrc_xref); - if (pic == NULL || pic->pic_dev == NULL) - return (ESRCH); - isrc->isrc_dev = pic->pic_dev; - } - - error = PIC_REGISTER(isrc->isrc_dev, isrc, &is_percpu); - if (error != 0) - return (error); - - mtx_lock(&isrc_table_lock); - isrc->isrc_flags |= INTR_ISRCF_REGISTERED; - if (is_percpu) - isrc->isrc_flags |= INTR_ISRCF_PERCPU; - isrc_update_name(isrc, NULL); - mtx_unlock(&isrc_table_lock); - return (0); + ddata->idd_xref = (intptr_t)node; + ddata->idd_data.type = INTR_MAP_DATA_FDT; + ddata->idd_data.fdt.ncells = ncells; + ddata->idd_data.fdt.cells = (pcell_t *)(ddata + 1); + memcpy(ddata->idd_data.fdt.cells, cells, cellsize); + return (ddata->idd_irq); } +#endif #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 successfull. */ if (irq_assign_cpu) { - error = PIC_BIND(isrc->isrc_dev, isrc); + 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 struct intr_pic * +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); SLIST_FOREACH(pic, &pic_list, pic_next) { if (pic->pic_xref != xref) continue; if (pic->pic_xref != 0 || pic->pic_dev == 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); 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 intr_pic_register(device_t dev, intptr_t xref) { struct intr_pic *pic; pic = pic_create(dev, xref); if (pic == NULL) return (ENOMEM); if (pic->pic_dev != dev) return (EINVAL); /* XXX it could be many things. */ debugf("PIC %p registered for %s \n", pic, device_get_nameunit(dev), xref); return (0); } /* * Unregister interrupt controller. */ int -intr_pic_unregister(device_t dev, intptr_t xref) +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) { if (pic_lookup(dev, xref) == NULL) { device_printf(dev, "not registered\n"); return (EINVAL); } 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_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand, - void *arg, u_int irq, int flags, void **cookiep) +intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, + u_int *irqp) { - const char *name; + int error; struct intr_irqsrc *isrc; + struct intr_pic *pic; + + if (data == NULL) + return (EINVAL); + + pic = pic_lookup(dev, xref); + if (pic == NULL || pic->pic_dev == NULL) + return (ESRCH); + + 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 thru 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); } -#endif - isrc = isrc_lookup(irq); - if (isrc == NULL) { - debugf("irq %u without source on %s\n", irq, name); - return (EINVAL); - } - - error = isrc_register(isrc); - if (error != 0) { - debugf("irq %u map error %d on %s\n", irq, error, name); - return (error); - } - -#ifdef INTR_SOLO 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); - isrc->isrc_handlers++; - if (isrc->isrc_handlers == 1) { - PIC_ENABLE_INTR(isrc->isrc_dev, isrc); - PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc); + 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); - return (0); + if (error != 0) + intr_event_remove_handler(*cookiep); + return (error); } int -intr_irq_remove_handler(device_t dev, u_int irq, void *cookie) +intr_teardown_irq(device_t dev, struct resource *res, void *cookie) { - struct intr_irqsrc *isrc; int error; + struct intr_map_data *data; + struct intr_irqsrc *isrc; - isrc = isrc_lookup(irq); + 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_SOURCE(isrc->isrc_dev, isrc); 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_SOURCE(isrc->isrc_dev, isrc); + 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_irq_config(u_int irq, enum intr_trigger trig, enum intr_polarity pol) +intr_describe_irq(device_t dev, struct resource *res, void *cookie, + const char *descr) { + int error; struct intr_irqsrc *isrc; - isrc = isrc_lookup(irq); - if (isrc == NULL) - return (EINVAL); + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); - if (isrc->isrc_handlers != 0) - return (EBUSY); /* interrrupt is enabled (active) */ - - /* - * Once an interrupt is enabled, we do not change its configuration. - * A controller PIC_ENABLE_INTR() method is called when an interrupt - * is going to be enabled. In this method, a controller should setup - * the interrupt according to saved configuration parameters. - */ - isrc->isrc_trig = trig; - isrc->isrc_pol = pol; - - return (0); -} - -int -intr_irq_describe(u_int irq, void *cookie, const char *descr) -{ - struct intr_irqsrc *isrc; - int error; - - isrc = isrc_lookup(irq); + 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_irq_bind(u_int irq, int cpu) +intr_bind_irq(device_t dev, struct resource *res, int cpu) { struct intr_irqsrc *isrc; - isrc = isrc_lookup(irq); + 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_PERCPU) + isrc->isrc_flags & INTR_ISRCF_PPI) 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(isrc->isrc_dev, isrc) != 0) + 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 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)" : "", - isrc->isrc_count[0]); - irqsum += isrc->isrc_count[0]; + isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); + irqsum += num; } db_printf("irq total %u\n", irqsum); } #endif Index: head/sys/sys/intr.h =================================================================== --- head/sys/sys/intr.h (revision 297538) +++ head/sys/sys/intr.h (revision 297539) @@ -1,141 +1,148 @@ -/* $NetBSD: intr.h,v 1.7 2003/06/16 20:01:00 thorpej Exp $ */ - /*- - * Copyright (c) 1997 Mark Brinicombe. + * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Mark Brinicombe - * for the NetBSD Project. - * 4. 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 ``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) + * 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 +enum intr_map_data_type { + INTR_MAP_DATA_ACPI, + INTR_MAP_DATA_FDT, +}; + +#ifdef DEV_ACPI +struct intr_map_data_acpi { + u_int irq; + enum intr_polarity pol; + enum intr_trigger trig; +}; +#endif +#ifdef FDT +struct intr_map_data_fdt { + u_int ncells; + pcell_t *cells; +}; +#endif + +struct intr_map_data { + enum intr_map_data_type type; + union { +#ifdef DEV_ACPI + struct intr_map_data_acpi acpi; +#endif +#ifdef FDT + struct intr_map_data_fdt fdt; +#endif + }; +}; + #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) -enum intr_isrc_type { - INTR_ISRCT_NAMESPACE, - INTR_ISRCT_FDT -}; - -#define INTR_ISRCF_REGISTERED 0x01 /* registered in a controller */ -#define INTR_ISRCF_PERCPU 0x02 /* per CPU interrupt */ +#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 */ - intptr_t isrc_xref; /* device reference key */ - uintptr_t isrc_data; /* device data for isrc */ u_int isrc_irq; /* unique identificator */ - enum intr_isrc_type isrc_type; /* how is isrc decribed */ u_int isrc_flags; char isrc_name[INTR_ISRC_NAMELEN]; - uint16_t isrc_nspc_type; - uint16_t isrc_nspc_num; - enum intr_trigger isrc_trig; - enum intr_polarity isrc_pol; 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 -#ifdef FDT - u_int isrc_ncells; - pcell_t isrc_cells[]; /* leave it last */ -#endif }; -struct intr_irqsrc *intr_isrc_alloc(u_int type, u_int extsize); -void intr_isrc_free(struct intr_irqsrc *isrc); +/* 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); -void intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...) - __printflike(2, 3); +int intr_isrc_dispatch(struct intr_irqsrc *, struct trapframe *); +u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask); -void intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf); +int 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); -#define INTR_IRQ_NSPC_NONE 0 -#define INTR_IRQ_NSPC_PLAIN 1 -#define INTR_IRQ_NSPC_IRQ 2 -#define INTR_IRQ_NSPC_IPI 3 +extern device_t intr_irq_root_dev; -u_int intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num); -#ifdef FDT -u_int intr_fdt_map_irq(phandle_t, pcell_t *, u_int); -#endif +/* Intr interface for BUS. */ +int intr_map_irq(device_t, intptr_t, struct intr_map_data *, u_int *); -extern device_t intr_irq_root_dev; +int intr_alloc_irq(device_t, struct resource *); +int intr_release_irq(device_t, struct resource *); -int intr_pic_register(device_t dev, intptr_t xref); -int intr_pic_unregister(device_t dev, intptr_t xref); -int intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, - void *arg, u_int ipicount); +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_irq_add_handler(device_t dev, driver_filter_t, driver_intr_t, void *, - u_int, int, void **); -int intr_irq_remove_handler(device_t dev, u_int, void *); -int intr_irq_config(u_int, enum intr_trigger, enum intr_polarity); -int intr_irq_describe(u_int, void *, const char *); +int intr_describe_irq(device_t, struct resource *, void *, const char *); -u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask); +#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 #ifdef SMP -int intr_irq_bind(u_int, int); +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 */