xref: /freebsd-12.1/sys/mips/mips/mips_pic.c (revision caeff9a3)
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 
394 	{ 0, 0 }
395 };
396 
397 static driver_t mips_pic_driver = {
398 	"cpupic",
399 	mips_pic_methods,
400 	sizeof(struct mips_pic_softc),
401 };
402 
403 static devclass_t mips_pic_devclass;
404 
405 #ifdef FDT
406 EARLY_DRIVER_MODULE(cpupic, ofwbus, mips_pic_driver, mips_pic_devclass, 0, 0,
407     BUS_PASS_INTERRUPT);
408 #else
409 EARLY_DRIVER_MODULE(cpupic, nexus, mips_pic_driver, mips_pic_devclass, 0, 0,
410     BUS_PASS_INTERRUPT);
411 #endif
412 
413 /**
414  * Return the MIPS interrupt map entry for @p r, or NULL if no such entry has
415  * been created.
416  */
417 static struct mips_pic_intr *
mips_pic_find_intr(struct resource * r)418 mips_pic_find_intr(struct resource *r)
419 {
420 	struct mips_pic_intr	*intr;
421 	rman_res_t		 irq;
422 
423 	irq = rman_get_start(r);
424 	if (irq != rman_get_end(r) || rman_get_size(r) != 1)
425 		return (NULL);
426 
427 	mtx_lock(&mips_pic_mtx);
428 	for (size_t i = 0; i < nitems(mips_pic_intrs); i++) {
429 		intr = &mips_pic_intrs[i];
430 
431 		if (intr->intr_irq != irq)
432 			continue;
433 
434 		mtx_unlock(&mips_pic_mtx);
435 		return (intr);
436 	}
437 	mtx_unlock(&mips_pic_mtx);
438 
439 	/* Not found */
440 	return (NULL);
441 }
442 
443 /**
444  * Allocate a fixed IRQ mapping for the given MIPS @p irq, or return the
445  * existing mapping if @p irq was previously mapped.
446  *
447  * @param	irq	The MIPS IRQ to be mapped.
448  * @param[out]	mapping	On success, will be populated with the interrupt
449  *			mapping.
450  *
451  * @retval 0		success
452  * @retval EINVAL	if @p irq is not a valid MIPS IRQ#.
453  * @retval non-zero	If allocating the MIPS IRQ mapping otherwise fails, a
454  *			regular unix error code will be returned.
455  */
456 static int
mips_pic_map_fixed_intr(u_int irq,struct mips_pic_intr ** mapping)457 mips_pic_map_fixed_intr(u_int irq, struct mips_pic_intr **mapping)
458 {
459 	struct mips_pic_intr		*intr;
460 	struct intr_map_data_mips_pic	*data;
461 	device_t			 pic_dev;
462 	uintptr_t			 xref;
463 
464 	if (irq < 0 || irq >= nitems(mips_pic_intrs))
465 		return (EINVAL);
466 
467 	mtx_lock(&mips_pic_mtx);
468 
469 	/* Fetch corresponding interrupt entry */
470 	intr = &mips_pic_intrs[irq];
471 	KASSERT(intr->mips_irq == irq,
472 		("intr %u found at index %u", intr->mips_irq, irq));
473 
474 	/* Already mapped? */
475 	if (intr->intr_irq != INTR_IRQ_INVALID) {
476 		mtx_unlock(&mips_pic_mtx);
477 		*mapping = intr;
478 		return (0);
479 	}
480 
481 	/* Map the interrupt */
482 	data = (struct intr_map_data_mips_pic *)intr_alloc_map_data(
483 		INTR_MAP_DATA_MIPS, sizeof(*data), M_WAITOK | M_ZERO);
484 	data->irq = intr->mips_irq;
485 
486 #ifdef FDT
487 	/* PIC must be attached on FDT devices */
488 	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
489 
490 	pic_dev = pic_sc->pic_dev;
491 	xref = pic_xref(pic_dev);
492 #else /* !FDT */
493 	/* PIC has a fixed xref, and may not have been attached yet */
494 	pic_dev = NULL;
495 	if (pic_sc != NULL)
496 		pic_dev = pic_sc->pic_dev;
497 
498 	xref = MIPS_PIC_XREF;
499 #endif /* FDT */
500 
501 	KASSERT(intr->intr_irq == INTR_IRQ_INVALID, ("duplicate map"));
502 	intr->intr_irq = intr_map_irq(pic_dev, xref, &data->hdr);
503 	*mapping = intr;
504 
505 	mtx_unlock(&mips_pic_mtx);
506 	return (0);
507 }
508 
509 /**
510  *
511  * Produce fixed IRQ mappings for all MIPS IRQs.
512  *
513  * Non-FDT/OFW MIPS targets do not provide an equivalent to OFW_BUS_MAP_INTR();
514  * it is instead necessary to reserve INTRNG IRQ# 0-7 for use by MIPS device
515  * drivers that assume INTRNG IRQs 0-7 are directly mapped to MIPS IRQs 0-7.
516  *
517  * XXX: There is no support in INTRNG for reserving a fixed IRQ range. However,
518  * we should be called prior to any other interrupt mapping requests, and work
519  * around this by iteratively allocating the required 0-7 MIP IRQ# range.
520  *
521  * @retval 0		success
522  * @retval non-zero	If allocating the MIPS IRQ mappings otherwise fails, a
523  *			regular unix error code will be returned.
524  */
525 int
mips_pic_map_fixed_intrs(void)526 mips_pic_map_fixed_intrs(void)
527 {
528 	int error;
529 
530 	for (u_int i = 0; i < nitems(mips_pic_intrs); i++) {
531 		struct mips_pic_intr *intr;
532 
533 		if ((error = mips_pic_map_fixed_intr(i, &intr)))
534 			return (error);
535 
536 		/* INTRNG IRQs 0-7 must be directly mapped to MIPS IRQs 0-7 */
537 		if (intr->intr_irq != intr->mips_irq) {
538 			panic("invalid IRQ mapping: %u->%u", intr->intr_irq,
539 			    intr->mips_irq);
540 		}
541 	}
542 
543 	return (0);
544 }
545 
546 /**
547  * If @p r references a MIPS interrupt mapped by the MIPS32 interrupt
548  * controller, handle interrupt activation internally.
549  *
550  * Otherwise, delegate directly to intr_activate_irq().
551  */
552 int
mips_pic_activate_intr(device_t child,struct resource * r)553 mips_pic_activate_intr(device_t child, struct resource *r)
554 {
555 	struct mips_pic_intr	*intr;
556 	int			 error;
557 
558 	/* Is this one of our shared MIPS interrupts? */
559 	if ((intr = mips_pic_find_intr(r)) == NULL) {
560 		/* Delegate to standard INTRNG activation */
561 		return (intr_activate_irq(child, r));
562 	}
563 
564 	/* Bump consumer count and request activation if required */
565 	mtx_lock(&mips_pic_mtx);
566 	if (intr->consumers == UINT_MAX) {
567 		mtx_unlock(&mips_pic_mtx);
568 		return (ENOMEM);
569 	}
570 
571 	if (intr->consumers == 0) {
572 		if ((error = intr_activate_irq(child, r))) {
573 			mtx_unlock(&mips_pic_mtx);
574 			return (error);
575 		}
576 	}
577 
578 	intr->consumers++;
579 	mtx_unlock(&mips_pic_mtx);
580 
581 	return (0);
582 }
583 
584 /**
585  * If @p r references a MIPS interrupt mapped by the MIPS32 interrupt
586  * controller, handle interrupt deactivation internally.
587  *
588  * Otherwise, delegate directly to intr_deactivate_irq().
589  */
590 int
mips_pic_deactivate_intr(device_t child,struct resource * r)591 mips_pic_deactivate_intr(device_t child, struct resource *r)
592 {
593 	struct mips_pic_intr	*intr;
594 	int			 error;
595 
596 	/* Is this one of our shared MIPS interrupts? */
597 	if ((intr = mips_pic_find_intr(r)) == NULL) {
598 		/* Delegate to standard INTRNG deactivation */
599 		return (intr_deactivate_irq(child, r));
600 	}
601 
602 	/* Decrement consumer count and request deactivation if required */
603 	mtx_lock(&mips_pic_mtx);
604 	KASSERT(intr->consumers > 0, ("refcount overrelease"));
605 
606 	if (intr->consumers == 1) {
607 		if ((error = intr_deactivate_irq(child, r))) {
608 			mtx_unlock(&mips_pic_mtx);
609 			return (error);
610 		}
611 	}
612 	intr->consumers--;
613 
614 	mtx_unlock(&mips_pic_mtx);
615 	return (0);
616 }
617 
618 void
cpu_init_interrupts(void)619 cpu_init_interrupts(void)
620 {
621 }
622 
623 /**
624  * Provide backwards-compatible support for registering a MIPS interrupt handler
625  * directly, without allocating a bus resource.
626  */
627 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)628 cpu_establish_intr(struct mips_pic_softc *sc, const char *name,
629     driver_filter_t *filt, void (*handler)(void*), void *arg, int irq,
630     int flags, void **cookiep)
631 {
632 	struct mips_pic_intr	*intr;
633 	struct resource		*res;
634 	int			 rid;
635 	int			 error;
636 
637 	rid = -1;
638 
639 	/* Fetch (or create) a fixed mapping */
640 	if ((error = mips_pic_map_fixed_intr(irq, &intr)))
641 		panic("Unable to map IRQ %d: %d", irq, error);
642 
643 	/* Fetch the backing resource, if any */
644 	mtx_lock(&mips_pic_mtx);
645 	res = intr->res;
646 	mtx_unlock(&mips_pic_mtx);
647 
648 	/* Allocate our IRQ resource */
649 	if (res == NULL) {
650 		/* Optimistically perform resource allocation */
651 		rid = intr->intr_irq;
652 		res = bus_alloc_resource(sc->pic_dev, SYS_RES_IRQ, &rid,
653 		    intr->intr_irq, intr->intr_irq, 1, RF_SHAREABLE|RF_ACTIVE);
654 
655 		if (res != NULL) {
656 			/* Try to update intr->res */
657 			mtx_lock(&mips_pic_mtx);
658 			if (intr->res == NULL) {
659 				intr->res = res;
660 			}
661 			mtx_unlock(&mips_pic_mtx);
662 
663 			/* If intr->res was updated concurrently, free our local
664 			 * resource allocation */
665 			if (intr->res != res) {
666 				bus_release_resource(sc->pic_dev, SYS_RES_IRQ,
667 				    rid, res);
668 			}
669 		} else {
670 			/* Maybe someone else allocated it? */
671 			mtx_lock(&mips_pic_mtx);
672 			res = intr->res;
673 			mtx_unlock(&mips_pic_mtx);
674 		}
675 
676 		if (res == NULL) {
677 			panic("Unable to allocate IRQ %d->%u resource", irq,
678 			    intr->intr_irq);
679 		}
680 	}
681 
682 	error = bus_setup_intr(sc->pic_dev, res, flags, filt, handler, arg,
683 	    cookiep);
684 	if (error)
685 		panic("Unable to add IRQ %d handler: %d", irq, error);
686 }
687 
688 void
cpu_establish_hardintr(const char * name,driver_filter_t * filt,void (* handler)(void *),void * arg,int irq,int flags,void ** cookiep)689 cpu_establish_hardintr(const char *name, driver_filter_t *filt,
690     void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
691 {
692 	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
693 
694 	if (irq < 0 || irq >= NHARD_IRQS)
695 		panic("%s called for unknown hard intr %d", __func__, irq);
696 
697 	cpu_establish_intr(pic_sc, name, filt, handler, arg, irq+NSOFT_IRQS,
698 	    flags, cookiep);
699 }
700 
701 void
cpu_establish_softintr(const char * name,driver_filter_t * filt,void (* handler)(void *),void * arg,int irq,int flags,void ** cookiep)702 cpu_establish_softintr(const char *name, driver_filter_t *filt,
703     void (*handler)(void*), void *arg, int irq, int flags,
704     void **cookiep)
705 {
706 	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
707 
708 	if (irq < 0 || irq >= NSOFT_IRQS)
709 		panic("%s called for unknown soft intr %d", __func__, irq);
710 
711 	cpu_establish_intr(pic_sc, name, filt, handler, arg, irq, flags,
712 	    cookiep);
713 }
714 
715