xref: /openbsd-src/sys/arch/macppc/dev/macintr.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: macintr.c,v 1.55 2019/09/03 17:51:52 deraadt 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 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 ((newcpl < IPL_SOFTTTY && ci->ci_ipending & ppc_smask[newcpl])) {
174 		s = splsofttty();
175 		dosoftint(newcpl);
176 		macintr_setipl(s); /* no-overhead splx */
177 	}
178 	ppc_intr_enable(intr);
179 }
180 
181 void
182 macintr_attach(struct device *parent, struct device *self, void *aux)
183 {
184 	struct cpu_info *ci = curcpu();
185 	struct confargs *ca = aux;
186 	extern intr_establish_t *intr_establish_func;
187 	extern intr_disestablish_t *intr_disestablish_func;
188 	struct intrq *iq;
189 	int i;
190 
191 	interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */
192 
193 	for (i = 0; i < ICU_LEN; i++) {
194 		iq = &macintr_handler[i];
195 		TAILQ_INIT(&iq->iq_list);
196 	}
197 	ppc_smask_init();
198 
199 	install_extint(mac_ext_intr);
200 	intr_establish_func  = macintr_establish;
201 	intr_disestablish_func  = macintr_disestablish;
202 
203 	ppc_intr_func.raise = macintr_splraise;
204 	ppc_intr_func.lower = macintr_spllower;
205 	ppc_intr_func.x = macintr_splx;
206 
207 	ci->ci_flags = 0;
208 
209 	macintr_collect_preconf_intr();
210 
211 	mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH,
212 	    macintr_prog_button, (void *)0x14, "progbutton");
213 
214 	ppc_intr_enable(1);
215 	printf("\n");
216 }
217 
218 void
219 macintr_collect_preconf_intr(void)
220 {
221 	int i;
222 	for (i = 0; i < ppc_configed_intr_cnt; i++) {
223 #ifdef DEBUG
224 		printf("\n\t%s irq %d level %d fun %p arg %p",
225 			ppc_configed_intr[i].ih_what,
226 			ppc_configed_intr[i].ih_irq,
227 			ppc_configed_intr[i].ih_level,
228 			ppc_configed_intr[i].ih_fun,
229 			ppc_configed_intr[i].ih_arg
230 			);
231 #endif
232 		macintr_establish(NULL,
233 			ppc_configed_intr[i].ih_irq,
234 			IST_LEVEL,
235 			ppc_configed_intr[i].ih_level,
236 			ppc_configed_intr[i].ih_fun,
237 			ppc_configed_intr[i].ih_arg,
238 			ppc_configed_intr[i].ih_what);
239 	}
240 }
241 
242 
243 /*
244  * programmer_button function to fix args to Debugger.
245  * deal with any enables/disables, if necessary.
246  */
247 int
248 macintr_prog_button (void *arg)
249 {
250 #ifdef DDB
251 	if (db_console)
252 		db_enter();
253 #else
254 	printf("programmer button pressed, debugger not available\n");
255 #endif
256 	return 1;
257 }
258 
259 /* Must be called with interrupt disable. */
260 void
261 macintr_setipl(int ipl)
262 {
263 	struct cpu_info *ci = curcpu();
264 
265 	ci->ci_cpl = ipl;
266 	if (heathrow_FCR)
267 		out32rb(INT_ENABLE_REG1,
268 		    macintr_ienable_h[macintr_pri_share[ipl]]);
269 
270 	out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]);
271 }
272 
273 /*
274  * Register an interrupt handler.
275  */
276 void *
277 macintr_establish(void * lcv, int irq, int type, int level,
278     int (*ih_fun)(void *), void *ih_arg, const char *name)
279 {
280 	struct cpu_info *ci = curcpu();
281 	struct intrq *iq;
282 	struct intrhand *ih;
283 	int s, flags;
284 
285 	if (!LEGAL_IRQ(irq) || type == IST_NONE) {
286 		printf("%s: bogus irq %d or type %d", __func__, irq, type);
287 		return (NULL);
288 	}
289 
290 	/* no point in sleeping unless someone can free memory. */
291 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
292 	if (ih == NULL)
293 		panic("intr_establish: can't malloc handler info");
294 
295 	iq = &macintr_handler[irq];
296 	switch (iq->iq_ist) {
297 	case IST_NONE:
298 		iq->iq_ist = type;
299 		break;
300 	case IST_EDGE:
301 		intr_shared_edge = 1;
302 		/* FALLTHROUGH */
303 	case IST_LEVEL:
304 		if (type == iq->iq_ist)
305 			break;
306 	case IST_PULSE:
307 		if (type != IST_NONE)
308 			panic("intr_establish: can't share %s with %s",
309 			    ppc_intr_typename(iq->iq_ist),
310 			    ppc_intr_typename(type));
311 		break;
312 	}
313 
314 	flags = level & IPL_MPSAFE;
315 	level &= ~IPL_MPSAFE;
316 
317 	KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE);
318 
319 	ih->ih_fun = ih_fun;
320 	ih->ih_arg = ih_arg;
321 	ih->ih_level = level;
322 	ih->ih_flags = flags;
323 	ih->ih_irq = irq;
324 	evcount_attach(&ih->ih_count, name, &ih->ih_irq);
325 
326 	/*
327 	 * Append handler to end of list
328 	 */
329 	s = ppc_intr_disable();
330 
331 	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
332 	macintr_calc_mask();
333 
334 	macintr_setipl(ci->ci_cpl);
335 	ppc_intr_enable(s);
336 
337 	return (ih);
338 }
339 
340 /*
341  * Deregister an interrupt handler.
342  */
343 void
344 macintr_disestablish(void *lcp, void *arg)
345 {
346 	struct cpu_info *ci = curcpu();
347 	struct intrhand *ih = arg;
348 	int irq = ih->ih_irq;
349 	int s;
350 	struct intrq *iq;
351 
352 	if (!LEGAL_IRQ(irq)) {
353 		printf("%s: bogus irq %d", __func__, irq);
354 		return;
355 	}
356 
357 	/*
358 	 * Remove the handler from the chain.
359 	 */
360 
361 	iq = &macintr_handler[irq];
362 	s = ppc_intr_disable();
363 
364 	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
365 	macintr_calc_mask();
366 
367 	macintr_setipl(ci->ci_cpl);
368 	ppc_intr_enable(s);
369 
370 	evcount_detach(&ih->ih_count);
371 	free(ih, M_DEVBUF, sizeof *ih);
372 
373 	if (TAILQ_EMPTY(&iq->iq_list))
374 		iq->iq_ist = IST_NONE;
375 }
376 
377 /*
378  * Recalculate the interrupt masks from scratch.
379  * We could code special registry and deregistry versions of this function that
380  * would be faster, but the code would be nastier, and we don't expect this to
381  * happen very much anyway.
382  */
383 void
384 macintr_calc_mask(void)
385 {
386 	int irq;
387 	struct intrhand *ih;
388 	int i;
389 
390 	for (i = IPL_NONE; i < IPL_NUM; i++) {
391 		macintr_pri_share[i] = i;
392 	}
393 
394 	for (irq = 0; irq < ICU_LEN; irq++) {
395 		int maxipl = IPL_NONE;
396 		int minipl = IPL_HIGH;
397 		struct intrq *iq = &macintr_handler[irq];
398 
399 		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
400 			if (ih->ih_level > maxipl)
401 				maxipl = ih->ih_level;
402 			if (ih->ih_level < minipl)
403 				minipl = ih->ih_level;
404 		}
405 
406 		iq->iq_ipl = maxipl;
407 
408 		if (maxipl == IPL_NONE) {
409 			minipl = IPL_NONE; /* Interrupt not enabled */
410 		} else {
411 			for (i = minipl; i < maxipl; i++)
412 				macintr_pri_share[i] =
413 				    macintr_pri_share[maxipl];
414 		}
415 
416 		/* Enable interrupts at lower levels */
417 
418 		if (irq < 32) {
419 			for (i = IPL_NONE; i < minipl; i++)
420 				macintr_ienable_l[i] |= (1 << irq);
421 			for (; i <= IPL_HIGH; i++)
422 				macintr_ienable_l[i] &= ~(1 << irq);
423 		} else {
424 			for (i = IPL_NONE; i < minipl; i++)
425 				macintr_ienable_h[i] |= (1 << (irq-32));
426 			for (; i <= IPL_HIGH; i++)
427 				macintr_ienable_h[i] &= ~(1 << (irq-32));
428 		}
429 	}
430 
431 #if 0
432 	for (i = 0; i < IPL_NUM; i++)
433 		printf("imask[%d] %x %x\n", i, macintr_ienable_l[i],
434 		    macintr_ienable_h[i]);
435 #endif
436 }
437 
438 /*
439  * external interrupt handler
440  */
441 void
442 mac_ext_intr(void)
443 {
444 	int irq = 0;
445 	int pcpl, ret;
446 	struct cpu_info *ci = curcpu();
447 	struct intrq *iq;
448 	struct intrhand *ih;
449 
450 	pcpl = ci->ci_cpl;	/* Turn off all */
451 
452 	irq = macintr_read_irq();
453 	while (irq != 255) {
454 		iq = &macintr_handler[irq];
455 		macintr_setipl(iq->iq_ipl);
456 
457 		TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
458 			ppc_intr_enable(1);
459 			ret = ((*ih->ih_fun)(ih->ih_arg));
460 			if (ret) {
461 				ih->ih_count.ec_count++;
462 				if (intr_shared_edge == 0 && ret == 1)
463 					break;
464 			}
465 			(void)ppc_intr_disable();
466 		}
467 		macintr_eoi(irq);
468 		macintr_setipl(pcpl);
469 
470 		uvmexp.intrs++;
471 
472 		irq = macintr_read_irq();
473 	}
474 
475 	macintr_splx(pcpl);	/* Process pendings. */
476 }
477 
478 void
479 macintr_eoi(int irq)
480 {
481 	u_int32_t state0, state1;
482 
483 	if (irq < 32) {
484 		state0 =  1 << irq;
485 		out32rb(INT_CLEAR_REG0, state0);
486 	} else {
487 		if (heathrow_FCR) {		/* has heathrow? */
488 			state1 = 1 << (irq - 32);
489 			out32rb(INT_CLEAR_REG1, state1);
490 		}
491 	}
492 }
493 
494 int
495 macintr_read_irq(void)
496 {
497 	struct cpu_info *ci = curcpu();
498 	u_int32_t state0, state1, irq_mask;
499 	int ipl, irq;
500 
501 	state0 = in32rb(INT_STATE_REG0);
502 
503 	if (heathrow_FCR)			/* has heathrow? */
504 		state1 = in32rb(INT_STATE_REG1);
505 	else
506 		state1 = 0;
507 
508 	for (ipl = IPL_HIGH; ipl >= ci->ci_cpl; ipl --) {
509 		irq_mask = state0 & macintr_ienable_l[ipl];
510 		if (irq_mask) {
511 			irq = ffs(irq_mask) - 1;
512 			return irq;
513 		}
514 		irq_mask = state1 & macintr_ienable_h[ipl];
515 		if (irq_mask) {
516 			irq = ffs(irq_mask) + 31;
517 			return irq;
518 		}
519 	}
520 	return 255;
521 }
522