xref: /openbsd-src/sys/arch/macppc/dev/macintr.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: macintr.c,v 1.6 2001/11/06 19:53:15 miod Exp $	*/
2 
3 /*-
4  * Copyright (c) 1995 Per Fogelstrom
5  * Copyright (c) 1993, 1994 Charles M. Hannum.
6  * Copyright (c) 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * William Jolitz and Don Ahn.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	@(#)isa.c	7.2 (Berkeley) 5/12/91
41  */
42 
43 #include <sys/param.h>
44 #include <sys/device.h>
45 #include <sys/ioctl.h>
46 #include <sys/mbuf.h>
47 #include <sys/socket.h>
48 #include <sys/systm.h>
49 
50 #include <uvm/uvm.h>
51 
52 #include <machine/autoconf.h>
53 #include <machine/intr.h>
54 #include <machine/psl.h>
55 #include <machine/pio.h>
56 #include <machine/powerpc.h>
57 
58 #include <dev/ofw/openfirm.h>
59 
60 #define ICU_LEN 64
61 #define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))
62 
63 static int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN];
64 static struct intrhand *intrhand[ICU_LEN];
65 static int hwirq[ICU_LEN], virq[64];
66 unsigned int imen = 0xffffffff;
67 int virq_max = 0;
68 
69 struct evcnt evirq[ICU_LEN*2];
70 
71 static int fakeintr __P((void *));
72 static char *intr_typename __P((int type));
73 static void intr_calculatemasks __P((void));
74 static void enable_irq __P((int x));
75 static __inline int cntlzw __P((int x));
76 static int mapirq __P((int irq));
77 static int read_irq __P((void));
78 static void mac_intr_do_pending_int __P((void));
79 
80 extern u_int32_t *heathrow_FCR;
81 
82 #define HWIRQ_MAX 27
83 #define HWIRQ_MASK 0x0fffffff
84 
85 #define INT_STATE_REG0  (interrupt_reg + 0x20)
86 #define INT_ENABLE_REG0 (interrupt_reg + 0x24)
87 #define INT_CLEAR_REG0  (interrupt_reg + 0x28)
88 #define INT_LEVEL_REG0  (interrupt_reg + 0x2c)
89 #define INT_STATE_REG1  (INT_STATE_REG0  - 0x10)
90 #define INT_ENABLE_REG1 (INT_ENABLE_REG0 - 0x10)
91 #define INT_CLEAR_REG1  (INT_CLEAR_REG0  - 0x10)
92 #define INT_LEVEL_REG1  (INT_LEVEL_REG0  - 0x10)
93 
94 struct macintr_softc {
95 	struct device sc_dev;
96 };
97 
98 int	macintr_match __P((struct device *parent, void *cf, void *aux));
99 void	macintr_attach __P((struct device *, struct device *, void *));
100 void	mac_do_pending_int __P((void));
101 void	mac_ext_intr __P((void));
102 
103 struct cfattach macintr_ca = {
104 	sizeof(struct macintr_softc),
105 	macintr_match,
106 	macintr_attach
107 };
108 
109 struct cfdriver macintr_cd = {
110 	NULL, "macintr", DV_DULL
111 };
112 
113 int
114 macintr_match(parent, cf, aux)
115 	struct device *parent;
116 	void *cf;
117 	void *aux;
118 {
119 	struct confargs *ca = aux;
120 	char type[40];
121 
122 	/*
123 	 * Match entry according to "present" openfirmware entry.
124 	 */
125 	if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) {
126 		OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
127 		if (strcmp(type,  "interrupt-controller") == 0) {
128 			return 1;
129 		}
130 	}
131 
132 	/*
133 	 * Check name for legacy interrupt controller, this is
134 	 * faked to allow old firmware which does not have an entry
135 	 * to attach to this device.
136 	 */
137 	if (strcmp(ca->ca_name, "legacy-interrupt-controller") == 0 ) {
138 		return 1;
139 	}
140 	return 0;
141 }
142 
143 u_int8_t *interrupt_reg;
144 typedef void  (void_f) (void);
145 extern void_f *pending_int_f;
146 int macintr_prog_button (void *arg);
147 
148 intr_establish_t macintr_establish;
149 intr_disestablish_t macintr_disestablish;
150 extern intr_establish_t *mac_intr_establish_func;
151 extern intr_disestablish_t *mac_intr_disestablish_func;
152 void macintr_collect_preconf_intr __P((void));
153 
154 void
155 macintr_attach(parent, self, aux)
156 	struct device *parent, *self;
157 	void *aux;
158 {
159 	struct confargs *ca = aux;
160 	extern intr_establish_t *intr_establish_func;
161 	extern intr_disestablish_t *intr_disestablish_func;
162 
163 	interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */
164 
165 	install_extint(mac_ext_intr);
166 	pending_int_f = mac_intr_do_pending_int;
167 	intr_establish_func  = macintr_establish;
168 	intr_disestablish_func  = macintr_disestablish;
169 	mac_intr_establish_func  = macintr_establish;
170 	mac_intr_disestablish_func  = macintr_disestablish;
171 
172 	macintr_collect_preconf_intr();
173 
174 	mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH,
175 		macintr_prog_button, (void *)0x14, "prog button");
176 
177 	ppc_intr_enable(1);
178 
179 	printf("\n");
180 }
181 void
182 macintr_collect_preconf_intr()
183 {
184 	int i;
185 	for (i = 0; i < ppc_configed_intr_cnt; i++) {
186 		printf("\n\t%s irq %d level %d fun %x arg %x",
187 			ppc_configed_intr[i].ih_what,
188 			ppc_configed_intr[i].ih_irq,
189 			ppc_configed_intr[i].ih_level,
190 			ppc_configed_intr[i].ih_fun,
191 			ppc_configed_intr[i].ih_arg
192 			);
193 		macintr_establish(NULL,
194 			ppc_configed_intr[i].ih_irq,
195 			IST_LEVEL,
196 			ppc_configed_intr[i].ih_level,
197 			ppc_configed_intr[i].ih_fun,
198 			ppc_configed_intr[i].ih_arg,
199 			ppc_configed_intr[i].ih_what);
200 	}
201 }
202 
203 
204 /*
205  * programmer_button function to fix args to Debugger.
206  * deal with any enables/disables, if necessary.
207  */
208 int
209 macintr_prog_button (void *arg)
210 {
211 #ifdef DDB
212         Debugger();
213 #else
214 	printf("programmer button pressed, debugger not available\n");
215 #endif
216 	return 1;
217 }
218 
219 static int
220 fakeintr(arg)
221 	void *arg;
222 {
223 
224 	return 0;
225 }
226 
227 void nameinterrupt( int replace, char *newstr);
228 
229 /*
230  * Register an interrupt handler.
231  */
232 void *
233 macintr_establish(lcv, irq, type, level, ih_fun, ih_arg, name)
234 	void * lcv;
235 	int irq;
236 	int type;
237 	int level;
238 	int (*ih_fun) __P((void *));
239 	void *ih_arg;
240 	char *name;
241 {
242 	struct intrhand **p, *q, *ih;
243 	static struct intrhand fakehand;
244 
245 	fakehand.ih_next = NULL;
246 	fakehand.ih_fun  = fakeintr;
247 
248 #if 0
249 printf("macintr_establish, hI %d L %d ", irq, type);
250 printf("addr reg0 %x\n", INT_STATE_REG0);
251 #endif
252 	nameinterrupt(irq, name);
253 	irq = mapirq(irq);
254 #if 0
255 printf("vI %d ", irq);
256 #endif
257 
258 	/* no point in sleeping unless someone can free memory. */
259 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
260 	if (ih == NULL)
261 		panic("intr_establish: can't malloc handler info");
262 
263 	if (!LEGAL_IRQ(irq) || type == IST_NONE)
264 		panic("intr_establish: bogus irq or type");
265 
266 	switch (intrtype[irq]) {
267 	case IST_NONE:
268 		intrtype[irq] = type;
269 		break;
270 	case IST_EDGE:
271 	case IST_LEVEL:
272 		if (type == intrtype[irq])
273 			break;
274 	case IST_PULSE:
275 		if (type != IST_NONE)
276 			panic("intr_establish: can't share %s with %s",
277 			    intr_typename(intrtype[irq]),
278 			    intr_typename(type));
279 		break;
280 	}
281 
282 	/*
283 	 * Figure out where to put the handler.
284 	 * This is O(N^2), but we want to preserve the order, and N is
285 	 * generally small.
286 	 */
287 	for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next)
288 		;
289 
290 	/*
291 	 * Actually install a fake handler momentarily, since we might be doing
292 	 * this with interrupts enabled and DON'T WANt the real routine called
293 	 * until masking is set up.
294 	 */
295 	fakehand.ih_level = level;
296 	*p = &fakehand;
297 
298 	intr_calculatemasks();
299 
300 	/*
301 	 * Poke the real handler in now.
302 	 */
303 	ih->ih_fun = ih_fun;
304 	ih->ih_arg = ih_arg;
305 	ih->ih_count = 0;
306 	ih->ih_next = NULL;
307 	ih->ih_level = level;
308 	ih->ih_irq = irq;
309 	*p = ih;
310 
311 	return (ih);
312 }
313 
314 /*
315  * Deregister an interrupt handler.
316  */
317 void
318 macintr_disestablish(lcp, arg)
319 	void *lcp;
320 	void *arg;
321 {
322 	struct intrhand *ih = arg;
323 	int irq = ih->ih_irq;
324 	struct intrhand **p, *q;
325 
326 	if (!LEGAL_IRQ(irq))
327 		panic("intr_disestablish: bogus irq");
328 
329 	/*
330 	 * Remove the handler from the chain.
331 	 * This is O(n^2), too.
332 	 */
333 	for (p = &intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next)
334 		;
335 	if (q)
336 		*p = q->ih_next;
337 	else
338 		panic("intr_disestablish: handler not registered");
339 	free((void *)ih, M_DEVBUF);
340 
341 	intr_calculatemasks();
342 
343 	if (intrhand[irq] == NULL)
344 		intrtype[irq] = IST_NONE;
345 }
346 
347 
348 static char *
349 intr_typename(type)
350 	int type;
351 {
352 
353 	switch (type) {
354         case IST_NONE :
355 		return ("none");
356         case IST_PULSE:
357 		return ("pulsed");
358         case IST_EDGE:
359 		return ("edge-triggered");
360         case IST_LEVEL:
361 		return ("level-triggered");
362 	default:
363 		panic("intr_typename: invalid type %d", type);
364 #if 1 /* XXX */
365 		return ("unknown");
366 #endif
367 	}
368 }
369 /*
370  * Recalculate the interrupt masks from scratch.
371  * We could code special registry and deregistry versions of this function that
372  * would be faster, but the code would be nastier, and we don't expect this to
373  * happen very much anyway.
374  */
375 static void
376 intr_calculatemasks()
377 {
378 	int irq, level;
379 	struct intrhand *q;
380 
381 	/* First, figure out which levels each IRQ uses. */
382 	for (irq = 0; irq < ICU_LEN; irq++) {
383 		register int levels = 0;
384 		for (q = intrhand[irq]; q; q = q->ih_next)
385 			levels |= 1 << q->ih_level;
386 		intrlevel[irq] = levels;
387 	}
388 
389 	/* Then figure out which IRQs use each level. */
390 	for (level = 0; level < 5; level++) {
391 		register int irqs = 0;
392 		for (irq = 0; irq < ICU_LEN; irq++)
393 			if (intrlevel[irq] & (1 << level))
394 				irqs |= 1 << irq;
395 		imask[level] = irqs | SINT_MASK;
396 	}
397 
398 	/*
399 	 * There are tty, network and disk drivers that use free() at interrupt
400 	 * time, so imp > (tty | net | bio).
401 	 */
402 	imask[IPL_IMP] |= imask[IPL_TTY] | imask[IPL_NET] | imask[IPL_BIO];
403 
404 	/*
405 	 * Enforce a hierarchy that gives slow devices a better chance at not
406 	 * dropping data.
407 	 */
408 	imask[IPL_TTY] |= imask[IPL_NET] | imask[IPL_BIO];
409 	imask[IPL_NET] |= imask[IPL_BIO];
410 
411 	/*
412 	 * These are pseudo-levels.
413 	 */
414 	imask[IPL_NONE] = 0x00000000;
415 	imask[IPL_HIGH] = 0xffffffff;
416 
417 	/* And eventually calculate the complete masks. */
418 	for (irq = 0; irq < ICU_LEN; irq++) {
419 		register int irqs = 1 << irq;
420 		for (q = intrhand[irq]; q; q = q->ih_next)
421 			irqs |= imask[q->ih_level];
422 		intrmask[irq] = irqs | SINT_MASK;
423 	}
424 
425 	/* Lastly, determine which IRQs are actually in use. */
426 	{
427 		register int irqs = 0;
428 		for (irq = 0; irq < ICU_LEN; irq++) {
429 			if (intrhand[irq])
430 				irqs |= 1 << irq;
431 		}
432 		imen = ~irqs;
433 		enable_irq(~imen);
434 	}
435 }
436 static void
437 enable_irq(x)
438 	int x;
439 {
440 	int state0, state1, v;
441 	int irq;
442 
443 	x &= HWIRQ_MASK;	/* XXX Higher bits are software interrupts. */
444 
445 	state0 = state1 = 0;
446 	while (x) {
447 		v = 31 - cntlzw(x);
448 		irq = hwirq[v];
449 		if (irq < 32) {
450 			state0 |= 1 << irq;
451 		} else {
452 			state1 |= 1 << (irq - 32);
453 		}
454 		x &= ~(1 << v);
455 	}
456 
457 	if (heathrow_FCR) {
458 		out32rb(INT_ENABLE_REG1, state1);
459 	}
460 	out32rb(INT_ENABLE_REG0, state0);
461 }
462 /*
463  * Map 64 irqs into 32 (bits).
464  */
465 static int
466 mapirq(irq)
467 	int irq;
468 {
469 	int v;
470 
471 	if (irq < 0 || irq >= 64)
472 		panic("invalid irq");
473 	virq_max++;
474 	v = virq_max;
475 	if (v > HWIRQ_MAX)
476 		panic("virq overflow");
477 
478 	hwirq[v] = irq;
479 	virq[irq] = v;
480 #if 0
481 printf("\nmapirq %x to %x\n", irq, v);
482 #endif
483 
484 	return v;
485 }
486 
487 /*
488  * Count leading zeros.
489  */
490 static __inline int
491 cntlzw(x)
492 	int x;
493 {
494 	int a;
495 
496 	__asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x));
497 
498 	return a;
499 }
500 
501 /*
502  * external interrupt handler
503  */
504 void
505 mac_ext_intr()
506 {
507 	int irq = 0;
508 	int o_imen, r_imen;
509 	int pcpl;
510 	struct intrhand *ih;
511 	volatile unsigned long int_state;
512 
513 	pcpl = splhigh();	/* Turn off all */
514 
515 #if 0
516 printf("mac_intr \n");
517 #endif
518 	int_state = read_irq();
519 	if (int_state == 0)
520 		goto out;
521 
522 start:
523 	irq = 31 - cntlzw(int_state);
524 	intrcnt[hwirq[irq]]++;
525 
526 	o_imen = imen;
527 	r_imen = 1 << irq;
528 
529 	if ((pcpl & r_imen) != 0) {
530 		ipending |= r_imen;	/* Masked! Mark this as pending */
531 		imen |= r_imen;
532 		enable_irq(~imen);
533 	} else {
534 		ih = intrhand[irq];
535 		while (ih) {
536 #if 0
537 printf("calling handler %x\n", ih->ih_fun);
538 #endif
539 			(*ih->ih_fun)(ih->ih_arg);
540 			ih = ih->ih_next;
541 		}
542 
543 		uvmexp.intrs++;
544 		evirq[hwirq[irq]].ev_count++;
545 	}
546 	int_state &= ~r_imen;
547 	if (int_state)
548 		goto start;
549 
550 out:
551 	splx(pcpl);	/* Process pendings. */
552 }
553 
554 void
555 mac_intr_do_pending_int()
556 {
557 	struct intrhand *ih;
558 	int irq;
559 	int pcpl;
560 	int hwpend;
561 	int emsr, dmsr;
562 	static int processing;
563 
564 	if (processing)
565 		return;
566 
567 	processing = 1;
568 	pcpl = splhigh();		/* Turn off all */
569 	asm volatile("mfmsr %0" : "=r"(emsr));
570 	dmsr = emsr & ~PSL_EE;
571 	asm volatile("mtmsr %0" :: "r"(dmsr));
572 
573 	hwpend = ipending & ~pcpl;	/* Do now unmasked pendings */
574 	imen &= ~hwpend;
575 	enable_irq(~imen);
576 	hwpend &= HWIRQ_MASK;
577 	while (hwpend) {
578 		irq = 31 - cntlzw(hwpend);
579 		hwpend &= ~(1L << irq);
580 		ih = intrhand[irq];
581 		while(ih) {
582 			(*ih->ih_fun)(ih->ih_arg);
583 			ih = ih->ih_next;
584 		}
585 
586 		evirq[hwirq[irq]].ev_count++;
587 	}
588 
589 	/*out32rb(INT_ENABLE_REG, ~imen);*/
590 
591 	do {
592 		if((ipending & SINT_CLOCK) & ~pcpl) {
593 			ipending &= ~SINT_CLOCK;
594 			softclock();
595 		}
596 		if((ipending & SINT_NET) & ~pcpl) {
597 			extern int netisr;
598 			int pisr = netisr;
599 			netisr = 0;
600 			ipending &= ~SINT_NET;
601 			softnet(pisr);
602 		}
603 		if((ipending & SINT_TTY) & ~pcpl) {
604 			ipending &= ~SINT_TTY;
605 			softtty();
606 		}
607 	} while (ipending & (SINT_NET|SINT_CLOCK|SINT_TTY) & ~cpl);
608 	ipending &= pcpl;
609 	cpl = pcpl;	/* Don't use splx... we are here already! */
610 	asm volatile("mtmsr %0" :: "r"(emsr));
611 	processing = 0;
612 }
613 
614 static int
615 read_irq()
616 {
617 	int rv = 0;
618 	int state0, state1, p;
619 	int state0save, state1save;
620 
621 	state0 = in32rb(INT_STATE_REG0);
622 	if (state0)
623 		out32rb(INT_CLEAR_REG0, state0);
624 		state0save = state0;
625 	while (state0) {
626 		p = 31 - cntlzw(state0);
627 		rv |= 1 << virq[p];
628 		state0 &= ~(1 << p);
629 	}
630 
631 	if (heathrow_FCR)			/* has heathrow? */
632 		state1 = in32rb(INT_STATE_REG1);
633 	else
634 		state1 = 0;
635 
636 	if (state1)
637 		out32rb(INT_CLEAR_REG1, state1);
638 	state1save = state1;
639 	while (state1) {
640 		p = 31 - cntlzw(state1);
641 		rv |= 1 << virq[p + 32];
642 		state1 &= ~(1 << p);
643 	}
644 #if 0
645 printf("mac_intr int_stat 0:%x 1:%x\n", state0save, state1save);
646 #endif
647 
648 	/* 1 << 0 is invalid. */
649 	return rv & ~1;
650 }
651