sio_pic.c revision 1.18 1 /* $NetBSD: sio_pic.c,v 1.18 1997/04/07 23:40:46 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 <machine/options.h> /* Config options headers */
31 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
32
33 __KERNEL_RCSID(0, "$NetBSD: sio_pic.c,v 1.18 1997/04/07 23:40:46 cgd Exp $");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/malloc.h>
39 #include <sys/syslog.h>
40
41 #include <machine/intr.h>
42 #include <machine/bus.h>
43
44 #include <dev/isa/isareg.h>
45 #include <dev/isa/isavar.h>
46 #include <alpha/pci/siovar.h>
47
48 #ifndef EVCNT_COUNTERS
49 #include <machine/intrcnt.h>
50 #endif
51
52 #include "sio.h"
53
54 /*
55 * To add to the long history of wonderful PROM console traits,
56 * AlphaStation PROMs don't reset themselves completely on boot!
57 * Therefore, if an interrupt was turned on when the kernel was
58 * started, we're not going to EVER turn it off... I don't know
59 * what will happen if new interrupts (that the PROM console doesn't
60 * want) are turned on. I'll burn that bridge when I come to it.
61 */
62 #define BROKEN_PROM_CONSOLE
63
64 /*
65 * Private functions and variables.
66 */
67
68 bus_space_tag_t sio_iot;
69 bus_space_handle_t sio_ioh_icu1, sio_ioh_icu2, sio_ioh_elcr;
70
71 #define ICU_LEN 16 /* number of ISA IRQs */
72
73 static struct alpha_shared_intr *sio_intr;
74 #ifdef EVCNT_COUNTERS
75 struct evcnt sio_intr_evcnt;
76 #endif
77
78 #ifndef STRAY_MAX
79 #ifdef BROKEN_PROM_CONSOLE
80 /*
81 * If prom console is broken, because initial interrupt settings
82 * must be kept, there's no way to escape stray interrupts.
83 */
84 #define STRAY_MAX 0
85 #else
86 #define STRAY_MAX 5
87 #endif
88 #endif
89
90 #ifdef BROKEN_PROM_CONSOLE
91 /*
92 * If prom console is broken, must remember the initial interrupt
93 * settings and enforce them. WHEE!
94 */
95 u_int8_t initial_ocw1[2];
96 u_int8_t initial_elcr[2];
97 #define INITIALLY_ENABLED(irq) \
98 ((initial_ocw1[(irq) / 8] & (1 << ((irq) % 8))) == 0)
99 #define INITIALLY_LEVEL_TRIGGERED(irq) \
100 ((initial_elcr[(irq) / 8] & (1 << ((irq) % 8))) != 0)
101 #else
102 #define INITIALLY_ENABLED(irq) ((irq) == 2 ? 1 : 0)
103 #define INITIALLY_LEVEL_TRIGGERED(irq) 0
104 #endif
105
106 void sio_setirqstat __P((int, int, int));
107
108 void
109 sio_setirqstat(irq, enabled, type)
110 int irq, enabled;
111 int type;
112 {
113 u_int8_t ocw1[2], elcr[2];
114 int icu, bit;
115
116 #if 0
117 printf("sio_setirqstat: irq %d: %s, %s\n", irq,
118 enabled ? "enabled" : "disabled", isa_intr_typename(type));
119 #endif
120
121 icu = irq / 8;
122 bit = irq % 8;
123
124 ocw1[0] = bus_space_read_1(sio_iot, sio_ioh_icu1, 1);
125 ocw1[1] = bus_space_read_1(sio_iot, sio_ioh_icu2, 1);
126 elcr[0] = bus_space_read_1(sio_iot, sio_ioh_elcr, 0); /* XXX */
127 elcr[1] = bus_space_read_1(sio_iot, sio_ioh_elcr, 1); /* XXX */
128
129 /*
130 * interrupt enable: set bit to mask (disable) interrupt.
131 */
132 if (enabled)
133 ocw1[icu] &= ~(1 << bit);
134 else
135 ocw1[icu] |= 1 << bit;
136
137 /*
138 * interrupt type select: set bit to get level-triggered.
139 */
140 if (type == IST_LEVEL)
141 elcr[icu] |= 1 << bit;
142 else
143 elcr[icu] &= ~(1 << bit);
144
145 #ifdef not_here
146 /* see the init function... */
147 ocw1[0] &= ~0x04; /* always enable IRQ2 on first PIC */
148 elcr[0] &= ~0x07; /* IRQ[0-2] must be edge-triggered */
149 elcr[1] &= ~0x21; /* IRQ[13,8] must be edge-triggered */
150 #endif
151
152 #ifdef BROKEN_PROM_CONSOLE
153 /*
154 * make sure that the initially clear bits (unmasked interrupts)
155 * are never set, and that the initially-level-triggered
156 * intrrupts always remain level-triggered, to keep the prom happy.
157 */
158 if ((ocw1[0] & ~initial_ocw1[0]) != 0 ||
159 (ocw1[1] & ~initial_ocw1[1]) != 0 ||
160 (elcr[0] & initial_elcr[0]) != initial_elcr[0] ||
161 (elcr[1] & initial_elcr[1]) != initial_elcr[1]) {
162 printf("sio_sis: initial: ocw = (%2x,%2x), elcr = (%2x,%2x)\n",
163 initial_ocw1[0], initial_ocw1[1],
164 initial_elcr[0], initial_elcr[1]);
165 printf(" current: ocw = (%2x,%2x), elcr = (%2x,%2x)\n",
166 ocw1[0], ocw1[1], elcr[0], elcr[1]);
167 panic("sio_setirqstat: hosed");
168 }
169 #endif
170
171 bus_space_write_1(sio_iot, sio_ioh_icu1, 1, ocw1[0]);
172 bus_space_write_1(sio_iot, sio_ioh_icu2, 1, ocw1[1]);
173 bus_space_write_1(sio_iot, sio_ioh_elcr, 0, elcr[0]); /* XXX */
174 bus_space_write_1(sio_iot, sio_ioh_elcr, 1, elcr[1]); /* XXX */
175 }
176
177 void
178 sio_intr_setup(iot)
179 bus_space_tag_t iot;
180 {
181 int i;
182
183 sio_iot = iot;
184
185 if (bus_space_map(sio_iot, IO_ICU1, IO_ICUSIZE, 0, &sio_ioh_icu1) ||
186 bus_space_map(sio_iot, IO_ICU2, IO_ICUSIZE, 0, &sio_ioh_icu2) ||
187 bus_space_map(sio_iot, 0x4d0, 2, 0, &sio_ioh_elcr))
188 panic("sio_intr_setup: can't map I/O ports");
189
190 #ifdef BROKEN_PROM_CONSOLE
191 /*
192 * Remember the initial values, because the prom is stupid.
193 */
194 initial_ocw1[0] = bus_space_read_1(sio_iot, sio_ioh_icu1, 1);
195 initial_ocw1[1] = bus_space_read_1(sio_iot, sio_ioh_icu2, 1);
196 initial_elcr[0] = bus_space_read_1(sio_iot, sio_ioh_elcr, 0); /* XXX */
197 initial_elcr[1] = bus_space_read_1(sio_iot, sio_ioh_elcr, 1); /* XXX */
198 #if 0
199 printf("initial_ocw1[0] = 0x%x\n", initial_ocw1[0]);
200 printf("initial_ocw1[1] = 0x%x\n", initial_ocw1[1]);
201 printf("initial_elcr[0] = 0x%x\n", initial_elcr[0]);
202 printf("initial_elcr[1] = 0x%x\n", initial_elcr[1]);
203 #endif
204 #endif
205
206 sio_intr = alpha_shared_intr_alloc(ICU_LEN);
207
208 /*
209 * set up initial values for interrupt enables.
210 */
211 for (i = 0; i < ICU_LEN; i++) {
212 alpha_shared_intr_set_maxstrays(sio_intr, i, STRAY_MAX);
213
214 switch (i) {
215 case 0:
216 case 1:
217 case 8:
218 case 13:
219 /*
220 * IRQs 0, 1, 8, and 13 must always be
221 * edge-triggered.
222 */
223 if (INITIALLY_LEVEL_TRIGGERED(i))
224 printf("sio_intr_setup: %d LT!\n", i);
225 sio_setirqstat(i, INITIALLY_ENABLED(i), IST_EDGE);
226 alpha_shared_intr_set_dfltsharetype(sio_intr, i,
227 IST_EDGE);
228 break;
229
230 case 2:
231 /*
232 * IRQ 2 must be edge-triggered, and should be
233 * enabled (otherwise IRQs 8-15 are ignored).
234 */
235 if (INITIALLY_LEVEL_TRIGGERED(i))
236 printf("sio_intr_setup: %d LT!\n", i);
237 if (!INITIALLY_ENABLED(i))
238 printf("sio_intr_setup: %d not enabled!\n", i);
239 sio_setirqstat(i, 1, IST_EDGE);
240 alpha_shared_intr_set_dfltsharetype(sio_intr, i,
241 IST_UNUSABLE);
242 break;
243
244 default:
245 /*
246 * Otherwise, disable the IRQ and set its
247 * type to (effectively) "unknown."
248 */
249 sio_setirqstat(i, INITIALLY_ENABLED(i),
250 INITIALLY_LEVEL_TRIGGERED(i) ? IST_LEVEL :
251 IST_NONE);
252 alpha_shared_intr_set_dfltsharetype(sio_intr, 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 isa 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, fn, arg)
276 void *v, *arg;
277 int irq;
278 int type;
279 int level;
280 int (*fn)(void *);
281 {
282 void *cookie;
283
284 if (irq > ICU_LEN || type == IST_NONE)
285 panic("sio_intr_establish: bogus irq or type");
286
287 cookie = alpha_shared_intr_establish(sio_intr, irq, type, level, fn,
288 arg, "isa irq");
289
290 if (cookie)
291 sio_setirqstat(irq, alpha_shared_intr_isactive(sio_intr, irq),
292 alpha_shared_intr_get_sharetype(sio_intr, irq));
293
294 return (cookie);
295 }
296
297 void
298 sio_intr_disestablish(v, cookie)
299 void *v;
300 void *cookie;
301 {
302
303 printf("sio_intr_disestablish(%p)\n", cookie);
304 /* XXX */
305
306 /* XXX NEVER ALLOW AN INITIALLY-ENABLED INTERRUPT TO BE DISABLED */
307 /* XXX NEVER ALLOW AN INITIALLY-LT INTERRUPT TO BECOME UNTYPED */
308 }
309
310 void
311 sio_iointr(framep, vec)
312 void *framep;
313 unsigned long vec;
314 {
315 int irq;
316
317 irq = (vec - 0x800) >> 4;
318 #ifdef DIAGNOSTIC
319 if (irq > ICU_LEN || irq < 0)
320 panic("sio_iointr: irq out of range (%d)", irq);
321 #endif
322
323 #ifdef EVCNT_COUNTERS
324 sio_intr_evcnt.ev_count++;
325 #else
326 #ifdef DEBUG
327 if (ICU_LEN != INTRCNT_ISA_IRQ_LEN)
328 panic("sio interrupt counter sizes inconsistent");
329 #endif
330 intrcnt[INTRCNT_ISA_IRQ + irq]++;
331 #endif
332
333 if (!alpha_shared_intr_dispatch(sio_intr, irq))
334 alpha_shared_intr_stray(sio_intr, irq, "isa irq");
335
336 /*
337 * Some versions of the machines which use the SIO
338 * (or is it some PALcode revisions on those machines?)
339 * require the non-specific EOI to be fed to the PIC(s)
340 * by the interrupt handler.
341 */
342 if (irq > 7)
343 bus_space_write_1(sio_iot,
344 sio_ioh_icu2, 0, 0x20 | (irq & 0x07)); /* XXX */
345 bus_space_write_1(sio_iot,
346 sio_ioh_icu1, 0, 0x20 | (irq > 7 ? 2 : irq)); /* XXX */
347 }
348