2 * Copyright (c) 2017 The FreeBSD Foundation
4 * This software was developed by Landon Fuller under sponsorship from
5 * the FreeBSD Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/limits.h>
37 #include <sys/systm.h>
39 #include <machine/bus.h>
40 #include <machine/intr.h>
41 #include <machine/resource.h>
44 #include <dev/bhnd/bhnd.h>
45 #include <dev/bhnd/siba/sibareg.h>
49 #include "bcm_mipsvar.h"
52 * Broadcom MIPS core driver.
54 * Abstract driver for Broadcom MIPS CPU/PIC cores.
57 static uintptr_t bcm_mips_pic_xref(struct bcm_mips_softc *sc);
58 static device_t bcm_mips_find_bhnd_parent(device_t dev);
59 static int bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
60 struct bcm_mips_irqsrc *isrc, struct resource *res);
61 static int bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
62 struct bcm_mips_irqsrc *isrc, struct resource *res);
64 static const int bcm_mips_debug = 0;
66 #define DPRINTF(fmt, ...) do { \
68 printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \
71 #define DENTRY(dev, fmt, ...) do { \
73 printf("%s(%s, " fmt ")\n", __FUNCTION__, \
74 device_get_nameunit(dev), ##__VA_ARGS__); \
78 * Register all interrupt source definitions.
81 bcm_mips_register_isrcs(struct bcm_mips_softc *sc)
87 xref = bcm_mips_pic_xref(sc);
89 name = device_get_nameunit(sc->dev);
90 for (size_t ivec = 0; ivec < nitems(sc->isrcs); ivec++) {
91 sc->isrcs[ivec].ivec = ivec;
92 sc->isrcs[ivec].cpuirq = NULL;
93 sc->isrcs[ivec].refs = 0;
95 error = intr_isrc_register(&sc->isrcs[ivec].isrc, sc->dev,
96 xref, "%s,%u", name, ivec);
98 for (size_t i = 0; i < ivec; i++)
99 intr_isrc_deregister(&sc->isrcs[i].isrc);
101 device_printf(sc->dev, "error registering IRQ %zu: "
102 "%d\n", ivec, error);
111 * Initialize the given @p cpuirq state as unavailable.
113 * @param sc BHND MIPS driver instance state.
114 * @param cpuirq The CPU IRQ state to be initialized.
117 * @retval non-zero if initializing @p cpuirq otherwise fails, a regular
118 * unix error code will be returned.
121 bcm_mips_init_cpuirq_unavail(struct bcm_mips_softc *sc,
122 struct bcm_mips_cpuirq *cpuirq)
126 KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
128 cpuirq->mips_irq = 0;
129 cpuirq->irq_rid = -1;
130 cpuirq->irq_res = NULL;
131 cpuirq->irq_cookie = NULL;
132 cpuirq->isrc_solo = NULL;
141 * Allocate required resources and initialize the given @p cpuirq state.
143 * @param sc BHND MIPS driver instance state.
144 * @param cpuirq The CPU IRQ state to be initialized.
145 * @param rid The resource ID to be assigned for the CPU IRQ resource,
146 * or -1 if no resource should be assigned.
147 * @param irq The MIPS HW IRQ# to be allocated.
148 * @param filter The interrupt filter to be setup.
151 * @retval non-zero if initializing @p cpuirq otherwise fails, a regular
152 * unix error code will be returned.
155 bcm_mips_init_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq,
156 int rid, u_int irq, driver_filter_t filter)
158 struct resource *res;
163 /* Must fall within MIPS HW IRQ range */
164 if (irq >= NHARD_IRQS)
167 /* HW IRQs are numbered relative to SW IRQs */
168 irq_real = irq + NSOFT_IRQS;
170 /* Try to assign and allocate the resource */
173 KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
175 error = bus_set_resource(sc->dev, SYS_RES_IRQ, rid, irq_real, 1);
178 device_printf(sc->dev, "failed to assign interrupt %u: "
183 res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
186 device_printf(sc->dev, "failed to allocate interrupt "
187 "%u resource\n", irq);
188 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
192 error = bus_setup_intr(sc->dev, res,
193 INTR_TYPE_MISC | INTR_MPSAFE | INTR_EXCL, filter, NULL, cpuirq,
198 printf("failed to setup internal interrupt handler: %d\n",
201 bus_release_resource(sc->dev, SYS_RES_IRQ, rid, res);
202 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
207 /* Initialize CPU IRQ state */
209 cpuirq->mips_irq = irq;
210 cpuirq->irq_rid = rid;
211 cpuirq->irq_res = res;
212 cpuirq->irq_cookie = cookie;
213 cpuirq->isrc_solo = NULL;
221 * Free any resources associated with the given @p cpuirq state.
223 * @param sc BHND MIPS driver instance state.
224 * @param cpuirq A CPU IRQ instance previously successfully initialized
225 * via bcm_mips_init_cpuirq().
228 * @retval non-zero if finalizing @p cpuirq otherwise fails, a regular
229 * unix error code will be returned.
232 bcm_mips_fini_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq)
238 if (cpuirq->sc == NULL) {
239 KASSERT(cpuirq->irq_res == NULL, ("leaking cpuirq resource"));
242 return (0); /* not initialized */
245 if (cpuirq->refs != 0) {
250 if (cpuirq->irq_cookie != NULL) {
251 KASSERT(cpuirq->irq_res != NULL, ("resource missing"));
253 error = bus_teardown_intr(sc->dev, cpuirq->irq_res,
260 cpuirq->irq_cookie = NULL;
263 if (cpuirq->irq_res != NULL) {
264 bus_release_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid,
266 cpuirq->irq_res = NULL;
269 if (cpuirq->irq_rid != -1) {
270 bus_delete_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid);
271 cpuirq->irq_rid = -1;
280 bcm_mips_attach_default(device_t dev)
282 /* subclassing drivers must provide an implementation of
284 panic("device_attach() unimplemented");
288 * BHND MIPS device attach.
290 * This must be called from subclass drivers' DEVICE_ATTACH().
292 * @param dev BHND MIPS device.
293 * @param num_cpuirqs The number of usable MIPS HW IRQs.
294 * @param timer_irq The MIPS HW IRQ assigned to the MIPS CPU timer.
295 * @param filter The subclass's core-specific IRQ dispatch filter. Will be
296 * passed the associated bcm_mips_cpuirq instance as its argument.
299 bcm_mips_attach(device_t dev, u_int num_cpuirqs, u_int timer_irq,
300 driver_filter_t filter)
302 struct bcm_mips_softc *sc;
303 struct intr_pic *pic;
309 sc = device_get_softc(dev);
311 sc->num_cpuirqs = num_cpuirqs;
312 sc->timer_irq = timer_irq;
314 /* Must not exceed the actual size of our fixed IRQ array */
315 if (sc->num_cpuirqs > nitems(sc->cpuirqs)) {
316 device_printf(dev, "%u nirqs exceeds maximum supported %zu",
317 sc->num_cpuirqs, nitems(sc->cpuirqs));
322 xref = bcm_mips_pic_xref(sc);
324 BCM_MIPS_LOCK_INIT(sc);
326 /* Register our interrupt sources */
327 if ((error = bcm_mips_register_isrcs(sc))) {
328 BCM_MIPS_LOCK_DESTROY(sc);
332 /* Initialize our CPU interrupt state */
333 irq_rid = bhnd_get_intr_count(dev); /* last bhnd-assigned RID + 1 */
335 for (u_int i = 0; i < sc->num_cpuirqs; i++) {
336 /* Must not overflow signed resource ID representation */
337 if (irq_rid >= INT_MAX) {
338 device_printf(dev, "exhausted IRQ resource IDs\n");
343 if (irq == sc->timer_irq) {
344 /* Mark the CPU timer's IRQ as unavailable */
345 error = bcm_mips_init_cpuirq_unavail(sc,
348 /* Initialize state */
349 error = bcm_mips_init_cpuirq(sc, &sc->cpuirqs[i],
350 irq_rid, irq, filter);
356 /* Increment IRQ and resource ID for next allocation */
361 /* Sanity check; our shared IRQ must be available */
362 if (sc->num_cpuirqs <= BCM_MIPS_IRQ_SHARED)
363 panic("missing shared interrupt %d\n", BCM_MIPS_IRQ_SHARED);
365 if (sc->cpuirqs[BCM_MIPS_IRQ_SHARED].irq_rid == -1)
366 panic("shared interrupt %d unavailable", BCM_MIPS_IRQ_SHARED);
369 if ((pic = intr_pic_register(dev, xref)) == NULL) {
370 device_printf(dev, "error registering PIC\n");
378 /* Deregister PIC before performing any other cleanup */
380 intr_pic_deregister(dev, 0);
382 /* Deregister all interrupt sources */
383 for (size_t i = 0; i < nitems(sc->isrcs); i++)
384 intr_isrc_deregister(&sc->isrcs[i].isrc);
386 /* Free our MIPS CPU interrupt handler state */
387 for (u_int i = 0; i < sc->num_cpuirqs; i++)
388 bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]);
390 BCM_MIPS_LOCK_DESTROY(sc);
395 bcm_mips_detach(device_t dev)
397 struct bcm_mips_softc *sc;
399 sc = device_get_softc(dev);
401 /* Deregister PIC before performing any other cleanup */
402 intr_pic_deregister(dev, 0);
404 /* Deregister all interrupt sources */
405 for (size_t i = 0; i < nitems(sc->isrcs); i++)
406 intr_isrc_deregister(&sc->isrcs[i].isrc);
408 /* Free our MIPS CPU interrupt handler state */
409 for (u_int i = 0; i < sc->num_cpuirqs; i++)
410 bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]);
417 bcm_mips_pic_map_intr(device_t dev, struct intr_map_data *d,
418 struct intr_irqsrc **isrcp)
420 struct bcm_mips_softc *sc;
421 struct bcm_mips_intr_map_data *data;
423 sc = device_get_softc(dev);
425 if (d->type != INTR_MAP_DATA_BCM_MIPS) {
426 DENTRY(dev, "type=%d", d->type);
430 data = (struct bcm_mips_intr_map_data *)d;
431 DENTRY(dev, "type=%d, ivec=%u", d->type, data->ivec);
432 if (data->ivec < 0 || data->ivec >= nitems(sc->isrcs))
435 *isrcp = &sc->isrcs[data->ivec].isrc;
439 /* PIC_SETUP_INTR() */
441 bcm_mips_pic_setup_intr(device_t dev, struct intr_irqsrc *irqsrc,
442 struct resource *res, struct intr_map_data *data)
444 struct bcm_mips_softc *sc;
445 struct bcm_mips_irqsrc *isrc;
448 sc = device_get_softc(dev);
449 isrc = (struct bcm_mips_irqsrc *)irqsrc;
451 /* Assign a CPU interrupt */
453 error = bcm_mips_retain_cpu_intr(sc, isrc, res);
459 /* PIC_TEARDOWN_INTR() */
461 bcm_mips_pic_teardown_intr(device_t dev, struct intr_irqsrc *irqsrc,
462 struct resource *res, struct intr_map_data *data)
464 struct bcm_mips_softc *sc;
465 struct bcm_mips_irqsrc *isrc;
468 sc = device_get_softc(dev);
469 isrc = (struct bcm_mips_irqsrc *)irqsrc;
471 /* Release the CPU interrupt */
473 error = bcm_mips_release_cpu_intr(sc, isrc, res);
479 /** return our PIC's xref */
481 bcm_mips_pic_xref(struct bcm_mips_softc *sc)
485 /* Determine our interrupt domain */
486 xref = BHND_BUS_GET_INTR_DOMAIN(device_get_parent(sc->dev), sc->dev,
488 KASSERT(xref != 0, ("missing interrupt domain"));
494 * Walk up the device tree from @p dev until we find a bhnd-attached core,
495 * returning either the core, or NULL if @p dev is not attached under a bhnd
499 bcm_mips_find_bhnd_parent(device_t dev)
502 devclass_t bhnd_class;
504 bhnd_class = devclass_find("bhnd");
506 while ((bus = device_get_parent(core)) != NULL) {
507 if (device_get_devclass(bus) == bhnd_class)
518 * Retain @p isrc and assign a MIPS CPU interrupt on behalf of @p res; if
519 * the @p isrc already has a MIPS CPU interrupt assigned, the existing
520 * reference will be left unmodified.
522 * @param sc BHND MIPS driver state.
523 * @param isrc The interrupt source corresponding to @p res.
524 * @param res The interrupt resource for which a MIPS CPU IRQ will be
528 bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
529 struct bcm_mips_irqsrc *isrc, struct resource *res)
531 struct bcm_mips_cpuirq *cpuirq;
532 bhnd_devclass_t devclass;
535 BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
537 /* Prefer existing assignment */
538 if (isrc->cpuirq != NULL) {
539 KASSERT(isrc->cpuirq->refs > 0, ("assigned IRQ has no "
542 /* Increment our reference count */
543 if (isrc->refs == UINT_MAX)
544 return (ENOMEM); /* would overflow */
550 /* Use the device class of the bhnd core to which the interrupt
551 * vector is routed to determine whether a shared interrupt should
553 devclass = BHND_DEVCLASS_OTHER;
554 core = bcm_mips_find_bhnd_parent(rman_get_device(res));
556 devclass = bhnd_get_class(core);
559 case BHND_DEVCLASS_CC:
560 case BHND_DEVCLASS_CC_B:
561 case BHND_DEVCLASS_PMU:
562 case BHND_DEVCLASS_RAM:
563 case BHND_DEVCLASS_MEMC:
564 case BHND_DEVCLASS_CPU:
565 case BHND_DEVCLASS_SOC_ROUTER:
566 case BHND_DEVCLASS_SOC_BRIDGE:
567 case BHND_DEVCLASS_EROM:
568 case BHND_DEVCLASS_NVRAM:
569 /* Always use a shared interrupt for these devices */
570 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
573 case BHND_DEVCLASS_PCI:
574 case BHND_DEVCLASS_PCIE:
575 case BHND_DEVCLASS_PCCARD:
576 case BHND_DEVCLASS_ENET:
577 case BHND_DEVCLASS_ENET_MAC:
578 case BHND_DEVCLASS_ENET_PHY:
579 case BHND_DEVCLASS_WLAN:
580 case BHND_DEVCLASS_WLAN_MAC:
581 case BHND_DEVCLASS_WLAN_PHY:
582 case BHND_DEVCLASS_USB_HOST:
583 case BHND_DEVCLASS_USB_DEV:
584 case BHND_DEVCLASS_USB_DUAL:
585 case BHND_DEVCLASS_OTHER:
586 case BHND_DEVCLASS_INVALID:
588 /* Fall back on a shared interrupt */
589 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
591 /* Try to assign a dedicated MIPS HW interrupt */
592 for (u_int i = 0; i < sc->num_cpuirqs; i++) {
593 if (i == BCM_MIPS_IRQ_SHARED)
596 if (sc->cpuirqs[i].irq_rid == -1)
597 continue; /* unavailable */
599 if (sc->cpuirqs[i].refs != 0)
600 continue; /* already assigned */
602 /* Found an unused CPU IRQ */
603 cpuirq = &sc->cpuirqs[i];
610 DPRINTF("routing backplane interrupt vector %u to MIPS IRQ %u\n",
611 isrc->ivec, cpuirq->mips_irq);
613 KASSERT(isrc->cpuirq == NULL, ("CPU IRQ already assigned"));
614 KASSERT(isrc->refs == 0, ("isrc has active references with no "
615 "assigned CPU IRQ"));
616 KASSERT(cpuirq->refs == 1 || cpuirq->isrc_solo == NULL,
617 ("single isrc dispatch enabled on cpuirq with multiple refs"));
619 /* Verify that bumping the cpuirq refcount below will not overflow */
620 if (cpuirq->refs == UINT_MAX)
623 /* Increment cpuirq refcount on behalf of the isrc */
626 /* Increment isrc refcount on behalf of the caller */
629 /* Assign the IRQ to the isrc */
630 isrc->cpuirq = cpuirq;
632 /* Can we enable the single isrc dispatch path? */
633 if (cpuirq->refs == 1 && cpuirq->mips_irq != BCM_MIPS_IRQ_SHARED)
634 cpuirq->isrc_solo = isrc;
640 * Release the MIPS CPU interrupt assigned to @p isrc on behalf of @p res.
642 * @param sc BHND MIPS driver state.
643 * @param isrc The interrupt source corresponding to @p res.
644 * @param res The interrupt resource being activated.
647 bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
648 struct bcm_mips_irqsrc *isrc, struct resource *res)
650 struct bcm_mips_cpuirq *cpuirq;
652 BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
654 /* Decrement the refcount */
655 KASSERT(isrc->refs > 0, ("isrc over-release"));
658 /* Nothing else to do if the isrc is still actively referenced */
662 /* Otherwise, we need to release our CPU IRQ reference */
663 cpuirq = isrc->cpuirq;
666 KASSERT(cpuirq != NULL, ("no assigned IRQ"));
667 KASSERT(cpuirq->refs > 0, ("cpuirq over-release"));
669 /* Disable single isrc dispatch path */
670 if (cpuirq->refs == 1 && cpuirq->isrc_solo != NULL) {
671 KASSERT(cpuirq->isrc_solo == isrc, ("invalid solo isrc"));
672 cpuirq->isrc_solo = NULL;
680 static device_method_t bcm_mips_methods[] = {
681 /* Device interface */
682 DEVMETHOD(device_attach, bcm_mips_attach_default),
683 DEVMETHOD(device_detach, bcm_mips_detach),
685 /* Interrupt controller interface */
686 DEVMETHOD(pic_map_intr, bcm_mips_pic_map_intr),
687 DEVMETHOD(pic_setup_intr, bcm_mips_pic_setup_intr),
688 DEVMETHOD(pic_teardown_intr, bcm_mips_pic_teardown_intr),
693 DEFINE_CLASS_0(bcm_mips, bcm_mips_driver, bcm_mips_methods, sizeof(struct bcm_mips_softc));
695 MODULE_VERSION(bcm_mips, 1);
696 MODULE_DEPEND(bcm_mips, bhnd, 1, 1, 1);