aboutsummaryrefslogtreecommitdiff
path: root/sys/mips
diff options
context:
space:
mode:
authorLandon J. Fuller <landonf@FreeBSD.org>2017-11-21 23:15:20 +0000
committerLandon J. Fuller <landonf@FreeBSD.org>2017-11-21 23:15:20 +0000
commitcaeff9a3c2626660d3e080d4d3b35bc53ec4417f (patch)
treeda61f12c95a2dad1fc6d62a43b769e317eb5cc7f /sys/mips
parent0a9cc964a1eae66f242631c1ef566560ab00aae2 (diff)
downloadsrc-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.c331
-rw-r--r--sys/mips/broadcom/bcm_machdep.c15
-rw-r--r--sys/mips/broadcom/bcm_machdep.h12
-rw-r--r--sys/mips/broadcom/bcm_mips.c698
-rw-r--r--sys/mips/broadcom/bcm_mips74k.c299
-rw-r--r--sys/mips/broadcom/bcm_mips74kreg.h8
-rw-r--r--sys/mips/broadcom/bcm_mipsvar.h114
-rw-r--r--sys/mips/broadcom/bcma_nexus.c5
-rw-r--r--sys/mips/broadcom/bhnd_nexus.c50
-rw-r--r--sys/mips/broadcom/files.broadcom2
-rw-r--r--sys/mips/broadcom/siba_nexus.c5
-rw-r--r--sys/mips/include/intr.h4
-rw-r--r--sys/mips/mips/mips_pic.c4
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 *);