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