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/5.2/sys/i386/isa/atpic.c 122898 2003-11-19 15:40:23Z jhb $");
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/mutex.h>
47
48 #include <machine/cpufunc.h>
49 #include <machine/frame.h>
50 #include <machine/intr_machdep.h>
51 #include <machine/md_var.h>
52 #include <machine/resource.h>
53 #include <machine/segments.h>
54
55 #include <i386/isa/icu.h>
56 #ifdef PC98
57 #include <pc98/pc98/pc98.h>
58 #else
59 #include <i386/isa/isa.h>
60 #endif
61 #include <isa/isavar.h>
62
63 #define MASTER 0
64 #define SLAVE 1
65
66 /* XXX: Magic numbers */
67 #ifdef PC98
68 #ifdef AUTO_EOI_1
69 #define MASTER_MODE 0x1f /* Master auto EOI, 8086 mode */
70 #else
71 #define MASTER_MODE 0x1d /* Master 8086 mode */
72 #endif
73 #define SLAVE_MODE 9 /* 8086 mode */
74 #else /* IBM-PC */
75 #ifdef AUTO_EOI_1
76 #define MASTER_MODE (ICW4_8086 | ICW4_AEOI)
77 #else
78 #define MASTER_MODE ICW4_8086
79 #endif
80 #ifdef AUTO_EOI_2
81 #define SLAVE_MODE (ICW4_8086 | ICW4_AEOI)
82 #else
83 #define SLAVE_MODE ICW4_8086
84 #endif
85 #endif /* PC98 */
86
87 static void atpic_init(void *dummy);
88
89 unsigned int imen; /* XXX */
90
91 inthand_t
92 IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
93 IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
94 IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
95 IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
96 IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
97 IDTVEC(atpic_intr15);
98
99 #define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq)
100
101 #define ATPIC(io, base, eoi, imenptr) \
102 { { atpic_enable_source, atpic_disable_source, (eoi), \
103 atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
104 atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
105
106 #define INTSRC(irq) \
107 { { &atpics[(irq) / 8].at_pic }, (irq) % 8, \
108 IDTVEC(atpic_intr ## irq ) }
109
110 struct atpic {
111 struct pic at_pic;
112 int at_ioaddr;
113 int at_irqbase;
114 uint8_t at_intbase;
115 uint8_t *at_imen;
116 };
117
118 struct atpic_intsrc {
119 struct intsrc at_intsrc;
120 int at_irq; /* Relative to PIC base. */
121 inthand_t *at_intr;
122 u_long at_count;
123 u_long at_straycount;
124 };
125
126 static void atpic_enable_source(struct intsrc *isrc);
127 static void atpic_disable_source(struct intsrc *isrc);
128 static void atpic_eoi_master(struct intsrc *isrc);
129 static void atpic_eoi_slave(struct intsrc *isrc);
130 static void atpic_enable_intr(struct intsrc *isrc);
131 static int atpic_vector(struct intsrc *isrc);
132 static void atpic_resume(struct intsrc *isrc);
133 static int atpic_source_pending(struct intsrc *isrc);
134 static void i8259_init(struct atpic *pic, int slave);
135
136 static struct atpic atpics[] = {
137 ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
138 ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
139 };
140
141 static struct atpic_intsrc atintrs[] = {
142 INTSRC(0),
143 INTSRC(1),
144 INTSRC(2),
145 INTSRC(3),
146 INTSRC(4),
147 INTSRC(5),
148 INTSRC(6),
149 INTSRC(7),
150 INTSRC(8),
151 INTSRC(9),
152 INTSRC(10),
153 INTSRC(11),
154 INTSRC(12),
155 INTSRC(13),
156 INTSRC(14),
157 INTSRC(15),
158 };
159
160 static void
161 atpic_enable_source(struct intsrc *isrc)
162 {
163 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
164 struct atpic *ap = (struct atpic *)isrc->is_pic;
165
166 mtx_lock_spin(&icu_lock);
167 *ap->at_imen &= ~(1 << ai->at_irq);
168 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
169 mtx_unlock_spin(&icu_lock);
170 }
171
172 static void
173 atpic_disable_source(struct intsrc *isrc)
174 {
175 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
176 struct atpic *ap = (struct atpic *)isrc->is_pic;
177
178 mtx_lock_spin(&icu_lock);
179 *ap->at_imen |= (1 << ai->at_irq);
180 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
181 mtx_unlock_spin(&icu_lock);
182 }
183
184 static void
185 atpic_eoi_master(struct intsrc *isrc)
186 {
187
188 KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
189 ("%s: mismatched pic", __func__));
190 #ifndef AUTO_EOI_1
191 mtx_lock_spin(&icu_lock);
192 outb(atpics[MASTER].at_ioaddr, ICU_EOI);
193 mtx_unlock_spin(&icu_lock);
194 #endif
195 }
196
197 /*
198 * The data sheet says no auto-EOI on slave, but it sometimes works.
199 * So, if AUTO_EOI_2 is enabled, we use it.
200 */
201 static void
202 atpic_eoi_slave(struct intsrc *isrc)
203 {
204
205 KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
206 ("%s: mismatched pic", __func__));
207 #ifndef AUTO_EOI_2
208 mtx_lock_spin(&icu_lock);
209 outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
210 #ifndef AUTO_EOI_1
211 outb(atpics[MASTER].at_ioaddr, ICU_EOI);
212 #endif
213 mtx_unlock_spin(&icu_lock);
214 #endif
215 }
216
217 static void
218 atpic_enable_intr(struct intsrc *isrc)
219 {
220 }
221
222 static int
223 atpic_vector(struct intsrc *isrc)
224 {
225 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
226 struct atpic *ap = (struct atpic *)isrc->is_pic;
227
228 return (IRQ(ap, ai));
229 }
230
231 static int
232 atpic_source_pending(struct intsrc *isrc)
233 {
234 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
235 struct atpic *ap = (struct atpic *)isrc->is_pic;
236
237 return (inb(ap->at_ioaddr) & (1 << ai->at_irq));
238 }
239
240 static void
241 atpic_resume(struct intsrc *isrc)
242 {
243 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
244 struct atpic *ap = (struct atpic *)isrc->is_pic;
245
246 if (ai->at_irq == 0)
247 i8259_init(ap, ap == &atpics[SLAVE]);
248 }
249
250 static void
251 i8259_init(struct atpic *pic, int slave)
252 {
253 int imr_addr;
254
255 /* Reset the PIC and program with next four bytes. */
256 mtx_lock_spin(&icu_lock);
257 #ifdef DEV_MCA
258 /* MCA uses level triggered interrupts. */
259 if (MCA_system)
260 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
261 else
262 #endif
263 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
264 imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
265
266 /* Start vector. */
267 outb(imr_addr, pic->at_intbase);
268
269 /*
270 * Setup slave links. For the master pic, indicate what line
271 * the slave is configured on. For the slave indicate
272 * which line on the master we are connected to.
273 */
274 if (slave)
275 outb(imr_addr, ICU_SLAVEID); /* my slave id is 7 */
276 else
277 outb(imr_addr, IRQ_SLAVE); /* slave on line 7 */
278
279 /* Set mode. */
280 if (slave)
281 outb(imr_addr, SLAVE_MODE);
282 else
283 outb(imr_addr, MASTER_MODE);
284
285 /* Set interrupt enable mask. */
286 outb(imr_addr, *pic->at_imen);
287
288 /* Reset is finished, default to IRR on read. */
289 outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
290
291 #ifndef PC98
292 /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
293 if (!slave)
294 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
295 #endif
296 mtx_unlock_spin(&icu_lock);
297 }
298
299 void
300 atpic_startup(void)
301 {
302 struct atpic_intsrc *ai;
303 int i;
304
305 /* Start off with all interrupts disabled. */
306 imen = 0xffff;
307 i8259_init(&atpics[MASTER], 0);
308 i8259_init(&atpics[SLAVE], 1);
309 atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
310
311 /* Install low-level interrupt handlers for all of our IRQs. */
312 for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
313 if (i == ICU_SLAVEID)
314 continue;
315 ai = &atintrs[i];
316 ai->at_intsrc.is_count = &ai->at_count;
317 ai->at_intsrc.is_straycount = &ai->at_straycount;
318 setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
319 ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
320 GSEL(GCODE_SEL, SEL_KPL));
321 }
322 }
323
324 static void
325 atpic_init(void *dummy __unused)
326 {
327 int i;
328
329 /* Loop through all interrupt sources and add them. */
330 for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
331 if (i == ICU_SLAVEID)
332 continue;
333 intr_register_source(&atintrs[i].at_intsrc);
334 }
335 }
336 SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
337
338 void
339 atpic_handle_intr(struct intrframe iframe)
340 {
341 struct intsrc *isrc;
342
343 KASSERT((uint)iframe.if_vec < ICU_LEN,
344 ("unknown int %d\n", iframe.if_vec));
345 isrc = &atintrs[iframe.if_vec].at_intsrc;
346
347 /*
348 * If we don't have an ithread, see if this is a spurious
349 * interrupt.
350 */
351 if (isrc->is_ithread == NULL &&
352 (iframe.if_vec == 7 || iframe.if_vec == 15)) {
353 int port, isr;
354
355 /*
356 * Read the ISR register to see if IRQ 7/15 is really
357 * pending. Reset read register back to IRR when done.
358 */
359 port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
360 mtx_lock_spin(&icu_lock);
361 outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
362 isr = inb(port);
363 outb(port, OCW3_SEL | OCW3_RR);
364 mtx_unlock_spin(&icu_lock);
365 if ((isr & IRQ7) == 0)
366 return;
367 }
368 intr_execute_handlers(isrc, &iframe);
369 }
370
371 #ifdef DEV_ISA
372 /*
373 * Bus attachment for the ISA PIC.
374 */
375 static struct isa_pnp_id atpic_ids[] = {
376 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
377 { 0 }
378 };
379
380 static int
381 atpic_probe(device_t dev)
382 {
383 int result;
384
385 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
386 if (result <= 0)
387 device_quiet(dev);
388 return (result);
389 }
390
391 /*
392 * We might be granted IRQ 2, as this is typically consumed by chaining
393 * between the two PIC components. If we're using the APIC, however,
394 * this may not be the case, and as such we should free the resource.
395 * (XXX untested)
396 *
397 * The generic ISA attachment code will handle allocating any other resources
398 * that we don't explicitly claim here.
399 */
400 static int
401 atpic_attach(device_t dev)
402 {
403 struct resource *res;
404 int rid;
405
406 /* Try to allocate our IRQ and then free it. */
407 rid = 0;
408 res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0);
409 if (res != NULL)
410 bus_release_resource(dev, SYS_RES_IRQ, rid, res);
411 return (0);
412 }
413
414 static device_method_t atpic_methods[] = {
415 /* Device interface */
416 DEVMETHOD(device_probe, atpic_probe),
417 DEVMETHOD(device_attach, atpic_attach),
418 DEVMETHOD(device_detach, bus_generic_detach),
419 DEVMETHOD(device_shutdown, bus_generic_shutdown),
420 DEVMETHOD(device_suspend, bus_generic_suspend),
421 DEVMETHOD(device_resume, bus_generic_resume),
422 { 0, 0 }
423 };
424
425 static driver_t atpic_driver = {
426 "atpic",
427 atpic_methods,
428 1, /* no softc */
429 };
430
431 static devclass_t atpic_devclass;
432
433 DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
434 #ifndef PC98
435 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
436 #endif
437
438 /*
439 * Return a bitmap of the current interrupt requests. This is 8259-specific
440 * and is only suitable for use at probe time.
441 */
442 intrmask_t
443 isa_irq_pending(void)
444 {
445 u_char irr1;
446 u_char irr2;
447
448 irr1 = inb(IO_ICU1);
449 irr2 = inb(IO_ICU2);
450 return ((irr2 << 8) | irr1);
451 }
452 #endif /* DEV_ISA */
Cache object: f8a11be0bb087353b0c3fc14da0697b1
|