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
macintr_match(struct device * parent,void * cf,void * aux)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
macintr_splraise(int newcpl)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
macintr_spllower(int newcpl)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
macintr_splx(int newcpl)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
macintr_attach(struct device * parent,struct device * self,void * aux)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
macintr_collect_preconf_intr(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
macintr_prog_button(void * arg)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
macintr_setipl(int ipl)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 *
macintr_establish(void * lcv,int irq,int type,int level,int (* ih_fun)(void *),void * ih_arg,const char * name)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
macintr_disestablish(void * lcp,void * arg)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
macintr_calc_mask(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
mac_ext_intr(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
macintr_eoi(int irq)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
macintr_read_irq(void)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