From owner-svn-src-projects@FreeBSD.ORG Mon Aug 11 16:44:07 2014 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id B1AF3115 for ; Mon, 11 Aug 2014 16:44:07 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 9A3872266 for ; Mon, 11 Aug 2014 16:44:07 +0000 (UTC) Received: from jceel (uid 1267) (envelope-from jceel@FreeBSD.org) id 2f8f by svn.freebsd.org (DragonFly Mail Agent v0.9+); Mon, 11 Aug 2014 16:44:06 +0000 From: Jakub Wojciech Klama Date: Mon, 11 Aug 2014 16:44:06 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r269817 - in projects/arm_intrng/sys: arm/arm arm/broadcom/bcm2835 arm/conf arm/include arm/lpc arm/ti arm/ti/omap4 boot/fdt/dts/arm conf dev/fdt X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: <53e8f2d6.2f8f.71f433d6@svn.freebsd.org> X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 11 Aug 2014 16:44:07 -0000 Author: jceel Date: Mon Aug 11 16:44:06 2014 New Revision: 269817 URL: http://svnweb.freebsd.org/changeset/base/269817 Log: Initial version of refactored interrupt handling framework for FreeBSD ARM. Currently supports following interrupt controllers: * lpc_intc in lpc port * aintc in ti port * intc in bcm2835 port (Raspberry PI) * ARM GIC It's mainly based on 2012 Google Summer of Code project, adapted for current HEAD and with added SMP (IPI) support. It has been tested on LPC3250, Pandaboard and RPI (both with and without SMP support). Introduces hierarchical interrupt controllers in the system, allowing to easily utilize multiplexers such as GPIOs to serve as interrupt sources. nexus driver is a top-level interrupt controller, exposing single interruprt (IRQ) on ARM. The GIC or any other used interrupt controller setups handler on that interrupt, exposing new IRQs available for other peripherals. In an example SoC, interrupt hierarchy may look like that: nexus0 (1 interrupts) | \-- gic0 (160 interrupts, uses irq nexus0:0) | \-- gpio0 (8 interrupts, uses irq gic0:42) | | | \-- mmcsd0 (uses irqs gpio0:1, gpio0:2) | \-- spi0 (uses irq gpio0:3) | ... \-- gpio1 (8 interrupts, uses irq gic0:43) \-- ehci0 (uses irq gic0:109) ... That change should not break any existing ports in any way, except for need to add 'arm/arm/intr.c' to 'files.*' of existing ports, as it's no longer compiled-in by default. Added: projects/arm_intrng/sys/arm/arm/intrng.c (contents, props changed) projects/arm_intrng/sys/arm/arm/pic_if.m Modified: projects/arm_intrng/sys/arm/arm/gic.c projects/arm_intrng/sys/arm/arm/intr.c projects/arm_intrng/sys/arm/arm/mp_machdep.c projects/arm_intrng/sys/arm/arm/nexus.c projects/arm_intrng/sys/arm/broadcom/bcm2835/bcm2835_intr.c projects/arm_intrng/sys/arm/broadcom/bcm2835/files.bcm2835 projects/arm_intrng/sys/arm/conf/EA3250 projects/arm_intrng/sys/arm/conf/PANDABOARD projects/arm_intrng/sys/arm/conf/RPI-B projects/arm_intrng/sys/arm/include/fdt.h projects/arm_intrng/sys/arm/include/intr.h projects/arm_intrng/sys/arm/include/smp.h projects/arm_intrng/sys/arm/lpc/files.lpc projects/arm_intrng/sys/arm/lpc/lpc_intc.c projects/arm_intrng/sys/arm/lpc/lpcreg.h projects/arm_intrng/sys/arm/ti/aintc.c projects/arm_intrng/sys/arm/ti/files.ti projects/arm_intrng/sys/arm/ti/omap4/omap4_mp.c projects/arm_intrng/sys/boot/fdt/dts/arm/bcm2835.dtsi projects/arm_intrng/sys/boot/fdt/dts/arm/ea3250.dts projects/arm_intrng/sys/boot/fdt/dts/arm/pandaboard.dts projects/arm_intrng/sys/conf/files.arm projects/arm_intrng/sys/conf/options.arm projects/arm_intrng/sys/dev/fdt/simplebus.c Modified: projects/arm_intrng/sys/arm/arm/gic.c ============================================================================== --- projects/arm_intrng/sys/arm/arm/gic.c Mon Aug 11 16:31:28 2014 (r269816) +++ projects/arm_intrng/sys/arm/arm/gic.c Mon Aug 11 16:44:06 2014 (r269817) @@ -55,6 +55,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include "pic_if.h" + /* We are using GICv2 register naming */ /* Distributor Registers */ @@ -95,42 +97,49 @@ __FBSDID("$FreeBSD$"); #define GICD_ICFGR_TRIG_MASK 0x2 struct arm_gic_softc { + device_t gic_dev; 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; + void * gic_intrhand; uint8_t ver; - device_t dev; struct mtx mutex; uint32_t nirqs; }; - 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 */ + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt */ { -1, 0 } }; static struct arm_gic_softc *arm_gic_sc = NULL; -#define gic_c_read_4(reg) \ - bus_space_read_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg) -#define gic_c_write_4(reg, val) \ - bus_space_write_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg, val) -#define gic_d_read_4(reg) \ - bus_space_read_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg) -#define gic_d_write_4(reg, val) \ - bus_space_write_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg, val) - -static int gic_config_irq(int irq, enum intr_trigger trig, - enum intr_polarity pol); -static void gic_post_filter(void *); +static int arm_gic_probe(device_t); +static int arm_gic_attach(device_t); +static void arm_gic_init_secondary(device_t); +static int arm_gic_intr(void *); +static int arm_gic_config(device_t, int, enum intr_trigger, enum intr_polarity); +static void arm_gic_eoi(device_t, int); +static void arm_gic_mask(device_t, int); +static void arm_gic_unmask(device_t, int); +static void arm_gic_ipi_send(device_t, cpuset_t, int); +static void arm_gic_ipi_clear(device_t, int); + +#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_4(_sc, _reg, _val) \ + bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) static int arm_gic_probe(device_t dev) { - if (!ofw_bus_status_okay(dev)) return (ENXIO); @@ -140,17 +149,13 @@ arm_gic_probe(device_t dev) return (BUS_PROBE_DEFAULT); } -void -gic_init_secondary(void) +static void +arm_gic_init_secondary(device_t dev) { - int i, nirqs; - - /* Get the number of interrupts */ - nirqs = gic_d_read_4(GICD_TYPER); - nirqs = 32 * ((nirqs & 0x1f) + 1); - - for (i = 0; i < nirqs; i += 4) - gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0); + struct arm_gic_softc *sc = device_get_softc(dev); + + for (int 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 < nirqs; i += 32) { @@ -158,14 +163,12 @@ gic_init_secondary(void) } /* Enable CPU interface */ - gic_c_write_4(GICC_CTLR, 1); - - /* Set priority mask register. */ - gic_c_write_4(GICC_PMR, 0xff); + gic_c_write_4(sc, GICC_CTLR, 1); /* Enable interrupt distribution */ - gic_d_write_4(GICD_CTLR, 0x01); - + gic_d_write_4(sc, GICD_CTLR, 0x01); + + /* Activate IRQ 29, ie private timer IRQ*/ /* Activate IRQ 29-30, ie private timer (secure & non-secure) IRQs */ gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); gic_d_write_4(GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F))); @@ -174,21 +177,18 @@ gic_init_secondary(void) static int arm_gic_attach(device_t dev) { - struct arm_gic_softc *sc; + struct arm_gic_softc *sc = device_get_softc(dev); int i; uint32_t icciidr; - if (arm_gic_sc) - return (ENXIO); - - sc = device_get_softc(dev); - sc->dev = 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; + arm_gic_sc = sc; + /* Initialize mutex */ mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); @@ -200,32 +200,35 @@ arm_gic_attach(device_t dev) sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); - arm_gic_sc = sc; + arm_register_pic(dev, PIC_FEATURE_IPI); + + if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_MISC | INTR_CONTROLLER, + arm_gic_intr, NULL, sc, &sc->gic_intrhand)) { + device_printf(dev, "could not setup interrupt handler\n"); + bus_release_resources(dev, arm_gic_spec, sc->gic_res); + return (ENXIO); + } /* Disable interrupt forwarding to the CPU interface */ - gic_d_write_4(GICD_CTLR, 0x00); + gic_d_write_4(sc, GICD_CTLR, 0x00); /* Get the number of interrupts */ - sc->nirqs = gic_d_read_4(GICD_TYPER); + sc->nirqs = gic_d_read_4(sc, GICD_TYPER); sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); - /* Set up function pointers */ - arm_post_filter = gic_post_filter; - arm_config_irq = gic_config_irq; - - icciidr = gic_c_read_4(GICC_IIDR); - device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x sc->nirqs %u\n", + icciidr = gic_c_read_4(sc, GICC_IIDR); + device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x nirqs %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(GICD_ICFGR(i >> 4), 0x00000000); + gic_d_write_4(sc, GICD_ICFGR(i >> 4), 0x00000000); } /* Disable all interrupts. */ for (i = 32; i < sc->nirqs; i += 32) { - gic_d_write_4(GICD_ICENABLER(i >> 5), 0xFFFFFFFF); + gic_d_write_4(sc, GICD_ICENABLER(i >> 5), 0xFFFFFFFF); } for (i = 0; i < sc->nirqs; i += 4) { @@ -235,100 +238,61 @@ arm_gic_attach(device_t dev) /* Set all the interrupts to be in Group 0 (secure) */ for (i = 0; i < sc->nirqs; i += 32) { - gic_d_write_4(GICD_IGROUPR(i >> 5), 0); + gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); } /* Enable CPU interface */ - gic_c_write_4(GICC_CTLR, 1); + gic_c_write_4(sc, GICC_CTLR, 1); /* Set priority mask register. */ - gic_c_write_4(GICC_PMR, 0xff); + gic_c_write_4(sc, GICC_PMR, 0xff); /* Enable interrupt distribution */ - gic_d_write_4(GICD_CTLR, 0x01); + gic_d_write_4(sc, GICD_CTLR, 0x01); return (0); } -static device_method_t arm_gic_methods[] = { - DEVMETHOD(device_probe, arm_gic_probe), - DEVMETHOD(device_attach, arm_gic_attach), - { 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); - -static void -gic_post_filter(void *arg) -{ - uintptr_t irq = (uintptr_t) arg; - - if (irq > GIC_LAST_IPI) - arm_irq_memory_barrier(irq); - gic_c_write_4(GICC_EOIR, irq); -} - -int -arm_get_next_irq(int last_irq) +static int +arm_gic_intr(void *arg) { - uint32_t active_irq; + struct arm_gic_softc *sc = (struct arm_gic_softc *)arg; + uint32_t active_irq, last_irq = 0; - active_irq = gic_c_read_4(GICC_IAR); + 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_IPI) - gic_c_write_4(GICC_EOIR, active_irq); + + if ((active_irq & 0x3ff) < 1) + gic_c_write_4(sc, GICC_EOIR, active_irq); active_irq &= 0x3FF; if (active_irq == 0x3FF) { if (last_irq == -1) - printf("Spurious interrupt detected\n"); - return -1; + printf("Spurious interrupt detected [0x%08x]\n", active_irq); + return (FILTER_HANDLED); } + + gic_c_write_4(sc, GICC_EOIR, active_irq); + arm_dispatch_irq(sc->gic_dev, NULL, active_irq); - return active_irq; -} - -void -arm_mask_irq(uintptr_t nb) -{ - - gic_d_write_4(GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F))); - gic_c_write_4(GICC_EOIR, nb); -} - -void -arm_unmask_irq(uintptr_t nb) -{ - - if (nb > GIC_LAST_IPI) - arm_irq_memory_barrier(nb); - gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F))); + return (FILTER_HANDLED); } static int -gic_config_irq(int irq, enum intr_trigger trig, +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 >= arm_gic_sc->nirqs)) + if ((irq < 0) || (irq >= sc->nirqs)) goto invalid_args; if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) && (trig != INTR_TRIGGER_CONFORM)) @@ -337,9 +301,9 @@ gic_config_irq(int irq, enum intr_trigge (pol != INTR_POLARITY_CONFORM)) goto invalid_args; - mtx_lock_spin(&arm_gic_sc->mutex); + mtx_lock_spin(&sc->mutex); - reg = gic_d_read_4(GICD_ICFGR(irq >> 4)); + reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4)); mask = (reg >> 2*(irq % 16)) & 0x3; if (pol == INTR_POLARITY_LOW) { @@ -361,32 +325,64 @@ gic_config_irq(int irq, enum intr_trigge /* Set mask */ reg = reg & ~(0x3 << 2*(irq % 16)); reg = reg | (mask << 2*(irq % 16)); - gic_d_write_4(GICD_ICFGR(irq >> 4), reg); + gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg); - mtx_unlock_spin(&arm_gic_sc->mutex); + mtx_unlock_spin(&sc->mutex); return (0); invalid_args: - device_printf(arm_gic_sc->dev, "gic_config_irg, invalid parameters\n"); + device_printf(dev, "gic_config_irg, invalid parameters\n"); return (EINVAL); } -#ifdef SMP -void -pic_ipi_send(cpuset_t cpus, u_int ipi) +static void +arm_gic_eoi(device_t dev, int irq) { + struct arm_gic_softc *sc = device_get_softc(dev); + + if (irq > GIC_LAST_IPI) + arm_irq_memory_barrier(irq); + + gic_c_write_4(sc, GICC_EOIR, irq); +} + + +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(GICC_EOIR, nb); +} + +static void +arm_gic_unmask(device_t dev, int irq) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + + if (nb > GIC_LAST_IPI) + arm_irq_memory_barrier(nb); + + gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F))); +} + +static void +arm_gic_ipi_send(device_t dev, cpuset_t cpus, 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(GICD_SGIR(0), val | ipi); + gic_d_write_4(sc, GICD_SGIR(0), val | ipi); } -int -pic_ipi_get(int i) +static int +arm_gic_ipi_read(device_t dev, int i) { if (i != -1) { @@ -394,16 +390,44 @@ pic_ipi_get(int i) * The intr code will automagically give the frame pointer * if the interrupt argument is 0. */ - if ((unsigned int)i > 16) + if ((unsigned int)i > 16) return (0); return (i); } + return (0x3ff); } -void -pic_ipi_clear(int ipi) +static void +arm_gic_ipi_clear(device_t dev, int ipi) { + /* no-op */ } -#endif + +static device_method_t arm_gic_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, arm_gic_probe), + DEVMETHOD(device_attach, arm_gic_attach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_config, arm_gic_config), + DEVMETHOD(pic_mask, arm_gic_mask), + DEVMETHOD(pic_unmask, arm_gic_unmask), + DEVMETHOD(pic_eoi, arm_gic_eoi), + DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), + DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), + DEVMETHOD(pic_ipi_clear, arm_gic_ipi_clear), + DEVMETHOD(pic_ipi_read, arm_gic_ipi_read), + { 0, 0 } +}; + +static driver_t arm_gic_driver = { + "gic", + arm_gic_methods, + sizeof(struct arm_gic_softc), +}; + +static devclass_t arm_gic_devclass; + +DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0); Modified: projects/arm_intrng/sys/arm/arm/intr.c ============================================================================== --- projects/arm_intrng/sys/arm/arm/intr.c Mon Aug 11 16:31:28 2014 (r269816) +++ projects/arm_intrng/sys/arm/arm/intr.c Mon Aug 11 16:44:06 2014 (r269817) @@ -89,6 +89,15 @@ intr_init(void *unused) SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); +const char * +arm_describe_irq(int irq) +{ + static char buffer[8]; + + sprintf(buffer, "%d", irq); + return (buffer); +} + void arm_setup_irqhandler(const char *name, driver_filter_t *filt, void (*hand)(void*), void *arg, int irq, int flags, void **cookiep) Added: projects/arm_intrng/sys/arm/arm/intrng.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/arm_intrng/sys/arm/arm/intrng.c Mon Aug 11 16:44:06 2014 (r269817) @@ -0,0 +1,444 @@ +/*- + * Copyright (c) 2012-2014 Jakub Wojciech Klama . + * 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pic_if.h" + +#define INTRNAME_LEN (MAXCOMLEN + 1) + +#define IRQ_PIC_IDX(_irq) ((_irq >> 8) & 0xff) +#define IRQ_VECTOR_IDX(_irq) ((_irq) & 0xff) +#define IRQ_GEN(_pic, _irq) (((_pic) << 8) | ((_irq) & 0xff)) + +#ifdef DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ + printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +typedef void (*mask_fn)(void *); + +struct arm_intr_controller { + device_t ic_dev; + phandle_t ic_node; +}; + +struct arm_intr_handler { + device_t ih_dev; + const char * ih_ipi_name; + int ih_intrcnt_idx; + int ih_irq; + struct intr_event * ih_event; + struct arm_intr_controller *ih_pic; +}; + +static void arm_mask_irq(void *); +static void arm_unmask_irq(void *); +static void arm_eoi(void *); + +static struct arm_intr_handler arm_intrs[NIRQ]; +static struct arm_intr_controller arm_pics[NPIC]; +static struct arm_intr_controller *arm_ipi_pic; + +static int intrcnt_index = 0; +static int last_printed = 0; + +/* Data for statistics reporting. */ +u_long intrcnt[NIRQ]; +char intrnames[NIRQ * INTRNAME_LEN]; +size_t sintrcnt = sizeof(intrcnt); +size_t sintrnames = sizeof(intrnames); +int (*arm_config_irq)(int irq, enum intr_trigger trig, + enum intr_polarity pol) = NULL; + +void +arm_intrnames_init(void) +{ + /* nothing... */ +} + +void +arm_dispatch_irq(device_t dev, struct trapframe *tf, int irq) +{ + struct arm_intr_handler *ih = NULL; + int i; + + debugf("pic %s, tf %p, irq %d\n", device_get_nameunit(dev), tf, irq); + + /* + * If we got null trapframe argument, that probably means + * a call from non-root interrupt controller. In that case, + * we'll just use the saved one. + */ + if (tf == NULL) + tf = PCPU_GET(curthread)->td_intr_frame; + + for (i = 0; arm_intrs[i].ih_dev != NULL; i++) { + if (arm_intrs[i].ih_pic->ic_dev == dev && + arm_intrs[i].ih_irq == irq) { + ih = &arm_intrs[i]; + break; + } + } + + if (ih == NULL) + panic("arm_dispatch_irq: unknown irq"); + + debugf("requested by %s\n", ih->ih_ipi_name != NULL + ? ih->ih_ipi_name + : device_get_nameunit(ih->ih_dev)); + + intrcnt[ih->ih_intrcnt_idx]++; + if (intr_event_handle(ih->ih_event, tf) != 0) { + /* Stray IRQ */ + arm_mask_irq(ih); + } + + debugf("done\n"); +} + +static struct arm_intr_handler * +arm_lookup_intr_handler(device_t pic, int irq) +{ + int i; + + for (i = 0; i < NIRQ; i++) { + if (arm_intrs[i].ih_pic != NULL && + arm_intrs[i].ih_pic->ic_dev == pic && + arm_intrs[i].ih_irq == irq) + return (&arm_intrs[i]); + + if (arm_intrs[i].ih_dev == NULL) + return (&arm_intrs[i]); + } + + return NULL; +} + +int +arm_fdt_map_irq(phandle_t ic, int irq) +{ + int i; + + ic = OF_xref_phandle(ic); + + debugf("ic %08x irq %d\n", ic, irq); + + if (ic == CORE_PIC_NODE) + return (IRQ_GEN(CORE_PIC_IDX, irq)); + + for (i = 0; arm_pics[i].ic_node != 0; i++) { + if (arm_pics[i].ic_node == ic) + return (IRQ_GEN(i, irq)); + } + + /* + * Interrupt controller is not registered yet, so + * we map a stub for it. 'i' is pointing to free + * first slot in arm_pics table. + */ + arm_pics[i].ic_node = ic; + return (IRQ_GEN(i, irq)); +} + +const char * +arm_describe_irq(int irq) +{ + struct arm_intr_controller *pic; + static char buffer[INTRNAME_LEN]; + static char name[INTRNAME_LEN]; + + pic = &arm_pics[IRQ_PIC_IDX(irq)]; + + if (pic->ic_dev == NULL) { + /* + * Interrupt controller not attached yet, so we'll use it's + * FDT "name" property instead + */ + OF_getprop(pic->ic_node, "name", name, sizeof(name)); + snprintf(buffer, sizeof(buffer), "%s.%d", name, + IRQ_VECTOR_IDX(irq)); + return (buffer); + } + + snprintf(buffer, sizeof(buffer), "%s.%d", + device_get_nameunit(pic->ic_dev), IRQ_VECTOR_IDX(irq)); + + return (buffer); +} + +void +arm_register_pic(device_t dev, int flags) +{ + struct arm_intr_controller *ic = NULL; + phandle_t node; + int i; + + node = ofw_bus_get_node(dev); + + /* Find room for IC */ + for (i = 0; i < NPIC; i++) { + if (arm_pics[i].ic_dev != NULL) + continue; + + if (arm_pics[i].ic_node == node) { + ic = &arm_pics[i]; + break; + } + + if (arm_pics[i].ic_node == 0) { + ic = &arm_pics[i]; + break; + } + } + + if (ic == NULL) + panic("not enough room to register interrupt controller"); + + ic->ic_dev = dev; + ic->ic_node = node; + + debugf("device %s node %08x slot %d\n", device_get_nameunit(dev), ic->ic_node, i); + + if (flags & PIC_FEATURE_IPI) { + if (arm_ipi_pic != NULL) + panic("there's already registered interrupt controller for serving IPIs"); + + arm_ipi_pic = ic; + } + + device_printf(dev, "registered as interrupt controller\n"); +} + +void +arm_setup_irqhandler(device_t dev, driver_filter_t *filt, + void (*hand)(void*), void *arg, int irq, int flags, void **cookiep) +{ + struct arm_intr_controller *pic; + struct arm_intr_handler *ih; + const char *name; + int error; + int ipi; + + if (irq < 0) + return; + + ipi = (flags & INTR_IPI) != 0; + pic = ipi ? arm_ipi_pic : &arm_pics[IRQ_PIC_IDX(irq)]; + ih = arm_lookup_intr_handler(pic->ic_dev, IRQ_VECTOR_IDX(irq)); + + if (ipi) { + name = (const char *)dev; + debugf("setup ipi %d\n", irq); + } else { + name = device_get_nameunit(dev); + debugf("setup irq %d on %s\n", IRQ_VECTOR_IDX(irq), + device_get_nameunit(pic->ic_dev)); + } + + debugf("pic %p, ih %p\n", pic, ih); + + if (ih->ih_event == NULL) { + error = intr_event_create(&ih->ih_event, (void *)ih, 0, irq, + (mask_fn)arm_mask_irq, (mask_fn)arm_unmask_irq, + arm_eoi, NULL, "intr%d:", irq); + + if (error) + return; + + ih->ih_dev = dev; + ih->ih_ipi_name = ipi ? name : NULL; + ih->ih_irq = IRQ_VECTOR_IDX(irq); + ih->ih_pic = pic; + + arm_unmask_irq(ih); + + last_printed += + snprintf(intrnames + last_printed, + INTRNAME_LEN, "%s:%d: %s", + device_get_nameunit(pic->ic_dev), + ih->ih_irq, name); + + last_printed++; + ih->ih_intrcnt_idx = intrcnt_index; + intrcnt_index++; + + } + + intr_event_add_handler(ih->ih_event, name, filt, hand, arg, + intr_priority(flags), flags, cookiep); + + /* Unmask IPIs immediately */ + if (ipi) + arm_unmask_irq(ih); +} + +int +arm_remove_irqhandler(int irq, void *cookie) +{ + struct arm_intr_controller *pic; + struct arm_intr_handler *ih; + int error; + + if (irq < 0) + return (ENXIO); + + pic = &arm_pics[IRQ_PIC_IDX(irq)]; + ih = arm_lookup_intr_handler(pic->ic_dev, IRQ_VECTOR_IDX(irq)); + + if (ih->ih_event == NULL) + return (ENXIO); + + arm_mask_irq(ih); + error = intr_event_remove_handler(cookie); + + if (!TAILQ_EMPTY(&ih->ih_event->ie_handlers)) + arm_unmask_irq(ih); + + return (error); +} + +static void +arm_mask_irq(void *arg) +{ + struct arm_intr_handler *ih = (struct arm_intr_handler *)arg; + + PIC_MASK(ih->ih_pic->ic_dev, ih->ih_irq); +} + +static void +arm_unmask_irq(void *arg) +{ + struct arm_intr_handler *ih = (struct arm_intr_handler *)arg; + + PIC_UNMASK(ih->ih_pic->ic_dev, ih->ih_irq); +} + +static void +arm_eoi(void *arg) +{ + struct arm_intr_handler *ih = (struct arm_intr_handler *)arg; + + PIC_EOI(ih->ih_pic->ic_dev, ih->ih_irq); +} + +int +arm_intrng_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol) +{ + struct arm_intr_controller *pic; + struct arm_intr_handler *ih; + + pic = &arm_pics[IRQ_PIC_IDX(irq)]; + ih = arm_lookup_intr_handler(pic->ic_dev, IRQ_VECTOR_IDX(irq)); + + if (ih == NULL) + return (ENXIO); + + return PIC_CONFIG(pic->ic_dev, ih->ih_irq, trig, pol); +} + +#ifdef SMP +void +arm_init_secondary_ic(void) +{ + + KASSERT(arm_ipi_pic != NULL, ("no IPI PIC attached")); + PIC_INIT_SECONDARY(arm_ipi_pic->ic_dev); +} + +void +pic_ipi_send(cpuset_t cpus, u_int ipi) +{ + + KASSERT(arm_ipi_pic != NULL, ("no IPI PIC attached")); + PIC_IPI_SEND(arm_ipi_pic->ic_dev, cpus, ipi); +} + +void +pic_ipi_clear(int ipi) +{ + + KASSERT(arm_ipi_pic != NULL, ("no IPI PIC attached")); + PIC_IPI_CLEAR(arm_ipi_pic->ic_dev, ipi); +} + +int +pic_ipi_read(int ipi) +{ + + KASSERT(arm_ipi_pic != NULL, ("no IPI PIC attached")); + return (PIC_IPI_READ(arm_ipi_pic->ic_dev, ipi)); +} + +void +arm_unmask_ipi(int ipi) +{ + + KASSERT(arm_ipi_pic != NULL, ("no IPI PIC attached")); + PIC_UNMASK(arm_ipi_pic->ic_dev, ipi); +} + +void +arm_mask_ipi(int ipi) +{ + + KASSERT(arm_ipi_pic != NULL, ("no IPI PIC attached")); + PIC_MASK(arm_ipi_pic->ic_dev, ipi); +} +#endif + +void dosoftints(void); +void +dosoftints(void) +{ +} + Modified: projects/arm_intrng/sys/arm/arm/mp_machdep.c ============================================================================== --- projects/arm_intrng/sys/arm/arm/mp_machdep.c Mon Aug 11 16:31:28 2014 (r269816) +++ projects/arm_intrng/sys/arm/arm/mp_machdep.c Mon Aug 11 16:44:06 2014 (r269817) @@ -235,7 +235,7 @@ init_secondary(int cpu) #endif for (int i = start; i <= end; i++) - arm_unmask_irq(i); + arm_unmask_ipi(i); enable_interrupts(I32_bit); loop_counter = 0; @@ -265,7 +265,7 @@ ipi_handler(void *arg) cpu = PCPU_GET(cpuid); - ipi = pic_ipi_get((int)arg); + ipi = pic_ipi_read((int)arg); while ((ipi != 0x3ff)) { switch (ipi) { @@ -328,7 +328,7 @@ ipi_handler(void *arg) } pic_ipi_clear(ipi); - ipi = pic_ipi_get(-1); + ipi = pic_ipi_read(-1); } return (FILTER_HANDLED); @@ -360,11 +360,10 @@ release_aps(void *dummy __unused) * if we used 0, the intr code will give the trap frame * pointer instead. */ - arm_setup_irqhandler("ipi", ipi_handler, NULL, (void *)i, i, - INTR_TYPE_MISC | INTR_EXCL, NULL); - - /* Enable ipi */ - arm_unmask_irq(i); + arm_setup_irqhandler((device_t)"ipi", ipi_handler, NULL, (void *)i, i, + INTR_TYPE_MISC | INTR_EXCL | INTR_IPI, NULL); + + arm_unmask_ipi(i); } atomic_store_rel_int(&aps_ready, 1); Modified: projects/arm_intrng/sys/arm/arm/nexus.c ============================================================================== --- projects/arm_intrng/sys/arm/arm/nexus.c Mon Aug 11 16:31:28 2014 (r269816) +++ projects/arm_intrng/sys/arm/arm/nexus.c Mon Aug 11 16:44:06 2014 (r269817) @@ -68,6 +68,8 @@ __FBSDID("$FreeBSD$"); #include "ofw_bus_if.h" #endif +#include "pic_if.h" + static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device"); struct nexus_device { @@ -77,6 +79,9 @@ struct nexus_device { #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev)) static struct rman mem_rman; +#if defined(ARM_INTRNG) +static device_t nexus_dev; +#endif static int nexus_probe(device_t); static int nexus_attach(device_t); @@ -94,6 +99,13 @@ static int nexus_deactivate_resource(dev 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 *); +#if defined(ARM_INTRNG) +static int nexus_pic_config(device_t, int, enum intr_trigger, enum intr_polarity); +static void nexus_pic_mask(device_t, int); +static void nexus_pic_unmask(device_t, int); +static void nexus_pic_eoi(device_t, int); +void arm_irq_handler(struct trapframe *tf, int irqnb); +#endif #ifdef FDT static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, @@ -104,6 +116,7 @@ static device_method_t nexus_methods[] = /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***