]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/broadcom/bcm_mips.c
zfs: merge openzfs/zfs@3b89d9518 (master) into main
[FreeBSD/FreeBSD.git] / sys / mips / broadcom / bcm_mips.c
1 /*-
2  * Copyright (c) 2017 The FreeBSD Foundation
3  *
4  * This software was developed by Landon Fuller under sponsorship from
5  * the FreeBSD Foundation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/bus.h>
35 #include <sys/module.h>
36 #include <sys/limits.h>
37 #include <sys/systm.h>
38
39 #include <machine/bus.h>
40 #include <machine/intr.h>
41 #include <machine/resource.h>
42 #include <sys/rman.h>
43
44 #include <dev/bhnd/bhnd.h>
45 #include <dev/bhnd/siba/sibareg.h>
46
47 #include "pic_if.h"
48
49 #include "bcm_mipsvar.h"
50
51 /*
52  * Broadcom MIPS core driver.
53  *
54  * Abstract driver for Broadcom MIPS CPU/PIC cores.
55  */
56
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);
63
64 static const int bcm_mips_debug = 0;
65
66 #define DPRINTF(fmt, ...) do {                                          \
67         if (bcm_mips_debug)                                             \
68                 printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__);        \
69 } while (0)
70
71 #define DENTRY(dev, fmt, ...) do {                                      \
72         if (bcm_mips_debug)                                             \
73                 printf("%s(%s, " fmt ")\n", __FUNCTION__,               \
74                     device_get_nameunit(dev), ##__VA_ARGS__);           \
75 } while (0)
76
77 /**
78  * Register all interrupt source definitions.
79  */
80 static int
81 bcm_mips_register_isrcs(struct bcm_mips_softc *sc)
82 {
83         const char      *name;
84         uintptr_t        xref;
85         int              error;
86
87         xref = bcm_mips_pic_xref(sc);
88
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;
94
95                 error = intr_isrc_register(&sc->isrcs[ivec].isrc, sc->dev,
96                     xref, "%s,%u", name, ivec);
97                 if (error) {
98                         for (size_t i = 0; i < ivec; i++)
99                                 intr_isrc_deregister(&sc->isrcs[i].isrc);
100
101                         device_printf(sc->dev, "error registering IRQ %zu: "
102                             "%d\n", ivec, error);
103                         return (error);
104                 }
105         }
106
107         return (0);
108 }
109
110 /**
111  * Initialize the given @p cpuirq state as unavailable.
112  * 
113  * @param sc            BHND MIPS driver instance state.
114  * @param cpuirq        The CPU IRQ state to be initialized.
115  *
116  * @retval 0            success
117  * @retval non-zero     if initializing @p cpuirq otherwise fails, a regular
118  *                      unix error code will be returned.
119  */
120 static int
121 bcm_mips_init_cpuirq_unavail(struct bcm_mips_softc *sc,
122     struct bcm_mips_cpuirq *cpuirq)
123 {
124         BCM_MIPS_LOCK(sc);
125
126         KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
127         cpuirq->sc = sc;
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;
133         cpuirq->refs = 0;
134
135         BCM_MIPS_UNLOCK(sc);
136
137         return (0);
138 }
139
140 /**
141  * Allocate required resources and initialize the given @p cpuirq state.
142  * 
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.
149  *
150  * @retval 0            success
151  * @retval non-zero     if initializing @p cpuirq otherwise fails, a regular
152  *                      unix error code will be returned.
153  */
154 static int
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)
157 {
158         struct resource *res;
159         void            *cookie;
160         u_int            irq_real;
161         int              error;
162
163         /* Must fall within MIPS HW IRQ range */
164         if (irq >= NHARD_IRQS)
165                 return (EINVAL);
166
167         /* HW IRQs are numbered relative to SW IRQs */
168         irq_real = irq + NSOFT_IRQS;
169
170         /* Try to assign and allocate the resource */
171         BCM_MIPS_LOCK(sc);
172
173         KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
174
175         error = bus_set_resource(sc->dev, SYS_RES_IRQ, rid, irq_real, 1);
176         if (error) {
177                 BCM_MIPS_UNLOCK(sc);
178                 device_printf(sc->dev, "failed to assign interrupt %u: "
179                     "%d\n", irq, error);
180                 return (error);
181         }
182
183         res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
184         if (res == NULL) {
185                 BCM_MIPS_UNLOCK(sc);
186                 device_printf(sc->dev, "failed to allocate interrupt "
187                         "%u resource\n", irq);
188                 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
189                 return (ENXIO);
190         }
191
192         error = bus_setup_intr(sc->dev, res,
193             INTR_TYPE_MISC | INTR_MPSAFE | INTR_EXCL, filter, NULL, cpuirq,
194             &cookie);
195         if (error) {
196                 BCM_MIPS_UNLOCK(sc);
197
198                 printf("failed to setup internal interrupt handler: %d\n",
199                     error);
200
201                 bus_release_resource(sc->dev, SYS_RES_IRQ, rid, res);
202                 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
203
204                 return (error);
205         }
206
207         /* Initialize CPU IRQ state */
208         cpuirq->sc = sc;
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;
214         cpuirq->refs = 0;
215
216         BCM_MIPS_UNLOCK(sc);
217         return (0);
218 }
219
220 /**
221  * Free any resources associated with the given @p cpuirq state.
222  * 
223  * @param sc            BHND MIPS driver instance state.
224  * @param cpuirq        A CPU IRQ instance previously successfully initialized
225  *                      via bcm_mips_init_cpuirq().
226  *
227  * @retval 0            success
228  * @retval non-zero     if finalizing @p cpuirq otherwise fails, a regular
229  *                      unix error code will be returned.
230  */
231 static int
232 bcm_mips_fini_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq)
233 {
234         int error;
235
236         BCM_MIPS_LOCK(sc);
237
238         if (cpuirq->sc == NULL) {
239                 KASSERT(cpuirq->irq_res == NULL, ("leaking cpuirq resource"));
240
241                 BCM_MIPS_UNLOCK(sc);
242                 return (0);     /* not initialized */
243         }
244
245         if (cpuirq->refs != 0) {
246                 BCM_MIPS_UNLOCK(sc);
247                 return (EBUSY);
248         }
249
250         if (cpuirq->irq_cookie != NULL) {
251                 KASSERT(cpuirq->irq_res != NULL, ("resource missing"));
252
253                 error = bus_teardown_intr(sc->dev, cpuirq->irq_res,
254                         cpuirq->irq_cookie);
255                 if (error) {
256                         BCM_MIPS_UNLOCK(sc);
257                         return (error);
258                 }
259
260                 cpuirq->irq_cookie = NULL;
261         }
262
263         if (cpuirq->irq_res != NULL) {
264                 bus_release_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid,
265                     cpuirq->irq_res);
266                 cpuirq->irq_res = NULL;
267         }
268
269         if (cpuirq->irq_rid != -1) {
270                 bus_delete_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid);
271                 cpuirq->irq_rid = -1;
272         }
273
274         BCM_MIPS_UNLOCK(sc);
275
276         return (0);
277 }
278
279 static int
280 bcm_mips_attach_default(device_t dev)
281 {
282         /* subclassing drivers must provide an implementation of
283          * DEVICE_ATTACH() */
284         panic("device_attach() unimplemented");
285 }
286
287 /**
288  * BHND MIPS device attach.
289  * 
290  * This must be called from subclass drivers' DEVICE_ATTACH().
291  * 
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.
297  */
298 int
299 bcm_mips_attach(device_t dev, u_int num_cpuirqs, u_int timer_irq,
300     driver_filter_t filter)
301 {
302         struct bcm_mips_softc   *sc;
303         struct intr_pic         *pic;
304         uintptr_t                xref;
305         u_int                    irq_rid;
306         rman_res_t               irq;
307         int                      error;
308
309         sc = device_get_softc(dev);
310         sc->dev = dev;
311         sc->num_cpuirqs = num_cpuirqs;
312         sc->timer_irq = timer_irq;
313
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));
318                 return (ENXIO);
319         }
320
321         pic = NULL;
322         xref = bcm_mips_pic_xref(sc);
323
324         BCM_MIPS_LOCK_INIT(sc);
325
326         /* Register our interrupt sources */
327         if ((error = bcm_mips_register_isrcs(sc))) {
328                 BCM_MIPS_LOCK_DESTROY(sc);
329                 return (error);
330         }
331
332         /* Initialize our CPU interrupt state */
333         irq_rid = bhnd_get_intr_count(dev); /* last bhnd-assigned RID + 1 */
334         irq = 0;
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");
339                         error = ENOMEM;
340                         goto failed;
341                 }
342
343                 if (irq == sc->timer_irq) {
344                         /* Mark the CPU timer's IRQ as unavailable */
345                         error = bcm_mips_init_cpuirq_unavail(sc,
346                             &sc->cpuirqs[i]);
347                 } else {
348                         /* Initialize state */
349                         error = bcm_mips_init_cpuirq(sc, &sc->cpuirqs[i],
350                             irq_rid, irq, filter);
351                 }
352
353                 if (error)
354                         goto failed;
355
356                 /* Increment IRQ and resource ID for next allocation */
357                 irq_rid++;
358                 irq++;
359         }
360
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);
364
365         if (sc->cpuirqs[BCM_MIPS_IRQ_SHARED].irq_rid == -1)
366                 panic("shared interrupt %d unavailable", BCM_MIPS_IRQ_SHARED);
367
368         /* Register PIC */
369         if ((pic = intr_pic_register(dev, xref)) == NULL) {
370                 device_printf(dev, "error registering PIC\n");
371                 error = ENXIO;
372                 goto failed;
373         }
374
375         return (0);
376
377 failed:
378         /* Deregister PIC before performing any other cleanup */
379         if (pic != NULL)
380                 intr_pic_deregister(dev, 0);
381
382         /* Deregister all interrupt sources */
383         for (size_t i = 0; i < nitems(sc->isrcs); i++)
384                 intr_isrc_deregister(&sc->isrcs[i].isrc);
385
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]);
389
390         BCM_MIPS_LOCK_DESTROY(sc);
391         return (error);
392 }
393
394 int
395 bcm_mips_detach(device_t dev)
396 {
397         struct bcm_mips_softc *sc;
398
399         sc = device_get_softc(dev);
400
401         /* Deregister PIC before performing any other cleanup */
402         intr_pic_deregister(dev, 0);
403
404         /* Deregister all interrupt sources */
405         for (size_t i = 0; i < nitems(sc->isrcs); i++)
406                 intr_isrc_deregister(&sc->isrcs[i].isrc);
407
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]);
411
412         return (0);
413 }
414
415 /* PIC_MAP_INTR() */
416 static int
417 bcm_mips_pic_map_intr(device_t dev, struct intr_map_data *d,
418     struct intr_irqsrc **isrcp)
419 {
420         struct bcm_mips_softc           *sc;
421         struct bcm_mips_intr_map_data   *data;
422
423         sc = device_get_softc(dev);
424
425         if (d->type != INTR_MAP_DATA_BCM_MIPS) {
426                 DENTRY(dev, "type=%d", d->type);
427                 return (ENOTSUP);
428         }
429
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))
433                 return (EINVAL);
434
435         *isrcp = &sc->isrcs[data->ivec].isrc;
436         return (0);
437 }
438
439 /* PIC_SETUP_INTR() */
440 static int
441 bcm_mips_pic_setup_intr(device_t dev, struct intr_irqsrc *irqsrc,
442     struct resource *res, struct intr_map_data *data)
443 {
444         struct bcm_mips_softc   *sc;
445         struct bcm_mips_irqsrc  *isrc;
446         int                      error;
447
448         sc = device_get_softc(dev);
449         isrc = (struct bcm_mips_irqsrc *)irqsrc;
450
451         /* Assign a CPU interrupt */
452         BCM_MIPS_LOCK(sc);
453         error = bcm_mips_retain_cpu_intr(sc, isrc, res);
454         BCM_MIPS_UNLOCK(sc);
455
456         return (error);
457 }
458
459 /* PIC_TEARDOWN_INTR() */
460 static int
461 bcm_mips_pic_teardown_intr(device_t dev, struct intr_irqsrc *irqsrc,
462     struct resource *res, struct intr_map_data *data)
463 {
464         struct bcm_mips_softc   *sc;
465         struct bcm_mips_irqsrc  *isrc;
466         int                      error;
467
468         sc = device_get_softc(dev);
469         isrc = (struct bcm_mips_irqsrc *)irqsrc;
470
471         /* Release the CPU interrupt */
472         BCM_MIPS_LOCK(sc);
473         error = bcm_mips_release_cpu_intr(sc, isrc, res);
474         BCM_MIPS_UNLOCK(sc);
475
476         return (error);
477 }
478
479 /** return our PIC's xref */
480 static uintptr_t
481 bcm_mips_pic_xref(struct bcm_mips_softc *sc)
482 {
483         uintptr_t xref;
484
485         /* Determine our interrupt domain */
486         xref = BHND_BUS_GET_INTR_DOMAIN(device_get_parent(sc->dev), sc->dev,
487             true);
488         KASSERT(xref != 0, ("missing interrupt domain"));
489
490         return (xref);
491 }
492
493 /**
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
496  * bus.
497  */    
498 static device_t
499 bcm_mips_find_bhnd_parent(device_t dev)
500 {
501         device_t        core, bus;
502         devclass_t      bhnd_class;
503
504         bhnd_class = devclass_find("bhnd");
505         core = dev;
506         while ((bus = device_get_parent(core)) != NULL) {
507                 if (device_get_devclass(bus) == bhnd_class)
508                         return (core);
509
510                 core = bus;
511         }
512
513         /* Not found */
514         return (NULL);
515 }
516
517 /**
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.
521  * 
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
525  *                      assigned.
526  */
527 static int
528 bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
529     struct bcm_mips_irqsrc *isrc, struct resource *res)
530 {
531         struct bcm_mips_cpuirq  *cpuirq;
532         bhnd_devclass_t          devclass;
533         device_t                 core;
534
535         BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
536
537         /* Prefer existing assignment */
538         if (isrc->cpuirq != NULL) {
539                 KASSERT(isrc->cpuirq->refs > 0, ("assigned IRQ has no "
540                     "references"));
541
542                 /* Increment our reference count */
543                 if (isrc->refs == UINT_MAX)
544                         return (ENOMEM);        /* would overflow */
545
546                 isrc->refs++;
547                 return (0);
548         }
549
550         /* Use the device class of the bhnd core to which the interrupt
551          * vector is routed to determine whether a shared interrupt should
552          * be preferred. */
553         devclass = BHND_DEVCLASS_OTHER;
554         core = bcm_mips_find_bhnd_parent(rman_get_device(res));
555         if (core != NULL)
556                 devclass = bhnd_get_class(core);
557
558         switch (devclass) {
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];
571                 break;
572
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:
587         default:
588                 /* Fall back on a shared interrupt */
589                 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
590
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)
594                                 continue;
595
596                         if (sc->cpuirqs[i].irq_rid == -1)
597                                 continue; /* unavailable */
598
599                         if (sc->cpuirqs[i].refs != 0)
600                                 continue; /* already assigned */
601
602                         /* Found an unused CPU IRQ */
603                         cpuirq = &sc->cpuirqs[i];
604                         break;
605                 }
606
607                 break;
608         }
609
610         DPRINTF("routing backplane interrupt vector %u to MIPS IRQ %u\n",
611             isrc->ivec, cpuirq->mips_irq);
612
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"));
618
619         /* Verify that bumping the cpuirq refcount below will not overflow */
620         if (cpuirq->refs == UINT_MAX)
621                 return (ENOMEM);
622
623         /* Increment cpuirq refcount on behalf of the isrc */
624         cpuirq->refs++;
625
626         /* Increment isrc refcount on behalf of the caller */
627         isrc->refs++;
628
629         /* Assign the IRQ to the isrc */
630         isrc->cpuirq = cpuirq;
631
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;
635
636         return (0);
637 }
638
639 /**
640  * Release the MIPS CPU interrupt assigned to @p isrc on behalf of @p res.
641  *
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.
645  */
646 static int
647 bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
648     struct bcm_mips_irqsrc *isrc, struct resource *res)
649 {
650         struct bcm_mips_cpuirq *cpuirq;
651
652         BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
653
654         /* Decrement the refcount */
655         KASSERT(isrc->refs > 0, ("isrc over-release"));
656         isrc->refs--;
657
658         /* Nothing else to do if the isrc is still actively referenced */
659         if (isrc->refs > 0)
660                 return (0);
661
662         /* Otherwise, we need to release our CPU IRQ reference */
663         cpuirq = isrc->cpuirq;
664         isrc->cpuirq = NULL;
665
666         KASSERT(cpuirq != NULL, ("no assigned IRQ"));
667         KASSERT(cpuirq->refs > 0, ("cpuirq over-release"));
668
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;
673         }
674
675         cpuirq->refs--;
676
677         return (0);
678 }
679
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),
684
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),
689
690         DEVMETHOD_END
691 };
692
693 DEFINE_CLASS_0(bcm_mips, bcm_mips_driver, bcm_mips_methods, sizeof(struct bcm_mips_softc));
694
695 MODULE_VERSION(bcm_mips, 1);
696 MODULE_DEPEND(bcm_mips, bhnd, 1, 1, 1);