FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/atpic.c
1 /*-
2 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
32 */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: releng/6.0/sys/i386/isa/atpic.c 146049 2005-05-10 12:02:18Z nyan $");
36
37 #include "opt_auto_eoi.h"
38 #include "opt_isa.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/bus.h>
43 #include <sys/interrupt.h>
44 #include <sys/kernel.h>
45 #include <sys/lock.h>
46 #include <sys/module.h>
47 #include <sys/mutex.h>
48
49 #include <machine/cpufunc.h>
50 #include <machine/frame.h>
51 #include <machine/intr_machdep.h>
52 #include <machine/md_var.h>
53 #include <machine/resource.h>
54 #include <machine/segments.h>
55
56 #include <dev/ic/i8259.h>
57 #include <i386/isa/icu.h>
58 #ifdef PC98
59 #include <pc98/cbus/cbus.h>
60 #else
61 #include <i386/isa/isa.h>
62 #endif
63 #include <isa/isavar.h>
64
65 #define MASTER 0
66 #define SLAVE 1
67
68 /*
69 * PC-98 machines wire the slave 8259A to pin 7 on the master PIC, and
70 * PC-AT machines wire the slave PIC to pin 2 on the master PIC.
71 */
72 #ifdef PC98
73 #define ICU_SLAVEID 7
74 #else
75 #define ICU_SLAVEID 2
76 #endif
77
78 /*
79 * Determine the base master and slave modes not including auto EOI support.
80 * All machines that FreeBSD supports use 8086 mode.
81 */
82 #ifdef PC98
83 /*
84 * PC-98 machines do not support auto EOI on the second PIC. Also, it
85 * seems that PC-98 machine PICs use buffered mode, and the master PIC
86 * uses special fully nested mode.
87 */
88 #define BASE_MASTER_MODE (ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086)
89 #define BASE_SLAVE_MODE (ICW4_BUF | ICW4_8086)
90 #else
91 #define BASE_MASTER_MODE ICW4_8086
92 #define BASE_SLAVE_MODE ICW4_8086
93 #endif
94
95 /* Enable automatic EOI if requested. */
96 #ifdef AUTO_EOI_1
97 #define MASTER_MODE (BASE_MASTER_MODE | ICW4_AEOI)
98 #else
99 #define MASTER_MODE BASE_MASTER_MODE
100 #endif
101 #ifdef AUTO_EOI_2
102 #define SLAVE_MODE (BASE_SLAVE_MODE | ICW4_AEOI)
103 #else
104 #define SLAVE_MODE BASE_SLAVE_MODE
105 #endif
106
107 #define IRQ_MASK(irq) (1 << (irq))
108 #define IMEN_MASK(ai) (IRQ_MASK((ai)->at_irq))
109
110 #define NUM_ISA_IRQS 16
111
112 static void atpic_init(void *dummy);
113
114 unsigned int imen; /* XXX */
115
116 inthand_t
117 IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
118 IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
119 IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
120 IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
121 IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
122 IDTVEC(atpic_intr15);
123
124 #define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq)
125
126 #define ATPIC(io, base, eoi, imenptr) \
127 { { atpic_enable_source, atpic_disable_source, (eoi), \
128 atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
129 atpic_resume, atpic_config_intr }, (io), (base), \
130 IDT_IO_INTS + (base), (imenptr) }
131
132 #define INTSRC(irq) \
133 { { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \
134 (irq) % 8 }
135
136 struct atpic {
137 struct pic at_pic;
138 int at_ioaddr;
139 int at_irqbase;
140 uint8_t at_intbase;
141 uint8_t *at_imen;
142 };
143
144 struct atpic_intsrc {
145 struct intsrc at_intsrc;
146 inthand_t *at_intr;
147 int at_irq; /* Relative to PIC base. */
148 enum intr_trigger at_trigger;
149 u_long at_count;
150 u_long at_straycount;
151 };
152
153 static void atpic_enable_source(struct intsrc *isrc);
154 static void atpic_disable_source(struct intsrc *isrc, int eoi);
155 static void atpic_eoi_master(struct intsrc *isrc);
156 static void atpic_eoi_slave(struct intsrc *isrc);
157 static void atpic_enable_intr(struct intsrc *isrc);
158 static int atpic_vector(struct intsrc *isrc);
159 static void atpic_resume(struct intsrc *isrc);
160 static int atpic_source_pending(struct intsrc *isrc);
161 static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
162 enum intr_polarity pol);
163 static void i8259_init(struct atpic *pic, int slave);
164
165 static struct atpic atpics[] = {
166 ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
167 ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
168 };
169
170 static struct atpic_intsrc atintrs[] = {
171 INTSRC(0),
172 INTSRC(1),
173 INTSRC(2),
174 INTSRC(3),
175 INTSRC(4),
176 INTSRC(5),
177 INTSRC(6),
178 INTSRC(7),
179 INTSRC(8),
180 INTSRC(9),
181 INTSRC(10),
182 INTSRC(11),
183 INTSRC(12),
184 INTSRC(13),
185 INTSRC(14),
186 INTSRC(15),
187 };
188
189 CTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS);
190
191 static __inline void
192 _atpic_eoi_master(struct intsrc *isrc)
193 {
194
195 KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
196 ("%s: mismatched pic", __func__));
197 #ifndef AUTO_EOI_1
198 outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
199 #endif
200 }
201
202 /*
203 * The data sheet says no auto-EOI on slave, but it sometimes works.
204 * So, if AUTO_EOI_2 is enabled, we use it.
205 */
206 static __inline void
207 _atpic_eoi_slave(struct intsrc *isrc)
208 {
209
210 KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
211 ("%s: mismatched pic", __func__));
212 #ifndef AUTO_EOI_2
213 outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
214 #ifndef AUTO_EOI_1
215 outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
216 #endif
217 #endif
218 }
219
220 static void
221 atpic_enable_source(struct intsrc *isrc)
222 {
223 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
224 struct atpic *ap = (struct atpic *)isrc->is_pic;
225
226 mtx_lock_spin(&icu_lock);
227 if (*ap->at_imen & IMEN_MASK(ai)) {
228 *ap->at_imen &= ~IMEN_MASK(ai);
229 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
230 }
231 mtx_unlock_spin(&icu_lock);
232 }
233
234 static void
235 atpic_disable_source(struct intsrc *isrc, int eoi)
236 {
237 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
238 struct atpic *ap = (struct atpic *)isrc->is_pic;
239
240 mtx_lock_spin(&icu_lock);
241 if (ai->at_trigger != INTR_TRIGGER_EDGE) {
242 *ap->at_imen |= IMEN_MASK(ai);
243 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
244 }
245
246 /*
247 * Take care to call these functions directly instead of through
248 * a function pointer. All of the referenced variables should
249 * still be hot in the cache.
250 */
251 if (eoi == PIC_EOI) {
252 if (isrc->is_pic == &atpics[MASTER].at_pic)
253 _atpic_eoi_master(isrc);
254 else
255 _atpic_eoi_slave(isrc);
256 }
257
258 mtx_unlock_spin(&icu_lock);
259 }
260
261 static void
262 atpic_eoi_master(struct intsrc *isrc)
263 {
264 #ifndef AUTO_EOI_1
265 mtx_lock_spin(&icu_lock);
266 _atpic_eoi_master(isrc);
267 mtx_unlock_spin(&icu_lock);
268 #endif
269 }
270
271 static void
272 atpic_eoi_slave(struct intsrc *isrc)
273 {
274 #ifndef AUTO_EOI_2
275 mtx_lock_spin(&icu_lock);
276 _atpic_eoi_slave(isrc);
277 mtx_unlock_spin(&icu_lock);
278 #endif
279 }
280
281 static void
282 atpic_enable_intr(struct intsrc *isrc)
283 {
284 }
285
286 static int
287 atpic_vector(struct intsrc *isrc)
288 {
289 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
290 struct atpic *ap = (struct atpic *)isrc->is_pic;
291
292 return (IRQ(ap, ai));
293 }
294
295 static int
296 atpic_source_pending(struct intsrc *isrc)
297 {
298 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
299 struct atpic *ap = (struct atpic *)isrc->is_pic;
300
301 return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
302 }
303
304 static void
305 atpic_resume(struct intsrc *isrc)
306 {
307 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
308 struct atpic *ap = (struct atpic *)isrc->is_pic;
309
310 if (ai->at_irq == 0) {
311 i8259_init(ap, ap == &atpics[SLAVE]);
312 #ifndef PC98
313 if (ap == &atpics[SLAVE] && elcr_found)
314 elcr_resume();
315 #endif
316 }
317 }
318
319 static int
320 atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
321 enum intr_polarity pol)
322 {
323 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
324 u_int vector;
325
326 /* Map conforming values to edge/hi and sanity check the values. */
327 if (trig == INTR_TRIGGER_CONFORM)
328 trig = INTR_TRIGGER_EDGE;
329 if (pol == INTR_POLARITY_CONFORM)
330 pol = INTR_POLARITY_HIGH;
331 vector = atpic_vector(isrc);
332 if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
333 (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
334 printf(
335 "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
336 vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
337 pol == INTR_POLARITY_HIGH ? "high" : "low");
338 return (EINVAL);
339 }
340
341 /* If there is no change, just return. */
342 if (ai->at_trigger == trig)
343 return (0);
344
345 #ifdef PC98
346 if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) &&
347 trig == INTR_TRIGGER_LEVEL) {
348 if (bootverbose)
349 printf(
350 "atpic: Ignoring invalid level/low configuration for IRQ%u\n",
351 vector);
352 return (EINVAL);
353 }
354 return (ENXIO);
355 #else
356 /*
357 * Certain IRQs can never be level/lo, so don't try to set them
358 * that way if asked. At least some ELCR registers ignore setting
359 * these bits as well.
360 */
361 if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
362 trig == INTR_TRIGGER_LEVEL) {
363 if (bootverbose)
364 printf(
365 "atpic: Ignoring invalid level/low configuration for IRQ%u\n",
366 vector);
367 return (EINVAL);
368 }
369 if (!elcr_found) {
370 if (bootverbose)
371 printf("atpic: No ELCR to configure IRQ%u as %s\n",
372 vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
373 "level/low");
374 return (ENXIO);
375 }
376 if (bootverbose)
377 printf("atpic: Programming IRQ%u as %s\n", vector,
378 trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
379 mtx_lock_spin(&icu_lock);
380 elcr_write_trigger(atpic_vector(isrc), trig);
381 ai->at_trigger = trig;
382 mtx_unlock_spin(&icu_lock);
383 return (0);
384 #endif /* PC98 */
385 }
386
387 static void
388 i8259_init(struct atpic *pic, int slave)
389 {
390 int imr_addr;
391
392 /* Reset the PIC and program with next four bytes. */
393 mtx_lock_spin(&icu_lock);
394 #ifdef DEV_MCA
395 /* MCA uses level triggered interrupts. */
396 if (MCA_system)
397 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
398 else
399 #endif
400 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
401 imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
402
403 /* Start vector. */
404 outb(imr_addr, pic->at_intbase);
405
406 /*
407 * Setup slave links. For the master pic, indicate what line
408 * the slave is configured on. For the slave indicate
409 * which line on the master we are connected to.
410 */
411 if (slave)
412 outb(imr_addr, ICU_SLAVEID);
413 else
414 outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
415
416 /* Set mode. */
417 if (slave)
418 outb(imr_addr, SLAVE_MODE);
419 else
420 outb(imr_addr, MASTER_MODE);
421
422 /* Set interrupt enable mask. */
423 outb(imr_addr, *pic->at_imen);
424
425 /* Reset is finished, default to IRR on read. */
426 outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
427
428 #ifndef PC98
429 /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
430 if (!slave)
431 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
432 #endif
433 mtx_unlock_spin(&icu_lock);
434 }
435
436 void
437 atpic_startup(void)
438 {
439 struct atpic_intsrc *ai;
440 int i;
441
442 /* Start off with all interrupts disabled. */
443 imen = 0xffff;
444 i8259_init(&atpics[MASTER], 0);
445 i8259_init(&atpics[SLAVE], 1);
446 atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
447
448 /* Install low-level interrupt handlers for all of our IRQs. */
449 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
450 if (i == ICU_SLAVEID)
451 continue;
452 ai->at_intsrc.is_count = &ai->at_count;
453 ai->at_intsrc.is_straycount = &ai->at_straycount;
454 setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
455 ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
456 GSEL(GCODE_SEL, SEL_KPL));
457 }
458
459 #ifdef DEV_MCA
460 /* For MCA systems, all interrupts are level triggered. */
461 if (MCA_system)
462 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
463 ai->at_trigger = INTR_TRIGGER_LEVEL;
464 else
465 #endif
466
467 #ifdef PC98
468 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
469 switch (i) {
470 case 0:
471 case 1:
472 case 7:
473 case 8:
474 ai->at_trigger = INTR_TRIGGER_EDGE;
475 break;
476 default:
477 ai->at_trigger = INTR_TRIGGER_LEVEL;
478 break;
479 }
480 #else
481 /*
482 * Look for an ELCR. If we find one, update the trigger modes.
483 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
484 * edge triggered and that everything else is level triggered.
485 * We only use the trigger information to reprogram the ELCR if
486 * we have one and as an optimization to avoid masking edge
487 * triggered interrupts. For the case that we don't have an ELCR,
488 * it doesn't hurt to mask an edge triggered interrupt, so we
489 * assume level trigger for any interrupt that we aren't sure is
490 * edge triggered.
491 */
492 if (elcr_found) {
493 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
494 ai->at_trigger = elcr_read_trigger(i);
495 } else {
496 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
497 switch (i) {
498 case 0:
499 case 1:
500 case 2:
501 case 8:
502 case 13:
503 ai->at_trigger = INTR_TRIGGER_EDGE;
504 break;
505 default:
506 ai->at_trigger = INTR_TRIGGER_LEVEL;
507 break;
508 }
509 }
510 #endif /* PC98 */
511 }
512
513 static void
514 atpic_init(void *dummy __unused)
515 {
516 struct atpic_intsrc *ai;
517 int i;
518
519 /* Loop through all interrupt sources and add them. */
520 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
521 if (i == ICU_SLAVEID)
522 continue;
523 intr_register_source(&ai->at_intsrc);
524 }
525 }
526 SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
527
528 void
529 atpic_handle_intr(struct intrframe iframe)
530 {
531 struct intsrc *isrc;
532
533 KASSERT((u_int)iframe.if_vec < NUM_ISA_IRQS,
534 ("unknown int %d\n", iframe.if_vec));
535 isrc = &atintrs[iframe.if_vec].at_intsrc;
536
537 /*
538 * If we don't have an ithread, see if this is a spurious
539 * interrupt.
540 */
541 if (isrc->is_ithread == NULL &&
542 (iframe.if_vec == 7 || iframe.if_vec == 15)) {
543 int port, isr;
544
545 /*
546 * Read the ISR register to see if IRQ 7/15 is really
547 * pending. Reset read register back to IRR when done.
548 */
549 port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
550 mtx_lock_spin(&icu_lock);
551 outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
552 isr = inb(port);
553 outb(port, OCW3_SEL | OCW3_RR);
554 mtx_unlock_spin(&icu_lock);
555 if ((isr & IRQ_MASK(7)) == 0)
556 return;
557 }
558 intr_execute_handlers(isrc, &iframe);
559 }
560
561 #ifdef DEV_ISA
562 /*
563 * Bus attachment for the ISA PIC.
564 */
565 static struct isa_pnp_id atpic_ids[] = {
566 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
567 { 0 }
568 };
569
570 static int
571 atpic_probe(device_t dev)
572 {
573 int result;
574
575 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
576 if (result <= 0)
577 device_quiet(dev);
578 return (result);
579 }
580
581 /*
582 * We might be granted IRQ 2, as this is typically consumed by chaining
583 * between the two PIC components. If we're using the APIC, however,
584 * this may not be the case, and as such we should free the resource.
585 * (XXX untested)
586 *
587 * The generic ISA attachment code will handle allocating any other resources
588 * that we don't explicitly claim here.
589 */
590 static int
591 atpic_attach(device_t dev)
592 {
593 struct resource *res;
594 int rid;
595
596 /* Try to allocate our IRQ and then free it. */
597 rid = 0;
598 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
599 if (res != NULL)
600 bus_release_resource(dev, SYS_RES_IRQ, rid, res);
601 return (0);
602 }
603
604 static device_method_t atpic_methods[] = {
605 /* Device interface */
606 DEVMETHOD(device_probe, atpic_probe),
607 DEVMETHOD(device_attach, atpic_attach),
608 DEVMETHOD(device_detach, bus_generic_detach),
609 DEVMETHOD(device_shutdown, bus_generic_shutdown),
610 DEVMETHOD(device_suspend, bus_generic_suspend),
611 DEVMETHOD(device_resume, bus_generic_resume),
612 { 0, 0 }
613 };
614
615 static driver_t atpic_driver = {
616 "atpic",
617 atpic_methods,
618 1, /* no softc */
619 };
620
621 static devclass_t atpic_devclass;
622
623 DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
624 #ifndef PC98
625 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
626 #endif
627
628 /*
629 * Return a bitmap of the current interrupt requests. This is 8259-specific
630 * and is only suitable for use at probe time.
631 */
632 intrmask_t
633 isa_irq_pending(void)
634 {
635 u_char irr1;
636 u_char irr2;
637
638 irr1 = inb(IO_ICU1);
639 irr2 = inb(IO_ICU2);
640 return ((irr2 << 8) | irr1);
641 }
642 #endif /* DEV_ISA */
Cache object: a21c973d8b2fe2ab9d4288e0443b89da
|