xref: /f-stack/freebsd/mips/mips/mips_pic.c (revision 22ce4aff)
1 /*-
2  * Copyright (c) 2015 Alexander Kabaev
3  * Copyright (c) 2006 Oleksandr Tymoshenko
4  * Copyright (c) 2002-2004 Juli Mallett <[email protected]>
5  * Copyright (c) 2017 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * Portions of this software were developed by Landon Fuller
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification, immediately at the beginning of the file.
17  * 2. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
24  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include "opt_platform.h"
38 #include "opt_hwpmc_hooks.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/bus.h>
43 #include <sys/kernel.h>
44 #include <sys/ktr.h>
45 #include <sys/module.h>
46 #include <sys/malloc.h>
47 #include <sys/rman.h>
48 #include <sys/pcpu.h>
49 #include <sys/proc.h>
50 #include <sys/cpuset.h>
51 #include <sys/limits.h>
52 #include <sys/lock.h>
53 #include <sys/mutex.h>
54 #include <sys/smp.h>
55 #include <sys/sched.h>
56 #include <sys/pmc.h>
57 #include <sys/pmckern.h>
58 
59 #include <machine/bus.h>
60 #include <machine/hwfunc.h>
61 #include <machine/intr.h>
62 #include <machine/smp.h>
63 
64 #ifdef FDT
65 #include <dev/fdt/fdt_common.h>
66 #include <dev/ofw/openfirm.h>
67 #include <dev/ofw/ofw_bus.h>
68 #include <dev/ofw/ofw_bus_subr.h>
69 #endif
70 
71 #include "pic_if.h"
72 
73 struct mips_pic_softc;
74 
75 static int			 mips_pic_intr(void *);
76 static struct mips_pic_intr	*mips_pic_find_intr(struct resource *r);
77 static int			 mips_pic_map_fixed_intr(u_int irq,
78 				     struct mips_pic_intr **mapping);
79 static void			 cpu_establish_intr(struct mips_pic_softc *sc,
80 				     const char *name, driver_filter_t *filt,
81 				     void (*handler)(void*), void *arg, int irq,
82 				     int flags, void **cookiep);
83 
84 #define	INTR_MAP_DATA_MIPS	INTR_MAP_DATA_PLAT_1
85 
86 struct intr_map_data_mips_pic {
87 	struct intr_map_data	hdr;
88 	u_int			irq;
89 };
90 
91 /**
92  * MIPS interrupt state; available prior to MIPS PIC device attachment.
93  */
94 static struct mips_pic_intr {
95 	u_int			 mips_irq;	/**< MIPS IRQ# 0-7 */
96 	u_int			 intr_irq;	/**< INTRNG IRQ#, or INTR_IRQ_INVALID if unmapped */
97 	u_int			 consumers;	/**< INTRNG activation refcount */
98 	struct resource		*res;		/**< resource shared by all interrupt handlers registered via
99 						     cpu_establish_hardintr() or cpu_establish_softintr(); NULL
100 						     if no interrupt handlers are yet registered. */
101 } mips_pic_intrs[] = {
102 	{ 0, INTR_IRQ_INVALID, 0, NULL },
103 	{ 1, INTR_IRQ_INVALID, 0, NULL },
104 	{ 2, INTR_IRQ_INVALID, 0, NULL },
105 	{ 3, INTR_IRQ_INVALID, 0, NULL },
106 	{ 4, INTR_IRQ_INVALID, 0, NULL },
107 	{ 5, INTR_IRQ_INVALID, 0, NULL },
108 	{ 6, INTR_IRQ_INVALID, 0, NULL },
109 	{ 7, INTR_IRQ_INVALID, 0, NULL },
110 };
111 
112 struct mtx mips_pic_mtx;
113 MTX_SYSINIT(mips_pic_mtx, &mips_pic_mtx, "mips intr controller mutex", MTX_DEF);
114 
115 struct mips_pic_irqsrc {
116 	struct intr_irqsrc	isrc;
117 	u_int			irq;
118 };
119 
120 struct mips_pic_softc {
121 	device_t			pic_dev;
122 	struct mips_pic_irqsrc		pic_irqs[NREAL_IRQS];
123 	uint32_t			nirqs;
124 };
125 
126 static struct mips_pic_softc *pic_sc;
127 
128 #define PIC_INTR_ISRC(sc, irq)		(&(sc)->pic_irqs[(irq)].isrc)
129 
130 #ifdef FDT
131 static struct ofw_compat_data compat_data[] = {
132 	{"mti,cpu-interrupt-controller",	true},
133 	{NULL,					false}
134 };
135 #endif
136 
137 #ifndef FDT
138 static void
mips_pic_identify(driver_t * drv,device_t parent)139 mips_pic_identify(driver_t *drv, device_t parent)
140 {
141 
142 	BUS_ADD_CHILD(parent, 0, "cpupic", 0);
143 }
144 #endif
145 
146 static int
mips_pic_probe(device_t dev)147 mips_pic_probe(device_t dev)
148 {
149 
150 #ifdef FDT
151 	if (!ofw_bus_status_okay(dev))
152 		return (ENXIO);
153 
154 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
155 		return (ENXIO);
156 #endif
157 	device_set_desc(dev, "MIPS32 Interrupt Controller");
158 	return (BUS_PROBE_DEFAULT);
159 }
160 
161 static inline void
pic_irq_unmask(struct mips_pic_softc * sc,u_int irq)162 pic_irq_unmask(struct mips_pic_softc *sc, u_int irq)
163 {
164 
165 	mips_wr_status(mips_rd_status() | ((1 << irq) << 8));
166 }
167 
168 static inline void
pic_irq_mask(struct mips_pic_softc * sc,u_int irq)169 pic_irq_mask(struct mips_pic_softc *sc, u_int irq)
170 {
171 
172 	mips_wr_status(mips_rd_status() & ~((1 << irq) << 8));
173 }
174 
175 static inline intptr_t
pic_xref(device_t dev)176 pic_xref(device_t dev)
177 {
178 #ifdef FDT
179 	return (OF_xref_from_node(ofw_bus_get_node(dev)));
180 #else
181 	return (MIPS_PIC_XREF);
182 #endif
183 }
184 
185 static int
mips_pic_register_isrcs(struct mips_pic_softc * sc)186 mips_pic_register_isrcs(struct mips_pic_softc *sc)
187 {
188 	int error;
189 	uint32_t irq, i, tmpirq;
190 	struct intr_irqsrc *isrc;
191 	char *name;
192 
193 	for (irq = 0; irq < sc->nirqs; irq++) {
194 		sc->pic_irqs[irq].irq = irq;
195 
196 		isrc = PIC_INTR_ISRC(sc, irq);
197 		if (irq < NSOFT_IRQS) {
198 			name = "sint";
199 			tmpirq = irq;
200 		} else {
201 			name = "int";
202 			tmpirq = irq - NSOFT_IRQS;
203 		}
204 		error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s%u",
205 		    name, tmpirq);
206 		if (error != 0) {
207 			for (i = 0; i < irq; i++) {
208 				intr_isrc_deregister(PIC_INTR_ISRC(sc, i));
209 			}
210 			device_printf(sc->pic_dev, "%s failed", __func__);
211 			return (error);
212 		}
213 	}
214 
215 	return (0);
216 }
217 
218 static int
mips_pic_attach(device_t dev)219 mips_pic_attach(device_t dev)
220 {
221 	struct		mips_pic_softc *sc;
222 	intptr_t	xref = pic_xref(dev);
223 
224 	if (pic_sc)
225 		return (ENXIO);
226 
227 	sc = device_get_softc(dev);
228 
229 	sc->pic_dev = dev;
230 	pic_sc = sc;
231 
232 	/* Set the number of interrupts */
233 	sc->nirqs = nitems(sc->pic_irqs);
234 
235 	/* Register the interrupts */
236 	if (mips_pic_register_isrcs(sc) != 0) {
237 		device_printf(dev, "could not register PIC ISRCs\n");
238 		goto cleanup;
239 	}
240 
241 	/*
242 	 * Now, when everything is initialized, it's right time to
243 	 * register interrupt controller to interrupt framefork.
244 	 */
245 	if (intr_pic_register(dev, xref) == NULL) {
246 		device_printf(dev, "could not register PIC\n");
247 		goto cleanup;
248 	}
249 
250 	/* Claim our root controller role */
251 	if (intr_pic_claim_root(dev, xref, mips_pic_intr, sc, 0) != 0) {
252 		device_printf(dev, "could not set PIC as a root\n");
253 		intr_pic_deregister(dev, xref);
254 		goto cleanup;
255 	}
256 
257 	return (0);
258 
259 cleanup:
260 	return(ENXIO);
261 }
262 
263 int
mips_pic_intr(void * arg)264 mips_pic_intr(void *arg)
265 {
266 	struct mips_pic_softc *sc = arg;
267 	register_t cause, status;
268 	int i, intr;
269 
270 	cause = mips_rd_cause();
271 	status = mips_rd_status();
272 	intr = (cause & MIPS_INT_MASK) >> 8;
273 	/*
274 	 * Do not handle masked interrupts. They were masked by
275 	 * pre_ithread function (mips_mask_XXX_intr) and will be
276 	 * unmasked once ithread is through with handler
277 	 */
278 	intr &= (status & MIPS_INT_MASK) >> 8;
279 	while ((i = fls(intr)) != 0) {
280 		i--; /* Get a 0-offset interrupt. */
281 		intr &= ~(1 << i);
282 
283 		if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i),
284 		    curthread->td_intr_frame) != 0) {
285 			device_printf(sc->pic_dev,
286 			    "Stray interrupt %u detected\n", i);
287 			pic_irq_mask(sc, i);
288 			continue;
289 		}
290 	}
291 
292 	KASSERT(i == 0, ("all interrupts handled"));
293 
294 #ifdef HWPMC_HOOKS
295 	if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) {
296 		struct trapframe *tf = PCPU_GET(curthread)->td_intr_frame;
297 
298 		pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
299 	}
300 #endif
301 	return (FILTER_HANDLED);
302 }
303 
304 static void
mips_pic_disable_intr(device_t dev,struct intr_irqsrc * isrc)305 mips_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
306 {
307 	u_int irq;
308 
309 	irq = ((struct mips_pic_irqsrc *)isrc)->irq;
310 	pic_irq_mask(device_get_softc(dev), irq);
311 }
312 
313 static void
mips_pic_enable_intr(device_t dev,struct intr_irqsrc * isrc)314 mips_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
315 {
316 	u_int irq;
317 
318 	irq = ((struct mips_pic_irqsrc *)isrc)->irq;
319 	pic_irq_unmask(device_get_softc(dev), irq);
320 }
321 
322 static int
mips_pic_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)323 mips_pic_map_intr(device_t dev, struct intr_map_data *data,
324     struct intr_irqsrc **isrcp)
325 {
326 	struct mips_pic_softc *sc;
327 	int res;
328 
329 	sc = device_get_softc(dev);
330 	res = 0;
331 #ifdef FDT
332 	if (data->type == INTR_MAP_DATA_FDT) {
333 		struct intr_map_data_fdt *daf;
334 
335 		daf = (struct intr_map_data_fdt *)data;
336 
337 		if (daf->ncells != 1 || daf->cells[0] >= sc->nirqs)
338 			return (EINVAL);
339 
340 		*isrcp = PIC_INTR_ISRC(sc, daf->cells[0]);
341 	} else
342 #endif
343 	if (data->type == INTR_MAP_DATA_MIPS) {
344 		struct intr_map_data_mips_pic *mpd;
345 
346 		mpd = (struct intr_map_data_mips_pic *)data;
347 
348 		if (mpd->irq < 0 || mpd->irq >= sc->nirqs)
349 			return (EINVAL);
350 
351 		*isrcp = PIC_INTR_ISRC(sc, mpd->irq);
352 	} else {
353 		res = ENOTSUP;
354 	}
355 
356 	return (res);
357 }
358 
359 static void
mips_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)360 mips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
361 {
362 
363 	mips_pic_disable_intr(dev, isrc);
364 }
365 
366 static void
mips_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)367 mips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
368 {
369 
370 	mips_pic_enable_intr(dev, isrc);
371 }
372 
373 static void
mips_pic_post_filter(device_t dev,struct intr_irqsrc * isrc)374 mips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
375 {
376 }
377 
378 static device_method_t mips_pic_methods[] = {
379 	/* Device interface */
380 #ifndef FDT
381 	DEVMETHOD(device_identify,	mips_pic_identify),
382 #endif
383 	DEVMETHOD(device_probe,		mips_pic_probe),
384 	DEVMETHOD(device_attach,	mips_pic_attach),
385 
386 	/* Interrupt controller interface */
387 	DEVMETHOD(pic_disable_intr,	mips_pic_disable_intr),
388 	DEVMETHOD(pic_enable_intr,	mips_pic_enable_intr),
389 	DEVMETHOD(pic_map_intr,		mips_pic_map_intr),
390 	DEVMETHOD(pic_pre_ithread,	mips_pic_pre_ithread),
391 	DEVMETHOD(pic_post_ithread,	mips_pic_post_ithread),
392 	DEVMETHOD(pic_post_filter,	mips_pic_post_filter),
393 	{ 0, 0 }
394 };
395 
396 static driver_t mips_pic_driver = {
397 	"cpupic",
398 	mips_pic_methods,
399 	sizeof(struct mips_pic_softc),
400 };
401 
402 static devclass_t mips_pic_devclass;
403 
404 #ifdef FDT
405 EARLY_DRIVER_MODULE(cpupic, ofwbus, mips_pic_driver, mips_pic_devclass, 0, 0,
406     BUS_PASS_INTERRUPT);
407 #else
408 EARLY_DRIVER_MODULE(cpupic, nexus, mips_pic_driver, mips_pic_devclass, 0, 0,
409     BUS_PASS_INTERRUPT);
410 #endif
411 
412 /**
413  * Return the MIPS interrupt map entry for @p r, or NULL if no such entry has
414  * been created.
415  */
416 static struct mips_pic_intr *
mips_pic_find_intr(struct resource * r)417 mips_pic_find_intr(struct resource *r)
418 {
419 	struct mips_pic_intr	*intr;
420 	rman_res_t		 irq;
421 
422 	irq = rman_get_start(r);
423 	if (irq != rman_get_end(r) || rman_get_size(r) != 1)
424 		return (NULL);
425 
426 	mtx_lock(&mips_pic_mtx);
427 	for (size_t i = 0; i < nitems(mips_pic_intrs); i++) {
428 		intr = &mips_pic_intrs[i];
429 
430 		if (intr->intr_irq != irq)
431 			continue;
432 
433 		mtx_unlock(&mips_pic_mtx);
434 		return (intr);
435 	}
436 	mtx_unlock(&mips_pic_mtx);
437 
438 	/* Not found */
439 	return (NULL);
440 }
441 
442 /**
443  * Allocate a fixed IRQ mapping for the given MIPS @p irq, or return the
444  * existing mapping if @p irq was previously mapped.
445  *
446  * @param	irq	The MIPS IRQ to be mapped.
447  * @param[out]	mapping	On success, will be populated with the interrupt
448  *			mapping.
449  *
450  * @retval 0		success
451  * @retval EINVAL	if @p irq is not a valid MIPS IRQ#.
452  * @retval non-zero	If allocating the MIPS IRQ mapping otherwise fails, a
453  *			regular unix error code will be returned.
454  */
455 static int
mips_pic_map_fixed_intr(u_int irq,struct mips_pic_intr ** mapping)456 mips_pic_map_fixed_intr(u_int irq, struct mips_pic_intr **mapping)
457 {
458 	struct mips_pic_intr		*intr;
459 	struct intr_map_data_mips_pic	*data;
460 	device_t			 pic_dev;
461 	uintptr_t			 xref;
462 
463 	if (irq < 0 || irq >= nitems(mips_pic_intrs))
464 		return (EINVAL);
465 
466 	mtx_lock(&mips_pic_mtx);
467 
468 	/* Fetch corresponding interrupt entry */
469 	intr = &mips_pic_intrs[irq];
470 	KASSERT(intr->mips_irq == irq,
471 		("intr %u found at index %u", intr->mips_irq, irq));
472 
473 	/* Already mapped? */
474 	if (intr->intr_irq != INTR_IRQ_INVALID) {
475 		mtx_unlock(&mips_pic_mtx);
476 		*mapping = intr;
477 		return (0);
478 	}
479 
480 	/* Map the interrupt */
481 	data = (struct intr_map_data_mips_pic *)intr_alloc_map_data(
482 		INTR_MAP_DATA_MIPS, sizeof(*data), M_WAITOK | M_ZERO);
483 	data->irq = intr->mips_irq;
484 
485 #ifdef FDT
486 	/* PIC must be attached on FDT devices */
487 	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
488 
489 	pic_dev = pic_sc->pic_dev;
490 	xref = pic_xref(pic_dev);
491 #else /* !FDT */
492 	/* PIC has a fixed xref, and may not have been attached yet */
493 	pic_dev = NULL;
494 	if (pic_sc != NULL)
495 		pic_dev = pic_sc->pic_dev;
496 
497 	xref = MIPS_PIC_XREF;
498 #endif /* FDT */
499 
500 	KASSERT(intr->intr_irq == INTR_IRQ_INVALID, ("duplicate map"));
501 	intr->intr_irq = intr_map_irq(pic_dev, xref, &data->hdr);
502 	*mapping = intr;
503 
504 	mtx_unlock(&mips_pic_mtx);
505 	return (0);
506 }
507 
508 /**
509  *
510  * Produce fixed IRQ mappings for all MIPS IRQs.
511  *
512  * Non-FDT/OFW MIPS targets do not provide an equivalent to OFW_BUS_MAP_INTR();
513  * it is instead necessary to reserve INTRNG IRQ# 0-7 for use by MIPS device
514  * drivers that assume INTRNG IRQs 0-7 are directly mapped to MIPS IRQs 0-7.
515  *
516  * XXX: There is no support in INTRNG for reserving a fixed IRQ range. However,
517  * we should be called prior to any other interrupt mapping requests, and work
518  * around this by iteratively allocating the required 0-7 MIP IRQ# range.
519  *
520  * @retval 0		success
521  * @retval non-zero	If allocating the MIPS IRQ mappings otherwise fails, a
522  *			regular unix error code will be returned.
523  */
524 int
mips_pic_map_fixed_intrs(void)525 mips_pic_map_fixed_intrs(void)
526 {
527 	int error;
528 
529 	for (u_int i = 0; i < nitems(mips_pic_intrs); i++) {
530 		struct mips_pic_intr *intr;
531 
532 		if ((error = mips_pic_map_fixed_intr(i, &intr)))
533 			return (error);
534 
535 		/* INTRNG IRQs 0-7 must be directly mapped to MIPS IRQs 0-7 */
536 		if (intr->intr_irq != intr->mips_irq) {
537 			panic("invalid IRQ mapping: %u->%u", intr->intr_irq,
538 			    intr->mips_irq);
539 		}
540 	}
541 
542 	return (0);
543 }
544 
545 /**
546  * If @p r references a MIPS interrupt mapped by the MIPS32 interrupt
547  * controller, handle interrupt activation internally.
548  *
549  * Otherwise, delegate directly to intr_activate_irq().
550  */
551 int
mips_pic_activate_intr(device_t child,struct resource * r)552 mips_pic_activate_intr(device_t child, struct resource *r)
553 {
554 	struct mips_pic_intr	*intr;
555 	int			 error;
556 
557 	/* Is this one of our shared MIPS interrupts? */
558 	if ((intr = mips_pic_find_intr(r)) == NULL) {
559 		/* Delegate to standard INTRNG activation */
560 		return (intr_activate_irq(child, r));
561 	}
562 
563 	/* Bump consumer count and request activation if required */
564 	mtx_lock(&mips_pic_mtx);
565 	if (intr->consumers == UINT_MAX) {
566 		mtx_unlock(&mips_pic_mtx);
567 		return (ENOMEM);
568 	}
569 
570 	if (intr->consumers == 0) {
571 		if ((error = intr_activate_irq(child, r))) {
572 			mtx_unlock(&mips_pic_mtx);
573 			return (error);
574 		}
575 	}
576 
577 	intr->consumers++;
578 	mtx_unlock(&mips_pic_mtx);
579 
580 	return (0);
581 }
582 
583 /**
584  * If @p r references a MIPS interrupt mapped by the MIPS32 interrupt
585  * controller, handle interrupt deactivation internally.
586  *
587  * Otherwise, delegate directly to intr_deactivate_irq().
588  */
589 int
mips_pic_deactivate_intr(device_t child,struct resource * r)590 mips_pic_deactivate_intr(device_t child, struct resource *r)
591 {
592 	struct mips_pic_intr	*intr;
593 	int			 error;
594 
595 	/* Is this one of our shared MIPS interrupts? */
596 	if ((intr = mips_pic_find_intr(r)) == NULL) {
597 		/* Delegate to standard INTRNG deactivation */
598 		return (intr_deactivate_irq(child, r));
599 	}
600 
601 	/* Decrement consumer count and request deactivation if required */
602 	mtx_lock(&mips_pic_mtx);
603 	KASSERT(intr->consumers > 0, ("refcount overrelease"));
604 
605 	if (intr->consumers == 1) {
606 		if ((error = intr_deactivate_irq(child, r))) {
607 			mtx_unlock(&mips_pic_mtx);
608 			return (error);
609 		}
610 	}
611 	intr->consumers--;
612 
613 	mtx_unlock(&mips_pic_mtx);
614 	return (0);
615 }
616 
617 void
cpu_init_interrupts(void)618 cpu_init_interrupts(void)
619 {
620 }
621 
622 /**
623  * Provide backwards-compatible support for registering a MIPS interrupt handler
624  * directly, without allocating a bus resource.
625  */
626 static void
cpu_establish_intr(struct mips_pic_softc * sc,const char * name,driver_filter_t * filt,void (* handler)(void *),void * arg,int irq,int flags,void ** cookiep)627 cpu_establish_intr(struct mips_pic_softc *sc, const char *name,
628     driver_filter_t *filt, void (*handler)(void*), void *arg, int irq,
629     int flags, void **cookiep)
630 {
631 	struct mips_pic_intr	*intr;
632 	struct resource		*res;
633 	int			 rid;
634 	int			 error;
635 
636 	rid = -1;
637 
638 	/* Fetch (or create) a fixed mapping */
639 	if ((error = mips_pic_map_fixed_intr(irq, &intr)))
640 		panic("Unable to map IRQ %d: %d", irq, error);
641 
642 	/* Fetch the backing resource, if any */
643 	mtx_lock(&mips_pic_mtx);
644 	res = intr->res;
645 	mtx_unlock(&mips_pic_mtx);
646 
647 	/* Allocate our IRQ resource */
648 	if (res == NULL) {
649 		/* Optimistically perform resource allocation */
650 		rid = intr->intr_irq;
651 		res = bus_alloc_resource(sc->pic_dev, SYS_RES_IRQ, &rid,
652 		    intr->intr_irq, intr->intr_irq, 1, RF_SHAREABLE|RF_ACTIVE);
653 
654 		if (res != NULL) {
655 			/* Try to update intr->res */
656 			mtx_lock(&mips_pic_mtx);
657 			if (intr->res == NULL) {
658 				intr->res = res;
659 			}
660 			mtx_unlock(&mips_pic_mtx);
661 
662 			/* If intr->res was updated concurrently, free our local
663 			 * resource allocation */
664 			if (intr->res != res) {
665 				bus_release_resource(sc->pic_dev, SYS_RES_IRQ,
666 				    rid, res);
667 			}
668 		} else {
669 			/* Maybe someone else allocated it? */
670 			mtx_lock(&mips_pic_mtx);
671 			res = intr->res;
672 			mtx_unlock(&mips_pic_mtx);
673 		}
674 
675 		if (res == NULL) {
676 			panic("Unable to allocate IRQ %d->%u resource", irq,
677 			    intr->intr_irq);
678 		}
679 	}
680 
681 	error = bus_setup_intr(sc->pic_dev, res, flags, filt, handler, arg,
682 	    cookiep);
683 	if (error)
684 		panic("Unable to add IRQ %d handler: %d", irq, error);
685 }
686 
687 void
cpu_establish_hardintr(const char * name,driver_filter_t * filt,void (* handler)(void *),void * arg,int irq,int flags,void ** cookiep)688 cpu_establish_hardintr(const char *name, driver_filter_t *filt,
689     void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
690 {
691 	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
692 
693 	if (irq < 0 || irq >= NHARD_IRQS)
694 		panic("%s called for unknown hard intr %d", __func__, irq);
695 
696 	cpu_establish_intr(pic_sc, name, filt, handler, arg, irq+NSOFT_IRQS,
697 	    flags, cookiep);
698 }
699 
700 void
cpu_establish_softintr(const char * name,driver_filter_t * filt,void (* handler)(void *),void * arg,int irq,int flags,void ** cookiep)701 cpu_establish_softintr(const char *name, driver_filter_t *filt,
702     void (*handler)(void*), void *arg, int irq, int flags,
703     void **cookiep)
704 {
705 	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
706 
707 	if (irq < 0 || irq >= NSOFT_IRQS)
708 		panic("%s called for unknown soft intr %d", __func__, irq);
709 
710 	cpu_establish_intr(pic_sc, name, filt, handler, arg, irq, flags,
711 	    cookiep);
712 }
713