sio_pic.c revision 1.7.4.1 1 /* $NetBSD: sio_pic.c,v 1.7.4.1 1996/06/05 03:42:29 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_chipset_tag_t sio_bc;
65 bus_io_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)();
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
118 sio_setirqstat(irq, enabled, type)
119 int irq, enabled;
120 int type;
121 {
122 u_int8_t ocw1[2], elcr[2];
123 int icu, bit;
124
125 #if 0
126 printf("sio_setirqstat: irq %d, %s, %s\n", irq,
127 enabled ? "enabled" : "disabled", isa_intr_typename(type));
128 #endif
129
130 sio_intrsharetype[irq] = type;
131
132 icu = irq / 8;
133 bit = irq % 8;
134
135 ocw1[0] = bus_io_read_1(sio_bc, sio_ioh_icu1, 1);
136 ocw1[1] = bus_io_read_1(sio_bc, sio_ioh_icu2, 1);
137 elcr[0] = bus_io_read_1(sio_bc, sio_ioh_elcr, 0); /* XXX */
138 elcr[1] = bus_io_read_1(sio_bc, sio_ioh_elcr, 1); /* XXX */
139
140 /*
141 * interrupt enable: set bit to mask (disable) interrupt.
142 */
143 if (enabled)
144 ocw1[icu] &= ~(1 << bit);
145 else
146 ocw1[icu] |= 1 << bit;
147
148 /*
149 * interrupt type select: set bit to get level-triggered.
150 */
151 if (type == IST_LEVEL)
152 elcr[icu] |= 1 << bit;
153 else
154 elcr[icu] &= ~(1 << bit);
155
156 #ifdef not_here
157 /* see the init function... */
158 ocw1[0] &= ~0x04; /* always enable IRQ2 on first PIC */
159 elcr[0] &= ~0x07; /* IRQ[0-2] must be edge-triggered */
160 elcr[1] &= ~0x21; /* IRQ[13,8] must be edge-triggered */
161 #endif
162
163 #ifdef BROKEN_PROM_CONSOLE
164 /*
165 * make sure that the initially clear bits (unmasked interrupts)
166 * are never set, and that the initially-level-triggered
167 * intrrupts always remain level-triggered, to keep the prom happy.
168 */
169 if ((ocw1[0] & ~initial_ocw1[0]) != 0 ||
170 (ocw1[1] & ~initial_ocw1[1]) != 0 ||
171 (elcr[0] & initial_elcr[0]) != initial_elcr[0] ||
172 (elcr[1] & initial_elcr[1]) != initial_elcr[1]) {
173 printf("sio_sis: initial: ocw = (%2x,%2x), elcr = (%2x,%2X)\n",
174 initial_ocw1[0], initial_ocw1[1],
175 initial_elcr[0], initial_elcr[1]);
176 printf(" current: ocw = (%2x,%2x), elcr = (%2x,%2X)\n",
177 ocw1[0], ocw1[1], elcr[0], elcr[1]);
178 panic("sio_setirqstat: hosed");
179 }
180 #endif
181
182 bus_io_write_1(sio_bc, sio_ioh_icu1, 1, ocw1[0]);
183 bus_io_write_1(sio_bc, sio_ioh_icu2, 1, ocw1[1]);
184 bus_io_write_1(sio_bc, sio_ioh_elcr, 0, elcr[0]); /* XXX */
185 bus_io_write_1(sio_bc, sio_ioh_elcr, 1, elcr[1]); /* XXX */
186 }
187
188 void
189 sio_intr_setup(bc)
190 bus_chipset_tag_t bc;
191 {
192 int i;
193
194 sio_bc = bc;
195
196 if (bus_io_map(sio_bc, IO_ICU1, IO_ICUSIZE, &sio_ioh_icu1) ||
197 bus_io_map(sio_bc, IO_ICU2, IO_ICUSIZE, &sio_ioh_icu2) ||
198 bus_io_map(sio_bc, 0x4d0, 2, &sio_ioh_elcr))
199 panic("sio_intr_setup: can't map I/O ports");
200
201 #ifdef BROKEN_PROM_CONSOLE
202 /*
203 * Remember the initial values, because the prom is stupid.
204 */
205 initial_ocw1[0] = bus_io_read_1(sio_bc, sio_ioh_icu1, 1);
206 initial_ocw1[1] = bus_io_read_1(sio_bc, sio_ioh_icu2, 1);
207 initial_elcr[0] = bus_io_read_1(sio_bc, sio_ioh_elcr, 0); /* XXX */
208 initial_elcr[1] = bus_io_read_1(sio_bc, sio_ioh_elcr, 1); /* XXX */
209 #if 0
210 printf("initial_ocw1[0] = 0x%x\n", initial_ocw1[0]);
211 printf("initial_ocw1[1] = 0x%x\n", initial_ocw1[1]);
212 printf("initial_elcr[0] = 0x%x\n", initial_elcr[0]);
213 printf("initial_elcr[1] = 0x%x\n", initial_elcr[1]);
214 #endif
215 #endif
216
217 /*
218 * set up initial values for interrupt enables.
219 */
220 for (i = 0; i < ICU_LEN; i++) {
221 switch (i) {
222 case 0:
223 case 1:
224 case 8:
225 case 13:
226 /*
227 * IRQs 0, 1, 8, and 13 must always be
228 * edge-triggered.
229 */
230 if (INITIALLY_LEVEL_TRIGGERED(i))
231 printf("sio_intr_setup: %d LT!\n", i);
232 sio_setirqstat(i, INITIALLY_ENABLED(i), IST_EDGE);
233 break;
234
235 case 2:
236 /*
237 * IRQ 2 must be edge-triggered, and should be
238 * enabled (otherwise IRQs 8-15 are ignored).
239 */
240 if (INITIALLY_LEVEL_TRIGGERED(i))
241 printf("sio_intr_setup: %d LT!\n", i);
242 if (!INITIALLY_ENABLED(i))
243 printf("sio_intr_setup: %d not enabled!\n", i);
244 sio_setirqstat(i, 1, IST_EDGE);
245 break;
246
247 default:
248 /*
249 * Otherwise, disable the IRQ and set its
250 * type to (effectively) "unknown."
251 */
252 sio_setirqstat(i, INITIALLY_ENABLED(i),
253 INITIALLY_LEVEL_TRIGGERED(i) ? IST_LEVEL :
254 IST_NONE);
255 break;
256 }
257 }
258 }
259
260 const char *
261 sio_intr_string(v, irq)
262 void *v;
263 int irq;
264 {
265 static char irqstr[12]; /* 8 + 2 + NULL + sanity */
266
267 if (irq == 0 || irq >= ICU_LEN || irq == 2)
268 panic("sio_intr_string: bogus IRQ 0x%x\n", irq);
269
270 sprintf(irqstr, "isa irq %d", irq);
271 return (irqstr);
272 }
273
274 void *
275 sio_intr_establish(v, irq, type, level, ih_fun, ih_arg)
276 void *v, *ih_arg;
277 int irq;
278 int type;
279 int level;
280 int (*ih_fun)(void *);
281 {
282 struct intrhand **p, *c, *ih;
283 extern int cold;
284
285 /* no point in sleeping unless someone can free memory. */
286 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
287 if (ih == NULL)
288 panic("sio_intr_establish: can't malloc handler info");
289
290 if (irq > ICU_LEN || type == IST_NONE)
291 panic("sio_intr_establish: bogus irq or type");
292
293 switch (sio_intrsharetype[irq]) {
294 case IST_EDGE:
295 case IST_LEVEL:
296 if (type == sio_intrsharetype[irq])
297 break;
298 case IST_PULSE:
299 if (type != IST_NONE)
300 #if 0 /* XXX */
301 panic("intr_establish: can't share %s with %s",
302 isa_intrsharetype_name(sio_intrsharetype[irq]),
303 isa_intrsharetype_name(type));
304 #else /* XXX */
305 /* XXX */ panic("foo XXX");
306 #endif /* XXX */
307 break;
308 }
309
310 /*
311 * Figure out where to put the handler.
312 * This is O(N^2), but we want to preserve the order, and N is
313 * generally small.
314 */
315 for (p = &sio_intrhand[irq]; (c = *p) != NULL; p = &c->ih_next)
316 ;
317
318 /*
319 * Poke the real handler in now.
320 */
321 ih->ih_fun = ih_fun;
322 ih->ih_arg = ih_arg;
323 ih->ih_count = 0;
324 ih->ih_next = NULL;
325 ih->ih_level = 0; /* XXX meaningless on alpha */
326 ih->ih_irq = irq;
327 *p = ih;
328
329 sio_setirqstat(irq, 1, type);
330
331 return ih;
332 }
333
334 void
335 sio_intr_disestablish(v, cookie)
336 void *v;
337 void *cookie;
338 {
339
340 printf("sio_intr_disestablish(%lx)\n", cookie);
341 /* XXX */
342
343 /* XXX NEVER ALLOW AN INITIALLY-ENABLED INTERRUPT TO BE DISABLED */
344 /* XXX NEVER ALLOW AN INITIALLY-LT INTERRUPT TO BECOME UNTYPED */
345 }
346
347 /*
348 * caught a stray interrupt; notify if not too many seen already.
349 */
350 void
351 sio_strayintr(irq)
352 int irq;
353 {
354
355 if (++sio_strayintrcnt[irq] <= STRAY_MAX)
356 log(LOG_ERR, "stray interrupt %d%s\n", irq,
357 sio_strayintrcnt[irq] >= STRAY_MAX ?
358 "; stopped logging" : "");
359 }
360
361 void
362 sio_iointr(framep, vec)
363 void *framep;
364 int vec;
365 {
366 int irq, handled;
367 struct intrhand *ih;
368
369 irq = (vec - 0x800) >> 4;
370 #ifdef DIAGNOSTIC
371 if (irq > ICU_LEN || irq < 0)
372 panic("sio_iointr: irq out of range (%d)", irq);
373 #endif
374
375 #ifdef EVCNT_COUNTERS
376 sio_intr_evcnt.ev_count++;
377 #else
378 if (ICU_LEN != INTRCNT_ISA_IRQ_LEN)
379 panic("sio interrupt counter sizes inconsistent");
380 intrcnt[INTRCNT_ISA_IRQ + irq]++;
381 #endif
382
383 /*
384 * We cdr down the intrhand chain, calling each handler with
385 * its appropriate argument;
386 *
387 * The handler returns one of three values:
388 * 0 - This interrupt wasn't for me.
389 * 1 - This interrupt was for me.
390 * -1 - This interrupt might have been for me, but I don't know.
391 * If there are no handlers, or they all return 0, we flags it as a
392 * `stray' interrupt. On a system with level-triggered interrupts,
393 * we could terminate immediately when one of them returns 1; but
394 * this is PC-ish!
395 */
396 for (ih = sio_intrhand[irq], handled = 0; ih != NULL;
397 ih = ih->ih_next) {
398 int rv;
399
400 rv = (*ih->ih_fun)(ih->ih_arg);
401
402 ih->ih_count++;
403 handled = handled || (rv != 0);
404 }
405
406 if (!handled)
407 sio_strayintr(irq);
408
409 /*
410 * Some versions of the machines which use the SIO
411 * (or is it some PALcode revisions on those machines?)
412 * require the non-specific EOI to be fed to the PIC(s)
413 * by the interrupt handler.
414 */
415 if (irq > 7)
416 bus_io_write_1(sio_bc,
417 sio_ioh_icu2, 0, 0x20 | (irq & 0x07)); /* XXX */
418 bus_io_write_1(sio_bc,
419 sio_ioh_icu1, 0, 0x20 | (irq > 7 ? 2 : irq)); /* XXX */
420 }
421