FreeBSD/Linux Kernel Cross Reference
sys/amd64/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$");
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 #include <sys/proc.h>
49
50 #include <machine/cpufunc.h>
51 #include <machine/frame.h>
52 #include <machine/intr_machdep.h>
53 #include <machine/md_var.h>
54 #include <machine/resource.h>
55 #include <machine/segments.h>
56
57 #include <dev/ic/i8259.h>
58 #include <amd64/isa/icu.h>
59 #include <amd64/isa/isa.h>
60
61 #include <isa/isavar.h>
62
63 #define MASTER 0
64 #define SLAVE 1
65
66 /*
67 * PC-AT machines wire the slave PIC to pin 2 on the master PIC.
68 */
69 #define ICU_SLAVEID 2
70
71 /*
72 * Determine the base master and slave modes not including auto EOI support.
73 * All machines that FreeBSD supports use 8086 mode.
74 */
75 #define BASE_MASTER_MODE ICW4_8086
76 #define BASE_SLAVE_MODE ICW4_8086
77
78 /* Enable automatic EOI if requested. */
79 #ifdef AUTO_EOI_1
80 #define MASTER_MODE (BASE_MASTER_MODE | ICW4_AEOI)
81 #else
82 #define MASTER_MODE BASE_MASTER_MODE
83 #endif
84 #ifdef AUTO_EOI_2
85 #define SLAVE_MODE (BASE_SLAVE_MODE | ICW4_AEOI)
86 #else
87 #define SLAVE_MODE BASE_SLAVE_MODE
88 #endif
89
90 #define IRQ_MASK(irq) (1 << (irq))
91 #define IMEN_MASK(ai) (IRQ_MASK((ai)->at_irq))
92
93 #define NUM_ISA_IRQS 16
94
95 static void atpic_init(void *dummy);
96
97 unsigned int imen; /* XXX */
98
99 inthand_t
100 IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
101 IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
102 IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
103 IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
104 IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
105 IDTVEC(atpic_intr15);
106
107 #define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq)
108
109 #define ATPIC(io, base, eoi, imenptr) \
110 { { atpic_enable_source, atpic_disable_source, (eoi), \
111 atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
112 atpic_resume, atpic_config_intr }, (io), (base), \
113 IDT_IO_INTS + (base), (imenptr) }
114
115 #define INTSRC(irq) \
116 { { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \
117 (irq) % 8 }
118
119 struct atpic {
120 struct pic at_pic;
121 int at_ioaddr;
122 int at_irqbase;
123 uint8_t at_intbase;
124 uint8_t *at_imen;
125 };
126
127 struct atpic_intsrc {
128 struct intsrc at_intsrc;
129 inthand_t *at_intr;
130 int at_irq; /* Relative to PIC base. */
131 enum intr_trigger at_trigger;
132 u_long at_count;
133 u_long at_straycount;
134 };
135
136 static void atpic_enable_source(struct intsrc *isrc);
137 static void atpic_disable_source(struct intsrc *isrc, int eoi);
138 static void atpic_eoi_master(struct intsrc *isrc);
139 static void atpic_eoi_slave(struct intsrc *isrc);
140 static void atpic_enable_intr(struct intsrc *isrc);
141 static int atpic_vector(struct intsrc *isrc);
142 static void atpic_resume(struct intsrc *isrc);
143 static int atpic_source_pending(struct intsrc *isrc);
144 static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
145 enum intr_polarity pol);
146 static void i8259_init(struct atpic *pic, int slave);
147
148 static struct atpic atpics[] = {
149 ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
150 ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
151 };
152
153 static struct atpic_intsrc atintrs[] = {
154 INTSRC(0),
155 INTSRC(1),
156 INTSRC(2),
157 INTSRC(3),
158 INTSRC(4),
159 INTSRC(5),
160 INTSRC(6),
161 INTSRC(7),
162 INTSRC(8),
163 INTSRC(9),
164 INTSRC(10),
165 INTSRC(11),
166 INTSRC(12),
167 INTSRC(13),
168 INTSRC(14),
169 INTSRC(15),
170 };
171
172 CTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS);
173
174 static __inline void
175 _atpic_eoi_master(struct intsrc *isrc)
176 {
177
178 KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
179 ("%s: mismatched pic", __func__));
180 #ifndef AUTO_EOI_1
181 outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
182 #endif
183 }
184
185 /*
186 * The data sheet says no auto-EOI on slave, but it sometimes works.
187 * So, if AUTO_EOI_2 is enabled, we use it.
188 */
189 static __inline void
190 _atpic_eoi_slave(struct intsrc *isrc)
191 {
192
193 KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
194 ("%s: mismatched pic", __func__));
195 #ifndef AUTO_EOI_2
196 outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
197 #ifndef AUTO_EOI_1
198 outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
199 #endif
200 #endif
201 }
202
203 static void
204 atpic_enable_source(struct intsrc *isrc)
205 {
206 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
207 struct atpic *ap = (struct atpic *)isrc->is_pic;
208
209 mtx_lock_spin(&icu_lock);
210 if (*ap->at_imen & IMEN_MASK(ai)) {
211 *ap->at_imen &= ~IMEN_MASK(ai);
212 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
213 }
214 mtx_unlock_spin(&icu_lock);
215 }
216
217 static void
218 atpic_disable_source(struct intsrc *isrc, int eoi)
219 {
220 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
221 struct atpic *ap = (struct atpic *)isrc->is_pic;
222
223 mtx_lock_spin(&icu_lock);
224 if (ai->at_trigger != INTR_TRIGGER_EDGE) {
225 *ap->at_imen |= IMEN_MASK(ai);
226 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
227 }
228
229 /*
230 * Take care to call these functions directly instead of through
231 * a function pointer. All of the referenced variables should
232 * still be hot in the cache.
233 */
234 if (eoi == PIC_EOI) {
235 if (isrc->is_pic == &atpics[MASTER].at_pic)
236 _atpic_eoi_master(isrc);
237 else
238 _atpic_eoi_slave(isrc);
239 }
240
241 mtx_unlock_spin(&icu_lock);
242 }
243
244 static void
245 atpic_eoi_master(struct intsrc *isrc)
246 {
247 #ifndef AUTO_EOI_1
248 mtx_lock_spin(&icu_lock);
249 _atpic_eoi_master(isrc);
250 mtx_unlock_spin(&icu_lock);
251 #endif
252 }
253
254 static void
255 atpic_eoi_slave(struct intsrc *isrc)
256 {
257 #ifndef AUTO_EOI_2
258 mtx_lock_spin(&icu_lock);
259 _atpic_eoi_slave(isrc);
260 mtx_unlock_spin(&icu_lock);
261 #endif
262 }
263
264 static void
265 atpic_enable_intr(struct intsrc *isrc)
266 {
267 }
268
269 static int
270 atpic_vector(struct intsrc *isrc)
271 {
272 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
273 struct atpic *ap = (struct atpic *)isrc->is_pic;
274
275 return (IRQ(ap, ai));
276 }
277
278 static int
279 atpic_source_pending(struct intsrc *isrc)
280 {
281 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
282 struct atpic *ap = (struct atpic *)isrc->is_pic;
283
284 return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
285 }
286
287 static void
288 atpic_resume(struct intsrc *isrc)
289 {
290 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
291 struct atpic *ap = (struct atpic *)isrc->is_pic;
292
293 if (ai->at_irq == 0) {
294 i8259_init(ap, ap == &atpics[SLAVE]);
295 if (ap == &atpics[SLAVE] && elcr_found)
296 elcr_resume();
297 }
298 }
299
300 static int
301 atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
302 enum intr_polarity pol)
303 {
304 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
305 u_int vector;
306
307 /* Map conforming values to edge/hi and sanity check the values. */
308 if (trig == INTR_TRIGGER_CONFORM)
309 trig = INTR_TRIGGER_EDGE;
310 if (pol == INTR_POLARITY_CONFORM)
311 pol = INTR_POLARITY_HIGH;
312 vector = atpic_vector(isrc);
313 if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
314 (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
315 printf(
316 "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
317 vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
318 pol == INTR_POLARITY_HIGH ? "high" : "low");
319 return (EINVAL);
320 }
321
322 /* If there is no change, just return. */
323 if (ai->at_trigger == trig)
324 return (0);
325
326 /*
327 * Certain IRQs can never be level/lo, so don't try to set them
328 * that way if asked. At least some ELCR registers ignore setting
329 * these bits as well.
330 */
331 if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
332 trig == INTR_TRIGGER_LEVEL) {
333 if (bootverbose)
334 printf(
335 "atpic: Ignoring invalid level/low configuration for IRQ%u\n",
336 vector);
337 return (EINVAL);
338 }
339 if (!elcr_found) {
340 if (bootverbose)
341 printf("atpic: No ELCR to configure IRQ%u as %s\n",
342 vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
343 "level/low");
344 return (ENXIO);
345 }
346 if (bootverbose)
347 printf("atpic: Programming IRQ%u as %s\n", vector,
348 trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
349 mtx_lock_spin(&icu_lock);
350 elcr_write_trigger(atpic_vector(isrc), trig);
351 ai->at_trigger = trig;
352 mtx_unlock_spin(&icu_lock);
353 return (0);
354 }
355
356 static void
357 i8259_init(struct atpic *pic, int slave)
358 {
359 int imr_addr;
360
361 /* Reset the PIC and program with next four bytes. */
362 mtx_lock_spin(&icu_lock);
363 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
364 imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
365
366 /* Start vector. */
367 outb(imr_addr, pic->at_intbase);
368
369 /*
370 * Setup slave links. For the master pic, indicate what line
371 * the slave is configured on. For the slave indicate
372 * which line on the master we are connected to.
373 */
374 if (slave)
375 outb(imr_addr, ICU_SLAVEID);
376 else
377 outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
378
379 /* Set mode. */
380 if (slave)
381 outb(imr_addr, SLAVE_MODE);
382 else
383 outb(imr_addr, MASTER_MODE);
384
385 /* Set interrupt enable mask. */
386 outb(imr_addr, *pic->at_imen);
387
388 /* Reset is finished, default to IRR on read. */
389 outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
390
391 /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
392 if (!slave)
393 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
394 mtx_unlock_spin(&icu_lock);
395 }
396
397 void
398 atpic_startup(void)
399 {
400 struct atpic_intsrc *ai;
401 int i;
402
403 /* Start off with all interrupts disabled. */
404 imen = 0xffff;
405 i8259_init(&atpics[MASTER], 0);
406 i8259_init(&atpics[SLAVE], 1);
407 atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
408
409 /* Install low-level interrupt handlers for all of our IRQs. */
410 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
411 if (i == ICU_SLAVEID)
412 continue;
413 ai->at_intsrc.is_count = &ai->at_count;
414 ai->at_intsrc.is_straycount = &ai->at_straycount;
415 setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
416 ai->at_irq, ai->at_intr, SDT_SYSIGT, SEL_KPL, 0);
417 }
418
419 /*
420 * Look for an ELCR. If we find one, update the trigger modes.
421 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
422 * edge triggered and that everything else is level triggered.
423 * We only use the trigger information to reprogram the ELCR if
424 * we have one and as an optimization to avoid masking edge
425 * triggered interrupts. For the case that we don't have an ELCR,
426 * it doesn't hurt to mask an edge triggered interrupt, so we
427 * assume level trigger for any interrupt that we aren't sure is
428 * edge triggered.
429 */
430 if (elcr_found) {
431 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
432 ai->at_trigger = elcr_read_trigger(i);
433 } else {
434 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
435 switch (i) {
436 case 0:
437 case 1:
438 case 2:
439 case 8:
440 case 13:
441 ai->at_trigger = INTR_TRIGGER_EDGE;
442 break;
443 default:
444 ai->at_trigger = INTR_TRIGGER_LEVEL;
445 break;
446 }
447 }
448 }
449
450 static void
451 atpic_init(void *dummy __unused)
452 {
453 struct atpic_intsrc *ai;
454 int i;
455
456 /* Loop through all interrupt sources and add them. */
457 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
458 if (i == ICU_SLAVEID)
459 continue;
460 intr_register_source(&ai->at_intsrc);
461 }
462 }
463 SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
464
465 void
466 atpic_handle_intr(void *cookie, struct intrframe iframe)
467 {
468 struct intsrc *isrc;
469 int vec = (uintptr_t)cookie;
470
471 KASSERT(vec < NUM_ISA_IRQS, ("unknown int %d\n", vec));
472 isrc = &atintrs[vec].at_intsrc;
473
474 /*
475 * If we don't have an ithread, see if this is a spurious
476 * interrupt.
477 */
478 if (isrc->is_ithread == NULL && (vec == 7 || vec == 15)) {
479 int port, isr;
480
481 /*
482 * Read the ISR register to see if IRQ 7/15 is really
483 * pending. Reset read register back to IRR when done.
484 */
485 port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
486 mtx_lock_spin(&icu_lock);
487 outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
488 isr = inb(port);
489 outb(port, OCW3_SEL | OCW3_RR);
490 mtx_unlock_spin(&icu_lock);
491 if ((isr & IRQ_MASK(7)) == 0)
492 return;
493 }
494 intr_execute_handlers(isrc, &iframe);
495 }
496
497 #ifdef DEV_ISA
498 /*
499 * Bus attachment for the ISA PIC.
500 */
501 static struct isa_pnp_id atpic_ids[] = {
502 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
503 { 0 }
504 };
505
506 static int
507 atpic_probe(device_t dev)
508 {
509 int result;
510
511 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
512 if (result <= 0)
513 device_quiet(dev);
514 return (result);
515 }
516
517 /*
518 * We might be granted IRQ 2, as this is typically consumed by chaining
519 * between the two PIC components. If we're using the APIC, however,
520 * this may not be the case, and as such we should free the resource.
521 * (XXX untested)
522 *
523 * The generic ISA attachment code will handle allocating any other resources
524 * that we don't explicitly claim here.
525 */
526 static int
527 atpic_attach(device_t dev)
528 {
529 struct resource *res;
530 int rid;
531
532 /* Try to allocate our IRQ and then free it. */
533 rid = 0;
534 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
535 if (res != NULL)
536 bus_release_resource(dev, SYS_RES_IRQ, rid, res);
537 return (0);
538 }
539
540 static device_method_t atpic_methods[] = {
541 /* Device interface */
542 DEVMETHOD(device_probe, atpic_probe),
543 DEVMETHOD(device_attach, atpic_attach),
544 DEVMETHOD(device_detach, bus_generic_detach),
545 DEVMETHOD(device_shutdown, bus_generic_shutdown),
546 DEVMETHOD(device_suspend, bus_generic_suspend),
547 DEVMETHOD(device_resume, bus_generic_resume),
548 { 0, 0 }
549 };
550
551 static driver_t atpic_driver = {
552 "atpic",
553 atpic_methods,
554 1, /* no softc */
555 };
556
557 static devclass_t atpic_devclass;
558
559 DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
560 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
561
562 /*
563 * Return a bitmap of the current interrupt requests. This is 8259-specific
564 * and is only suitable for use at probe time.
565 */
566 intrmask_t
567 isa_irq_pending(void)
568 {
569 u_char irr1;
570 u_char irr2;
571
572 irr1 = inb(IO_ICU1);
573 irr2 = inb(IO_ICU2);
574 return ((irr2 << 8) | irr1);
575 }
576 #endif /* DEV_ISA */
Cache object: d4fedd5b24996e73924be11e096ae504
|