2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
3 * Copyright (c) 2017 The FreeBSD Foundation
6 * Portions of this software were developed by Landon Fuller
7 * under sponsorship from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer,
14 * without modification.
15 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17 * redistribution must be conditioned upon including a substantially
18 * similar Disclaimer requirement for further binary redistribution.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGES.
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
38 #include <sys/kernel.h>
40 #include <sys/module.h>
43 #include <machine/bus.h>
46 #include <machine/intr.h>
47 #include <machine/resource.h>
49 #include <dev/bhnd/bhnd.h>
50 #include <dev/bhnd/siba/sibareg.h>
54 #include "bcm_mipsvar.h"
55 #include "bcm_bmipsreg.h"
58 * BMIPS32 and BMIPS3300 core driver.
60 * These cores are only found on siba(4) chipsets, allowing
61 * us to assume the availability of siba interrupt registers.
64 struct bcm_bmips_softc;
66 static int bcm_bmips_pic_intr(void *arg);
67 static void bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq,
69 static void bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq,
72 static const struct bhnd_device bcm_bmips_devs[] = {
73 BHND_DEVICE(BCM, MIPS33, NULL, NULL, BHND_DF_SOC),
77 struct bcm_bmips_softc {
78 struct bcm_mips_softc bcm_mips; /**< parent softc */
80 struct resource *mem; /**< cpu core registers */
82 struct resource *cfg; /**< cpu core's cfg0 register block */
86 #define BCM_BMIPS_NCPU_IRQS 5 /**< MIPS HW IRQs 0-4 are assignable */
87 #define BCM_BMIPS_TIMER_IRQ 5 /**< MIPS HW IRQ5 is always assigned to the timer */
90 bcm_bmips_probe(device_t dev)
92 const struct bhnd_device *id;
94 id = bhnd_device_lookup(dev, bcm_bmips_devs, sizeof(bcm_bmips_devs[0]));
98 /* Check the chip type; should only be found on siba(4) chipsets */
99 if (bhnd_get_chipid(dev)->chip_type != BHND_CHIPTYPE_SIBA)
102 bhnd_set_default_core_desc(dev);
103 return (BUS_PROBE_DEFAULT);
107 bcm_bmips_attach(device_t dev)
109 struct bcm_bmips_softc *sc;
112 sc = device_get_softc(dev);
115 /* Allocate our core's register block */
117 sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
119 if (sc->mem == NULL) {
120 device_printf(dev, "failed to allocate cpu register block\n");
125 /* Determine the resource ID for our siba CFG0 registers */
126 sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
127 if (sc->cfg_rid == -1) {
128 device_printf(dev, "missing required cfg0 register block\n");
133 /* Allocate our CFG0 register block */
134 sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
135 RF_ACTIVE|RF_SHAREABLE);
136 if (sc->cfg == NULL) {
137 device_printf(dev, "failed to allocate cfg0 register block\n");
142 /* Clear interrupt map */
143 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0); /* MIPS IRQ0 */
144 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0); /* MIPS IRQ1-4 */
146 /* Initialize the generic BHND MIPS driver state */
147 error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
156 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
159 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
165 bcm_bmips_detach(device_t dev)
167 struct bcm_bmips_softc *sc;
170 sc = device_get_softc(dev);
172 if ((error = bcm_mips_detach(dev)))
175 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
176 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
181 /* PIC_DISABLE_INTR() */
183 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
185 struct bcm_bmips_softc *sc;
186 struct bcm_mips_irqsrc *isrc;
188 sc = device_get_softc(dev);
189 isrc = (struct bcm_mips_irqsrc *)irqsrc;
191 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
193 bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
196 /* PIC_ENABLE_INTR() */
198 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
200 struct bcm_bmips_softc *sc;
201 struct bcm_mips_irqsrc *isrc;
203 sc = device_get_softc(dev);
204 isrc = (struct bcm_mips_irqsrc *)irqsrc;
206 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
208 bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
211 /* PIC_PRE_ITHREAD() */
213 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
215 bcm_bmips_pic_disable_intr(dev, isrc);
218 /* PIC_POST_ITHREAD() */
220 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
222 bcm_bmips_pic_enable_intr(dev, isrc);
225 /* PIC_POST_FILTER() */
227 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
232 * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
236 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
238 KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
239 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
245 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
246 sbintvec &= ~(1 << ivec);
247 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
251 /* Can we route this via ipsflag? */
252 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
253 ("cannot route high sbflag# ivec %u", ivec));
255 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
257 ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
258 SIBA_IPS_INT_MASK(mips_irq));
259 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
265 * Enable routing of an interrupt.
268 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
270 KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
271 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
277 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
278 sbintvec |= (1 << ivec);
279 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
283 /* Can we route this via ipsflag? */
284 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
285 ("cannot route high sbflag# ivec %u", ivec));
287 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
288 ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
289 SIBA_IPS_INT_MASK(mips_irq);
290 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
294 /* our MIPS CPU interrupt filter */
296 bcm_bmips_pic_intr(void *arg)
298 struct bcm_bmips_softc *sc;
299 struct bcm_mips_cpuirq *cpuirq;
300 struct bcm_mips_irqsrc *isrc_solo;
301 uint32_t sbintvec, sbstatus;
306 sc = (struct bcm_bmips_softc*)cpuirq->sc;
308 /* Fetch current interrupt state */
309 sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
311 /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
312 mips_irq = cpuirq->mips_irq;
314 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
318 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
320 /* Map to an intvec-compatible representation */
323 sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
327 sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
331 sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
335 sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
339 panic("invalid irq %u", mips_irq);
343 /* Ignore interrupts not routed to this MIPS IRQ */
344 sbstatus &= sbintvec;
346 /* Handle isrc_solo direct dispatch path */
347 isrc_solo = cpuirq->isrc_solo;
348 if (isrc_solo != NULL) {
349 if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
350 error = intr_isrc_dispatch(&isrc_solo->isrc,
351 curthread->td_intr_frame);
353 device_printf(sc->dev, "Stray interrupt %u "
354 "detected\n", isrc_solo->ivec);
355 bcm_bmips_pic_disable_intr(sc->dev,
360 sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
362 return (FILTER_HANDLED);
364 /* Report and mask additional stray interrupts */
365 while ((i = fls(sbstatus)) != 0) {
366 i--; /* Get a 0-offset interrupt. */
367 sbstatus &= ~(1 << i);
369 device_printf(sc->dev, "Stray interrupt %u "
371 bcm_bmips_mask_irq(sc, mips_irq, i);
374 return (FILTER_HANDLED);
377 /* Standard dispatch path */
378 while ((i = fls(sbstatus)) != 0) {
379 i--; /* Get a 0-offset interrupt. */
380 sbstatus &= ~(1 << i);
382 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
384 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
385 curthread->td_intr_frame);
387 device_printf(sc->dev, "Stray interrupt %u detected\n",
389 bcm_bmips_mask_irq(sc, mips_irq, i);
394 return (FILTER_HANDLED);
397 static device_method_t bcm_bmips_methods[] = {
398 /* Device interface */
399 DEVMETHOD(device_probe, bcm_bmips_probe),
400 DEVMETHOD(device_attach, bcm_bmips_attach),
401 DEVMETHOD(device_detach, bcm_bmips_detach),
403 /* Interrupt controller interface */
404 DEVMETHOD(pic_disable_intr, bcm_bmips_pic_disable_intr),
405 DEVMETHOD(pic_enable_intr, bcm_bmips_pic_enable_intr),
406 DEVMETHOD(pic_pre_ithread, bcm_bmips_pic_pre_ithread),
407 DEVMETHOD(pic_post_ithread, bcm_bmips_pic_post_ithread),
408 DEVMETHOD(pic_post_filter, bcm_bmips_pic_post_filter),
413 static devclass_t bcm_mips_devclass;
415 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
416 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
418 MODULE_VERSION(bcm_bmips, 1);
419 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);