xref: /openbsd-src/sys/arch/macppc/dev/macintr.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: macintr.c,v 1.44 2011/09/16 17:56:52 drahn Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
5  * Copyright (c) 1995 Per Fogelstrom
6  * Copyright (c) 1993, 1994 Charles M. Hannum.
7  * Copyright (c) 1990 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * William Jolitz and Don Ahn.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)isa.c	7.2 (Berkeley) 5/12/91
38  */
39 
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/ioctl.h>
43 #include <sys/mbuf.h>
44 #include <sys/socket.h>
45 #include <sys/systm.h>
46 
47 #include <uvm/uvm.h>
48 #include <ddb/db_var.h>
49 
50 #include <machine/atomic.h>
51 #include <machine/autoconf.h>
52 #include <machine/intr.h>
53 #include <machine/psl.h>
54 #include <machine/pio.h>
55 #include <machine/powerpc.h>
56 
57 #include <dev/ofw/openfirm.h>
58 
59 #define ICU_LEN 64
60 #define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))
61 
62 int macintr_ienable_l[IPL_NUM], macintr_ienable_h[IPL_NUM];
63 int macintr_pri_share[IPL_NUM];
64 
65 struct intrq macintr_handler[ICU_LEN];
66 
67 void macintr_calc_mask(void);
68 void macintr_eoi(int irq);
69 int macintr_read_irq(void);
70 static void macintr_do_pending_int(void);
71 
72 extern u_int32_t *heathrow_FCR;
73 
74 #define INT_STATE_REG0  (interrupt_reg + 0x20)
75 #define INT_ENABLE_REG0 (interrupt_reg + 0x24)
76 #define INT_CLEAR_REG0  (interrupt_reg + 0x28)
77 #define INT_LEVEL_REG0  (interrupt_reg + 0x2c)
78 #define INT_STATE_REG1  (INT_STATE_REG0  - 0x10)
79 #define INT_ENABLE_REG1 (INT_ENABLE_REG0 - 0x10)
80 #define INT_CLEAR_REG1  (INT_CLEAR_REG0  - 0x10)
81 #define INT_LEVEL_REG1  (INT_LEVEL_REG0  - 0x10)
82 
83 struct macintr_softc {
84 	struct device sc_dev;
85 };
86 
87 int	macintr_match(struct device *parent, void *cf, void *aux);
88 void	macintr_attach(struct device *, struct device *, void *);
89 void	mac_do_pending_int(void);
90 void	mac_ext_intr(void);
91 void	macintr_collect_preconf_intr(void);
92 void	macintr_setipl(int ipl);
93 
94 struct cfattach macintr_ca = {
95 	sizeof(struct macintr_softc),
96 	macintr_match,
97 	macintr_attach
98 };
99 
100 struct cfdriver macintr_cd = {
101 	NULL, "macintr", DV_DULL
102 };
103 
104 int
105 macintr_match(struct device *parent, void *cf, void *aux)
106 {
107 	struct confargs *ca = aux;
108 	char type[40];
109 
110 	/*
111 	 * Match entry according to "present" openfirmware entry.
112 	 */
113 	if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) {
114 		OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
115 		if (strcmp(type,  "interrupt-controller") == 0)
116 			return 1;
117 	}
118 
119 	/*
120 	 * Check name for legacy interrupt controller, this is
121 	 * faked to allow old firmware which does not have an entry
122 	 * to attach to this device.
123 	 */
124 	if (strcmp(ca->ca_name, "legacy-interrupt-controller") == 0 )
125 		return 1;
126 	return 0;
127 }
128 
129 u_int8_t *interrupt_reg;
130 typedef void  (void_f) (void);
131 extern void_f *pending_int_f;
132 int macintr_prog_button (void *arg);
133 
134 intr_establish_t macintr_establish;
135 intr_disestablish_t macintr_disestablish;
136 extern intr_establish_t *mac_intr_establish_func;
137 extern intr_disestablish_t *mac_intr_disestablish_func;
138 
139 ppc_splraise_t macintr_splraise;
140 ppc_spllower_t macintr_spllower;
141 ppc_splx_t macintr_splx;
142 
143 
144 int
145 macintr_splraise(int newcpl)
146 {
147 	struct cpu_info *ci = curcpu();
148 	newcpl = macintr_pri_share[newcpl];
149 	int ocpl = ci->ci_cpl;
150 	if (ocpl > newcpl)
151 		newcpl = ocpl;
152 
153 	macintr_setipl(newcpl);
154 
155 	return ocpl;
156 }
157 
158 int
159 macintr_spllower(int newcpl)
160 {
161 	struct cpu_info *ci = curcpu();
162 	int ocpl = ci->ci_cpl;
163 
164 	macintr_splx(newcpl);
165 
166 	return ocpl;
167 }
168 
169 void
170 macintr_splx(int newcpl)
171 {
172 	struct cpu_info *ci = curcpu();
173 
174 	macintr_setipl(newcpl);
175 	if (ci->ci_ipending & ppc_smask[newcpl])
176 		macintr_do_pending_int();
177 }
178 
179 void
180 macintr_attach(struct device *parent, struct device *self, void *aux)
181 {
182 	struct cpu_info *ci = curcpu();
183 	struct confargs *ca = aux;
184 	extern intr_establish_t *intr_establish_func;
185 	extern intr_disestablish_t *intr_disestablish_func;
186 	struct intrq *iq;
187 	int i;
188 
189 	interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */
190 
191 	for (i = 0; i < ICU_LEN; i++) {
192 		iq = &macintr_handler[i];
193 		TAILQ_INIT(&iq->iq_list);
194 	}
195 	ppc_smask_init();
196 
197 	install_extint(mac_ext_intr);
198 	pending_int_f = macintr_do_pending_int;
199 	intr_establish_func  = macintr_establish;
200 	intr_disestablish_func  = macintr_disestablish;
201 	mac_intr_establish_func  = macintr_establish;
202 	mac_intr_disestablish_func  = macintr_disestablish;
203 
204 	ppc_intr_func.raise = macintr_splraise;
205 	ppc_intr_func.lower = macintr_spllower;
206 	ppc_intr_func.x = macintr_splx;
207 
208 	ci->ci_iactive = 0;
209 
210 	macintr_collect_preconf_intr();
211 
212 	mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH,
213 	    macintr_prog_button, (void *)0x14, "progbutton");
214 
215 	ppc_intr_enable(1);
216 	printf("\n");
217 }
218 
219 void
220 macintr_collect_preconf_intr()
221 {
222 	int i;
223 	for (i = 0; i < ppc_configed_intr_cnt; i++) {
224 #ifdef DEBUG
225 		printf("\n\t%s irq %d level %d fun %p arg %p",
226 			ppc_configed_intr[i].ih_what,
227 			ppc_configed_intr[i].ih_irq,
228 			ppc_configed_intr[i].ih_level,
229 			ppc_configed_intr[i].ih_fun,
230 			ppc_configed_intr[i].ih_arg
231 			);
232 #endif
233 		macintr_establish(NULL,
234 			ppc_configed_intr[i].ih_irq,
235 			IST_LEVEL,
236 			ppc_configed_intr[i].ih_level,
237 			ppc_configed_intr[i].ih_fun,
238 			ppc_configed_intr[i].ih_arg,
239 			ppc_configed_intr[i].ih_what);
240 	}
241 }
242 
243 
244 /*
245  * programmer_button function to fix args to Debugger.
246  * deal with any enables/disables, if necessary.
247  */
248 int
249 macintr_prog_button (void *arg)
250 {
251 #ifdef DDB
252 	if (db_console)
253 		Debugger();
254 #else
255 	printf("programmer button pressed, debugger not available\n");
256 #endif
257 	return 1;
258 }
259 
260 void
261 macintr_setipl(int ipl)
262 {
263 	struct cpu_info *ci = curcpu();
264 	int s;
265 	s = ppc_intr_disable();
266 	ci->ci_cpl = ipl;
267 	if (heathrow_FCR)
268 		out32rb(INT_ENABLE_REG1,
269 		    macintr_ienable_h[macintr_pri_share[ipl]]);
270 
271 	out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]);
272 	ppc_intr_enable(s);
273 }
274 
275 /*
276  * Register an interrupt handler.
277  */
278 void *
279 macintr_establish(void * lcv, int irq, int type, int level,
280     int (*ih_fun)(void *), void *ih_arg, const char *name)
281 {
282 	struct cpu_info *ci = curcpu();
283 	struct intrq *iq;
284 	struct intrhand *ih;
285 	int s;
286 
287 #if 0
288 printf("macintr_establish, hI %d L %d %s", irq, level, ppc_intr_typename(type));
289 #endif
290 
291 	/* no point in sleeping unless someone can free memory. */
292 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
293 	if (ih == NULL)
294 		panic("intr_establish: can't malloc handler info");
295 
296 	if (!LEGAL_IRQ(irq) || type == IST_NONE)
297 		panic("intr_establish: bogus irq or type");
298 
299 	iq = &macintr_handler[irq];
300 	switch (iq->iq_ist) {
301 	case IST_NONE:
302 		iq->iq_ist = type;
303 		break;
304 	case IST_EDGE:
305 		intr_shared_edge = 1;
306 		/* FALLTHROUGH */
307 	case IST_LEVEL:
308 		if (type == iq->iq_ist)
309 			break;
310 	case IST_PULSE:
311 		if (type != IST_NONE)
312 			panic("intr_establish: can't share %s with %s",
313 			    ppc_intr_typename(iq->iq_ist),
314 			    ppc_intr_typename(type));
315 		break;
316 	}
317 
318 	ih->ih_fun = ih_fun;
319 	ih->ih_arg = ih_arg;
320 	ih->ih_level = level;
321 	ih->ih_irq = irq;
322 	evcount_attach(&ih->ih_count, name, &ih->ih_irq);
323 
324 	/*
325 	 * Append handler to end of list
326 	 */
327 	s = ppc_intr_disable();
328 
329 	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
330 	macintr_calc_mask();
331 
332 	macintr_setipl(ci->ci_cpl);
333 	ppc_intr_enable(s);
334 
335 	return (ih);
336 }
337 
338 /*
339  * Deregister an interrupt handler.
340  */
341 void
342 macintr_disestablish(void *lcp, void *arg)
343 {
344 	struct cpu_info *ci = curcpu();
345 	struct intrhand *ih = arg;
346 	int irq = ih->ih_irq;
347 	int s;
348 	struct intrq *iq;
349 
350 	if (!LEGAL_IRQ(irq))
351 		panic("intr_disestablish: bogus irq");
352 
353 	/*
354 	 * Remove the handler from the chain.
355 	 */
356 
357 	iq = &macintr_handler[irq];
358 	s = ppc_intr_disable();
359 
360 	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
361 	macintr_calc_mask();
362 
363 	macintr_setipl(ci->ci_cpl);
364 	ppc_intr_enable(s);
365 
366 	evcount_detach(&ih->ih_count);
367 	free((void *)ih, M_DEVBUF);
368 
369 	if (TAILQ_EMPTY(&iq->iq_list))
370 		iq->iq_ist = IST_NONE;
371 }
372 
373 /*
374  * Recalculate the interrupt masks from scratch.
375  * We could code special registry and deregistry versions of this function that
376  * would be faster, but the code would be nastier, and we don't expect this to
377  * happen very much anyway.
378  */
379 void
380 macintr_calc_mask()
381 {
382 	int irq;
383 	struct intrhand *ih;
384 	int i;
385 
386 	for (i = IPL_NONE; i < IPL_NUM; i++) {
387 		macintr_pri_share[i] = i;
388 	}
389 
390 	for (irq = 0; irq < ICU_LEN; irq++) {
391 		int maxipl = IPL_NONE;
392 		int minipl = IPL_HIGH;
393 		struct intrq *iq = &macintr_handler[irq];
394 
395 		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
396 			if (ih->ih_level > maxipl)
397 				maxipl = ih->ih_level;
398 			if (ih->ih_level < minipl)
399 				minipl = ih->ih_level;
400 		}
401 
402 		iq->iq_ipl = maxipl;
403 
404 		if (maxipl == IPL_NONE) {
405 			minipl = IPL_NONE; /* Interrupt not enabled */
406 		} else {
407 			for (i = minipl; i < maxipl; i++)
408 				macintr_pri_share[i] =
409 				    macintr_pri_share[maxipl];
410 		}
411 
412 		/* Enable interrupts at lower levels */
413 
414 		if (irq < 32) {
415 			for (i = IPL_NONE; i < minipl; i++)
416 				macintr_ienable_l[i] |= (1 << irq);
417 			for (; i <= IPL_HIGH; i++)
418 				macintr_ienable_l[i] &= ~(1 << irq);
419 		} else {
420 			for (i = IPL_NONE; i < minipl; i++)
421 				macintr_ienable_h[i] |= (1 << (irq-32));
422 			for (; i <= IPL_HIGH; i++)
423 				macintr_ienable_h[i] &= ~(1 << (irq-32));
424 		}
425 	}
426 
427 #if 0
428 	for (i = 0; i < IPL_NUM; i++)
429 		printf("imask[%d] %x %x\n", i, macintr_ienable_l[i],
430 		    macintr_ienable_h[i]);
431 #endif
432 }
433 
434 /*
435  * external interrupt handler
436  */
437 void
438 mac_ext_intr()
439 {
440 	int irq = 0;
441 	int pcpl, ret;
442 	struct cpu_info *ci = curcpu();
443 	struct intrq *iq;
444 	struct intrhand *ih;
445 
446 	pcpl = ci->ci_cpl;	/* Turn off all */
447 
448 	irq = macintr_read_irq();
449 	while (irq != 255) {
450 		iq = &macintr_handler[irq];
451 		macintr_setipl(iq->iq_ipl);
452 
453 		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
454 			ppc_intr_enable(1);
455 			ret = ((*ih->ih_fun)(ih->ih_arg));
456 			if (ret) {
457 				ih->ih_count.ec_count++;
458 				if (intr_shared_edge == 0 && ret == 1)
459 					break;
460 			}
461 			(void)ppc_intr_disable();
462 		}
463 		macintr_eoi(irq);
464 		macintr_setipl(pcpl);
465 
466 		uvmexp.intrs++;
467 
468 		irq = macintr_read_irq();
469 	}
470 
471 	ppc_intr_enable(1);
472 	splx(pcpl);	/* Process pendings. */
473 }
474 
475 void
476 macintr_do_pending_int()
477 {
478 	struct cpu_info *ci = curcpu();
479 	int pcpl = ci->ci_cpl; /* XXX */
480 	int s;
481 	s = ppc_intr_disable();
482 	if (ci->ci_iactive & CI_IACTIVE_PROCESSING_SOFT) {
483 		ppc_intr_enable(s);
484 		return;
485 	}
486 	atomic_setbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT);
487 
488 	do {
489 		if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTCLOCK)) &&
490 		    (pcpl < IPL_SOFTCLOCK)) {
491  			ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTCLOCK);
492 			softintr_dispatch(SI_SOFTCLOCK);
493  		}
494 		if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTNET)) &&
495 		    (pcpl < IPL_SOFTNET)) {
496 			ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTNET);
497 			softintr_dispatch(SI_SOFTNET);
498 		}
499 		if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTTTY)) &&
500 		    (pcpl < IPL_SOFTTTY)) {
501 			ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTTTY);
502 			softintr_dispatch(SI_SOFTTTY);
503 		}
504 
505 	} while (ci->ci_ipending & ppc_smask[pcpl]);
506 	macintr_setipl(pcpl);
507 	ppc_intr_enable(s);
508 
509 	atomic_clearbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT);
510 }
511 
512 void
513 macintr_eoi(int irq)
514 {
515 	u_int32_t state0, state1;
516 
517 	if (irq < 32) {
518 		state0 =  1 << irq;
519 		out32rb(INT_CLEAR_REG0, state0);
520 	} else {
521 		if (heathrow_FCR) {		/* has heathrow? */
522 			state1 = 1 << (irq - 32);
523 			out32rb(INT_CLEAR_REG1, state1);
524 		}
525 	}
526 }
527 
528 int
529 macintr_read_irq()
530 {
531 	struct cpu_info *ci = curcpu();
532 	u_int32_t state0, state1, irq_mask;
533 	int ipl, irq;
534 
535 	state0 = in32rb(INT_STATE_REG0);
536 
537 	if (heathrow_FCR)			/* has heathrow? */
538 		state1 = in32rb(INT_STATE_REG1);
539 	else
540 		state1 = 0;
541 
542 	for (ipl = IPL_HIGH; ipl >= ci->ci_cpl; ipl --) {
543 		irq_mask = state0 & macintr_ienable_l[ipl];
544 		if (irq_mask) {
545 			irq = ffs(irq_mask) - 1;
546 			return irq;
547 		}
548 		irq_mask = state1 & macintr_ienable_h[ipl];
549 		if (irq_mask) {
550 			irq = ffs(irq_mask) + 31;
551 			return irq;
552 		}
553 	}
554 	return 255;
555 }
556