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