1 /*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91
37 * $FreeBSD$
38 */
39
40 #include "opt_auto_eoi.h"
41
42 #include <sys/param.h>
43 #ifndef SMP
44 #include <machine/lock.h>
45 #endif
46 #include <sys/systm.h>
47 #include <sys/syslog.h>
48 #include <machine/ipl.h>
49 #include <machine/md_var.h>
50 #include <machine/segments.h>
51 #if defined(APIC_IO)
52 #include <machine/smp.h>
53 #include <machine/smptests.h> /** FAST_HI */
54 #endif /* APIC_IO */
55 #include <i386/isa/isa_device.h>
56 #ifdef PC98
57 #include <pc98/pc98/pc98.h>
58 #include <pc98/pc98/pc98_machdep.h>
59 #include <pc98/pc98/epsonio.h>
60 #else
61 #include <i386/isa/isa.h>
62 #endif
63 #include <i386/isa/icu.h>
64 #include "vector.h"
65
66 #include <i386/isa/intr_machdep.h>
67 #include <sys/interrupt.h>
68 #ifdef APIC_IO
69 #include <machine/clock.h>
70 #endif
71
72 /* XXX should be in suitable include files */
73 #ifdef PC98
74 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */
75 #define ICU_SLAVEID 7
76 #else
77 #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
78 #define ICU_SLAVEID 2
79 #endif
80
81 #ifdef APIC_IO
82 /*
83 * This is to accommodate "mixed-mode" programming for
84 * motherboards that don't connect the 8254 to the IO APIC.
85 */
86 #define AUTO_EOI_1 1
87 #endif
88
89 u_long *intr_countp[ICU_LEN];
90 inthand2_t *intr_handler[ICU_LEN];
91 u_int intr_mask[ICU_LEN];
92 static u_int* intr_mptr[ICU_LEN];
93 void *intr_unit[ICU_LEN];
94
95 static inthand_t *fastintr[ICU_LEN] = {
96 &IDTVEC(fastintr0), &IDTVEC(fastintr1),
97 &IDTVEC(fastintr2), &IDTVEC(fastintr3),
98 &IDTVEC(fastintr4), &IDTVEC(fastintr5),
99 &IDTVEC(fastintr6), &IDTVEC(fastintr7),
100 &IDTVEC(fastintr8), &IDTVEC(fastintr9),
101 &IDTVEC(fastintr10), &IDTVEC(fastintr11),
102 &IDTVEC(fastintr12), &IDTVEC(fastintr13),
103 &IDTVEC(fastintr14), &IDTVEC(fastintr15)
104 #if defined(APIC_IO)
105 , &IDTVEC(fastintr16), &IDTVEC(fastintr17),
106 &IDTVEC(fastintr18), &IDTVEC(fastintr19),
107 &IDTVEC(fastintr20), &IDTVEC(fastintr21),
108 &IDTVEC(fastintr22), &IDTVEC(fastintr23)
109 #endif /* APIC_IO */
110 };
111
112 static inthand_t *slowintr[ICU_LEN] = {
113 &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
114 &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
115 &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
116 &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15)
117 #if defined(APIC_IO)
118 , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19),
119 &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23)
120 #endif /* APIC_IO */
121 };
122
123 static inthand2_t isa_strayintr;
124
125 #ifdef PC98
126 #define NMI_PARITY 0x04
127 #define NMI_EPARITY 0x02
128 #else
129 #define NMI_PARITY (1 << 7)
130 #define NMI_IOCHAN (1 << 6)
131 #define ENMI_WATCHDOG (1 << 7)
132 #define ENMI_BUSTIMER (1 << 6)
133 #define ENMI_IOSTATUS (1 << 5)
134 #endif
135
136 /*
137 * Handle a NMI, possibly a machine check.
138 * return true to panic system, false to ignore.
139 */
140 int
141 isa_nmi(cd)
142 int cd;
143 {
144 #ifdef PC98
145 int port = inb(0x33);
146 if (epson_machine_id == 0x20)
147 epson_outb(0xc16, epson_inb(0xc16) | 0x1);
148 if (port & NMI_PARITY) {
149 panic("BASE RAM parity error, likely hardware failure.");
150 } else if (port & NMI_EPARITY) {
151 panic("EXTENDED RAM parity error, likely hardware failure.");
152 } else {
153 printf("\nNMI Resume ??\n");
154 return(0);
155 }
156 #else /* IBM-PC */
157 int isa_port = inb(0x61);
158 int eisa_port = inb(0x461);
159
160 if (isa_port & NMI_PARITY)
161 panic("RAM parity error, likely hardware failure.");
162
163 if (isa_port & NMI_IOCHAN)
164 panic("I/O channel check, likely hardware failure.");
165
166 /*
167 * On a real EISA machine, this will never happen. However it can
168 * happen on ISA machines which implement XT style floating point
169 * error handling (very rare). Save them from a meaningless panic.
170 */
171 if (eisa_port == 0xff)
172 return(0);
173
174 if (eisa_port & ENMI_WATCHDOG)
175 panic("EISA watchdog timer expired, likely hardware failure.");
176
177 if (eisa_port & ENMI_BUSTIMER)
178 panic("EISA bus timeout, likely hardware failure.");
179
180 if (eisa_port & ENMI_IOSTATUS)
181 panic("EISA I/O port status error.");
182
183 printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port);
184 return(0);
185 #endif
186 }
187
188 /*
189 * Fill in default interrupt table (in case of spuruious interrupt
190 * during configuration of kernel, setup interrupt control unit
191 */
192 void
193 isa_defaultirq()
194 {
195 int i;
196
197 /* icu vectors */
198 for (i = 0; i < ICU_LEN; i++)
199 icu_unset(i, (inthand2_t *)NULL);
200
201 /* initialize 8259's */
202 outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
203
204 outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */
205 outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */
206 #ifdef PC98
207 #ifdef AUTO_EOI_1
208 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */
209 #else
210 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */
211 #endif
212 #else /* IBM-PC */
213 #ifdef AUTO_EOI_1
214 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */
215 #else
216 outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */
217 #endif
218 #endif /* PC98 */
219 outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */
220 outb(IO_ICU1, 0x0a); /* default to IRR on read */
221 #ifndef PC98
222 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
223 #endif /* !PC98 */
224
225 outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
226 outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */
227 outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */
228 #ifdef PC98
229 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */
230 #else /* IBM-PC */
231 #ifdef AUTO_EOI_2
232 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */
233 #else
234 outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */
235 #endif
236 #endif /* PC98 */
237 outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */
238 outb(IO_ICU2, 0x0a); /* default to IRR on read */
239 }
240
241 /*
242 * Caught a stray interrupt, notify
243 */
244 static void
245 isa_strayintr(vcookiep)
246 void *vcookiep;
247 {
248 int intr = (void **)vcookiep - &intr_unit[0];
249
250 /* DON'T BOTHER FOR NOW! */
251 /* for some reason, we get bursts of intr #7, even if not enabled! */
252 /*
253 * Well the reason you got bursts of intr #7 is because someone
254 * raised an interrupt line and dropped it before the 8259 could
255 * prioritize it. This is documented in the intel data book. This
256 * means you have BAD hardware! I have changed this so that only
257 * the first 5 get logged, then it quits logging them, and puts
258 * out a special message. rgrimes 3/25/1993
259 */
260 /*
261 * XXX TODO print a different message for #7 if it is for a
262 * glitch. Glitches can be distinguished from real #7's by
263 * testing that the in-service bit is _not_ set. The test
264 * must be done before sending an EOI so it can't be done if
265 * we are using AUTO_EOI_1.
266 */
267 if (intrcnt[NR_DEVICES + intr] <= 5)
268 log(LOG_ERR, "stray irq %d\n", intr);
269 if (intrcnt[NR_DEVICES + intr] == 5)
270 log(LOG_CRIT,
271 "too many stray irq %d's; not logging any more\n", intr);
272 }
273
274 /*
275 * Return a bitmap of the current interrupt requests. This is 8259-specific
276 * and is only suitable for use at probe time.
277 */
278 intrmask_t
279 isa_irq_pending()
280 {
281 u_char irr1;
282 u_char irr2;
283
284 irr1 = inb(IO_ICU1);
285 irr2 = inb(IO_ICU2);
286 return ((irr2 << 8) | irr1);
287 }
288
289 int
290 update_intr_masks(void)
291 {
292 int intr, n=0;
293 u_int mask,*maskptr;
294
295 for (intr=0; intr < ICU_LEN; intr ++) {
296 #if defined(APIC_IO)
297 /* no 8259 SLAVE to ignore */
298 #else
299 if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */
300 #endif /* APIC_IO */
301 maskptr = intr_mptr[intr];
302 if (!maskptr) continue;
303 *maskptr |= SWI_CLOCK_MASK | (1 << intr);
304 mask = *maskptr;
305 if (mask != intr_mask[intr]) {
306 #if 0
307 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
308 intr, intr_mask[intr], mask, maskptr);
309 #endif
310 intr_mask[intr]=mask;
311 n++;
312 }
313
314 }
315 return (n);
316 }
317
318 /*
319 * The find_device_id function is only required because of the way the
320 * device names are currently stored for reporting in systat or vmstat.
321 * In fact, those programs should be modified to use the sysctl interface
322 * to obtain a list of driver names by traversing intreclist_head[irq].
323 */
324 static int
325 find_device_id(int irq)
326 {
327 char buf[16];
328 char *cp;
329 int free_id, id;
330
331 snprintf(buf, sizeof(buf), "pci irq%d", irq);
332 cp = intrnames;
333 /* default to 0, which corresponds to clk0 */
334 free_id = 0;
335
336 for (id = 0; id < NR_DEVICES; id++) {
337 if (strcmp(cp, buf) == 0)
338 return (id);
339 if (free_id == 0 && strcmp(cp, "pci irqnn") == 0)
340 free_id = id;
341 while (*cp++ != '\0');
342 }
343 #if 0
344 if (free_id == 0) {
345 /*
346 * All pci irq counters are in use, perhaps because config
347 * is old so there aren't any. Abuse the clk0 counter.
348 */
349 printf("\tcounting shared irq%d as clk0 irq\n", irq);
350 }
351 #endif
352 return (free_id);
353 }
354
355 void
356 update_intrname(int intr, int device_id)
357 {
358 char *cp;
359 int id;
360
361 if (device_id == -1)
362 device_id = find_device_id(intr);
363
364 if ((u_int)device_id >= NR_DEVICES)
365 return;
366
367 intr_countp[intr] = &intrcnt[device_id];
368
369 for (cp = intrnames, id = 0; id <= device_id; id++)
370 while (*cp++ != '\0')
371 ;
372 if (cp > eintrnames)
373 return;
374 if (intr < 10) {
375 cp[-3] = intr + '';
376 cp[-2] = ' ';
377 } else if (intr < 20) {
378 cp[-3] = '1';
379 cp[-2] = intr - 10 + '';
380 } else {
381 cp[-3] = '2';
382 cp[-2] = intr - 20 + '';
383 }
384 }
385
386
387 int
388 icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
389 {
390 #ifdef FAST_HI
391 int select; /* the select register is 8 bits */
392 int vector;
393 u_int32_t value; /* the window register is 32 bits */
394 #endif /* FAST_HI */
395 u_long ef;
396 u_int mask = (maskptr ? *maskptr : 0);
397
398 #if defined(APIC_IO)
399 if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */
400 #else
401 if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID)
402 #endif /* APIC_IO */
403 if (intr_handler[intr] != isa_strayintr)
404 return (EBUSY);
405
406 ef = read_eflags();
407 disable_intr();
408 intr_handler[intr] = handler;
409 intr_mptr[intr] = maskptr;
410 intr_mask[intr] = mask | SWI_CLOCK_MASK | (1 << intr);
411 intr_unit[intr] = arg;
412 #ifdef FAST_HI
413 if (flags & INTR_FAST) {
414 vector = TPR_FAST_INTS + intr;
415 setidt(vector, fastintr[intr],
416 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
417 }
418 else {
419 vector = TPR_SLOW_INTS + intr;
420 #ifdef APIC_INTR_REORDER
421 #ifdef APIC_INTR_HIGHPRI_CLOCK
422 /* XXX: Hack (kludge?) for more accurate clock. */
423 if (intr == apic_8254_intr || intr == 8) {
424 vector = TPR_FAST_INTS + intr;
425 }
426 #endif
427 #endif
428 setidt(vector, slowintr[intr],
429 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
430 }
431 #ifdef APIC_INTR_REORDER
432 set_lapic_isrloc(intr, vector);
433 #endif
434 /*
435 * Reprogram the vector in the IO APIC.
436 */
437 if (int_to_apicintpin[intr].ioapic >= 0) {
438 select = int_to_apicintpin[intr].redirindex;
439 value = io_apic_read(int_to_apicintpin[intr].ioapic,
440 select) & ~IOART_INTVEC;
441 io_apic_write(int_to_apicintpin[intr].ioapic,
442 select, value | vector);
443 }
444 #else
445 setidt(ICU_OFFSET + intr,
446 flags & INTR_FAST ? fastintr[intr] : slowintr[intr],
447 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
448 #endif /* FAST_HI */
449 INTREN(1 << intr);
450 MPINTR_UNLOCK();
451 write_eflags(ef);
452 return (0);
453 }
454
455 void
456 register_imask(dvp, mask)
457 struct isa_device *dvp;
458 u_int mask;
459 {
460 if (dvp->id_alive && dvp->id_irq) {
461 int intr;
462
463 intr = ffs(dvp->id_irq) - 1;
464 intr_mask[intr] = mask | (1 <<intr);
465 }
466 (void) update_intr_masks();
467 }
468
469 int
470 icu_unset(intr, handler)
471 int intr;
472 inthand2_t *handler;
473 {
474 u_long ef;
475
476 if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr])
477 return (EINVAL);
478
479 INTRDIS(1 << intr);
480 ef = read_eflags();
481 disable_intr();
482 intr_countp[intr] = &intrcnt[NR_DEVICES + intr];
483 intr_handler[intr] = isa_strayintr;
484 intr_mptr[intr] = NULL;
485 intr_mask[intr] = HWI_MASK | SWI_MASK;
486 intr_unit[intr] = &intr_unit[intr];
487 #ifdef FAST_HI_XXX
488 /* XXX how do I re-create dvp here? */
489 setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr,
490 slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
491 #else /* FAST_HI */
492 #ifdef APIC_INTR_REORDER
493 set_lapic_isrloc(intr, ICU_OFFSET + intr);
494 #endif
495 setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
496 GSEL(GCODE_SEL, SEL_KPL));
497 #endif /* FAST_HI */
498 MPINTR_UNLOCK();
499 write_eflags(ef);
500 return (0);
501 }
Cache object: 4a80e45668ac00a4229c8edca7e94701
|