xref: /openbsd-src/sys/arch/macppc/dev/macintr.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: macintr.c,v 1.18 2003/07/02 21:30:13 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(parent, cf, aux)
112 	struct device *parent;
113 	void *cf;
114 	void *aux;
115 {
116 	struct confargs *ca = aux;
117 	char type[40];
118 
119 	/*
120 	 * Match entry according to "present" openfirmware entry.
121 	 */
122 	if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) {
123 		OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
124 		if (strcmp(type,  "interrupt-controller") == 0) {
125 			return 1;
126 		}
127 	}
128 
129 	/*
130 	 * Check name for legacy interrupt controller, this is
131 	 * faked to allow old firmware which does not have an entry
132 	 * to attach to this device.
133 	 */
134 	if (strcmp(ca->ca_name, "legacy-interrupt-controller") == 0 ) {
135 		return 1;
136 	}
137 	return 0;
138 }
139 
140 u_int8_t *interrupt_reg;
141 typedef void  (void_f) (void);
142 extern void_f *pending_int_f;
143 int macintr_prog_button (void *arg);
144 
145 intr_establish_t macintr_establish;
146 intr_disestablish_t macintr_disestablish;
147 extern intr_establish_t *mac_intr_establish_func;
148 extern intr_disestablish_t *mac_intr_disestablish_func;
149 void macintr_collect_preconf_intr(void);
150 
151 void
152 macintr_attach(parent, self, aux)
153 	struct device *parent, *self;
154 	void *aux;
155 {
156 	struct confargs *ca = aux;
157 	extern intr_establish_t *intr_establish_func;
158 	extern intr_disestablish_t *intr_disestablish_func;
159 
160 	interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */
161 
162 	install_extint(mac_ext_intr);
163 	pending_int_f = mac_intr_do_pending_int;
164 	intr_establish_func  = macintr_establish;
165 	intr_disestablish_func  = macintr_disestablish;
166 	mac_intr_establish_func  = macintr_establish;
167 	mac_intr_disestablish_func  = macintr_disestablish;
168 
169 	macintr_collect_preconf_intr();
170 
171 	mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH,
172 		macintr_prog_button, (void *)0x14, "prog button");
173 
174 	ppc_intr_enable(1);
175 
176 	printf("\n");
177 }
178 void
179 macintr_collect_preconf_intr()
180 {
181 	int i;
182 	for (i = 0; i < ppc_configed_intr_cnt; i++) {
183 		printf("\n\t%s irq %d level %d fun %x arg %x",
184 			ppc_configed_intr[i].ih_what,
185 			ppc_configed_intr[i].ih_irq,
186 			ppc_configed_intr[i].ih_level,
187 			ppc_configed_intr[i].ih_fun,
188 			ppc_configed_intr[i].ih_arg
189 			);
190 		macintr_establish(NULL,
191 			ppc_configed_intr[i].ih_irq,
192 			IST_LEVEL,
193 			ppc_configed_intr[i].ih_level,
194 			ppc_configed_intr[i].ih_fun,
195 			ppc_configed_intr[i].ih_arg,
196 			ppc_configed_intr[i].ih_what);
197 	}
198 }
199 
200 
201 /*
202  * programmer_button function to fix args to Debugger.
203  * deal with any enables/disables, if necessary.
204  */
205 int
206 macintr_prog_button (void *arg)
207 {
208 #ifdef DDB
209 	if (db_console)
210 		Debugger();
211 #else
212 	printf("programmer button pressed, debugger not available\n");
213 #endif
214 	return 1;
215 }
216 
217 static int
218 fakeintr(arg)
219 	void *arg;
220 {
221 
222 	return 0;
223 }
224 
225 void nameinterrupt( int replace, char *newstr);
226 
227 /*
228  * Register an interrupt handler.
229  */
230 void *
231 macintr_establish(lcv, irq, type, level, ih_fun, ih_arg, name)
232 	void * lcv;
233 	int irq;
234 	int type;
235 	int level;
236 	int (*ih_fun)(void *);
237 	void *ih_arg;
238 	char *name;
239 {
240 	struct intrhand **p, *q, *ih;
241 	static struct intrhand fakehand;
242 
243 	fakehand.ih_next = NULL;
244 	fakehand.ih_fun  = fakeintr;
245 
246 #if 0
247 printf("macintr_establish, hI %d L %d ", irq, type);
248 printf("addr reg0 %x\n", INT_STATE_REG0);
249 #endif
250 	nameinterrupt(irq, name);
251 	irq = mapirq(irq);
252 #if 0
253 printf("vI %d ", irq);
254 #endif
255 
256 	/* no point in sleeping unless someone can free memory. */
257 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
258 	if (ih == NULL)
259 		panic("intr_establish: can't malloc handler info");
260 
261 	if (!LEGAL_IRQ(irq) || type == IST_NONE)
262 		panic("intr_establish: bogus irq or type");
263 
264 	switch (m_intrtype[irq]) {
265 	case IST_NONE:
266 		m_intrtype[irq] = type;
267 		break;
268 	case IST_EDGE:
269 	case IST_LEVEL:
270 		if (type == m_intrtype[irq])
271 			break;
272 	case IST_PULSE:
273 		if (type != IST_NONE)
274 			panic("intr_establish: can't share %s with %s",
275 			    intr_typename(m_intrtype[irq]),
276 			    intr_typename(type));
277 		break;
278 	}
279 
280 	/*
281 	 * Figure out where to put the handler.
282 	 * This is O(N^2), but we want to preserve the order, and N is
283 	 * generally small.
284 	 */
285 	for (p = &m_intrhand[irq]; (q = *p) != NULL; p = &q->ih_next)
286 		;
287 
288 	/*
289 	 * Actually install a fake handler momentarily, since we might be doing
290 	 * this with interrupts enabled and DON'T WANt the real routine called
291 	 * until masking is set up.
292 	 */
293 	fakehand.ih_level = level;
294 	*p = &fakehand;
295 
296 	intr_calculatemasks();
297 
298 	/*
299 	 * Poke the real handler in now.
300 	 */
301 	ih->ih_fun = ih_fun;
302 	ih->ih_arg = ih_arg;
303 	ih->ih_count = 0;
304 	ih->ih_next = NULL;
305 	ih->ih_level = level;
306 	ih->ih_irq = irq;
307 	*p = ih;
308 
309 	return (ih);
310 }
311 
312 /*
313  * Deregister an interrupt handler.
314  */
315 void
316 macintr_disestablish(lcp, arg)
317 	void *lcp;
318 	void *arg;
319 {
320 	struct intrhand *ih = arg;
321 	int irq = ih->ih_irq;
322 	struct intrhand **p, *q;
323 
324 	if (!LEGAL_IRQ(irq))
325 		panic("intr_disestablish: bogus irq");
326 
327 	/*
328 	 * Remove the handler from the chain.
329 	 * This is O(n^2), too.
330 	 */
331 	for (p = &m_intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next)
332 		;
333 	if (q)
334 		*p = q->ih_next;
335 	else
336 		panic("intr_disestablish: handler not registered");
337 	free((void *)ih, M_DEVBUF);
338 
339 	intr_calculatemasks();
340 
341 	if (m_intrhand[irq] == NULL)
342 		m_intrtype[irq] = IST_NONE;
343 }
344 
345 
346 static char *
347 intr_typename(type)
348 	int type;
349 {
350 
351 	switch (type) {
352         case IST_NONE :
353 		return ("none");
354         case IST_PULSE:
355 		return ("pulsed");
356         case IST_EDGE:
357 		return ("edge-triggered");
358         case IST_LEVEL:
359 		return ("level-triggered");
360 	default:
361 		panic("intr_typename: invalid type %d", type);
362 #if 1 /* XXX */
363 		return ("unknown");
364 #endif
365 	}
366 }
367 /*
368  * Recalculate the interrupt masks from scratch.
369  * We could code special registry and deregistry versions of this function that
370  * would be faster, but the code would be nastier, and we don't expect this to
371  * happen very much anyway.
372  */
373 static void
374 intr_calculatemasks()
375 {
376 	int irq, level;
377 	struct intrhand *q;
378 
379 	/* First, figure out which levels each IRQ uses. */
380 	for (irq = 0; irq < ICU_LEN; irq++) {
381 		register int levels = 0;
382 		for (q = m_intrhand[irq]; q; q = q->ih_next)
383 			levels |= 1 << q->ih_level;
384 		m_intrlevel[irq] = levels;
385 	}
386 
387 	/* Then figure out which IRQs use each level. */
388 	for (level = 0; level < 5; level++) {
389 		register int irqs = 0;
390 		for (irq = 0; irq < ICU_LEN; irq++)
391 			if (m_intrlevel[irq] & (1 << level))
392 				irqs |= 1 << irq;
393 		imask[level] = irqs | SINT_MASK;
394 	}
395 
396 	/*
397 	 * There are tty, network and disk drivers that use free() at interrupt
398 	 * time, so imp > (tty | net | bio).
399 	 *
400 	 * Enforce a hierarchy that gives slow devices a better chance at not
401 	 * dropping data.
402 	 */
403 	imask[IPL_NET] |= imask[IPL_BIO];
404 	imask[IPL_TTY] |= imask[IPL_NET];
405 	imask[IPL_IMP] |= imask[IPL_TTY];
406 	imask[IPL_CLOCK] |= imask[IPL_IMP] | SPL_CLOCK;
407 
408 	/*
409 	 * These are pseudo-levels.
410 	 */
411 	imask[IPL_NONE] = 0x00000000;
412 	imask[IPL_HIGH] = 0xffffffff;
413 
414 	/* And eventually calculate the complete masks. */
415 	for (irq = 0; irq < ICU_LEN; irq++) {
416 		register int irqs = 1 << irq;
417 		for (q = m_intrhand[irq]; q; q = q->ih_next)
418 			irqs |= imask[q->ih_level];
419 		m_intrmask[irq] = irqs | SINT_MASK;
420 	}
421 
422 	/* Lastly, determine which IRQs are actually in use. */
423 	{
424 		register int irqs = 0;
425 		for (irq = 0; irq < ICU_LEN; irq++) {
426 			if (m_intrhand[irq])
427 				irqs |= 1 << irq;
428 		}
429 		imen_m = ~irqs;
430 		enable_irq(~imen_m);
431 	}
432 }
433 static void
434 enable_irq(x)
435 	int x;
436 {
437 	int state0, state1, v;
438 	int irq;
439 
440 	x &= HWIRQ_MASK;	/* XXX Higher bits are software interrupts. */
441 
442 	state0 = state1 = 0;
443 	while (x) {
444 		v = 31 - cntlzw(x);
445 		irq = m_hwirq[v];
446 		if (irq < 32) {
447 			state0 |= 1 << irq;
448 		} else {
449 			state1 |= 1 << (irq - 32);
450 		}
451 		x &= ~(1 << v);
452 	}
453 
454 	if (heathrow_FCR) {
455 		out32rb(INT_ENABLE_REG1, state1);
456 	}
457 	out32rb(INT_ENABLE_REG0, state0);
458 }
459 
460 int m_virq_inited = 0;
461 
462 /*
463  * Map 64 irqs into 32 (bits).
464  */
465 static int
466 mapirq(irq)
467 	int irq;
468 {
469 	int v;
470 	int i;
471 
472 	if (m_virq_inited == 0) {
473 		m_virq_max = 0;
474 		for (i = 0; i < ICU_LEN; i++) {
475 			m_virq[i] = 0;
476 		}
477 		m_virq_inited = 1;
478 	}
479 
480 	/* irq in table already? */
481 	if (m_virq[irq] != 0) {
482 		return m_virq[irq];
483 	}
484 
485 	if (irq < 0 || irq >= 64)
486 		panic("invalid irq");
487 	m_virq_max++;
488 	v = m_virq_max;
489 	if (v > HWIRQ_MAX)
490 		panic("virq overflow");
491 
492 	m_hwirq[v] = irq;
493 	m_virq[irq] = v;
494 #if 0
495 printf("\nmapirq %x to %x\n", irq, v);
496 #endif
497 
498 	return v;
499 }
500 
501 /*
502  * Count leading zeros.
503  */
504 static __inline int
505 cntlzw(x)
506 	int x;
507 {
508 	int a;
509 
510 	__asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x));
511 
512 	return a;
513 }
514 
515 /*
516  * external interrupt handler
517  */
518 void
519 mac_ext_intr()
520 {
521 	int irq = 0;
522 	int o_imen, r_imen;
523 	int pcpl;
524 	struct intrhand *ih;
525 	volatile unsigned long int_state;
526 
527 	pcpl = cpl;	/* Turn off all */
528 
529 	int_state = read_irq();
530 	if (int_state == 0)
531 		goto out;
532 
533 start:
534 	irq = 31 - cntlzw(int_state);
535 	intrcnt[m_hwirq[irq]]++;
536 
537 	o_imen = imen_m;
538 	r_imen = 1 << irq;
539 
540 	if ((cpl & r_imen) != 0) {
541 		ipending |= r_imen;	/* Masked! Mark this as pending */
542 		imen_m |= r_imen;
543 		enable_irq(~imen_m);
544 	} else {
545 		splraise(m_intrmask[irq]);
546 
547 		ih = m_intrhand[irq];
548 		while (ih) {
549 			(*ih->ih_fun)(ih->ih_arg);
550 			ih = ih->ih_next;
551 		}
552 
553 		uvmexp.intrs++;
554 		m_evirq[m_hwirq[irq]].ev_count++;
555 	}
556 	int_state &= ~r_imen;
557 	if (int_state)
558 		goto start;
559 
560 out:
561 	splx(pcpl);	/* Process pendings. */
562 }
563 
564 void
565 mac_intr_do_pending_int()
566 {
567 	struct intrhand *ih;
568 	int irq;
569 	int pcpl;
570 	int hwpend;
571 	int s;
572 	static int processing;
573 
574 	if (processing)
575 		return;
576 
577 	processing = 1;
578 	pcpl = splhigh();		/* Turn off all */
579 	s = ppc_intr_disable();
580 
581 	hwpend = ipending & ~pcpl;	/* Do now unmasked pendings */
582 	imen_m &= ~hwpend;
583 	enable_irq(~imen_m);
584 	hwpend &= HWIRQ_MASK;
585 	while (hwpend) {
586 		irq = 31 - cntlzw(hwpend);
587 		hwpend &= ~(1L << irq);
588 		ih = m_intrhand[irq];
589 		while(ih) {
590 			(*ih->ih_fun)(ih->ih_arg);
591 			ih = ih->ih_next;
592 		}
593 
594 		m_evirq[m_hwirq[irq]].ev_count++;
595 	}
596 
597 	/*out32rb(INT_ENABLE_REG, ~imen_m);*/
598 
599 	do {
600 		if((ipending & SINT_CLOCK) & ~pcpl) {
601 			ipending &= ~SINT_CLOCK;
602 			softclock();
603 		}
604 		if((ipending & SINT_NET) & ~pcpl) {
605 			extern int netisr;
606 			int pisr = netisr;
607 			netisr = 0;
608 			ipending &= ~SINT_NET;
609 			softnet(pisr);
610 		}
611 		if((ipending & SINT_TTY) & ~pcpl) {
612 			ipending &= ~SINT_TTY;
613 			softtty();
614 		}
615 	} while (ipending & (SINT_NET|SINT_CLOCK|SINT_TTY) & ~cpl);
616 	ipending &= pcpl;
617 	cpl = pcpl;	/* Don't use splx... we are here already! */
618 	ppc_intr_enable(s);
619 	processing = 0;
620 }
621 
622 static int
623 read_irq()
624 {
625 	int rv = 0;
626 	int state0, state1, p;
627 	int state0save, state1save;
628 
629 	state0 = in32rb(INT_STATE_REG0);
630 	if (state0)
631 		out32rb(INT_CLEAR_REG0, state0);
632 		state0save = state0;
633 	while (state0) {
634 		p = 31 - cntlzw(state0);
635 		rv |= 1 << m_virq[p];
636 		state0 &= ~(1 << p);
637 	}
638 
639 	if (heathrow_FCR)			/* has heathrow? */
640 		state1 = in32rb(INT_STATE_REG1);
641 	else
642 		state1 = 0;
643 
644 	if (state1)
645 		out32rb(INT_CLEAR_REG1, state1);
646 	state1save = state1;
647 	while (state1) {
648 		p = 31 - cntlzw(state1);
649 		rv |= 1 << m_virq[p + 32];
650 		state1 &= ~(1 << p);
651 	}
652 #if 0
653 printf("mac_intr int_stat 0:%x 1:%x\n", state0save, state1save);
654 #endif
655 
656 	/* 1 << 0 is invalid. */
657 	return rv & ~1;
658 }
659