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