diff options
author | Landon J. Fuller <landonf@FreeBSD.org> | 2017-11-21 23:15:20 +0000 |
---|---|---|
committer | Landon J. Fuller <landonf@FreeBSD.org> | 2017-11-21 23:15:20 +0000 |
commit | caeff9a3c2626660d3e080d4d3b35bc53ec4417f (patch) | |
tree | da61f12c95a2dad1fc6d62a43b769e317eb5cc7f /sys/mips | |
parent | 0a9cc964a1eae66f242631c1ef566560ab00aae2 (diff) | |
download | src-caeff9a3c2626660d3e080d4d3b35bc53ec4417f.tar.gz src-caeff9a3c2626660d3e080d4d3b35bc53ec4417f.zip |
bhnd(4): implement MIPS and PCI(e) interrupt support
On BHND MIPS SoCs, this replaces the use of hard-coded MIPS IRQ#s in the
common bhnd(4) core drivers; we now register an INTRNG child PIC that
handles routing of backplane interrupt vectors via the MIPS core.
On BHND PCI devices, backplane interrupt vectors are now routed to the
PCI/PCIe host bridge core when bus_setup_intr() is called, where they are
dispatched by the PCI core via a host interrupt (e.g. INTx/MSI).
The bhndb(4) bridge driver tracks registered interrupt handlers for the
bridged bhnd(4) devices and manages backplane interrupt routing, while
delegating actual bus interrupt setup/teardown to the parent bus on behalf
of the bridged cores.
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D12518
Notes
Notes:
svn path=/head/; revision=326079
Diffstat (limited to 'sys/mips')
-rw-r--r-- | sys/mips/broadcom/bcm_bmips.c | 331 | ||||
-rw-r--r-- | sys/mips/broadcom/bcm_machdep.c | 15 | ||||
-rw-r--r-- | sys/mips/broadcom/bcm_machdep.h | 12 | ||||
-rw-r--r-- | sys/mips/broadcom/bcm_mips.c | 698 | ||||
-rw-r--r-- | sys/mips/broadcom/bcm_mips74k.c | 299 | ||||
-rw-r--r-- | sys/mips/broadcom/bcm_mips74kreg.h | 8 | ||||
-rw-r--r-- | sys/mips/broadcom/bcm_mipsvar.h | 114 | ||||
-rw-r--r-- | sys/mips/broadcom/bcma_nexus.c | 5 | ||||
-rw-r--r-- | sys/mips/broadcom/bhnd_nexus.c | 50 | ||||
-rw-r--r-- | sys/mips/broadcom/files.broadcom | 2 | ||||
-rw-r--r-- | sys/mips/broadcom/siba_nexus.c | 5 | ||||
-rw-r--r-- | sys/mips/include/intr.h | 4 | ||||
-rw-r--r-- | sys/mips/mips/mips_pic.c | 4 |
13 files changed, 1492 insertions, 55 deletions
diff --git a/sys/mips/broadcom/bcm_bmips.c b/sys/mips/broadcom/bcm_bmips.c index b69abd99228e..100664bb26a1 100644 --- a/sys/mips/broadcom/bcm_bmips.c +++ b/sys/mips/broadcom/bcm_bmips.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * 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: @@ -34,13 +38,20 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/bus.h> #include <sys/module.h> +#include <sys/proc.h> #include <machine/bus.h> #include <sys/rman.h> + +#include <machine/intr.h> #include <machine/resource.h> #include <dev/bhnd/bhnd.h> +#include <dev/bhnd/siba/sibareg.h> + +#include "pic_if.h" +#include "bcm_mipsvar.h" #include "bcm_bmipsreg.h" /* @@ -50,74 +61,360 @@ __FBSDID("$FreeBSD$"); * us to assume the availability of siba interrupt registers. */ +struct bcm_bmips_softc; + +static int bcm_bmips_pic_intr(void *arg); +static void bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, + u_int ivec); +static void bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, + u_int ivec); + static const struct bhnd_device bcm_bmips_devs[] = { BHND_DEVICE(BCM, MIPS33, NULL, NULL, BHND_DF_SOC), BHND_DEVICE_END }; struct bcm_bmips_softc { + struct bcm_mips_softc bcm_mips; /**< parent softc */ device_t dev; - struct resource *mem_res; + struct resource *mem; /**< cpu core registers */ int mem_rid; + struct resource *cfg; /**< cpu core's cfg0 register block */ + int cfg_rid; }; +#define BCM_BMIPS_NCPU_IRQS 5 /**< MIPS HW IRQs 0-4 are assignable */ +#define BCM_BMIPS_TIMER_IRQ 5 /**< MIPS HW IRQ5 is always assigned to the timer */ + static int bcm_bmips_probe(device_t dev) { - const struct bhnd_device *id; + const struct bhnd_device *id; - id = bhnd_device_lookup(dev, bcm_bmips_devs, - sizeof(bcm_bmips_devs[0])); + id = bhnd_device_lookup(dev, bcm_bmips_devs, sizeof(bcm_bmips_devs[0])); if (id == NULL) return (ENXIO); + /* Check the chip type; should only be found on siba(4) chipsets */ + if (bhnd_get_chipid(dev)->chip_type != BHND_CHIPTYPE_SIBA) + return (ENXIO); + bhnd_set_default_core_desc(dev); return (BUS_PROBE_DEFAULT); } + static int bcm_bmips_attach(device_t dev) { - struct bcm_bmips_softc *sc; + struct bcm_bmips_softc *sc; + int error; sc = device_get_softc(dev); sc->dev = dev; - /* Allocate bus resources */ + /* Allocate our core's register block */ sc->mem_rid = 0; - sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); - if (sc->mem_res == NULL) - return (ENXIO); + if (sc->mem == NULL) { + device_printf(dev, "failed to allocate cpu register block\n"); + error = ENXIO; + goto failed; + } + + /* Determine the resource ID for our siba CFG0 registers */ + sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0); + if (sc->cfg_rid == -1) { + device_printf(dev, "missing required cfg0 register block\n"); + error = ENXIO; + goto failed; + } + + /* Allocate our CFG0 register block */ + sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid, + RF_ACTIVE|RF_SHAREABLE); + if (sc->cfg == NULL) { + device_printf(dev, "failed to allocate cfg0 register block\n"); + error = ENXIO; + goto failed; + } + + /* Clear interrupt map */ + bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0); /* MIPS IRQ0 */ + bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0); /* MIPS IRQ1-4 */ + + /* Initialize the generic BHND MIPS driver state */ + error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ, + bcm_bmips_pic_intr); + if (error) + goto failed; return (0); + +failed: + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); + + if (sc->cfg != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg); + + return (error); } static int bcm_bmips_detach(device_t dev) { - struct bcm_bmips_softc *sc; + struct bcm_bmips_softc *sc; + int error; sc = device_get_softc(dev); - bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); + if ((error = bcm_mips_detach(dev))) + return (error); + + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); + bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg); return (0); } +/* PIC_DISABLE_INTR() */ +static void +bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc) +{ + struct bcm_bmips_softc *sc; + struct bcm_mips_irqsrc *isrc; + + sc = device_get_softc(dev); + isrc = (struct bcm_mips_irqsrc *)irqsrc; + + KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ")); + + bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec); +} + +/* PIC_ENABLE_INTR() */ +static void +bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc) +{ + struct bcm_bmips_softc *sc; + struct bcm_mips_irqsrc *isrc; + + sc = device_get_softc(dev); + isrc = (struct bcm_mips_irqsrc *)irqsrc; + + KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ")); + + bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec); +} + +/* PIC_PRE_ITHREAD() */ +static void +bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + bcm_bmips_pic_disable_intr(dev, isrc); +} + +/* PIC_POST_ITHREAD() */ +static void +bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + bcm_bmips_pic_enable_intr(dev, isrc); +} + +/* PIC_POST_FILTER() */ +static void +bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} + +/** + * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ + * @p mips_irq. + */ +static void +bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec) +{ + KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec")); + KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u", + mips_irq)); + + if (mips_irq == 0) { + uint32_t sbintvec; + + sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC); + sbintvec &= ~(1 << ivec); + bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec); + } else { + uint32_t ipsflag; + + /* Can we route this via ipsflag? */ + KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0, + ("cannot route high sbflag# ivec %u", ivec)); + + ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG); + ipsflag &= ~( + ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) & + SIBA_IPS_INT_MASK(mips_irq)); + bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag); + } + +} + +/** + * Enable routing of an interrupt. + */ +static void +bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec) +{ + KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec")); + KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u", + mips_irq)); + + if (mips_irq == 0) { + uint32_t sbintvec; + + sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC); + sbintvec |= (1 << ivec); + bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec); + } else { + uint32_t ipsflag; + + /* Can we route this via ipsflag? */ + KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0, + ("cannot route high sbflag# ivec %u", ivec)); + + ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG); + ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) & + SIBA_IPS_INT_MASK(mips_irq); + bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag); + } +} + +/* our MIPS CPU interrupt filter */ +static int +bcm_bmips_pic_intr(void *arg) +{ + struct bcm_bmips_softc *sc; + struct bcm_mips_cpuirq *cpuirq; + struct bcm_mips_irqsrc *isrc_solo; + uint32_t sbintvec, sbstatus; + u_int mips_irq, i; + int error; + + cpuirq = arg; + sc = (struct bcm_bmips_softc*)cpuirq->sc; + + /* Fetch current interrupt state */ + sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST); + + /* Fetch mask of interrupt vectors routed to this MIPS IRQ */ + mips_irq = cpuirq->mips_irq; + if (mips_irq == 0) { + sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC); + } else { + uint32_t ipsflag; + + ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG); + + /* Map to an intvec-compatible representation */ + switch (mips_irq) { + case 1: + sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >> + SIBA_IPS_INT1_SHIFT; + break; + case 2: + sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >> + SIBA_IPS_INT2_SHIFT; + break; + case 3: + sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >> + SIBA_IPS_INT3_SHIFT; + break; + case 4: + sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >> + SIBA_IPS_INT4_SHIFT; + break; + default: + panic("invalid irq %u", mips_irq); + } + } + + /* Ignore interrupts not routed to this MIPS IRQ */ + sbstatus &= sbintvec; + + /* Handle isrc_solo direct dispatch path */ + isrc_solo = cpuirq->isrc_solo; + if (isrc_solo != NULL) { + if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) { + error = intr_isrc_dispatch(&isrc_solo->isrc, + curthread->td_intr_frame); + if (error) { + device_printf(sc->dev, "Stray interrupt %u " + "detected\n", isrc_solo->ivec); + bcm_bmips_pic_disable_intr(sc->dev, + &isrc_solo->isrc); + } + } + + sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo)); + if (sbstatus == 0) + return (FILTER_HANDLED); + + /* Report and mask additional stray interrupts */ + while ((i = fls(sbstatus)) != 0) { + i--; /* Get a 0-offset interrupt. */ + sbstatus &= ~(1 << i); + + device_printf(sc->dev, "Stray interrupt %u " + "detected\n", i); + bcm_bmips_mask_irq(sc, mips_irq, i); + } + + return (FILTER_HANDLED); + } + + /* Standard dispatch path */ + while ((i = fls(sbstatus)) != 0) { + i--; /* Get a 0-offset interrupt. */ + sbstatus &= ~(1 << i); + + KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i)); + + error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc, + curthread->td_intr_frame); + if (error) { + device_printf(sc->dev, "Stray interrupt %u detected\n", + i); + bcm_bmips_mask_irq(sc, mips_irq, i); + continue; + } + } + + return (FILTER_HANDLED); +} + static device_method_t bcm_bmips_methods[] = { /* Device interface */ - DEVMETHOD(device_probe, bcm_bmips_probe), - DEVMETHOD(device_attach, bcm_bmips_attach), - DEVMETHOD(device_detach, bcm_bmips_detach), - + DEVMETHOD(device_probe, bcm_bmips_probe), + DEVMETHOD(device_attach, bcm_bmips_attach), + DEVMETHOD(device_detach, bcm_bmips_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, bcm_bmips_pic_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_bmips_pic_enable_intr), + DEVMETHOD(pic_pre_ithread, bcm_bmips_pic_pre_ithread), + DEVMETHOD(pic_post_ithread, bcm_bmips_pic_post_ithread), + DEVMETHOD(pic_post_filter, bcm_bmips_pic_post_filter), + DEVMETHOD_END }; static devclass_t bcm_mips_devclass; -DEFINE_CLASS_0(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc)); -EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); +DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver); +EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(bcm_bmips, 1); MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1); diff --git a/sys/mips/broadcom/bcm_machdep.c b/sys/mips/broadcom/bcm_machdep.c index b61cca31a148..20b6f5602e08 100644 --- a/sys/mips/broadcom/bcm_machdep.c +++ b/sys/mips/broadcom/bcm_machdep.c @@ -126,6 +126,13 @@ static const struct bhnd_core_match bcm_chipc_cores[] = { { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_4706_CC) }, }; +static const struct bhnd_core_match bcm_cpu0_cores[] = { + { + BHND_MATCH_CORE_CLASS(BHND_DEVCLASS_CPU), + BHND_MATCH_CORE_UNIT(0) + } +}; + static const struct bhnd_core_match bcm_pmu_cores[] = { { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_PMU) }, }; @@ -429,6 +436,14 @@ bcm_init_platform_data(struct bcm_platform *bp) } } + /* Find CPU core info */ + error = bcm_find_core(bp, bcm_cpu0_cores, nitems(bcm_cpu0_cores), + &bp->cpu_id, &bp->cpu_addr); + if (error) { + BCM_ERR("error locating CPU core: %d\n", error); + return (error); + } + /* Initialize our platform service registry */ if ((error = bhnd_service_registry_init(&bp->services))) { BCM_ERR("error initializing service registry: %d\n", error); diff --git a/sys/mips/broadcom/bcm_machdep.h b/sys/mips/broadcom/bcm_machdep.h index 7d85338f906c..7a8b155961b9 100644 --- a/sys/mips/broadcom/bcm_machdep.h +++ b/sys/mips/broadcom/bcm_machdep.h @@ -55,12 +55,15 @@ struct bcm_platform { uint32_t cc_caps; /**< chipc capabilities */ uint32_t cc_caps_ext; /**< chipc extended capabilies */ + struct bhnd_core_info cpu_id; /**< cpu core info */ + uintptr_t cpu_addr; /**< cpu core phys address */ + /* On non-AOB devices, the PMU register block is mapped to chipc; * the pmu_id and pmu_addr values will be copied from cc_id * and cc_addr. */ - struct bhnd_core_info pmu_id; /**< PMU core info */ + struct bhnd_core_info pmu_id; /**< PMU core info */ uintptr_t pmu_addr; /**< PMU core phys address, or - 0x0 if no PMU */ + 0x0 if no PMU */ struct bhnd_pmu_query pmu; /**< PMU query instance */ @@ -122,6 +125,11 @@ int bcm_get_nvram(struct bcm_platform *bp, #define BCM_CHIPC_WRITE_4(_bp, _reg, _val) \ BCM_CORE_WRITE_4(_bp, cc_addr, (_reg), (_val)) +#define BCM_CPU_READ_4(_bp, _reg) \ + BCM_CORE_READ_4(_bp, cpu_addr, (_reg)) +#define BCM_CPU_WRITE_4(_bp, _reg, _val) \ + BCM_CORE_WRITE_4(_bp, cpu_addr, (_reg), (_val)) + #define BCM_PMU_READ_4(_bp, _reg) \ BCM_CORE_READ_4(_bp, pmu_addr, (_reg)) #define BCM_PMU_WRITE_4(_bp, _reg, _val) \ diff --git a/sys/mips/broadcom/bcm_mips.c b/sys/mips/broadcom/bcm_mips.c new file mode 100644 index 000000000000..ab38f01b0636 --- /dev/null +++ b/sys/mips/broadcom/bcm_mips.c @@ -0,0 +1,698 @@ +/*- + * Copyright (c) 2017 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Landon Fuller 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <sys/limits.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <dev/bhnd/bhnd.h> +#include <dev/bhnd/siba/sibareg.h> + +#include "pic_if.h" + +#include "bcm_mipsvar.h" + +/* + * Broadcom MIPS core driver. + * + * Abstract driver for Broadcom MIPS CPU/PIC cores. + */ + +static uintptr_t bcm_mips_pic_xref(struct bcm_mips_softc *sc); +static device_t bcm_mips_find_bhnd_parent(device_t dev); +static int bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc, + struct bcm_mips_irqsrc *isrc, struct resource *res); +static int bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc, + struct bcm_mips_irqsrc *isrc, struct resource *res); + +static const int bcm_mips_debug = 0; + +#define DPRINTF(fmt, ...) do { \ + if (bcm_mips_debug) \ + printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \ +} while (0) + +#define DENTRY(dev, fmt, ...) do { \ + if (bcm_mips_debug) \ + printf("%s(%s, " fmt ")\n", __FUNCTION__, \ + device_get_nameunit(dev), ##__VA_ARGS__); \ +} while (0) + +/** + * Register all interrupt source definitions. + */ +static int +bcm_mips_register_isrcs(struct bcm_mips_softc *sc) +{ + const char *name; + uintptr_t xref; + int error; + + xref = bcm_mips_pic_xref(sc); + + name = device_get_nameunit(sc->dev); + for (size_t ivec = 0; ivec < nitems(sc->isrcs); ivec++) { + sc->isrcs[ivec].ivec = ivec; + sc->isrcs[ivec].cpuirq = NULL; + sc->isrcs[ivec].refs = 0; + + error = intr_isrc_register(&sc->isrcs[ivec].isrc, sc->dev, + xref, "%s,%u", name, ivec); + if (error) { + for (size_t i = 0; i < ivec; i++) + intr_isrc_deregister(&sc->isrcs[i].isrc); + + device_printf(sc->dev, "error registering IRQ %zu: " + "%d\n", ivec, error); + return (error); + } + } + + return (0); +} + +/** + * Initialize the given @p cpuirq state as unavailable. + * + * @param sc BHND MIPS driver instance state. + * @param cpuirq The CPU IRQ state to be initialized. + * + * @retval 0 success + * @retval non-zero if initializing @p cpuirq otherwise fails, a regular + * unix error code will be returned. + */ +static int +bcm_mips_init_cpuirq_unavail(struct bcm_mips_softc *sc, + struct bcm_mips_cpuirq *cpuirq) +{ + BCM_MIPS_LOCK(sc); + + KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized")); + cpuirq->sc = sc; + cpuirq->mips_irq = 0; + cpuirq->irq_rid = -1; + cpuirq->irq_res = NULL; + cpuirq->irq_cookie = NULL; + cpuirq->isrc_solo = NULL; + cpuirq->refs = 0; + + BCM_MIPS_UNLOCK(sc); + + return (0); +} + +/** + * Allocate required resources and initialize the given @p cpuirq state. + * + * @param sc BHND MIPS driver instance state. + * @param cpuirq The CPU IRQ state to be initialized. + * @param rid The resource ID to be assigned for the CPU IRQ resource, + * or -1 if no resource should be assigned. + * @param irq The MIPS HW IRQ# to be allocated. + * @param filter The interrupt filter to be setup. + * + * @retval 0 success + * @retval non-zero if initializing @p cpuirq otherwise fails, a regular + * unix error code will be returned. + */ +static int +bcm_mips_init_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq, + int rid, u_int irq, driver_filter_t filter) +{ + struct resource *res; + void *cookie; + u_int irq_real; + int error; + + /* Must fall within MIPS HW IRQ range */ + if (irq >= NHARD_IRQS) + return (EINVAL); + + /* HW IRQs are numbered relative to SW IRQs */ + irq_real = irq + NSOFT_IRQS; + + /* Try to assign and allocate the resource */ + BCM_MIPS_LOCK(sc); + + KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized")); + + error = bus_set_resource(sc->dev, SYS_RES_IRQ, rid, irq_real, 1); + if (error) { + BCM_MIPS_UNLOCK(sc); + device_printf(sc->dev, "failed to assign interrupt %u: " + "%d\n", irq, error); + return (error); + } + + res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (res == NULL) { + BCM_MIPS_UNLOCK(sc); + device_printf(sc->dev, "failed to allocate interrupt " + "%u resource\n", irq); + bus_delete_resource(sc->dev, SYS_RES_IRQ, rid); + return (ENXIO); + } + + error = bus_setup_intr(sc->dev, res, + INTR_TYPE_MISC | INTR_MPSAFE | INTR_EXCL, filter, NULL, cpuirq, + &cookie); + if (error) { + BCM_MIPS_UNLOCK(sc); + + printf("failed to setup internal interrupt handler: %d\n", + error); + + bus_release_resource(sc->dev, SYS_RES_IRQ, rid, res); + bus_delete_resource(sc->dev, SYS_RES_IRQ, rid); + + return (error); + } + + /* Initialize CPU IRQ state */ + cpuirq->sc = sc; + cpuirq->mips_irq = irq; + cpuirq->irq_rid = rid; + cpuirq->irq_res = res; + cpuirq->irq_cookie = cookie; + cpuirq->isrc_solo = NULL; + cpuirq->refs = 0; + + BCM_MIPS_UNLOCK(sc); + return (0); +} + +/** + * Free any resources associated with the given @p cpuirq state. + * + * @param sc BHND MIPS driver instance state. + * @param cpuirq A CPU IRQ instance previously successfully initialized + * via bcm_mips_init_cpuirq(). + * + * @retval 0 success + * @retval non-zero if finalizing @p cpuirq otherwise fails, a regular + * unix error code will be returned. + */ +static int +bcm_mips_fini_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq) +{ + int error; + + BCM_MIPS_LOCK(sc); + + if (cpuirq->sc == NULL) { + KASSERT(cpuirq->irq_res == NULL, ("leaking cpuirq resource")); + + BCM_MIPS_UNLOCK(sc); + return (0); /* not initialized */ + } + + if (cpuirq->refs != 0) { + BCM_MIPS_UNLOCK(sc); + return (EBUSY); + } + + if (cpuirq->irq_cookie != NULL) { + KASSERT(cpuirq->irq_res != NULL, ("resource missing")); + + error = bus_teardown_intr(sc->dev, cpuirq->irq_res, + cpuirq->irq_cookie); + if (error) { + BCM_MIPS_UNLOCK(sc); + return (error); + } + + cpuirq->irq_cookie = NULL; + } + + if (cpuirq->irq_res != NULL) { + bus_release_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid, + cpuirq->irq_res); + cpuirq->irq_res = NULL; + } + + if (cpuirq->irq_rid != -1) { + bus_delete_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid); + cpuirq->irq_rid = -1; + } + + BCM_MIPS_UNLOCK(sc); + + return (0); +} + +static int +bcm_mips_attach_default(device_t dev) +{ + /* subclassing drivers must provide an implementation of + * DEVICE_ATTACH() */ + panic("device_attach() unimplemented"); +} + +/** + * BHND MIPS device attach. + * + * This must be called from subclass drivers' DEVICE_ATTACH(). + * + * @param dev BHND MIPS device. + * @param num_cpuirqs The number of usable MIPS HW IRQs. + * @param timer_irq The MIPS HW IRQ assigned to the MIPS CPU timer. + * @param filter The subclass's core-specific IRQ dispatch filter. Will be + * passed the associated bcm_mips_cpuirq instance as its argument. + */ +int +bcm_mips_attach(device_t dev, u_int num_cpuirqs, u_int timer_irq, + driver_filter_t filter) +{ + struct bcm_mips_softc *sc; + struct intr_pic *pic; + uintptr_t xref; + u_int irq_rid; + rman_res_t irq; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->num_cpuirqs = num_cpuirqs; + sc->timer_irq = timer_irq; + + /* Must not exceed the actual size of our fixed IRQ array */ + if (sc->num_cpuirqs > nitems(sc->cpuirqs)) { + device_printf(dev, "%u nirqs exceeds maximum supported %zu", + sc->num_cpuirqs, nitems(sc->cpuirqs)); + return (ENXIO); + } + + pic = NULL; + xref = bcm_mips_pic_xref(sc); + + BCM_MIPS_LOCK_INIT(sc); + + /* Register our interrupt sources */ + if ((error = bcm_mips_register_isrcs(sc))) { + BCM_MIPS_LOCK_DESTROY(sc); + return (error); + } + + /* Initialize our CPU interrupt state */ + irq_rid = bhnd_get_intr_count(dev); /* last bhnd-assigned RID + 1 */ + irq = 0; + for (u_int i = 0; i < sc->num_cpuirqs; i++) { + /* Must not overflow signed resource ID representation */ + if (irq_rid >= INT_MAX) { + device_printf(dev, "exhausted IRQ resource IDs\n"); + error = ENOMEM; + goto failed; + } + + if (irq == sc->timer_irq) { + /* Mark the CPU timer's IRQ as unavailable */ + error = bcm_mips_init_cpuirq_unavail(sc, + &sc->cpuirqs[i]); + } else { + /* Initialize state */ + error = bcm_mips_init_cpuirq(sc, &sc->cpuirqs[i], + irq_rid, irq, filter); + } + + if (error) + goto failed; + + /* Increment IRQ and resource ID for next allocation */ + irq_rid++; + irq++; + } + + /* Sanity check; our shared IRQ must be available */ + if (sc->num_cpuirqs <= BCM_MIPS_IRQ_SHARED) + panic("missing shared interrupt %d\n", BCM_MIPS_IRQ_SHARED); + + if (sc->cpuirqs[BCM_MIPS_IRQ_SHARED].irq_rid == -1) + panic("shared interrupt %d unavailable", BCM_MIPS_IRQ_SHARED); + + /* Register PIC */ + if ((pic = intr_pic_register(dev, xref)) == NULL) { + device_printf(dev, "error registering PIC\n"); + error = ENXIO; + goto failed; + } + + return (0); + +failed: + /* Deregister PIC before performing any other cleanup */ + if (pic != NULL) + intr_pic_deregister(dev, 0); + + /* Deregister all interrupt sources */ + for (size_t i = 0; i < nitems(sc->isrcs); i++) + intr_isrc_deregister(&sc->isrcs[i].isrc); + + /* Free our MIPS CPU interrupt handler state */ + for (u_int i = 0; i < sc->num_cpuirqs; i++) + bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]); + + BCM_MIPS_LOCK_DESTROY(sc); + return (error); +} + +int +bcm_mips_detach(device_t dev) +{ + struct bcm_mips_softc *sc; + + sc = device_get_softc(dev); + + /* Deregister PIC before performing any other cleanup */ + intr_pic_deregister(dev, 0); + + /* Deregister all interrupt sources */ + for (size_t i = 0; i < nitems(sc->isrcs); i++) + intr_isrc_deregister(&sc->isrcs[i].isrc); + + /* Free our MIPS CPU interrupt handler state */ + for (u_int i = 0; i < sc->num_cpuirqs; i++) + bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]); + + return (0); +} + +/* PIC_MAP_INTR() */ +static int +bcm_mips_pic_map_intr(device_t dev, struct intr_map_data *d, + struct intr_irqsrc **isrcp) +{ + struct bcm_mips_softc *sc; + struct bcm_mips_intr_map_data *data; + + sc = device_get_softc(dev); + + if (d->type != INTR_MAP_DATA_BCM_MIPS) { + DENTRY(dev, "type=%d", d->type); + return (ENOTSUP); + } + + data = (struct bcm_mips_intr_map_data *)d; + DENTRY(dev, "type=%d, ivec=%u", d->type, data->ivec); + if (data->ivec < 0 || data->ivec >= nitems(sc->isrcs)) + return (EINVAL); + + *isrcp = &sc->isrcs[data->ivec].isrc; + return (0); +} + +/* PIC_SETUP_INTR() */ +static int +bcm_mips_pic_setup_intr(device_t dev, struct intr_irqsrc *irqsrc, + struct resource *res, struct intr_map_data *data) +{ + struct bcm_mips_softc *sc; + struct bcm_mips_irqsrc *isrc; + int error; + + sc = device_get_softc(dev); + isrc = (struct bcm_mips_irqsrc *)irqsrc; + + /* Assign a CPU interrupt */ + BCM_MIPS_LOCK(sc); + error = bcm_mips_retain_cpu_intr(sc, isrc, res); + BCM_MIPS_UNLOCK(sc); + + return (error); +} + +/* PIC_TEARDOWN_INTR() */ +static int +bcm_mips_pic_teardown_intr(device_t dev, struct intr_irqsrc *irqsrc, + struct resource *res, struct intr_map_data *data) +{ + struct bcm_mips_softc *sc; + struct bcm_mips_irqsrc *isrc; + int error; + + sc = device_get_softc(dev); + isrc = (struct bcm_mips_irqsrc *)irqsrc; + + /* Release the CPU interrupt */ + BCM_MIPS_LOCK(sc); + error = bcm_mips_release_cpu_intr(sc, isrc, res); + BCM_MIPS_UNLOCK(sc); + + return (error); +} + + +/** return our PIC's xref */ +static uintptr_t +bcm_mips_pic_xref(struct bcm_mips_softc *sc) +{ + uintptr_t xref; + + /* Determine our interrupt domain */ + xref = BHND_BUS_GET_INTR_DOMAIN(device_get_parent(sc->dev), sc->dev, + true); + KASSERT(xref != 0, ("missing interrupt domain")); + + return (xref); +} + +/** + * Walk up the device tree from @p dev until we find a bhnd-attached core, + * returning either the core, or NULL if @p dev is not attached under a bhnd + * bus. + */ +static device_t +bcm_mips_find_bhnd_parent(device_t dev) +{ + device_t core, bus; + devclass_t bhnd_class; + + bhnd_class = devclass_find("bhnd"); + core = dev; + while ((bus = device_get_parent(core)) != NULL) { + if (device_get_devclass(bus) == bhnd_class) + return (core); + + core = bus; + } + + /* Not found */ + return (NULL); +} + +/** + * Retain @p isrc and assign a MIPS CPU interrupt on behalf of @p res; if + * the @p isrc already has a MIPS CPU interrupt assigned, the existing + * reference will be left unmodified. + * + * @param sc BHND MIPS driver state. + * @param isrc The interrupt source corresponding to @p res. + * @param res The interrupt resource for which a MIPS CPU IRQ will be + * assigned. + */ +static int +bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc, + struct bcm_mips_irqsrc *isrc, struct resource *res) +{ + struct bcm_mips_cpuirq *cpuirq; + bhnd_devclass_t devclass; + device_t core; + + BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED); + + /* Prefer existing assignment */ + if (isrc->cpuirq != NULL) { + KASSERT(isrc->cpuirq->refs > 0, ("assigned IRQ has no " + "references")); + + /* Increment our reference count */ + if (isrc->refs == UINT_MAX) + return (ENOMEM); /* would overflow */ + + isrc->refs++; + return (0); + } + + /* Use the device class of the bhnd core to which the interrupt + * vector is routed to determine whether a shared interrupt should + * be preferred. */ + devclass = BHND_DEVCLASS_OTHER; + core = bcm_mips_find_bhnd_parent(rman_get_device(res)); + if (core != NULL) + devclass = bhnd_get_class(core); + + switch (devclass) { + case BHND_DEVCLASS_CC: + case BHND_DEVCLASS_CC_B: + case BHND_DEVCLASS_PMU: + case BHND_DEVCLASS_RAM: + case BHND_DEVCLASS_MEMC: + case BHND_DEVCLASS_CPU: + case BHND_DEVCLASS_SOC_ROUTER: + case BHND_DEVCLASS_SOC_BRIDGE: + case BHND_DEVCLASS_EROM: + case BHND_DEVCLASS_NVRAM: + /* Always use a shared interrupt for these devices */ + cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED]; + break; + + case BHND_DEVCLASS_PCI: + case BHND_DEVCLASS_PCIE: + case BHND_DEVCLASS_PCCARD: + case BHND_DEVCLASS_ENET: + case BHND_DEVCLASS_ENET_MAC: + case BHND_DEVCLASS_ENET_PHY: + case BHND_DEVCLASS_WLAN: + case BHND_DEVCLASS_WLAN_MAC: + case BHND_DEVCLASS_WLAN_PHY: + case BHND_DEVCLASS_USB_HOST: + case BHND_DEVCLASS_USB_DEV: + case BHND_DEVCLASS_USB_DUAL: + case BHND_DEVCLASS_OTHER: + case BHND_DEVCLASS_INVALID: + default: + /* Fall back on a shared interrupt */ + cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED]; + + /* Try to assign a dedicated MIPS HW interrupt */ + for (u_int i = 0; i < sc->num_cpuirqs; i++) { + if (i == BCM_MIPS_IRQ_SHARED) + continue; + + if (sc->cpuirqs[i].irq_rid == -1) + continue; /* unavailable */ + + if (sc->cpuirqs[i].refs != 0) + continue; /* already assigned */ + + /* Found an unused CPU IRQ */ + cpuirq = &sc->cpuirqs[i]; + break; + } + + break; + } + + DPRINTF("routing backplane interrupt vector %u to MIPS IRQ %u\n", + isrc->ivec, cpuirq->mips_irq); + + KASSERT(isrc->cpuirq == NULL, ("CPU IRQ already assigned")); + KASSERT(isrc->refs == 0, ("isrc has active references with no " + "assigned CPU IRQ")); + KASSERT(cpuirq->refs == 1 || cpuirq->isrc_solo == NULL, + ("single isrc dispatch enabled on cpuirq with multiple refs")); + + /* Verify that bumping the cpuirq refcount below will not overflow */ + if (cpuirq->refs == UINT_MAX) + return (ENOMEM); + + /* Increment cpuirq refcount on behalf of the isrc */ + cpuirq->refs++; + + /* Increment isrc refcount on behalf of the caller */ + isrc->refs++; + + /* Assign the IRQ to the isrc */ + isrc->cpuirq = cpuirq; + + /* Can we enable the single isrc dispatch path? */ + if (cpuirq->refs == 1 && cpuirq->mips_irq != BCM_MIPS_IRQ_SHARED) + cpuirq->isrc_solo = isrc; + + return (0); +} + +/** + * Release the MIPS CPU interrupt assigned to @p isrc on behalf of @p res. + * + * @param sc BHND MIPS driver state. + * @param isrc The interrupt source corresponding to @p res. + * @param res The interrupt resource being activated. + */ +static int +bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc, + struct bcm_mips_irqsrc *isrc, struct resource *res) +{ + struct bcm_mips_cpuirq *cpuirq; + + BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED); + + /* Decrement the refcount */ + KASSERT(isrc->refs > 0, ("isrc over-release")); + isrc->refs--; + + /* Nothing else to do if the isrc is still actively referenced */ + if (isrc->refs > 0) + return (0); + + /* Otherwise, we need to release our CPU IRQ reference */ + cpuirq = isrc->cpuirq; + isrc->cpuirq = NULL; + + KASSERT(cpuirq != NULL, ("no assigned IRQ")); + KASSERT(cpuirq->refs > 0, ("cpuirq over-release")); + + /* Disable single isrc dispatch path */ + if (cpuirq->refs == 1 && cpuirq->isrc_solo != NULL) { + KASSERT(cpuirq->isrc_solo == isrc, ("invalid solo isrc")); + cpuirq->isrc_solo = NULL; + } + + cpuirq->refs--; + + return (0); +} + +static device_method_t bcm_mips_methods[] = { + /* Device interface */ + DEVMETHOD(device_attach, bcm_mips_attach_default), + DEVMETHOD(device_detach, bcm_mips_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_map_intr, bcm_mips_pic_map_intr), + DEVMETHOD(pic_setup_intr, bcm_mips_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, bcm_mips_pic_teardown_intr), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(bcm_mips, bcm_mips_driver, bcm_mips_methods, sizeof(struct bcm_mips_softc)); + +MODULE_VERSION(bcm_mips, 1); +MODULE_DEPEND(bcm_mips, bhnd, 1, 1, 1); diff --git a/sys/mips/broadcom/bcm_mips74k.c b/sys/mips/broadcom/bcm_mips74k.c index 66ccadd96e2f..3b25b5bde3bf 100644 --- a/sys/mips/broadcom/bcm_mips74k.c +++ b/sys/mips/broadcom/bcm_mips74k.c @@ -1,8 +1,12 @@ /*- * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com> * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * 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: @@ -35,43 +39,106 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/bus.h> #include <sys/module.h> +#include <sys/proc.h> #include <machine/bus.h> #include <sys/rman.h> + +#include <machine/cpufunc.h> +#include <machine/intr.h> #include <machine/resource.h> #include <dev/bhnd/bhnd.h> +#include <dev/bhnd/bcma/bcma_dmp.h> + +#include "pic_if.h" +#include "bcm_machdep.h" + +#include "bcm_mipsvar.h" #include "bcm_mips74kreg.h" /* * Broadcom MIPS74K Core * - * These cores are only found on bcma(4) chipsets, allowing - * us to assume the availability of bcma interrupt registers. + * These cores are only found on bcma(4) chipsets. */ +struct bcm_mips74k_softc; + +static int bcm_mips74k_pic_intr(void *arg); +static void bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc, + u_int mips_irq, u_int ivec); +static void bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc, + u_int mips_irq, u_int ivec); + static const struct bhnd_device bcm_mips74k_devs[] = { BHND_DEVICE(MIPS, MIPS74K, NULL, NULL, BHND_DF_SOC), BHND_DEVICE_END }; struct bcm_mips74k_softc { + struct bcm_mips_softc bcm_mips; /**< parent softc */ device_t dev; - struct resource *mem_res; + struct resource *mem; /**< cpu core registers */ int mem_rid; }; +/* Early routing of the CPU timer interrupt is required */ +static void +bcm_mips74k_timer_init(void *unused) +{ + struct bcm_platform *bp; + u_int irq; + uint32_t mask; + + bp = bcm_get_platform(); + + /* Must be a MIPS74K core attached to a BCMA interconnect */ + if (!bhnd_core_matches(&bp->cpu_id, &(struct bhnd_core_match) { + BHND_MATCH_CORE(BHND_MFGID_MIPS, BHND_COREID_MIPS74K) + })) { + if (bootverbose) { + BCM_ERR("not a MIPS74K core: %s %s\n", + bhnd_vendor_name(bp->cpu_id.vendor), + bhnd_core_name(&bp->cpu_id)); + } + + return; + } + + if (!BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(bp->cid.chip_type)) { + if (bootverbose) + BCM_ERR("not a BCMA device\n"); + return; + } + + /* Route the timer bus ivec to the CPU's timer IRQ, and disable any + * other vectors assigned to the IRQ. */ + irq = BCM_MIPS74K_GET_TIMER_IRQ(); + mask = BCM_MIPS74K_INTR_SEL_FLAG(BCM_MIPS74K_TIMER_IVEC); + + BCM_CPU_WRITE_4(bp, BCM_MIPS74K_INTR_SEL(irq), mask); +} + static int bcm_mips74k_probe(device_t dev) { const struct bhnd_device *id; + const struct bhnd_chipid *cid; id = bhnd_device_lookup(dev, bcm_mips74k_devs, sizeof(bcm_mips74k_devs[0])); if (id == NULL) return (ENXIO); + /* Check the chip type; the MIPS74K core should only be found + * on bcma(4) chipsets (and we rely on bcma OOB interrupt + * routing). */ + cid = bhnd_get_chipid(dev); + if (!BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(cid->chip_type)) + return (ENXIO); + bhnd_set_default_core_desc(dev); return (BUS_PROBE_DEFAULT); } @@ -79,21 +146,40 @@ bcm_mips74k_probe(device_t dev) static int bcm_mips74k_attach(device_t dev) { - struct bcm_mips74k_softc *sc; + struct bcm_mips74k_softc *sc; + u_int timer_irq; + int error; sc = device_get_softc(dev); sc->dev = dev; - /* Allocate bus resources */ + /* Allocate our core's register block */ sc->mem_rid = 0; - sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); - if (sc->mem_res == NULL) + if (sc->mem == NULL) { + device_printf(dev, "failed to allocate cpu register block\n"); return (ENXIO); + } + + /* Clear interrupt map */ + timer_irq = BCM_MIPS74K_GET_TIMER_IRQ(); + for (size_t i = 0; i < BCM_MIPS74K_NUM_INTR; i++) { + /* We don't use the timer IRQ; leave it routed to the + * MIPS CPU */ + if (i == timer_irq) + continue; + + bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(i), 0); + } - /* Route MIPS timer to IRQ5 */ - bus_write_4(sc->mem_res, BCM_MIPS74K_INTR5_SEL, - (1<<BCM_MIPS74K_TIMER_IVEC)); + /* Initialize the generic BHND MIPS driver state */ + error = bcm_mips_attach(dev, BCM_MIPS74K_NUM_INTR, timer_irq, + bcm_mips74k_pic_intr); + if (error) { + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); + return (error); + } return (0); } @@ -102,26 +188,205 @@ static int bcm_mips74k_detach(device_t dev) { struct bcm_mips74k_softc *sc; + int error; sc = device_get_softc(dev); - bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); + if ((error = bcm_mips_detach(dev))) + return (error); + + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); return (0); } + +/* PIC_DISABLE_INTR() */ +static void +bcm_mips74k_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc) +{ + struct bcm_mips74k_softc *sc; + struct bcm_mips_irqsrc *isrc; + + sc = device_get_softc(dev); + isrc = (struct bcm_mips_irqsrc *)irqsrc; + + KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ")); + + bcm_mips74k_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec); +} + +/* PIC_ENABLE_INTR() */ +static void +bcm_mips74k_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc) +{ + struct bcm_mips74k_softc *sc; + struct bcm_mips_irqsrc *isrc; + + sc = device_get_softc(dev); + isrc = (struct bcm_mips_irqsrc *)irqsrc; + + KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ")); + + bcm_mips74k_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec); +} + +/* PIC_PRE_ITHREAD() */ +static void +bcm_mips74k_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + bcm_mips74k_pic_disable_intr(dev, isrc); +} + +/* PIC_POST_ITHREAD() */ +static void +bcm_mips74k_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + bcm_mips74k_pic_enable_intr(dev, isrc); +} + +/* PIC_POST_FILTER() */ +static void +bcm_mips74k_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} + +/** + * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ + * @p mips_irq. + */ +static void +bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec) +{ + uint32_t oobsel; + + KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u", + mips_irq)); + KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u", + mips_irq)); + KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec")); + + oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq)); + oobsel &= ~(BCM_MIPS74K_INTR_SEL_FLAG(ivec)); + bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel); +} + +/** + * Enable routing of an interrupt. + */ +static void +bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec) +{ + uint32_t oobsel; + + KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u", + mips_irq)); + KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u", + mips_irq)); + KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec")); + + oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq)); + oobsel |= BCM_MIPS74K_INTR_SEL_FLAG(ivec); + bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel); +} + +/* our MIPS CPU interrupt filter */ +static int +bcm_mips74k_pic_intr(void *arg) +{ + struct bcm_mips74k_softc *sc; + struct bcm_mips_cpuirq *cpuirq; + struct bcm_mips_irqsrc *isrc_solo; + uint32_t oobsel, intr; + u_int i; + int error; + + cpuirq = arg; + sc = (struct bcm_mips74k_softc*)cpuirq->sc; + + /* Fetch current interrupt state */ + intr = bus_read_4(sc->mem, BCM_MIPS74K_INTR_STATUS); + + /* Fetch mask of interrupt vectors routed to this MIPS IRQ */ + KASSERT(cpuirq->mips_irq < BCM_MIPS74K_NUM_INTR, + ("invalid irq %u", cpuirq->mips_irq)); + + oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(cpuirq->mips_irq)); + + /* Ignore interrupts not routed to this MIPS IRQ */ + intr &= oobsel; + + /* Handle isrc_solo direct dispatch path */ + isrc_solo = cpuirq->isrc_solo; + if (isrc_solo != NULL) { + if (intr & BCM_MIPS_IVEC_MASK(isrc_solo)) { + error = intr_isrc_dispatch(&isrc_solo->isrc, + curthread->td_intr_frame); + if (error) { + device_printf(sc->dev, "Stray interrupt %u " + "detected\n", isrc_solo->ivec); + bcm_mips74k_pic_disable_intr(sc->dev, + &isrc_solo->isrc); + } + } + + intr &= ~(BCM_MIPS_IVEC_MASK(isrc_solo)); + if (intr == 0) + return (FILTER_HANDLED); + + /* Report and mask additional stray interrupts */ + while ((i = fls(intr)) != 0) { + i--; /* Get a 0-offset interrupt. */ + intr &= ~(1 << i); + + device_printf(sc->dev, "Stray interrupt %u " + "detected\n", i); + bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i); + } + + return (FILTER_HANDLED); + } + + /* Standard dispatch path */ + while ((i = fls(intr)) != 0) { + i--; /* Get a 0-offset interrupt. */ + intr &= ~(1 << i); + + KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i)); + + error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc, + curthread->td_intr_frame); + if (error) { + device_printf(sc->dev, "Stray interrupt %u detected\n", + i); + bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i); + continue; + } + } + + return (FILTER_HANDLED); +} + static device_method_t bcm_mips74k_methods[] = { /* Device interface */ - DEVMETHOD(device_probe, bcm_mips74k_probe), - DEVMETHOD(device_attach, bcm_mips74k_attach), - DEVMETHOD(device_detach, bcm_mips74k_detach), - + DEVMETHOD(device_probe, bcm_mips74k_probe), + DEVMETHOD(device_attach, bcm_mips74k_attach), + DEVMETHOD(device_detach, bcm_mips74k_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, bcm_mips74k_pic_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_mips74k_pic_enable_intr), + DEVMETHOD(pic_pre_ithread, bcm_mips74k_pic_pre_ithread), + DEVMETHOD(pic_post_ithread, bcm_mips74k_pic_post_ithread), + DEVMETHOD(pic_post_filter, bcm_mips74k_pic_post_filter), + DEVMETHOD_END }; static devclass_t bcm_mips_devclass; -DEFINE_CLASS_0(bcm_mips, bcm_mips74k_driver, bcm_mips74k_methods, sizeof(struct bcm_mips74k_softc)); -EARLY_DRIVER_MODULE(bcm_mips74k, bhnd, bcm_mips74k_driver, bcm_mips_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); +DEFINE_CLASS_1(bcm_mips, bcm_mips74k_driver, bcm_mips74k_methods, sizeof(struct bcm_mips_softc), bcm_mips_driver); +EARLY_DRIVER_MODULE(bcm_mips74k, bhnd, bcm_mips74k_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +SYSINIT(cpu_init, SI_SUB_CPU, SI_ORDER_FIRST, bcm_mips74k_timer_init, NULL); MODULE_VERSION(bcm_mips74k, 1); MODULE_DEPEND(bcm_mips74k, bhnd, 1, 1, 1); diff --git a/sys/mips/broadcom/bcm_mips74kreg.h b/sys/mips/broadcom/bcm_mips74kreg.h index fb1f516125e8..d9b09e238ab4 100644 --- a/sys/mips/broadcom/bcm_mips74kreg.h +++ b/sys/mips/broadcom/bcm_mips74kreg.h @@ -46,9 +46,13 @@ #define BCM_MIPS74K_INTR3_SEL 0x20 /**< IRQ3 OOBSEL mask */ #define BCM_MIPS74K_INTR4_SEL 0x24 /**< IRQ4 OOBSEL mask */ #define BCM_MIPS74K_INTR5_SEL 0x28 /**< IRQ5 OOBSEL mask */ +#define BCM_MIPS74K_NUM_INTR 6 /**< routable CPU interrupt count */ #define BCM_MIPS74K_INTR_SEL(_intr) \ (BCM_MIPS74K_INTR0_SEL + ((_intr) * 4)) +#define BCM_MIPS74K_INTR_SEL_FLAG(_i) (1<<_i) + +#define BCM_MIPS74K_TIMER_IVEC 31 /**< MIPS timer's bus interrupt vector */ #define BCM_MIPS74K_NMI_MASK 0x2C /**< nmi mask */ @@ -56,7 +60,9 @@ #define BCM_MIPS74K_GPIO_OUT 0x44 /**< gpio output enable */ #define BCM_MIPS74K_GPIO_EN 0x48 /**< gpio enable */ +/** The MIPS timer interrupt IRQ assignment */ +#define BCM_MIPS74K_GET_TIMER_IRQ() \ + ((mips_rd_intctl() & MIPS_INTCTL_IPTI_MASK) >> MIPS_INTCTL_IPTI_SHIFT) -#define BCM_MIPS74K_TIMER_IVEC 31 /**< MIPS timer OOBSEL value */ #endif /* _MIPS_BROADCOM_MIPS74KREG_H_ */ diff --git a/sys/mips/broadcom/bcm_mipsvar.h b/sys/mips/broadcom/bcm_mipsvar.h new file mode 100644 index 000000000000..bc8e19888fe9 --- /dev/null +++ b/sys/mips/broadcom/bcm_mipsvar.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2017 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Landon Fuller 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * $FreeBSD$ + */ + +#ifndef _MIPS_BROADCOM_BCM_MIPSVAR_H_ +#define _MIPS_BROADCOM_BCM_MIPSVAR_H_ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/intr.h> +#include <sys/lock.h> + +#include <machine/intr.h> + +DECLARE_CLASS(bcm_mips_driver); + +struct bcm_mips_irqsrc; +struct bcm_mips_softc; + +#define BCM_MIPS_NINTR 32 /**< maximum number of addressable backplane interrupt vectors */ +#define BCM_MIPS_IRQ_SHARED 0 /**< MIPS CPU IRQ reserved for shared interrupt handling */ +#define INTR_MAP_DATA_BCM_MIPS INTR_MAP_DATA_PLAT_2 /**< Broadcom MIPS PIC interrupt map data type */ + + +int bcm_mips_attach(device_t dev, u_int num_cpuirqs, u_int timer_irq, + driver_filter_t filter); +int bcm_mips_detach(device_t dev); + +/** + * Broadcom MIPS PIC interrupt map data. + */ +struct bcm_mips_intr_map_data { + struct intr_map_data mdata; + u_int ivec; /**< bus interrupt vector */ +}; + +/** + * Nested MIPS CPU interrupt handler state. + */ +struct bcm_mips_cpuirq { + struct bcm_mips_softc *sc; /**< driver instance state, or NULL if uninitialized. */ + u_int mips_irq; /**< mips hardware interrupt number (relative to NSOFT_IRQ) */ + int irq_rid; /**< mips IRQ resource id, or -1 if this entry is unavailable */ + struct resource *irq_res; /**< mips interrupt resource */ + void *irq_cookie; /**< mips interrupt handler cookie, or NULL */ + struct bcm_mips_irqsrc *isrc_solo; /**< solo isrc assigned to this interrupt, or NULL */ + u_int refs; /**< isrc consumer refcount */ +}; + +/** + * Broadcom MIPS PIC interrupt source definition. + */ +struct bcm_mips_irqsrc { + struct intr_irqsrc isrc; + u_int ivec; /**< bus interrupt vector */ + u_int refs; /**< active reference count */ + struct bcm_mips_cpuirq *cpuirq; /**< assigned MIPS HW IRQ, or NULL if no assignment */ +}; + +/** + * bcm_mips driver instance state. Must be first member of all subclass + * softc structures. + */ +struct bcm_mips_softc { + device_t dev; + struct bcm_mips_cpuirq cpuirqs[NREAL_IRQS]; /**< nested CPU IRQ handlers */ + u_int num_cpuirqs; /**< number of nested CPU IRQ handlers */ + u_int timer_irq; /**< CPU timer IRQ */ + struct bcm_mips_irqsrc isrcs[BCM_MIPS_NINTR]; + struct mtx mtx; +}; + + +#define BCM_MIPS_IVEC_MASK(_isrc) (1 << ((_isrc)->ivec)) + +#define BCM_MIPS_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "bhnd mips driver lock", MTX_DEF) +#define BCM_MIPS_LOCK(sc) mtx_lock(&(sc)->mtx) +#define BCM_MIPS_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define BCM_MIPS_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) +#define BCM_MIPS_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) + +#endif /* _MIPS_BROADCOM_BCM_MIPSVAR_H_ */ diff --git a/sys/mips/broadcom/bcma_nexus.c b/sys/mips/broadcom/bcma_nexus.c index aba057abdeab..4e3eaa7a7c1d 100644 --- a/sys/mips/broadcom/bcma_nexus.c +++ b/sys/mips/broadcom/bcma_nexus.c @@ -45,7 +45,9 @@ __FBSDID("$FreeBSD$"); #include <dev/bhnd/bhnd_ids.h> #include <dev/bhnd/bcma/bcmavar.h> +#include <dev/bhnd/bcma/bcma_dmp.h> +#include "bcm_mipsvar.h" #include "bcm_machdep.h" #include "bhnd_nexusvar.h" @@ -57,6 +59,9 @@ __FBSDID("$FreeBSD$"); static int bcma_nexus_attach(device_t); static int bcma_nexus_probe(device_t); +_Static_assert(BCMA_OOB_NUM_BUSLINES == BCM_MIPS_NINTR, "BCMA incompatible " + "with generic NINTR"); + static int bcma_nexus_probe(device_t dev) { diff --git a/sys/mips/broadcom/bhnd_nexus.c b/sys/mips/broadcom/bhnd_nexus.c index 72996f9ee010..4b8393a5aaf0 100644 --- a/sys/mips/broadcom/bhnd_nexus.c +++ b/sys/mips/broadcom/bhnd_nexus.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> +#include <sys/intr.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/rman.h> @@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include <dev/bhnd/bhnd_ids.h> #include "bcm_machdep.h" +#include "bcm_mipsvar.h" #include "bhnd_nexusvar.h" @@ -149,28 +151,47 @@ bhnd_nexus_get_chipid(device_t dev, device_t child) } /** - * Default bhnd_nexus implementation of BHND_BUS_GET_INTR_COUNT(). + * Default bhnd_nexus implementation of BHND_BUS_MAP_INTR(). */ static int -bhnd_nexus_get_intr_count(device_t dev, device_t child) +bhnd_nexus_map_intr(device_t dev, device_t child, u_int intr, rman_res_t *irq) { - // TODO: arch-specific interrupt handling. + struct bcm_mips_intr_map_data *imd; + u_int ivec; + uintptr_t xref; + int error; + + /* Fetch the backplane interrupt vector */ + if ((error = bhnd_get_intr_ivec(child, intr, &ivec))) { + device_printf(dev, "error fetching ivec for intr %u: %d\n", + intr, error); + return (error); + } + + /* Determine our interrupt domain */ + xref = BHND_BUS_GET_INTR_DOMAIN(dev, child, false); + KASSERT(xref != 0, ("missing interrupt domain")); + + /* Allocate our map data */ + imd = (struct bcm_mips_intr_map_data *)intr_alloc_map_data( + INTR_MAP_DATA_BCM_MIPS, sizeof(*imd), M_WAITOK | M_ZERO); + imd->ivec = ivec; + + /* Map the IRQ */ + *irq = intr_map_irq(NULL, xref, &imd->mdata); return (0); } /** - * Default bhnd_nexus implementation of BHND_BUS_ASSIGN_INTR(). + * Default bhnd_nexus implementation of BHND_BUS_UNMAP_INTR(). */ -static int -bhnd_nexus_assign_intr(device_t dev, device_t child, int rid) +static void +bhnd_nexus_unmap_intr(device_t dev, device_t child, rman_res_t irq) { - uint32_t ivec; - int error; - - if ((error = bhnd_get_core_ivec(child, rid, &ivec))) - return (error); + if (irq > UINT_MAX) + panic("invalid irq: %ju", (uintmax_t)irq); - return (bus_set_resource(child, SYS_RES_IRQ, rid, ivec, 1)); + intr_unmap_irq(irq); } static device_method_t bhnd_nexus_methods[] = { @@ -185,8 +206,9 @@ static device_method_t bhnd_nexus_methods[] = { DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_nexus_is_hw_disabled), DEVMETHOD(bhnd_bus_get_attach_type, bhnd_nexus_get_attach_type), DEVMETHOD(bhnd_bus_get_chipid, bhnd_nexus_get_chipid), - DEVMETHOD(bhnd_bus_get_intr_count, bhnd_nexus_get_intr_count), - DEVMETHOD(bhnd_bus_assign_intr, bhnd_nexus_assign_intr), + DEVMETHOD(bhnd_bus_get_intr_domain, bhnd_bus_generic_get_intr_domain), + DEVMETHOD(bhnd_bus_map_intr, bhnd_nexus_map_intr), + DEVMETHOD(bhnd_bus_unmap_intr, bhnd_nexus_unmap_intr), DEVMETHOD_END }; diff --git a/sys/mips/broadcom/files.broadcom b/sys/mips/broadcom/files.broadcom index 4dadde293e86..244124a13f67 100644 --- a/sys/mips/broadcom/files.broadcom +++ b/sys/mips/broadcom/files.broadcom @@ -7,6 +7,8 @@ mips/broadcom/bcm_machdep.c standard mips/broadcom/bcm_bmips.c optional siba_nexus siba mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma +mips/broadcom/bcm_mips.c optional siba_nexus siba | \ + bcma_nexus bcma mips/broadcom/bcm_nvram_cfe.c optional bhnd siba_nexus cfe | \ bhnd bcma_nexus cfe mips/broadcom/bcm_pmu.c standard diff --git a/sys/mips/broadcom/siba_nexus.c b/sys/mips/broadcom/siba_nexus.c index 479a1c013608..0625e779a6fa 100644 --- a/sys/mips/broadcom/siba_nexus.c +++ b/sys/mips/broadcom/siba_nexus.c @@ -39,9 +39,11 @@ __FBSDID("$FreeBSD$"); #include <dev/bhnd/bhnd_ids.h> +#include <dev/bhnd/siba/sibareg.h> #include <dev/bhnd/siba/sibavar.h> #include "bcm_machdep.h" +#include "bcm_mipsvar.h" #include "bhnd_nexusvar.h" @@ -51,6 +53,9 @@ __FBSDID("$FreeBSD$"); * Derived from Bruce M. Simpson' original siba(4) driver. */ +_Static_assert(SIBA_MAX_INTR == BCM_MIPS_NINTR, "SIBA incompatible with " + "generic NINTR"); + static int siba_nexus_probe(device_t dev) { diff --git a/sys/mips/include/intr.h b/sys/mips/include/intr.h index ed32f4f96b04..52126d14c8d1 100644 --- a/sys/mips/include/intr.h +++ b/sys/mips/include/intr.h @@ -59,6 +59,10 @@ #define MIPS_PIC_XREF 1 /**< unique xref */ #endif +#define NHARD_IRQS 6 +#define NSOFT_IRQS 2 +#define NREAL_IRQS (NHARD_IRQS + NSOFT_IRQS) + #define INTR_IRQ_NSPC_SWI 4 /* MIPS32 PIC APIs */ diff --git a/sys/mips/mips/mips_pic.c b/sys/mips/mips/mips_pic.c index 86c9eaac8b50..b53613c4018e 100644 --- a/sys/mips/mips/mips_pic.c +++ b/sys/mips/mips/mips_pic.c @@ -70,10 +70,6 @@ __FBSDID("$FreeBSD$"); #include "pic_if.h" -#define NHARD_IRQS 6 -#define NSOFT_IRQS 2 -#define NREAL_IRQS (NHARD_IRQS + NSOFT_IRQS) - struct mips_pic_softc; static int mips_pic_intr(void *); |