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