|
|
1.1 root 1: /*
2: * QEMU 8259 interrupt controller emulation
1.1.1.5 root 3: *
1.1 root 4: * Copyright (c) 2003-2004 Fabrice Bellard
1.1.1.5 root 5: *
1.1 root 6: * Permission is hereby granted, free of charge, to any person obtaining a copy
7: * of this software and associated documentation files (the "Software"), to deal
8: * in the Software without restriction, including without limitation the rights
9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10: * copies of the Software, and to permit persons to whom the Software is
11: * furnished to do so, subject to the following conditions:
12: *
13: * The above copyright notice and this permission notice shall be included in
14: * all copies or substantial portions of the Software.
15: *
16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22: * THE SOFTWARE.
23: */
1.1.1.5 root 24: #include "hw.h"
25: #include "pc.h"
26: #include "isa.h"
1.1.1.7 root 27: #include "monitor.h"
28: #include "qemu-timer.h"
1.1 root 29:
30: /* debug PIC */
31: //#define DEBUG_PIC
32:
1.1.1.9 root 33: #ifdef DEBUG_PIC
34: #define DPRINTF(fmt, ...) \
35: do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
36: #else
37: #define DPRINTF(fmt, ...)
38: #endif
39:
1.1 root 40: //#define DEBUG_IRQ_LATENCY
41: //#define DEBUG_IRQ_COUNT
42:
1.1.1.11! root 43: struct PicState {
! 44: ISADevice dev;
1.1 root 45: uint8_t last_irr; /* edge detection */
46: uint8_t irr; /* interrupt request register */
47: uint8_t imr; /* interrupt mask register */
48: uint8_t isr; /* interrupt service register */
49: uint8_t priority_add; /* highest irq priority */
50: uint8_t irq_base;
51: uint8_t read_reg_select;
52: uint8_t poll;
53: uint8_t special_mask;
54: uint8_t init_state;
55: uint8_t auto_eoi;
56: uint8_t rotate_on_auto_eoi;
57: uint8_t special_fully_nested_mode;
58: uint8_t init4; /* true if 4 byte init */
1.1.1.5 root 59: uint8_t single_mode; /* true if slave pic is not initialized */
1.1 root 60: uint8_t elcr; /* PIIX edge/trigger selection*/
61: uint8_t elcr_mask;
1.1.1.11! root 62: qemu_irq int_out[1];
! 63: uint32_t master; /* reflects /SP input pin */
! 64: uint32_t iobase;
! 65: uint32_t elcr_addr;
! 66: MemoryRegion base_io;
! 67: MemoryRegion elcr_io;
1.1 root 68: };
69:
1.1.1.11! root 70: #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
1.1 root 71: static int irq_level[16];
72: #endif
73: #ifdef DEBUG_IRQ_COUNT
74: static uint64_t irq_count[16];
75: #endif
1.1.1.11! root 76: #ifdef DEBUG_IRQ_LATENCY
! 77: static int64_t irq_time[16];
! 78: #endif
! 79: PicState *isa_pic;
! 80: static PicState *slave_pic;
1.1 root 81:
82: /* return the highest priority found in mask (highest = smallest
83: number). Return 8 if no irq */
1.1.1.11! root 84: static int get_priority(PicState *s, int mask)
1.1 root 85: {
86: int priority;
1.1.1.11! root 87:
! 88: if (mask == 0) {
1.1 root 89: return 8;
1.1.1.11! root 90: }
1.1 root 91: priority = 0;
1.1.1.11! root 92: while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
1.1 root 93: priority++;
1.1.1.11! root 94: }
1.1 root 95: return priority;
96: }
97:
98: /* return the pic wanted interrupt. return -1 if none */
99: static int pic_get_irq(PicState *s)
100: {
101: int mask, cur_priority, priority;
102:
103: mask = s->irr & ~s->imr;
104: priority = get_priority(s, mask);
1.1.1.11! root 105: if (priority == 8) {
1.1 root 106: return -1;
1.1.1.11! root 107: }
1.1 root 108: /* compute current priority. If special fully nested mode on the
109: master, the IRQ coming from the slave is not taken into account
110: for the priority computation. */
111: mask = s->isr;
1.1.1.11! root 112: if (s->special_mask) {
1.1.1.6 root 113: mask &= ~s->imr;
1.1.1.11! root 114: }
! 115: if (s->special_fully_nested_mode && s->master) {
1.1 root 116: mask &= ~(1 << 2);
1.1.1.11! root 117: }
1.1 root 118: cur_priority = get_priority(s, mask);
119: if (priority < cur_priority) {
120: /* higher priority found: an irq should be generated */
121: return (priority + s->priority_add) & 7;
122: } else {
123: return -1;
124: }
125: }
126:
1.1.1.11! root 127: /* Update INT output. Must be called every time the output may have changed. */
! 128: static void pic_update_irq(PicState *s)
! 129: {
! 130: int irq;
1.1.1.4 root 131:
1.1.1.11! root 132: irq = pic_get_irq(s);
! 133: if (irq >= 0) {
! 134: DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
! 135: s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
! 136: qemu_irq_raise(s->int_out[0]);
! 137: } else {
! 138: qemu_irq_lower(s->int_out[0]);
1.1.1.4 root 139: }
1.1 root 140: }
141:
1.1.1.11! root 142: /* set irq level. If an edge is detected, then the IRR is set to 1 */
! 143: static void pic_set_irq(void *opaque, int irq, int level)
1.1 root 144: {
1.1.1.11! root 145: PicState *s = opaque;
! 146: int mask = 1 << irq;
1.1 root 147:
1.1.1.11! root 148: #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
! 149: defined(DEBUG_IRQ_LATENCY)
! 150: int irq_index = s->master ? irq : irq + 8;
! 151: #endif
1.1 root 152: #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
1.1.1.11! root 153: if (level != irq_level[irq_index]) {
! 154: DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
! 155: irq_level[irq_index] = level;
1.1 root 156: #ifdef DEBUG_IRQ_COUNT
1.1.1.11! root 157: if (level == 1) {
! 158: irq_count[irq_index]++;
! 159: }
1.1 root 160: #endif
161: }
162: #endif
163: #ifdef DEBUG_IRQ_LATENCY
164: if (level) {
1.1.1.11! root 165: irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
1.1 root 166: }
167: #endif
1.1.1.11! root 168:
! 169: if (s->elcr & mask) {
! 170: /* level triggered */
! 171: if (level) {
! 172: s->irr |= mask;
! 173: s->last_irr |= mask;
! 174: } else {
! 175: s->irr &= ~mask;
! 176: s->last_irr &= ~mask;
! 177: }
! 178: } else {
! 179: /* edge triggered */
! 180: if (level) {
! 181: if ((s->last_irr & mask) == 0) {
! 182: s->irr |= mask;
! 183: }
! 184: s->last_irr |= mask;
! 185: } else {
! 186: s->last_irr &= ~mask;
! 187: }
! 188: }
1.1 root 189: pic_update_irq(s);
190: }
191:
192: /* acknowledge interrupt 'irq' */
1.1.1.11! root 193: static void pic_intack(PicState *s, int irq)
1.1 root 194: {
195: if (s->auto_eoi) {
1.1.1.11! root 196: if (s->rotate_on_auto_eoi) {
1.1 root 197: s->priority_add = (irq + 1) & 7;
1.1.1.11! root 198: }
1.1 root 199: } else {
200: s->isr |= (1 << irq);
201: }
202: /* We don't clear a level sensitive interrupt here */
1.1.1.11! root 203: if (!(s->elcr & (1 << irq))) {
1.1 root 204: s->irr &= ~(1 << irq);
1.1.1.11! root 205: }
! 206: pic_update_irq(s);
1.1 root 207: }
208:
1.1.1.11! root 209: int pic_read_irq(PicState *s)
1.1 root 210: {
211: int irq, irq2, intno;
212:
1.1.1.11! root 213: irq = pic_get_irq(s);
1.1 root 214: if (irq >= 0) {
215: if (irq == 2) {
1.1.1.11! root 216: irq2 = pic_get_irq(slave_pic);
1.1 root 217: if (irq2 >= 0) {
1.1.1.11! root 218: pic_intack(slave_pic, irq2);
1.1 root 219: } else {
220: /* spurious IRQ on slave controller */
221: irq2 = 7;
222: }
1.1.1.11! root 223: intno = slave_pic->irq_base + irq2;
1.1 root 224: } else {
1.1.1.11! root 225: intno = s->irq_base + irq;
1.1 root 226: }
1.1.1.11! root 227: pic_intack(s, irq);
1.1 root 228: } else {
229: /* spurious IRQ on host controller */
230: irq = 7;
1.1.1.11! root 231: intno = s->irq_base + irq;
1.1 root 232: }
1.1.1.5 root 233:
1.1.1.11! root 234: #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
! 235: if (irq == 2) {
! 236: irq = irq2 + 8;
! 237: }
! 238: #endif
1.1 root 239: #ifdef DEBUG_IRQ_LATENCY
1.1.1.5 root 240: printf("IRQ%d latency=%0.3fus\n",
241: irq,
1.1.1.10 root 242: (double)(qemu_get_clock_ns(vm_clock) -
1.1.1.8 root 243: irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
1.1 root 244: #endif
1.1.1.9 root 245: DPRINTF("pic_interrupt: irq=%d\n", irq);
1.1 root 246: return intno;
247: }
248:
1.1.1.11! root 249: static void pic_init_reset(PicState *s)
1.1 root 250: {
251: s->last_irr = 0;
252: s->irr = 0;
253: s->imr = 0;
254: s->isr = 0;
255: s->priority_add = 0;
256: s->irq_base = 0;
257: s->read_reg_select = 0;
258: s->poll = 0;
259: s->special_mask = 0;
260: s->init_state = 0;
261: s->auto_eoi = 0;
262: s->rotate_on_auto_eoi = 0;
263: s->special_fully_nested_mode = 0;
264: s->init4 = 0;
1.1.1.5 root 265: s->single_mode = 0;
1.1.1.2 root 266: /* Note: ELCR is not reset */
1.1.1.11! root 267: pic_update_irq(s);
! 268: }
! 269:
! 270: static void pic_reset(DeviceState *dev)
! 271: {
! 272: PicState *s = container_of(dev, PicState, dev.qdev);
! 273:
! 274: pic_init_reset(s);
! 275: s->elcr = 0;
1.1 root 276: }
277:
1.1.1.11! root 278: static void pic_ioport_write(void *opaque, target_phys_addr_t addr64,
! 279: uint64_t val64, unsigned size)
1.1 root 280: {
281: PicState *s = opaque;
1.1.1.11! root 282: uint32_t addr = addr64;
! 283: uint32_t val = val64;
1.1 root 284: int priority, cmd, irq;
285:
1.1.1.9 root 286: DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
1.1 root 287: if (addr == 0) {
288: if (val & 0x10) {
1.1.1.11! root 289: pic_init_reset(s);
1.1 root 290: s->init_state = 1;
291: s->init4 = val & 1;
1.1.1.5 root 292: s->single_mode = val & 2;
1.1.1.11! root 293: if (val & 0x08) {
1.1 root 294: hw_error("level sensitive irq not supported");
1.1.1.11! root 295: }
1.1 root 296: } else if (val & 0x08) {
1.1.1.11! root 297: if (val & 0x04) {
1.1 root 298: s->poll = 1;
1.1.1.11! root 299: }
! 300: if (val & 0x02) {
1.1 root 301: s->read_reg_select = val & 1;
1.1.1.11! root 302: }
! 303: if (val & 0x40) {
1.1 root 304: s->special_mask = (val >> 5) & 1;
1.1.1.11! root 305: }
1.1 root 306: } else {
307: cmd = val >> 5;
1.1.1.11! root 308: switch (cmd) {
1.1 root 309: case 0:
310: case 4:
311: s->rotate_on_auto_eoi = cmd >> 2;
312: break;
313: case 1: /* end of interrupt */
314: case 5:
315: priority = get_priority(s, s->isr);
316: if (priority != 8) {
317: irq = (priority + s->priority_add) & 7;
318: s->isr &= ~(1 << irq);
1.1.1.11! root 319: if (cmd == 5) {
1.1 root 320: s->priority_add = (irq + 1) & 7;
1.1.1.11! root 321: }
! 322: pic_update_irq(s);
1.1 root 323: }
324: break;
325: case 3:
326: irq = val & 7;
327: s->isr &= ~(1 << irq);
1.1.1.11! root 328: pic_update_irq(s);
1.1 root 329: break;
330: case 6:
331: s->priority_add = (val + 1) & 7;
1.1.1.11! root 332: pic_update_irq(s);
1.1 root 333: break;
334: case 7:
335: irq = val & 7;
336: s->isr &= ~(1 << irq);
337: s->priority_add = (irq + 1) & 7;
1.1.1.11! root 338: pic_update_irq(s);
1.1 root 339: break;
340: default:
341: /* no operation */
342: break;
343: }
344: }
345: } else {
1.1.1.11! root 346: switch (s->init_state) {
1.1 root 347: case 0:
348: /* normal mode */
349: s->imr = val;
1.1.1.11! root 350: pic_update_irq(s);
1.1 root 351: break;
352: case 1:
353: s->irq_base = val & 0xf8;
1.1.1.5 root 354: s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
1.1 root 355: break;
356: case 2:
357: if (s->init4) {
358: s->init_state = 3;
359: } else {
360: s->init_state = 0;
361: }
362: break;
363: case 3:
364: s->special_fully_nested_mode = (val >> 4) & 1;
365: s->auto_eoi = (val >> 1) & 1;
366: s->init_state = 0;
367: break;
368: }
369: }
370: }
371:
1.1.1.11! root 372: static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr,
! 373: unsigned size)
1.1 root 374: {
375: PicState *s = opaque;
376: int ret;
377:
378: if (s->poll) {
1.1.1.11! root 379: ret = pic_get_irq(s);
! 380: if (ret >= 0) {
! 381: pic_intack(s, ret);
! 382: ret |= 0x80;
! 383: } else {
! 384: ret = 0;
! 385: }
1.1 root 386: s->poll = 0;
387: } else {
388: if (addr == 0) {
1.1.1.11! root 389: if (s->read_reg_select) {
1.1 root 390: ret = s->isr;
1.1.1.11! root 391: } else {
1.1 root 392: ret = s->irr;
1.1.1.11! root 393: }
1.1 root 394: } else {
395: ret = s->imr;
396: }
397: }
1.1.1.11! root 398: DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
1.1 root 399: return ret;
400: }
401:
1.1.1.11! root 402: int pic_get_output(PicState *s)
1.1 root 403: {
1.1.1.11! root 404: return (pic_get_irq(s) >= 0);
1.1 root 405: }
406:
1.1.1.11! root 407: static void elcr_ioport_write(void *opaque, target_phys_addr_t addr,
! 408: uint64_t val, unsigned size)
1.1 root 409: {
410: PicState *s = opaque;
411: s->elcr = val & s->elcr_mask;
412: }
413:
1.1.1.11! root 414: static uint64_t elcr_ioport_read(void *opaque, target_phys_addr_t addr,
! 415: unsigned size)
1.1 root 416: {
417: PicState *s = opaque;
418: return s->elcr;
419: }
420:
1.1.1.8 root 421: static const VMStateDescription vmstate_pic = {
422: .name = "i8259",
423: .version_id = 1,
424: .minimum_version_id = 1,
425: .minimum_version_id_old = 1,
1.1.1.11! root 426: .fields = (VMStateField[]) {
1.1.1.8 root 427: VMSTATE_UINT8(last_irr, PicState),
428: VMSTATE_UINT8(irr, PicState),
429: VMSTATE_UINT8(imr, PicState),
430: VMSTATE_UINT8(isr, PicState),
431: VMSTATE_UINT8(priority_add, PicState),
432: VMSTATE_UINT8(irq_base, PicState),
433: VMSTATE_UINT8(read_reg_select, PicState),
434: VMSTATE_UINT8(poll, PicState),
435: VMSTATE_UINT8(special_mask, PicState),
436: VMSTATE_UINT8(init_state, PicState),
437: VMSTATE_UINT8(auto_eoi, PicState),
438: VMSTATE_UINT8(rotate_on_auto_eoi, PicState),
439: VMSTATE_UINT8(special_fully_nested_mode, PicState),
440: VMSTATE_UINT8(init4, PicState),
441: VMSTATE_UINT8(single_mode, PicState),
442: VMSTATE_UINT8(elcr, PicState),
443: VMSTATE_END_OF_LIST()
444: }
445: };
1.1 root 446:
1.1.1.11! root 447: static const MemoryRegionOps pic_base_ioport_ops = {
! 448: .read = pic_ioport_read,
! 449: .write = pic_ioport_write,
! 450: .impl = {
! 451: .min_access_size = 1,
! 452: .max_access_size = 1,
! 453: },
! 454: };
! 455:
! 456: static const MemoryRegionOps pic_elcr_ioport_ops = {
! 457: .read = elcr_ioport_read,
! 458: .write = elcr_ioport_write,
! 459: .impl = {
! 460: .min_access_size = 1,
! 461: .max_access_size = 1,
! 462: },
! 463: };
! 464:
! 465: static int pic_initfn(ISADevice *dev)
1.1 root 466: {
1.1.1.11! root 467: PicState *s = DO_UPCAST(PicState, dev, dev);
! 468:
! 469: memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
! 470: memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
! 471:
! 472: isa_register_ioport(NULL, &s->base_io, s->iobase);
! 473: if (s->elcr_addr != -1) {
! 474: isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr);
1.1 root 475: }
1.1.1.11! root 476:
! 477: qdev_init_gpio_out(&dev->qdev, s->int_out, ARRAY_SIZE(s->int_out));
! 478: qdev_init_gpio_in(&dev->qdev, pic_set_irq, 8);
! 479:
! 480: qdev_set_legacy_instance_id(&dev->qdev, s->iobase, 1);
! 481:
! 482: return 0;
1.1 root 483: }
484:
1.1.1.7 root 485: void pic_info(Monitor *mon)
1.1 root 486: {
487: int i;
488: PicState *s;
1.1.1.5 root 489:
1.1.1.11! root 490: if (!isa_pic) {
1.1 root 491: return;
1.1.1.11! root 492: }
! 493: for (i = 0; i < 2; i++) {
! 494: s = i == 0 ? isa_pic : slave_pic;
1.1.1.7 root 495: monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
496: "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
497: i, s->irr, s->imr, s->isr, s->priority_add,
498: s->irq_base, s->read_reg_select, s->elcr,
499: s->special_fully_nested_mode);
1.1 root 500: }
501: }
502:
1.1.1.7 root 503: void irq_info(Monitor *mon)
1.1 root 504: {
505: #ifndef DEBUG_IRQ_COUNT
1.1.1.7 root 506: monitor_printf(mon, "irq statistic code not compiled.\n");
1.1 root 507: #else
508: int i;
509: int64_t count;
510:
1.1.1.7 root 511: monitor_printf(mon, "IRQ statistics:\n");
1.1 root 512: for (i = 0; i < 16; i++) {
513: count = irq_count[i];
1.1.1.11! root 514: if (count > 0) {
1.1.1.7 root 515: monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
1.1.1.11! root 516: }
1.1 root 517: }
518: #endif
519: }
520:
1.1.1.5 root 521: qemu_irq *i8259_init(qemu_irq parent_irq)
1.1 root 522: {
1.1.1.11! root 523: qemu_irq *irq_set;
! 524: ISADevice *dev;
! 525: int i;
! 526:
! 527: irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
1.1.1.5 root 528:
1.1.1.11! root 529: dev = isa_create("isa-i8259");
! 530: qdev_prop_set_uint32(&dev->qdev, "iobase", 0x20);
! 531: qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d0);
! 532: qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xf8);
! 533: qdev_prop_set_bit(&dev->qdev, "master", true);
! 534: qdev_init_nofail(&dev->qdev);
! 535:
! 536: qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
! 537: for (i = 0 ; i < 8; i++) {
! 538: irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
! 539: }
! 540:
! 541: isa_pic = DO_UPCAST(PicState, dev, dev);
! 542:
! 543: dev = isa_create("isa-i8259");
! 544: qdev_prop_set_uint32(&dev->qdev, "iobase", 0xa0);
! 545: qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d1);
! 546: qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xde);
! 547: qdev_init_nofail(&dev->qdev);
! 548:
! 549: qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
! 550: for (i = 0 ; i < 8; i++) {
! 551: irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
! 552: }
! 553:
! 554: slave_pic = DO_UPCAST(PicState, dev, dev);
! 555:
! 556: return irq_set;
! 557: }
! 558:
! 559: static ISADeviceInfo i8259_info = {
! 560: .qdev.name = "isa-i8259",
! 561: .qdev.size = sizeof(PicState),
! 562: .qdev.vmsd = &vmstate_pic,
! 563: .qdev.reset = pic_reset,
! 564: .qdev.no_user = 1,
! 565: .init = pic_initfn,
! 566: .qdev.props = (Property[]) {
! 567: DEFINE_PROP_HEX32("iobase", PicState, iobase, -1),
! 568: DEFINE_PROP_HEX32("elcr_addr", PicState, elcr_addr, -1),
! 569: DEFINE_PROP_HEX8("elcr_mask", PicState, elcr_mask, -1),
! 570: DEFINE_PROP_BIT("master", PicState, master, 0, false),
! 571: DEFINE_PROP_END_OF_LIST(),
! 572: },
! 573: };
! 574:
! 575: static void pic_register(void)
! 576: {
! 577: isa_qdev_register(&i8259_info);
1.1 root 578: }
1.1.1.11! root 579: device_init(pic_register)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.