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