sio_pic.c revision 1.15 1 /* $NetBSD: sio_pic.c,v 1.15 1996/11/13 21:13:34 cgd Exp $ */
2
3 /*
4 * Copyright (c) 1995, 1996 Carnegie-Mellon University.
5 * All rights reserved.
6 *
7 * Author: Chris G. Demetriou
8 *
9 * Permission to use, copy, modify and distribute this software and
10 * its documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation.
14 *
15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18 *
19 * Carnegie Mellon requests users of this software to return to
20 *
21 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
22 * School of Computer Science
23 * Carnegie Mellon University
24 * Pittsburgh PA 15213-3890
25 *
26 * any improvements or extensions that they make and grant Carnegie the
27 * rights to redistribute these changes.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/device.h>
33 #include <sys/malloc.h>
34 #include <sys/syslog.h>
35
36 #include <machine/intr.h>
37 #include <machine/bus.h>
38
39 #include <dev/isa/isareg.h>
40 #include <dev/isa/isavar.h>
41 #include <alpha/pci/siovar.h>
42
43 #ifndef EVCNT_COUNTERS
44 #include <machine/intrcnt.h>
45 #endif
46
47 #include "sio.h"
48
49 /*
50 * To add to the long history of wonderful PROM console traits,
51 * AlphaStation PROMs don't reset themselves completely on boot!
52 * Therefore, if an interrupt was turned on when the kernel was
53 * started, we're not going to EVER turn it off... I don't know
54 * what will happen if new interrupts (that the PROM console doesn't
55 * want) are turned on. I'll burn that bridge when I come to it.
56 */
57 #define BROKEN_PROM_CONSOLE
58
59 /*
60 * Private functions and variables.
61 */
62 static void sio_strayintr __P((int));
63
64 bus_space_tag_t sio_iot;
65 bus_space_handle_t sio_ioh_icu1, sio_ioh_icu2, sio_ioh_elcr;
66
67 /*
68 * Interrupt handler chains. sio_intr_establish() inserts a handler into
69 * the list. The handler is called with its (single) argument.
70 */
71 struct intrhand {
72 int (*ih_fun) __P((void *));
73 void *ih_arg;
74 u_long ih_count;
75 struct intrhand *ih_next;
76 int ih_level;
77 int ih_irq;
78 };
79
80 #define ICU_LEN 16 /* number of ISA IRQs */
81
82 static struct intrhand *sio_intrhand[ICU_LEN];
83 static int sio_intrsharetype[ICU_LEN];
84 static u_long sio_strayintrcnt[ICU_LEN];
85 #ifdef EVCNT_COUNTERS
86 struct evcnt sio_intr_evcnt;
87 #endif
88
89 #ifndef STRAY_MAX
90 #ifdef BROKEN_PROM_CONSOLE
91 /*
92 * If prom console is broken, because initial interrupt settings
93 * must be kept, there's no way to escape stray interrupts.
94 */
95 #define STRAY_MAX 0
96 #else
97 #define STRAY_MAX 5
98 #endif
99 #endif
100
101 #ifdef BROKEN_PROM_CONSOLE
102 /*
103 * If prom console is broken, must remember the initial interrupt
104 * settings and enforce them. WHEE!
105 */
106 u_int8_t initial_ocw1[2];
107 u_int8_t initial_elcr[2];
108 #define INITIALLY_ENABLED(irq) \
109 ((initial_ocw1[(irq) / 8] & (1 << ((irq) % 8))) == 0)
110 #define INITIALLY_LEVEL_TRIGGERED(irq) \
111 ((initial_elcr[(irq) / 8] & (1 << ((irq) % 8))) != 0)
112 #else
113 #define INITIALLY_ENABLED(irq) ((irq) == 2 ? 1 : 0)
114 #define INITIALLY_LEVEL_TRIGGERED(irq) 0
115 #endif
116
117 void sio_setirqstat __P((int, int, int));
118
119 void
120 sio_setirqstat(irq, enabled, type)
121 int irq, enabled;
122 int type;
123 {
124 u_int8_t ocw1[2], elcr[2];
125 int icu, bit;
126
127 #if 0
128 printf("sio_setirqstat: irq %d: %s, %s\n", irq,
129 enabled ? "enabled" : "disabled", isa_intr_typename(type));
130 #endif
131
132 sio_intrsharetype[irq] = type;
133
134 icu = irq / 8;
135 bit = irq % 8;
136
137 ocw1[0] = bus_space_read_1(sio_iot, sio_ioh_icu1, 1);
138 ocw1[1] = bus_space_read_1(sio_iot, sio_ioh_icu2, 1);
139 elcr[0] = bus_space_read_1(sio_iot, sio_ioh_elcr, 0); /* XXX */
140 elcr[1] = bus_space_read_1(sio_iot, sio_ioh_elcr, 1); /* XXX */
141
142 /*
143 * interrupt enable: set bit to mask (disable) interrupt.
144 */
145 if (enabled)
146 ocw1[icu] &= ~(1 << bit);
147 else
148 ocw1[icu] |= 1 << bit;
149
150 /*
151 * interrupt type select: set bit to get level-triggered.
152 */
153 if (type == IST_LEVEL)
154 elcr[icu] |= 1 << bit;
155 else
156 elcr[icu] &= ~(1 << bit);
157
158 #ifdef not_here
159 /* see the init function... */
160 ocw1[0] &= ~0x04; /* always enable IRQ2 on first PIC */
161 elcr[0] &= ~0x07; /* IRQ[0-2] must be edge-triggered */
162 elcr[1] &= ~0x21; /* IRQ[13,8] must be edge-triggered */
163 #endif
164
165 #ifdef BROKEN_PROM_CONSOLE
166 /*
167 * make sure that the initially clear bits (unmasked interrupts)
168 * are never set, and that the initially-level-triggered
169 * intrrupts always remain level-triggered, to keep the prom happy.
170 */
171 if ((ocw1[0] & ~initial_ocw1[0]) != 0 ||
172 (ocw1[1] & ~initial_ocw1[1]) != 0 ||
173 (elcr[0] & initial_elcr[0]) != initial_elcr[0] ||
174 (elcr[1] & initial_elcr[1]) != initial_elcr[1]) {
175 printf("sio_sis: initial: ocw = (%2x,%2x), elcr = (%2x,%2x)\n",
176 initial_ocw1[0], initial_ocw1[1],
177 initial_elcr[0], initial_elcr[1]);
178 printf(" current: ocw = (%2x,%2x), elcr = (%2x,%2x)\n",
179 ocw1[0], ocw1[1], elcr[0], elcr[1]);
180 panic("sio_setirqstat: hosed");
181 }
182 #endif
183
184 bus_space_write_1(sio_iot, sio_ioh_icu1, 1, ocw1[0]);
185 bus_space_write_1(sio_iot, sio_ioh_icu2, 1, ocw1[1]);
186 bus_space_write_1(sio_iot, sio_ioh_elcr, 0, elcr[0]); /* XXX */
187 bus_space_write_1(sio_iot, sio_ioh_elcr, 1, elcr[1]); /* XXX */
188 }
189
190 void
191 sio_intr_setup(iot)
192 bus_space_tag_t iot;
193 {
194 int i;
195
196 sio_iot = iot;
197
198 if (bus_space_map(sio_iot, IO_ICU1, IO_ICUSIZE, 0, &sio_ioh_icu1) ||
199 bus_space_map(sio_iot, IO_ICU2, IO_ICUSIZE, 0, &sio_ioh_icu2) ||
200 bus_space_map(sio_iot, 0x4d0, 2, 0, &sio_ioh_elcr))
201 panic("sio_intr_setup: can't map I/O ports");
202
203 #ifdef BROKEN_PROM_CONSOLE
204 /*
205 * Remember the initial values, because the prom is stupid.
206 */
207 initial_ocw1[0] = bus_space_read_1(sio_iot, sio_ioh_icu1, 1);
208 initial_ocw1[1] = bus_space_read_1(sio_iot, sio_ioh_icu2, 1);
209 initial_elcr[0] = bus_space_read_1(sio_iot, sio_ioh_elcr, 0); /* XXX */
210 initial_elcr[1] = bus_space_read_1(sio_iot, sio_ioh_elcr, 1); /* XXX */
211 #if 0
212 printf("initial_ocw1[0] = 0x%x\n", initial_ocw1[0]);
213 printf("initial_ocw1[1] = 0x%x\n", initial_ocw1[1]);
214 printf("initial_elcr[0] = 0x%x\n", initial_elcr[0]);
215 printf("initial_elcr[1] = 0x%x\n", initial_elcr[1]);
216 #endif
217 #endif
218
219 /*
220 * set up initial values for interrupt enables.
221 */
222 for (i = 0; i < ICU_LEN; i++) {
223 switch (i) {
224 case 0:
225 case 1:
226 case 8:
227 case 13:
228 /*
229 * IRQs 0, 1, 8, and 13 must always be
230 * edge-triggered.
231 */
232 if (INITIALLY_LEVEL_TRIGGERED(i))
233 printf("sio_intr_setup: %d LT!\n", i);
234 sio_setirqstat(i, INITIALLY_ENABLED(i), IST_EDGE);
235 break;
236
237 case 2:
238 /*
239 * IRQ 2 must be edge-triggered, and should be
240 * enabled (otherwise IRQs 8-15 are ignored).
241 */
242 if (INITIALLY_LEVEL_TRIGGERED(i))
243 printf("sio_intr_setup: %d LT!\n", i);
244 if (!INITIALLY_ENABLED(i))
245 printf("sio_intr_setup: %d not enabled!\n", i);
246 sio_setirqstat(i, 1, IST_EDGE);
247 break;
248
249 default:
250 /*
251 * Otherwise, disable the IRQ and set its
252 * type to (effectively) "unknown."
253 */
254 sio_setirqstat(i, INITIALLY_ENABLED(i),
255 INITIALLY_LEVEL_TRIGGERED(i) ? IST_LEVEL :
256 IST_NONE);
257 break;
258 }
259 }
260 }
261
262 const char *
263 sio_intr_string(v, irq)
264 void *v;
265 int irq;
266 {
267 static char irqstr[12]; /* 8 + 2 + NULL + sanity */
268
269 if (irq == 0 || irq >= ICU_LEN || irq == 2)
270 panic("sio_intr_string: bogus IRQ 0x%x\n", irq);
271
272 sprintf(irqstr, "isa irq %d", irq);
273 return (irqstr);
274 }
275
276 void *
277 sio_intr_establish(v, irq, type, level, ih_fun, ih_arg)
278 void *v, *ih_arg;
279 int irq;
280 int type;
281 int level;
282 int (*ih_fun)(void *);
283 {
284 struct intrhand **p, *c, *ih;
285 extern int cold;
286
287 /* no point in sleeping unless someone can free memory. */
288 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
289 if (ih == NULL)
290 panic("sio_intr_establish: can't malloc handler info");
291
292 if (irq > ICU_LEN || type == IST_NONE)
293 panic("sio_intr_establish: bogus irq or type");
294
295 switch (sio_intrsharetype[irq]) {
296 case IST_EDGE:
297 case IST_LEVEL:
298 if (type == sio_intrsharetype[irq])
299 break;
300 case IST_PULSE:
301 if (type != IST_NONE) {
302 if (sio_intrhand[irq] == NULL) {
303 printf("sio_intr_establish: irq %d: warning: using %s on %s\n",
304 irq, isa_intr_typename(type),
305 isa_intr_typename(sio_intrsharetype[irq]));
306 type = sio_intrsharetype[irq];
307 } else {
308 panic("sio_intr_establish: irq %d: can't share %s with %s",
309 irq, isa_intr_typename(type),
310 isa_intr_typename(sio_intrsharetype[irq]));
311 }
312 }
313 break;
314 }
315
316 /*
317 * Figure out where to put the handler.
318 * This is O(N^2), but we want to preserve the order, and N is
319 * generally small.
320 */
321 for (p = &sio_intrhand[irq]; (c = *p) != NULL; p = &c->ih_next)
322 ;
323
324 /*
325 * Poke the real handler in now.
326 */
327 ih->ih_fun = ih_fun;
328 ih->ih_arg = ih_arg;
329 ih->ih_count = 0;
330 ih->ih_next = NULL;
331 ih->ih_level = 0; /* XXX meaningless on alpha */
332 ih->ih_irq = irq;
333 *p = ih;
334
335 sio_setirqstat(irq, 1, type);
336
337 return ih;
338 }
339
340 void
341 sio_intr_disestablish(v, cookie)
342 void *v;
343 void *cookie;
344 {
345
346 printf("sio_intr_disestablish(%p)\n", cookie);
347 /* XXX */
348
349 /* XXX NEVER ALLOW AN INITIALLY-ENABLED INTERRUPT TO BE DISABLED */
350 /* XXX NEVER ALLOW AN INITIALLY-LT INTERRUPT TO BECOME UNTYPED */
351 }
352
353 /*
354 * caught a stray interrupt; notify if not too many seen already.
355 */
356 void
357 sio_strayintr(irq)
358 int irq;
359 {
360
361 sio_strayintrcnt[irq]++;
362
363 #ifdef notyet
364 if (sio_strayintrcnt[irq] == STRAY_MAX)
365 sio_disable_intr(irq);
366
367 log(LOG_ERR, "stray isa irq %d\n", irq);
368 if (sio_strayintrcnt[irq] == STRAY_MAX)
369 log(LOG_ERR, "disabling interrupts on isa irq %d\n", irq);
370 #else
371 if (sio_strayintrcnt[irq] <= STRAY_MAX)
372 log(LOG_ERR, "stray isa irq %d%s\n", irq,
373 sio_strayintrcnt[irq] >= STRAY_MAX ?
374 "; stopped logging" : "");
375 #endif
376 }
377
378 void
379 sio_iointr(framep, vec)
380 void *framep;
381 unsigned long vec;
382 {
383 int irq, handled;
384 struct intrhand *ih;
385
386 irq = (vec - 0x800) >> 4;
387 #ifdef DIAGNOSTIC
388 if (irq > ICU_LEN || irq < 0)
389 panic("sio_iointr: irq out of range (%d)", irq);
390 #endif
391
392 #ifdef EVCNT_COUNTERS
393 sio_intr_evcnt.ev_count++;
394 #else
395 if (ICU_LEN != INTRCNT_ISA_IRQ_LEN)
396 panic("sio interrupt counter sizes inconsistent");
397 intrcnt[INTRCNT_ISA_IRQ + irq]++;
398 #endif
399
400 /*
401 * We cdr down the intrhand chain, calling each handler with
402 * its appropriate argument;
403 *
404 * The handler returns one of three values:
405 * 0 - This interrupt wasn't for me.
406 * 1 - This interrupt was for me.
407 * -1 - This interrupt might have been for me, but I don't know.
408 * If there are no handlers, or they all return 0, we flags it as a
409 * `stray' interrupt. On a system with level-triggered interrupts,
410 * we could terminate immediately when one of them returns 1; but
411 * this is PC-ish!
412 */
413 for (ih = sio_intrhand[irq], handled = 0; ih != NULL;
414 ih = ih->ih_next) {
415 int rv;
416
417 rv = (*ih->ih_fun)(ih->ih_arg);
418
419 ih->ih_count++;
420 handled = handled || (rv != 0);
421 }
422
423 if (!handled)
424 sio_strayintr(irq);
425
426 /*
427 * Some versions of the machines which use the SIO
428 * (or is it some PALcode revisions on those machines?)
429 * require the non-specific EOI to be fed to the PIC(s)
430 * by the interrupt handler.
431 */
432 if (irq > 7)
433 bus_space_write_1(sio_iot,
434 sio_ioh_icu2, 0, 0x20 | (irq & 0x07)); /* XXX */
435 bus_space_write_1(sio_iot,
436 sio_ioh_icu1, 0, 0x20 | (irq > 7 ? 2 : irq)); /* XXX */
437 }
438