2 * Copyright (c) 2017 The FreeBSD Foundation
5 * This software was developed by Landon Fuller under sponsorship from
6 * the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/limits.h>
38 #include <sys/systm.h>
40 #include <machine/bus.h>
41 #include <machine/intr.h>
42 #include <machine/resource.h>
45 #include <dev/bhnd/bhnd.h>
46 #include <dev/bhnd/siba/sibareg.h>
50 #include "bcm_mipsvar.h"
53 * Broadcom MIPS core driver.
55 * Abstract driver for Broadcom MIPS CPU/PIC cores.
58 static uintptr_t bcm_mips_pic_xref(struct bcm_mips_softc *sc);
59 static device_t bcm_mips_find_bhnd_parent(device_t dev);
60 static int bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
61 struct bcm_mips_irqsrc *isrc, struct resource *res);
62 static int bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
63 struct bcm_mips_irqsrc *isrc, struct resource *res);
65 static const int bcm_mips_debug = 0;
67 #define DPRINTF(fmt, ...) do { \
69 printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \
72 #define DENTRY(dev, fmt, ...) do { \
74 printf("%s(%s, " fmt ")\n", __FUNCTION__, \
75 device_get_nameunit(dev), ##__VA_ARGS__); \
79 * Register all interrupt source definitions.
82 bcm_mips_register_isrcs(struct bcm_mips_softc *sc)
88 xref = bcm_mips_pic_xref(sc);
90 name = device_get_nameunit(sc->dev);
91 for (size_t ivec = 0; ivec < nitems(sc->isrcs); ivec++) {
92 sc->isrcs[ivec].ivec = ivec;
93 sc->isrcs[ivec].cpuirq = NULL;
94 sc->isrcs[ivec].refs = 0;
96 error = intr_isrc_register(&sc->isrcs[ivec].isrc, sc->dev,
97 xref, "%s,%u", name, ivec);
99 for (size_t i = 0; i < ivec; i++)
100 intr_isrc_deregister(&sc->isrcs[i].isrc);
102 device_printf(sc->dev, "error registering IRQ %zu: "
103 "%d\n", ivec, error);
112 * Initialize the given @p cpuirq state as unavailable.
114 * @param sc BHND MIPS driver instance state.
115 * @param cpuirq The CPU IRQ state to be initialized.
118 * @retval non-zero if initializing @p cpuirq otherwise fails, a regular
119 * unix error code will be returned.
122 bcm_mips_init_cpuirq_unavail(struct bcm_mips_softc *sc,
123 struct bcm_mips_cpuirq *cpuirq)
127 KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
129 cpuirq->mips_irq = 0;
130 cpuirq->irq_rid = -1;
131 cpuirq->irq_res = NULL;
132 cpuirq->irq_cookie = NULL;
133 cpuirq->isrc_solo = NULL;
142 * Allocate required resources and initialize the given @p cpuirq state.
144 * @param sc BHND MIPS driver instance state.
145 * @param cpuirq The CPU IRQ state to be initialized.
146 * @param rid The resource ID to be assigned for the CPU IRQ resource,
147 * or -1 if no resource should be assigned.
148 * @param irq The MIPS HW IRQ# to be allocated.
149 * @param filter The interrupt filter to be setup.
152 * @retval non-zero if initializing @p cpuirq otherwise fails, a regular
153 * unix error code will be returned.
156 bcm_mips_init_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq,
157 int rid, u_int irq, driver_filter_t filter)
159 struct resource *res;
164 /* Must fall within MIPS HW IRQ range */
165 if (irq >= NHARD_IRQS)
168 /* HW IRQs are numbered relative to SW IRQs */
169 irq_real = irq + NSOFT_IRQS;
171 /* Try to assign and allocate the resource */
174 KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
176 error = bus_set_resource(sc->dev, SYS_RES_IRQ, rid, irq_real, 1);
179 device_printf(sc->dev, "failed to assign interrupt %u: "
184 res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
187 device_printf(sc->dev, "failed to allocate interrupt "
188 "%u resource\n", irq);
189 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
193 error = bus_setup_intr(sc->dev, res,
194 INTR_TYPE_MISC | INTR_MPSAFE | INTR_EXCL, filter, NULL, cpuirq,
199 printf("failed to setup internal interrupt handler: %d\n",
202 bus_release_resource(sc->dev, SYS_RES_IRQ, rid, res);
203 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
208 /* Initialize CPU IRQ state */
210 cpuirq->mips_irq = irq;
211 cpuirq->irq_rid = rid;
212 cpuirq->irq_res = res;
213 cpuirq->irq_cookie = cookie;
214 cpuirq->isrc_solo = NULL;
222 * Free any resources associated with the given @p cpuirq state.
224 * @param sc BHND MIPS driver instance state.
225 * @param cpuirq A CPU IRQ instance previously successfully initialized
226 * via bcm_mips_init_cpuirq().
229 * @retval non-zero if finalizing @p cpuirq otherwise fails, a regular
230 * unix error code will be returned.
233 bcm_mips_fini_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq)
239 if (cpuirq->sc == NULL) {
240 KASSERT(cpuirq->irq_res == NULL, ("leaking cpuirq resource"));
243 return (0); /* not initialized */
246 if (cpuirq->refs != 0) {
251 if (cpuirq->irq_cookie != NULL) {
252 KASSERT(cpuirq->irq_res != NULL, ("resource missing"));
254 error = bus_teardown_intr(sc->dev, cpuirq->irq_res,
261 cpuirq->irq_cookie = NULL;
264 if (cpuirq->irq_res != NULL) {
265 bus_release_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid,
267 cpuirq->irq_res = NULL;
270 if (cpuirq->irq_rid != -1) {
271 bus_delete_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid);
272 cpuirq->irq_rid = -1;
281 bcm_mips_attach_default(device_t dev)
283 /* subclassing drivers must provide an implementation of
285 panic("device_attach() unimplemented");
289 * BHND MIPS device attach.
291 * This must be called from subclass drivers' DEVICE_ATTACH().
293 * @param dev BHND MIPS device.
294 * @param num_cpuirqs The number of usable MIPS HW IRQs.
295 * @param timer_irq The MIPS HW IRQ assigned to the MIPS CPU timer.
296 * @param filter The subclass's core-specific IRQ dispatch filter. Will be
297 * passed the associated bcm_mips_cpuirq instance as its argument.
300 bcm_mips_attach(device_t dev, u_int num_cpuirqs, u_int timer_irq,
301 driver_filter_t filter)
303 struct bcm_mips_softc *sc;
304 struct intr_pic *pic;
310 sc = device_get_softc(dev);
312 sc->num_cpuirqs = num_cpuirqs;
313 sc->timer_irq = timer_irq;
315 /* Must not exceed the actual size of our fixed IRQ array */
316 if (sc->num_cpuirqs > nitems(sc->cpuirqs)) {
317 device_printf(dev, "%u nirqs exceeds maximum supported %zu",
318 sc->num_cpuirqs, nitems(sc->cpuirqs));
323 xref = bcm_mips_pic_xref(sc);
325 BCM_MIPS_LOCK_INIT(sc);
327 /* Register our interrupt sources */
328 if ((error = bcm_mips_register_isrcs(sc))) {
329 BCM_MIPS_LOCK_DESTROY(sc);
333 /* Initialize our CPU interrupt state */
334 irq_rid = bhnd_get_intr_count(dev); /* last bhnd-assigned RID + 1 */
336 for (u_int i = 0; i < sc->num_cpuirqs; i++) {
337 /* Must not overflow signed resource ID representation */
338 if (irq_rid >= INT_MAX) {
339 device_printf(dev, "exhausted IRQ resource IDs\n");
344 if (irq == sc->timer_irq) {
345 /* Mark the CPU timer's IRQ as unavailable */
346 error = bcm_mips_init_cpuirq_unavail(sc,
349 /* Initialize state */
350 error = bcm_mips_init_cpuirq(sc, &sc->cpuirqs[i],
351 irq_rid, irq, filter);
357 /* Increment IRQ and resource ID for next allocation */
362 /* Sanity check; our shared IRQ must be available */
363 if (sc->num_cpuirqs <= BCM_MIPS_IRQ_SHARED)
364 panic("missing shared interrupt %d\n", BCM_MIPS_IRQ_SHARED);
366 if (sc->cpuirqs[BCM_MIPS_IRQ_SHARED].irq_rid == -1)
367 panic("shared interrupt %d unavailable", BCM_MIPS_IRQ_SHARED);
370 if ((pic = intr_pic_register(dev, xref)) == NULL) {
371 device_printf(dev, "error registering PIC\n");
379 /* Deregister PIC before performing any other cleanup */
381 intr_pic_deregister(dev, 0);
383 /* Deregister all interrupt sources */
384 for (size_t i = 0; i < nitems(sc->isrcs); i++)
385 intr_isrc_deregister(&sc->isrcs[i].isrc);
387 /* Free our MIPS CPU interrupt handler state */
388 for (u_int i = 0; i < sc->num_cpuirqs; i++)
389 bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]);
391 BCM_MIPS_LOCK_DESTROY(sc);
396 bcm_mips_detach(device_t dev)
398 struct bcm_mips_softc *sc;
400 sc = device_get_softc(dev);
402 /* Deregister PIC before performing any other cleanup */
403 intr_pic_deregister(dev, 0);
405 /* Deregister all interrupt sources */
406 for (size_t i = 0; i < nitems(sc->isrcs); i++)
407 intr_isrc_deregister(&sc->isrcs[i].isrc);
409 /* Free our MIPS CPU interrupt handler state */
410 for (u_int i = 0; i < sc->num_cpuirqs; i++)
411 bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]);
418 bcm_mips_pic_map_intr(device_t dev, struct intr_map_data *d,
419 struct intr_irqsrc **isrcp)
421 struct bcm_mips_softc *sc;
422 struct bcm_mips_intr_map_data *data;
424 sc = device_get_softc(dev);
426 if (d->type != INTR_MAP_DATA_BCM_MIPS) {
427 DENTRY(dev, "type=%d", d->type);
431 data = (struct bcm_mips_intr_map_data *)d;
432 DENTRY(dev, "type=%d, ivec=%u", d->type, data->ivec);
433 if (data->ivec < 0 || data->ivec >= nitems(sc->isrcs))
436 *isrcp = &sc->isrcs[data->ivec].isrc;
440 /* PIC_SETUP_INTR() */
442 bcm_mips_pic_setup_intr(device_t dev, struct intr_irqsrc *irqsrc,
443 struct resource *res, struct intr_map_data *data)
445 struct bcm_mips_softc *sc;
446 struct bcm_mips_irqsrc *isrc;
449 sc = device_get_softc(dev);
450 isrc = (struct bcm_mips_irqsrc *)irqsrc;
452 /* Assign a CPU interrupt */
454 error = bcm_mips_retain_cpu_intr(sc, isrc, res);
460 /* PIC_TEARDOWN_INTR() */
462 bcm_mips_pic_teardown_intr(device_t dev, struct intr_irqsrc *irqsrc,
463 struct resource *res, struct intr_map_data *data)
465 struct bcm_mips_softc *sc;
466 struct bcm_mips_irqsrc *isrc;
469 sc = device_get_softc(dev);
470 isrc = (struct bcm_mips_irqsrc *)irqsrc;
472 /* Release the CPU interrupt */
474 error = bcm_mips_release_cpu_intr(sc, isrc, res);
480 /** return our PIC's xref */
482 bcm_mips_pic_xref(struct bcm_mips_softc *sc)
486 /* Determine our interrupt domain */
487 xref = BHND_BUS_GET_INTR_DOMAIN(device_get_parent(sc->dev), sc->dev,
489 KASSERT(xref != 0, ("missing interrupt domain"));
495 * Walk up the device tree from @p dev until we find a bhnd-attached core,
496 * returning either the core, or NULL if @p dev is not attached under a bhnd
500 bcm_mips_find_bhnd_parent(device_t dev)
503 devclass_t bhnd_class;
505 bhnd_class = devclass_find("bhnd");
507 while ((bus = device_get_parent(core)) != NULL) {
508 if (device_get_devclass(bus) == bhnd_class)
519 * Retain @p isrc and assign a MIPS CPU interrupt on behalf of @p res; if
520 * the @p isrc already has a MIPS CPU interrupt assigned, the existing
521 * reference will be left unmodified.
523 * @param sc BHND MIPS driver state.
524 * @param isrc The interrupt source corresponding to @p res.
525 * @param res The interrupt resource for which a MIPS CPU IRQ will be
529 bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
530 struct bcm_mips_irqsrc *isrc, struct resource *res)
532 struct bcm_mips_cpuirq *cpuirq;
533 bhnd_devclass_t devclass;
536 BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
538 /* Prefer existing assignment */
539 if (isrc->cpuirq != NULL) {
540 KASSERT(isrc->cpuirq->refs > 0, ("assigned IRQ has no "
543 /* Increment our reference count */
544 if (isrc->refs == UINT_MAX)
545 return (ENOMEM); /* would overflow */
551 /* Use the device class of the bhnd core to which the interrupt
552 * vector is routed to determine whether a shared interrupt should
554 devclass = BHND_DEVCLASS_OTHER;
555 core = bcm_mips_find_bhnd_parent(rman_get_device(res));
557 devclass = bhnd_get_class(core);
560 case BHND_DEVCLASS_CC:
561 case BHND_DEVCLASS_CC_B:
562 case BHND_DEVCLASS_PMU:
563 case BHND_DEVCLASS_RAM:
564 case BHND_DEVCLASS_MEMC:
565 case BHND_DEVCLASS_CPU:
566 case BHND_DEVCLASS_SOC_ROUTER:
567 case BHND_DEVCLASS_SOC_BRIDGE:
568 case BHND_DEVCLASS_EROM:
569 case BHND_DEVCLASS_NVRAM:
570 /* Always use a shared interrupt for these devices */
571 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
574 case BHND_DEVCLASS_PCI:
575 case BHND_DEVCLASS_PCIE:
576 case BHND_DEVCLASS_PCCARD:
577 case BHND_DEVCLASS_ENET:
578 case BHND_DEVCLASS_ENET_MAC:
579 case BHND_DEVCLASS_ENET_PHY:
580 case BHND_DEVCLASS_WLAN:
581 case BHND_DEVCLASS_WLAN_MAC:
582 case BHND_DEVCLASS_WLAN_PHY:
583 case BHND_DEVCLASS_USB_HOST:
584 case BHND_DEVCLASS_USB_DEV:
585 case BHND_DEVCLASS_USB_DUAL:
586 case BHND_DEVCLASS_OTHER:
587 case BHND_DEVCLASS_INVALID:
589 /* Fall back on a shared interrupt */
590 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
592 /* Try to assign a dedicated MIPS HW interrupt */
593 for (u_int i = 0; i < sc->num_cpuirqs; i++) {
594 if (i == BCM_MIPS_IRQ_SHARED)
597 if (sc->cpuirqs[i].irq_rid == -1)
598 continue; /* unavailable */
600 if (sc->cpuirqs[i].refs != 0)
601 continue; /* already assigned */
603 /* Found an unused CPU IRQ */
604 cpuirq = &sc->cpuirqs[i];
611 DPRINTF("routing backplane interrupt vector %u to MIPS IRQ %u\n",
612 isrc->ivec, cpuirq->mips_irq);
614 KASSERT(isrc->cpuirq == NULL, ("CPU IRQ already assigned"));
615 KASSERT(isrc->refs == 0, ("isrc has active references with no "
616 "assigned CPU IRQ"));
617 KASSERT(cpuirq->refs == 1 || cpuirq->isrc_solo == NULL,
618 ("single isrc dispatch enabled on cpuirq with multiple refs"));
620 /* Verify that bumping the cpuirq refcount below will not overflow */
621 if (cpuirq->refs == UINT_MAX)
624 /* Increment cpuirq refcount on behalf of the isrc */
627 /* Increment isrc refcount on behalf of the caller */
630 /* Assign the IRQ to the isrc */
631 isrc->cpuirq = cpuirq;
633 /* Can we enable the single isrc dispatch path? */
634 if (cpuirq->refs == 1 && cpuirq->mips_irq != BCM_MIPS_IRQ_SHARED)
635 cpuirq->isrc_solo = isrc;
641 * Release the MIPS CPU interrupt assigned to @p isrc on behalf of @p res.
643 * @param sc BHND MIPS driver state.
644 * @param isrc The interrupt source corresponding to @p res.
645 * @param res The interrupt resource being activated.
648 bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
649 struct bcm_mips_irqsrc *isrc, struct resource *res)
651 struct bcm_mips_cpuirq *cpuirq;
653 BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
655 /* Decrement the refcount */
656 KASSERT(isrc->refs > 0, ("isrc over-release"));
659 /* Nothing else to do if the isrc is still actively referenced */
663 /* Otherwise, we need to release our CPU IRQ reference */
664 cpuirq = isrc->cpuirq;
667 KASSERT(cpuirq != NULL, ("no assigned IRQ"));
668 KASSERT(cpuirq->refs > 0, ("cpuirq over-release"));
670 /* Disable single isrc dispatch path */
671 if (cpuirq->refs == 1 && cpuirq->isrc_solo != NULL) {
672 KASSERT(cpuirq->isrc_solo == isrc, ("invalid solo isrc"));
673 cpuirq->isrc_solo = NULL;
681 static device_method_t bcm_mips_methods[] = {
682 /* Device interface */
683 DEVMETHOD(device_attach, bcm_mips_attach_default),
684 DEVMETHOD(device_detach, bcm_mips_detach),
686 /* Interrupt controller interface */
687 DEVMETHOD(pic_map_intr, bcm_mips_pic_map_intr),
688 DEVMETHOD(pic_setup_intr, bcm_mips_pic_setup_intr),
689 DEVMETHOD(pic_teardown_intr, bcm_mips_pic_teardown_intr),
694 DEFINE_CLASS_0(bcm_mips, bcm_mips_driver, bcm_mips_methods, sizeof(struct bcm_mips_softc));
696 MODULE_VERSION(bcm_mips, 1);
697 MODULE_DEPEND(bcm_mips, bhnd, 1, 1, 1);