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