xref: /freebsd/sys/powerpc/powerpc/intr_machdep.c (revision e28a4053)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * William Jolitz.
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  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR 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  * Copyright (c) 2002 Benno Rice.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  *
57  *	from: @(#)isa.c	7.2 (Berkeley) 5/13/91
58  *	form: src/sys/i386/isa/intr_machdep.c,v 1.57 2001/07/20
59  *
60  * $FreeBSD$
61  */
62 
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/kernel.h>
66 #include <sys/queue.h>
67 #include <sys/bus.h>
68 #include <sys/interrupt.h>
69 #include <sys/ktr.h>
70 #include <sys/lock.h>
71 #include <sys/malloc.h>
72 #include <sys/mutex.h>
73 #include <sys/pcpu.h>
74 #include <sys/smp.h>
75 #include <sys/syslog.h>
76 #include <sys/vmmeter.h>
77 #include <sys/proc.h>
78 
79 #include <machine/frame.h>
80 #include <machine/intr_machdep.h>
81 #include <machine/md_var.h>
82 #include <machine/smp.h>
83 #include <machine/trap.h>
84 
85 #include "pic_if.h"
86 
87 #define	MAX_STRAY_LOG	5
88 
89 MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data");
90 
91 struct powerpc_intr {
92 	struct intr_event *event;
93 	long	*cntp;
94 	u_int	irq;
95 	device_t pic;
96 	u_int	intline;
97 	u_int	vector;
98 	u_int	cntindex;
99 	cpumask_t cpu;
100 	enum intr_trigger trig;
101 	enum intr_polarity pol;
102 };
103 
104 struct pic {
105 	device_t pic;
106 	uint32_t pic_id;
107 	int ipi_irq;
108 };
109 
110 static u_int intrcnt_index = 0;
111 static struct mtx intr_table_lock;
112 static struct powerpc_intr *powerpc_intrs[INTR_VECTORS];
113 static struct pic piclist[MAX_PICS];
114 static u_int nvectors;		/* Allocated vectors */
115 static u_int npics;		/* PICs registered */
116 static u_int stray_count;
117 
118 device_t root_pic;
119 
120 #ifdef SMP
121 static void *ipi_cookie;
122 #endif
123 
124 static void
125 intr_init(void *dummy __unused)
126 {
127 
128 	mtx_init(&intr_table_lock, "intr sources lock", NULL, MTX_DEF);
129 }
130 SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);
131 
132 #ifdef SMP
133 static void
134 smp_intr_init(void *dummy __unused)
135 {
136 	struct powerpc_intr *i;
137 	int vector;
138 
139 	for (vector = 0; vector < nvectors; vector++) {
140 		i = powerpc_intrs[vector];
141 		if (i != NULL && i->pic == root_pic)
142 			PIC_BIND(i->pic, i->intline, i->cpu);
143 	}
144 }
145 
146 SYSINIT(smp_intr_init, SI_SUB_SMP, SI_ORDER_ANY, smp_intr_init, NULL);
147 #endif
148 
149 static void
150 intrcnt_setname(const char *name, int index)
151 {
152 
153 	snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
154 	    MAXCOMLEN, name);
155 }
156 
157 void
158 intrcnt_add(const char *name, u_long **countp)
159 {
160 	int idx;
161 
162 	idx = atomic_fetchadd_int(&intrcnt_index, 1);
163 	*countp = &intrcnt[idx];
164 	intrcnt_setname(name, idx);
165 }
166 
167 static struct powerpc_intr *
168 intr_lookup(u_int irq)
169 {
170 	char intrname[8];
171 	struct powerpc_intr *i, *iscan;
172 	int vector;
173 
174 	mtx_lock(&intr_table_lock);
175 	for (vector = 0; vector < nvectors; vector++) {
176 		i = powerpc_intrs[vector];
177 		if (i != NULL && i->irq == irq) {
178 			mtx_unlock(&intr_table_lock);
179 			return (i);
180 		}
181 	}
182 
183 	i = malloc(sizeof(*i), M_INTR, M_NOWAIT);
184 	if (i == NULL) {
185 		mtx_unlock(&intr_table_lock);
186 		return (NULL);
187 	}
188 
189 	i->event = NULL;
190 	i->cntp = NULL;
191 	i->trig = INTR_TRIGGER_CONFORM;
192 	i->pol = INTR_POLARITY_CONFORM;
193 	i->irq = irq;
194 	i->pic = NULL;
195 	i->vector = -1;
196 
197 #ifdef SMP
198 	i->cpu = all_cpus;
199 #else
200 	i->cpu = 1;
201 #endif
202 
203 	for (vector = 0; vector < INTR_VECTORS && vector <= nvectors;
204 	    vector++) {
205 		iscan = powerpc_intrs[vector];
206 		if (iscan != NULL && iscan->irq == irq)
207 			break;
208 		if (iscan == NULL && i->vector == -1)
209 			i->vector = vector;
210 		iscan = NULL;
211 	}
212 
213 	if (iscan == NULL && i->vector != -1) {
214 		powerpc_intrs[i->vector] = i;
215 		i->cntindex = atomic_fetchadd_int(&intrcnt_index, 1);
216 		i->cntp = &intrcnt[i->cntindex];
217 		sprintf(intrname, "irq%u:", i->irq);
218 		intrcnt_setname(intrname, i->cntindex);
219 		nvectors++;
220 	}
221 	mtx_unlock(&intr_table_lock);
222 
223 	if (iscan != NULL || i->vector == -1) {
224 		free(i, M_INTR);
225 		i = iscan;
226 	}
227 
228 	return (i);
229 }
230 
231 static int
232 powerpc_map_irq(struct powerpc_intr *i)
233 {
234 
235 	i->intline = INTR_INTLINE(i->irq);
236 	i->pic = piclist[INTR_IGN(i->irq)].pic;
237 
238 	/* Try a best guess if that failed */
239 	if (i->pic == NULL)
240 		i->pic = root_pic;
241 
242 	return (0);
243 }
244 
245 static void
246 powerpc_intr_eoi(void *arg)
247 {
248 	struct powerpc_intr *i = arg;
249 
250 	PIC_EOI(i->pic, i->intline);
251 }
252 
253 static void
254 powerpc_intr_pre_ithread(void *arg)
255 {
256 	struct powerpc_intr *i = arg;
257 
258 	PIC_MASK(i->pic, i->intline);
259 	PIC_EOI(i->pic, i->intline);
260 }
261 
262 static void
263 powerpc_intr_post_ithread(void *arg)
264 {
265 	struct powerpc_intr *i = arg;
266 
267 	PIC_UNMASK(i->pic, i->intline);
268 }
269 
270 static int
271 powerpc_assign_intr_cpu(void *arg, u_char cpu)
272 {
273 #ifdef SMP
274 	struct powerpc_intr *i = arg;
275 
276 	if (cpu == NOCPU)
277 		i->cpu = all_cpus;
278 	else
279 		i->cpu = 1 << cpu;
280 
281 	if (!cold && i->pic != NULL && i->pic == root_pic)
282 		PIC_BIND(i->pic, i->intline, i->cpu);
283 
284 	return (0);
285 #else
286 	return (EOPNOTSUPP);
287 #endif
288 }
289 
290 void
291 powerpc_register_pic(device_t dev, u_int ipi)
292 {
293 	int i;
294 
295 	mtx_lock(&intr_table_lock);
296 
297 	for (i = 0; i < npics; i++) {
298 		if (piclist[i].pic_id == PIC_ID(dev))
299 			break;
300 	}
301 	piclist[i].pic = dev;
302 	piclist[i].pic_id = PIC_ID(dev);
303 	piclist[i].ipi_irq = ipi;
304 	if (i == npics)
305 		npics++;
306 
307 	mtx_unlock(&intr_table_lock);
308 }
309 
310 int
311 powerpc_ign_lookup(uint32_t pic_id)
312 {
313 	int i;
314 
315 	mtx_lock(&intr_table_lock);
316 
317 	for (i = 0; i < npics; i++) {
318 		if (piclist[i].pic_id == pic_id) {
319 			mtx_unlock(&intr_table_lock);
320 			return (i);
321 		}
322 	}
323 	piclist[i].pic = NULL;
324 	piclist[i].pic_id = pic_id;
325 	piclist[i].ipi_irq = 0;
326 	npics++;
327 
328 	mtx_unlock(&intr_table_lock);
329 
330 	return (i);
331 }
332 
333 int
334 powerpc_enable_intr(void)
335 {
336 	struct powerpc_intr *i;
337 	int error, vector;
338 #ifdef SMP
339 	int n;
340 #endif
341 
342 	if (npics == 0)
343 		panic("no PIC detected\n");
344 
345 #ifdef SMP
346 	/* Install an IPI handler. */
347 
348 	for (n = 0; n < npics; n++) {
349 		if (piclist[n].pic != root_pic)
350 			continue;
351 
352 		error = powerpc_setup_intr("IPI",
353 		    INTR_VEC(piclist[n].pic_id, piclist[n].ipi_irq),
354 		    powerpc_ipi_handler, NULL, NULL,
355 		    INTR_TYPE_MISC | INTR_EXCL | INTR_FAST, &ipi_cookie);
356 		if (error) {
357 			printf("unable to setup IPI handler\n");
358 			return (error);
359 		}
360 	}
361 #endif
362 
363 	for (vector = 0; vector < nvectors; vector++) {
364 		i = powerpc_intrs[vector];
365 		if (i == NULL)
366 			continue;
367 
368 		error = powerpc_map_irq(i);
369 		if (error)
370 			continue;
371 
372 		if (i->trig != INTR_TRIGGER_CONFORM ||
373 		    i->pol != INTR_POLARITY_CONFORM)
374 			PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
375 
376 		if (i->event != NULL)
377 			PIC_ENABLE(i->pic, i->intline, vector);
378 	}
379 
380 	return (0);
381 }
382 
383 int
384 powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter,
385     driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep)
386 {
387 	struct powerpc_intr *i;
388 	int error, enable = 0;
389 
390 	i = intr_lookup(irq);
391 	if (i == NULL)
392 		return (ENOMEM);
393 
394 	if (i->event == NULL) {
395 		error = intr_event_create(&i->event, (void *)i, 0, irq,
396 		    powerpc_intr_pre_ithread, powerpc_intr_post_ithread,
397 		    powerpc_intr_eoi, powerpc_assign_intr_cpu, "irq%u:", irq);
398 		if (error)
399 			return (error);
400 
401 		enable = 1;
402 	}
403 
404 	error = intr_event_add_handler(i->event, name, filter, handler, arg,
405 	    intr_priority(flags), flags, cookiep);
406 
407 	mtx_lock(&intr_table_lock);
408 	intrcnt_setname(i->event->ie_fullname, i->cntindex);
409 	mtx_unlock(&intr_table_lock);
410 
411 	if (!cold) {
412 		error = powerpc_map_irq(i);
413 
414 		if (!error && (i->trig != INTR_TRIGGER_CONFORM ||
415 		    i->pol != INTR_POLARITY_CONFORM))
416 			PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
417 
418 		if (!error && i->pic == root_pic)
419 			PIC_BIND(i->pic, i->intline, i->cpu);
420 
421 		if (!error && enable)
422 			PIC_ENABLE(i->pic, i->intline, i->vector);
423 	}
424 	return (error);
425 }
426 
427 int
428 powerpc_teardown_intr(void *cookie)
429 {
430 
431 	return (intr_event_remove_handler(cookie));
432 }
433 
434 #ifdef SMP
435 int
436 powerpc_bind_intr(u_int irq, u_char cpu)
437 {
438 	struct powerpc_intr *i;
439 
440 	i = intr_lookup(irq);
441 	if (i == NULL)
442 		return (ENOMEM);
443 
444 	return (intr_event_bind(i->event, cpu));
445 }
446 #endif
447 
448 int
449 powerpc_config_intr(int irq, enum intr_trigger trig, enum intr_polarity pol)
450 {
451 	struct powerpc_intr *i;
452 
453 	i = intr_lookup(irq);
454 	if (i == NULL)
455 		return (ENOMEM);
456 
457 	i->trig = trig;
458 	i->pol = pol;
459 
460 	if (!cold && i->pic != NULL)
461 		PIC_CONFIG(i->pic, i->intline, trig, pol);
462 
463 	return (0);
464 }
465 
466 void
467 powerpc_dispatch_intr(u_int vector, struct trapframe *tf)
468 {
469 	struct powerpc_intr *i;
470 	struct intr_event *ie;
471 
472 	i = powerpc_intrs[vector];
473 	if (i == NULL)
474 		goto stray;
475 
476 	(*i->cntp)++;
477 
478 	ie = i->event;
479 	KASSERT(ie != NULL, ("%s: interrupt without an event", __func__));
480 
481 	if (intr_event_handle(ie, tf) != 0) {
482 		goto stray;
483 	}
484 	return;
485 
486 stray:
487 	stray_count++;
488 	if (stray_count <= MAX_STRAY_LOG) {
489 		printf("stray irq %d\n", i ? i->irq : -1);
490 		if (stray_count >= MAX_STRAY_LOG) {
491 			printf("got %d stray interrupts, not logging anymore\n",
492 			    MAX_STRAY_LOG);
493 		}
494 	}
495 	if (i != NULL)
496 		PIC_MASK(i->pic, i->intline);
497 }
498