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);
108 bcm_bmips_attach(device_t dev)
110 struct bcm_bmips_softc *sc;
113 sc = device_get_softc(dev);
116 /* Allocate our core's register block */
118 sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
120 if (sc->mem == NULL) {
121 device_printf(dev, "failed to allocate cpu register block\n");
126 /* Determine the resource ID for our siba CFG0 registers */
127 sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
128 if (sc->cfg_rid == -1) {
129 device_printf(dev, "missing required cfg0 register block\n");
134 /* Allocate our CFG0 register block */
135 sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
136 RF_ACTIVE|RF_SHAREABLE);
137 if (sc->cfg == NULL) {
138 device_printf(dev, "failed to allocate cfg0 register block\n");
143 /* Clear interrupt map */
144 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0); /* MIPS IRQ0 */
145 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0); /* MIPS IRQ1-4 */
147 /* Initialize the generic BHND MIPS driver state */
148 error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
157 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
160 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
166 bcm_bmips_detach(device_t dev)
168 struct bcm_bmips_softc *sc;
171 sc = device_get_softc(dev);
173 if ((error = bcm_mips_detach(dev)))
176 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
177 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
182 /* PIC_DISABLE_INTR() */
184 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
186 struct bcm_bmips_softc *sc;
187 struct bcm_mips_irqsrc *isrc;
189 sc = device_get_softc(dev);
190 isrc = (struct bcm_mips_irqsrc *)irqsrc;
192 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
194 bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
197 /* PIC_ENABLE_INTR() */
199 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
201 struct bcm_bmips_softc *sc;
202 struct bcm_mips_irqsrc *isrc;
204 sc = device_get_softc(dev);
205 isrc = (struct bcm_mips_irqsrc *)irqsrc;
207 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
209 bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
212 /* PIC_PRE_ITHREAD() */
214 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
216 bcm_bmips_pic_disable_intr(dev, isrc);
219 /* PIC_POST_ITHREAD() */
221 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
223 bcm_bmips_pic_enable_intr(dev, isrc);
226 /* PIC_POST_FILTER() */
228 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
233 * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
237 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
239 KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
240 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
246 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
247 sbintvec &= ~(1 << ivec);
248 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
252 /* Can we route this via ipsflag? */
253 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
254 ("cannot route high sbflag# ivec %u", ivec));
256 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
258 ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
259 SIBA_IPS_INT_MASK(mips_irq));
260 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
266 * Enable routing of an interrupt.
269 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
271 KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
272 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
278 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
279 sbintvec |= (1 << ivec);
280 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
284 /* Can we route this via ipsflag? */
285 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
286 ("cannot route high sbflag# ivec %u", ivec));
288 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
289 ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
290 SIBA_IPS_INT_MASK(mips_irq);
291 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
295 /* our MIPS CPU interrupt filter */
297 bcm_bmips_pic_intr(void *arg)
299 struct bcm_bmips_softc *sc;
300 struct bcm_mips_cpuirq *cpuirq;
301 struct bcm_mips_irqsrc *isrc_solo;
302 uint32_t sbintvec, sbstatus;
307 sc = (struct bcm_bmips_softc*)cpuirq->sc;
309 /* Fetch current interrupt state */
310 sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
312 /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
313 mips_irq = cpuirq->mips_irq;
315 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
319 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
321 /* Map to an intvec-compatible representation */
324 sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
328 sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
332 sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
336 sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
340 panic("invalid irq %u", mips_irq);
344 /* Ignore interrupts not routed to this MIPS IRQ */
345 sbstatus &= sbintvec;
347 /* Handle isrc_solo direct dispatch path */
348 isrc_solo = cpuirq->isrc_solo;
349 if (isrc_solo != NULL) {
350 if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
351 error = intr_isrc_dispatch(&isrc_solo->isrc,
352 curthread->td_intr_frame);
354 device_printf(sc->dev, "Stray interrupt %u "
355 "detected\n", isrc_solo->ivec);
356 bcm_bmips_pic_disable_intr(sc->dev,
361 sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
363 return (FILTER_HANDLED);
365 /* Report and mask additional stray interrupts */
366 while ((i = fls(sbstatus)) != 0) {
367 i--; /* Get a 0-offset interrupt. */
368 sbstatus &= ~(1 << i);
370 device_printf(sc->dev, "Stray interrupt %u "
372 bcm_bmips_mask_irq(sc, mips_irq, i);
375 return (FILTER_HANDLED);
378 /* Standard dispatch path */
379 while ((i = fls(sbstatus)) != 0) {
380 i--; /* Get a 0-offset interrupt. */
381 sbstatus &= ~(1 << i);
383 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
385 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
386 curthread->td_intr_frame);
388 device_printf(sc->dev, "Stray interrupt %u detected\n",
390 bcm_bmips_mask_irq(sc, mips_irq, i);
395 return (FILTER_HANDLED);
398 static device_method_t bcm_bmips_methods[] = {
399 /* Device interface */
400 DEVMETHOD(device_probe, bcm_bmips_probe),
401 DEVMETHOD(device_attach, bcm_bmips_attach),
402 DEVMETHOD(device_detach, bcm_bmips_detach),
404 /* Interrupt controller interface */
405 DEVMETHOD(pic_disable_intr, bcm_bmips_pic_disable_intr),
406 DEVMETHOD(pic_enable_intr, bcm_bmips_pic_enable_intr),
407 DEVMETHOD(pic_pre_ithread, bcm_bmips_pic_pre_ithread),
408 DEVMETHOD(pic_post_ithread, bcm_bmips_pic_post_ithread),
409 DEVMETHOD(pic_post_filter, bcm_bmips_pic_post_filter),
414 static devclass_t bcm_mips_devclass;
416 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
417 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
419 MODULE_VERSION(bcm_bmips, 1);
420 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);