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