xref: /f-stack/freebsd/mips/broadcom/bcm_mips.c (revision 22ce4aff)
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
bcm_mips_register_isrcs(struct bcm_mips_softc * sc)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
bcm_mips_init_cpuirq_unavail(struct bcm_mips_softc * sc,struct bcm_mips_cpuirq * cpuirq)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
bcm_mips_init_cpuirq(struct bcm_mips_softc * sc,struct bcm_mips_cpuirq * cpuirq,int rid,u_int irq,driver_filter_t filter)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
bcm_mips_fini_cpuirq(struct bcm_mips_softc * sc,struct bcm_mips_cpuirq * cpuirq)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
bcm_mips_attach_default(device_t dev)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
bcm_mips_attach(device_t dev,u_int num_cpuirqs,u_int timer_irq,driver_filter_t filter)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
bcm_mips_detach(device_t dev)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
bcm_mips_pic_map_intr(device_t dev,struct intr_map_data * d,struct intr_irqsrc ** isrcp)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
bcm_mips_pic_setup_intr(device_t dev,struct intr_irqsrc * irqsrc,struct resource * res,struct intr_map_data * data)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
bcm_mips_pic_teardown_intr(device_t dev,struct intr_irqsrc * irqsrc,struct resource * res,struct intr_map_data * data)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 /** return our PIC's xref */
481 static uintptr_t
bcm_mips_pic_xref(struct bcm_mips_softc * sc)482 bcm_mips_pic_xref(struct bcm_mips_softc *sc)
483 {
484 	uintptr_t xref;
485 
486 	/* Determine our interrupt domain */
487 	xref = BHND_BUS_GET_INTR_DOMAIN(device_get_parent(sc->dev), sc->dev,
488 	    true);
489 	KASSERT(xref != 0, ("missing interrupt domain"));
490 
491 	return (xref);
492 }
493 
494 /**
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
497  * bus.
498  */
499 static device_t
bcm_mips_find_bhnd_parent(device_t dev)500 bcm_mips_find_bhnd_parent(device_t dev)
501 {
502 	device_t	core, bus;
503 	devclass_t	bhnd_class;
504 
505 	bhnd_class = devclass_find("bhnd");
506 	core = dev;
507 	while ((bus = device_get_parent(core)) != NULL) {
508 		if (device_get_devclass(bus) == bhnd_class)
509 			return (core);
510 
511 		core = bus;
512 	}
513 
514 	/* Not found */
515 	return (NULL);
516 }
517 
518 /**
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.
522  *
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
526  *			assigned.
527  */
528 static int
bcm_mips_retain_cpu_intr(struct bcm_mips_softc * sc,struct bcm_mips_irqsrc * isrc,struct resource * res)529 bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
530     struct bcm_mips_irqsrc *isrc, struct resource *res)
531 {
532 	struct bcm_mips_cpuirq	*cpuirq;
533 	bhnd_devclass_t		 devclass;
534 	device_t		 core;
535 
536 	BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
537 
538 	/* Prefer existing assignment */
539 	if (isrc->cpuirq != NULL) {
540 		KASSERT(isrc->cpuirq->refs > 0, ("assigned IRQ has no "
541 		    "references"));
542 
543 		/* Increment our reference count */
544 		if (isrc->refs == UINT_MAX)
545 			return (ENOMEM);	/* would overflow */
546 
547 		isrc->refs++;
548 		return (0);
549 	}
550 
551 	/* Use the device class of the bhnd core to which the interrupt
552 	 * vector is routed to determine whether a shared interrupt should
553 	 * be preferred. */
554 	devclass = BHND_DEVCLASS_OTHER;
555 	core = bcm_mips_find_bhnd_parent(rman_get_device(res));
556 	if (core != NULL)
557 		devclass = bhnd_get_class(core);
558 
559 	switch (devclass) {
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];
572 		break;
573 
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:
588 	default:
589 		/* Fall back on a shared interrupt */
590 		cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
591 
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)
595 				continue;
596 
597 			if (sc->cpuirqs[i].irq_rid == -1)
598 				continue; /* unavailable */
599 
600 			if (sc->cpuirqs[i].refs != 0)
601 				continue; /* already assigned */
602 
603 			/* Found an unused CPU IRQ */
604 			cpuirq = &sc->cpuirqs[i];
605 			break;
606 		}
607 
608 		break;
609 	}
610 
611 	DPRINTF("routing backplane interrupt vector %u to MIPS IRQ %u\n",
612 	    isrc->ivec, cpuirq->mips_irq);
613 
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"));
619 
620 	/* Verify that bumping the cpuirq refcount below will not overflow */
621 	if (cpuirq->refs == UINT_MAX)
622 		return (ENOMEM);
623 
624 	/* Increment cpuirq refcount on behalf of the isrc */
625 	cpuirq->refs++;
626 
627 	/* Increment isrc refcount on behalf of the caller */
628 	isrc->refs++;
629 
630 	/* Assign the IRQ to the isrc */
631 	isrc->cpuirq = cpuirq;
632 
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;
636 
637 	return (0);
638 }
639 
640 /**
641  * Release the MIPS CPU interrupt assigned to @p isrc on behalf of @p res.
642  *
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.
646  */
647 static int
bcm_mips_release_cpu_intr(struct bcm_mips_softc * sc,struct bcm_mips_irqsrc * isrc,struct resource * res)648 bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
649     struct bcm_mips_irqsrc *isrc, struct resource *res)
650 {
651 	struct bcm_mips_cpuirq *cpuirq;
652 
653 	BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
654 
655 	/* Decrement the refcount */
656 	KASSERT(isrc->refs > 0, ("isrc over-release"));
657 	isrc->refs--;
658 
659 	/* Nothing else to do if the isrc is still actively referenced */
660 	if (isrc->refs > 0)
661 		return (0);
662 
663 	/* Otherwise, we need to release our CPU IRQ reference */
664 	cpuirq = isrc->cpuirq;
665 	isrc->cpuirq = NULL;
666 
667 	KASSERT(cpuirq != NULL, ("no assigned IRQ"));
668 	KASSERT(cpuirq->refs > 0, ("cpuirq over-release"));
669 
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;
674 	}
675 
676 	cpuirq->refs--;
677 
678 	return (0);
679 }
680 
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),
685 
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),
690 
691 	DEVMETHOD_END
692 };
693 
694 DEFINE_CLASS_0(bcm_mips, bcm_mips_driver, bcm_mips_methods, sizeof(struct bcm_mips_softc));
695 
696 MODULE_VERSION(bcm_mips, 1);
697 MODULE_DEPEND(bcm_mips, bhnd, 1, 1, 1);
698