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