2 * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
3 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
4 * Copyright (c) 2017 The FreeBSD Foundation
7 * Portions of this software were developed by Landon Fuller
8 * under sponsorship from the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer,
15 * without modification.
16 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
17 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
18 * redistribution must be conditioned upon including a substantially
19 * similar Disclaimer requirement for further binary redistribution.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
25 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
27 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGES.
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/kernel.h>
41 #include <sys/module.h>
44 #include <machine/bus.h>
47 #include <machine/cpufunc.h>
48 #include <machine/intr.h>
49 #include <machine/resource.h>
51 #include <dev/bhnd/bhnd.h>
52 #include <dev/bhnd/bcma/bcma_dmp.h>
56 #include "bcm_machdep.h"
58 #include "bcm_mipsvar.h"
59 #include "bcm_mips74kreg.h"
62 * Broadcom MIPS74K Core
64 * These cores are only found on bcma(4) chipsets.
67 struct bcm_mips74k_softc;
69 static int bcm_mips74k_pic_intr(void *arg);
70 static void bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc,
71 u_int mips_irq, u_int ivec);
72 static void bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc,
73 u_int mips_irq, u_int ivec);
75 static const struct bhnd_device bcm_mips74k_devs[] = {
76 BHND_DEVICE(MIPS, MIPS74K, NULL, NULL, BHND_DF_SOC),
80 struct bcm_mips74k_softc {
81 struct bcm_mips_softc bcm_mips; /**< parent softc */
83 struct resource *mem; /**< cpu core registers */
87 /* Early routing of the CPU timer interrupt is required */
89 bcm_mips74k_timer_init(void *unused)
91 struct bcm_platform *bp;
95 bp = bcm_get_platform();
97 /* Must be a MIPS74K core attached to a BCMA interconnect */
98 if (!bhnd_core_matches(&bp->cpu_id, &(struct bhnd_core_match) {
99 BHND_MATCH_CORE(BHND_MFGID_MIPS, BHND_COREID_MIPS74K)
102 BCM_ERR("not a MIPS74K core: %s %s\n",
103 bhnd_vendor_name(bp->cpu_id.vendor),
104 bhnd_core_name(&bp->cpu_id));
110 if (!BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(bp->cid.chip_type)) {
112 BCM_ERR("not a BCMA device\n");
116 /* Route the timer bus ivec to the CPU's timer IRQ, and disable any
117 * other vectors assigned to the IRQ. */
118 irq = BCM_MIPS74K_GET_TIMER_IRQ();
119 mask = BCM_MIPS74K_INTR_SEL_FLAG(BCM_MIPS74K_TIMER_IVEC);
121 BCM_CPU_WRITE_4(bp, BCM_MIPS74K_INTR_SEL(irq), mask);
125 bcm_mips74k_probe(device_t dev)
127 const struct bhnd_device *id;
128 const struct bhnd_chipid *cid;
130 id = bhnd_device_lookup(dev, bcm_mips74k_devs,
131 sizeof(bcm_mips74k_devs[0]));
135 /* Check the chip type; the MIPS74K core should only be found
136 * on bcma(4) chipsets (and we rely on bcma OOB interrupt
138 cid = bhnd_get_chipid(dev);
139 if (!BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(cid->chip_type))
142 bhnd_set_default_core_desc(dev);
143 return (BUS_PROBE_DEFAULT);
147 bcm_mips74k_attach(device_t dev)
149 struct bcm_mips74k_softc *sc;
153 sc = device_get_softc(dev);
156 /* Allocate our core's register block */
158 sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
160 if (sc->mem == NULL) {
161 device_printf(dev, "failed to allocate cpu register block\n");
165 /* Clear interrupt map */
166 timer_irq = BCM_MIPS74K_GET_TIMER_IRQ();
167 for (size_t i = 0; i < BCM_MIPS74K_NUM_INTR; i++) {
168 /* We don't use the timer IRQ; leave it routed to the
173 bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(i), 0);
176 /* Initialize the generic BHND MIPS driver state */
177 error = bcm_mips_attach(dev, BCM_MIPS74K_NUM_INTR, timer_irq,
178 bcm_mips74k_pic_intr);
180 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
188 bcm_mips74k_detach(device_t dev)
190 struct bcm_mips74k_softc *sc;
193 sc = device_get_softc(dev);
195 if ((error = bcm_mips_detach(dev)))
198 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
204 /* PIC_DISABLE_INTR() */
206 bcm_mips74k_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
208 struct bcm_mips74k_softc *sc;
209 struct bcm_mips_irqsrc *isrc;
211 sc = device_get_softc(dev);
212 isrc = (struct bcm_mips_irqsrc *)irqsrc;
214 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
216 bcm_mips74k_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
219 /* PIC_ENABLE_INTR() */
221 bcm_mips74k_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
223 struct bcm_mips74k_softc *sc;
224 struct bcm_mips_irqsrc *isrc;
226 sc = device_get_softc(dev);
227 isrc = (struct bcm_mips_irqsrc *)irqsrc;
229 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
231 bcm_mips74k_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
234 /* PIC_PRE_ITHREAD() */
236 bcm_mips74k_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
238 bcm_mips74k_pic_disable_intr(dev, isrc);
241 /* PIC_POST_ITHREAD() */
243 bcm_mips74k_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
245 bcm_mips74k_pic_enable_intr(dev, isrc);
248 /* PIC_POST_FILTER() */
250 bcm_mips74k_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
255 * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
259 bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec)
263 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
265 KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u",
267 KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec"));
269 oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq));
270 oobsel &= ~(BCM_MIPS74K_INTR_SEL_FLAG(ivec));
271 bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel);
275 * Enable routing of an interrupt.
278 bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec)
282 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
284 KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u",
286 KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec"));
288 oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq));
289 oobsel |= BCM_MIPS74K_INTR_SEL_FLAG(ivec);
290 bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel);
293 /* our MIPS CPU interrupt filter */
295 bcm_mips74k_pic_intr(void *arg)
297 struct bcm_mips74k_softc *sc;
298 struct bcm_mips_cpuirq *cpuirq;
299 struct bcm_mips_irqsrc *isrc_solo;
300 uint32_t oobsel, intr;
305 sc = (struct bcm_mips74k_softc*)cpuirq->sc;
307 /* Fetch current interrupt state */
308 intr = bus_read_4(sc->mem, BCM_MIPS74K_INTR_STATUS);
310 /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
311 KASSERT(cpuirq->mips_irq < BCM_MIPS74K_NUM_INTR,
312 ("invalid irq %u", cpuirq->mips_irq));
314 oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(cpuirq->mips_irq));
316 /* Ignore interrupts not routed to this MIPS IRQ */
319 /* Handle isrc_solo direct dispatch path */
320 isrc_solo = cpuirq->isrc_solo;
321 if (isrc_solo != NULL) {
322 if (intr & BCM_MIPS_IVEC_MASK(isrc_solo)) {
323 error = intr_isrc_dispatch(&isrc_solo->isrc,
324 curthread->td_intr_frame);
326 device_printf(sc->dev, "Stray interrupt %u "
327 "detected\n", isrc_solo->ivec);
328 bcm_mips74k_pic_disable_intr(sc->dev,
333 intr &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
335 return (FILTER_HANDLED);
337 /* Report and mask additional stray interrupts */
338 while ((i = fls(intr)) != 0) {
339 i--; /* Get a 0-offset interrupt. */
342 device_printf(sc->dev, "Stray interrupt %u "
344 bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i);
347 return (FILTER_HANDLED);
350 /* Standard dispatch path */
351 while ((i = fls(intr)) != 0) {
352 i--; /* Get a 0-offset interrupt. */
355 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
357 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
358 curthread->td_intr_frame);
360 device_printf(sc->dev, "Stray interrupt %u detected\n",
362 bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i);
367 return (FILTER_HANDLED);
370 static device_method_t bcm_mips74k_methods[] = {
371 /* Device interface */
372 DEVMETHOD(device_probe, bcm_mips74k_probe),
373 DEVMETHOD(device_attach, bcm_mips74k_attach),
374 DEVMETHOD(device_detach, bcm_mips74k_detach),
376 /* Interrupt controller interface */
377 DEVMETHOD(pic_disable_intr, bcm_mips74k_pic_disable_intr),
378 DEVMETHOD(pic_enable_intr, bcm_mips74k_pic_enable_intr),
379 DEVMETHOD(pic_pre_ithread, bcm_mips74k_pic_pre_ithread),
380 DEVMETHOD(pic_post_ithread, bcm_mips74k_pic_post_ithread),
381 DEVMETHOD(pic_post_filter, bcm_mips74k_pic_post_filter),
386 static devclass_t bcm_mips_devclass;
388 DEFINE_CLASS_1(bcm_mips, bcm_mips74k_driver, bcm_mips74k_methods, sizeof(struct bcm_mips_softc), bcm_mips_driver);
389 EARLY_DRIVER_MODULE(bcm_mips74k, bhnd, bcm_mips74k_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
390 SYSINIT(cpu_init, SI_SUB_CPU, SI_ORDER_FIRST, bcm_mips74k_timer_init, NULL);
391 MODULE_VERSION(bcm_mips74k, 1);
392 MODULE_DEPEND(bcm_mips74k, bhnd, 1, 1, 1);