i8259: Completely privatize PicState
[qemu/ar7.git] / hw / i8259.c
blobcfaa35c092323b8b16c047063ad94807fdcc694c
1 /*
2 * QEMU 8259 interrupt controller emulation
4 * Copyright (c) 2003-2004 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
24 #include "hw.h"
25 #include "pc.h"
26 #include "isa.h"
27 #include "monitor.h"
28 #include "qemu-timer.h"
30 /* debug PIC */
31 //#define DEBUG_PIC
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
40 //#define DEBUG_IRQ_LATENCY
41 //#define DEBUG_IRQ_COUNT
43 typedef struct PicState PicState;
45 struct PicState {
46 ISADevice dev;
47 uint8_t last_irr; /* edge detection */
48 uint8_t irr; /* interrupt request register */
49 uint8_t imr; /* interrupt mask register */
50 uint8_t isr; /* interrupt service register */
51 uint8_t priority_add; /* highest irq priority */
52 uint8_t irq_base;
53 uint8_t read_reg_select;
54 uint8_t poll;
55 uint8_t special_mask;
56 uint8_t init_state;
57 uint8_t auto_eoi;
58 uint8_t rotate_on_auto_eoi;
59 uint8_t special_fully_nested_mode;
60 uint8_t init4; /* true if 4 byte init */
61 uint8_t single_mode; /* true if slave pic is not initialized */
62 uint8_t elcr; /* PIIX edge/trigger selection*/
63 uint8_t elcr_mask;
64 qemu_irq int_out[1];
65 uint32_t master; /* reflects /SP input pin */
66 uint32_t iobase;
67 uint32_t elcr_addr;
68 MemoryRegion base_io;
69 MemoryRegion elcr_io;
72 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
73 static int irq_level[16];
74 #endif
75 #ifdef DEBUG_IRQ_COUNT
76 static uint64_t irq_count[16];
77 #endif
78 #ifdef DEBUG_IRQ_LATENCY
79 static int64_t irq_time[16];
80 #endif
81 DeviceState *isa_pic;
82 static PicState *slave_pic;
84 /* return the highest priority found in mask (highest = smallest
85 number). Return 8 if no irq */
86 static int get_priority(PicState *s, int mask)
88 int priority;
90 if (mask == 0) {
91 return 8;
93 priority = 0;
94 while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
95 priority++;
97 return priority;
100 /* return the pic wanted interrupt. return -1 if none */
101 static int pic_get_irq(PicState *s)
103 int mask, cur_priority, priority;
105 mask = s->irr & ~s->imr;
106 priority = get_priority(s, mask);
107 if (priority == 8) {
108 return -1;
110 /* compute current priority. If special fully nested mode on the
111 master, the IRQ coming from the slave is not taken into account
112 for the priority computation. */
113 mask = s->isr;
114 if (s->special_mask) {
115 mask &= ~s->imr;
117 if (s->special_fully_nested_mode && s->master) {
118 mask &= ~(1 << 2);
120 cur_priority = get_priority(s, mask);
121 if (priority < cur_priority) {
122 /* higher priority found: an irq should be generated */
123 return (priority + s->priority_add) & 7;
124 } else {
125 return -1;
129 /* Update INT output. Must be called every time the output may have changed. */
130 static void pic_update_irq(PicState *s)
132 int irq;
134 irq = pic_get_irq(s);
135 if (irq >= 0) {
136 DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
137 s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
138 qemu_irq_raise(s->int_out[0]);
139 } else {
140 qemu_irq_lower(s->int_out[0]);
144 /* set irq level. If an edge is detected, then the IRR is set to 1 */
145 static void pic_set_irq(void *opaque, int irq, int level)
147 PicState *s = opaque;
148 int mask = 1 << irq;
150 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
151 defined(DEBUG_IRQ_LATENCY)
152 int irq_index = s->master ? irq : irq + 8;
153 #endif
154 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
155 if (level != irq_level[irq_index]) {
156 DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
157 irq_level[irq_index] = level;
158 #ifdef DEBUG_IRQ_COUNT
159 if (level == 1) {
160 irq_count[irq_index]++;
162 #endif
164 #endif
165 #ifdef DEBUG_IRQ_LATENCY
166 if (level) {
167 irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
169 #endif
171 if (s->elcr & mask) {
172 /* level triggered */
173 if (level) {
174 s->irr |= mask;
175 s->last_irr |= mask;
176 } else {
177 s->irr &= ~mask;
178 s->last_irr &= ~mask;
180 } else {
181 /* edge triggered */
182 if (level) {
183 if ((s->last_irr & mask) == 0) {
184 s->irr |= mask;
186 s->last_irr |= mask;
187 } else {
188 s->last_irr &= ~mask;
191 pic_update_irq(s);
194 /* acknowledge interrupt 'irq' */
195 static void pic_intack(PicState *s, int irq)
197 if (s->auto_eoi) {
198 if (s->rotate_on_auto_eoi) {
199 s->priority_add = (irq + 1) & 7;
201 } else {
202 s->isr |= (1 << irq);
204 /* We don't clear a level sensitive interrupt here */
205 if (!(s->elcr & (1 << irq))) {
206 s->irr &= ~(1 << irq);
208 pic_update_irq(s);
211 int pic_read_irq(DeviceState *d)
213 PicState *s = DO_UPCAST(PicState, dev.qdev, d);
214 int irq, irq2, intno;
216 irq = pic_get_irq(s);
217 if (irq >= 0) {
218 if (irq == 2) {
219 irq2 = pic_get_irq(slave_pic);
220 if (irq2 >= 0) {
221 pic_intack(slave_pic, irq2);
222 } else {
223 /* spurious IRQ on slave controller */
224 irq2 = 7;
226 intno = slave_pic->irq_base + irq2;
227 } else {
228 intno = s->irq_base + irq;
230 pic_intack(s, irq);
231 } else {
232 /* spurious IRQ on host controller */
233 irq = 7;
234 intno = s->irq_base + irq;
237 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
238 if (irq == 2) {
239 irq = irq2 + 8;
241 #endif
242 #ifdef DEBUG_IRQ_LATENCY
243 printf("IRQ%d latency=%0.3fus\n",
244 irq,
245 (double)(qemu_get_clock_ns(vm_clock) -
246 irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
247 #endif
248 DPRINTF("pic_interrupt: irq=%d\n", irq);
249 return intno;
252 static void pic_init_reset(PicState *s)
254 s->last_irr = 0;
255 s->irr = 0;
256 s->imr = 0;
257 s->isr = 0;
258 s->priority_add = 0;
259 s->irq_base = 0;
260 s->read_reg_select = 0;
261 s->poll = 0;
262 s->special_mask = 0;
263 s->init_state = 0;
264 s->auto_eoi = 0;
265 s->rotate_on_auto_eoi = 0;
266 s->special_fully_nested_mode = 0;
267 s->init4 = 0;
268 s->single_mode = 0;
269 /* Note: ELCR is not reset */
270 pic_update_irq(s);
273 static void pic_reset(DeviceState *dev)
275 PicState *s = DO_UPCAST(PicState, dev.qdev, dev);
277 pic_init_reset(s);
278 s->elcr = 0;
281 static void pic_ioport_write(void *opaque, target_phys_addr_t addr64,
282 uint64_t val64, unsigned size)
284 PicState *s = opaque;
285 uint32_t addr = addr64;
286 uint32_t val = val64;
287 int priority, cmd, irq;
289 DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
290 if (addr == 0) {
291 if (val & 0x10) {
292 pic_init_reset(s);
293 s->init_state = 1;
294 s->init4 = val & 1;
295 s->single_mode = val & 2;
296 if (val & 0x08) {
297 hw_error("level sensitive irq not supported");
299 } else if (val & 0x08) {
300 if (val & 0x04) {
301 s->poll = 1;
303 if (val & 0x02) {
304 s->read_reg_select = val & 1;
306 if (val & 0x40) {
307 s->special_mask = (val >> 5) & 1;
309 } else {
310 cmd = val >> 5;
311 switch (cmd) {
312 case 0:
313 case 4:
314 s->rotate_on_auto_eoi = cmd >> 2;
315 break;
316 case 1: /* end of interrupt */
317 case 5:
318 priority = get_priority(s, s->isr);
319 if (priority != 8) {
320 irq = (priority + s->priority_add) & 7;
321 s->isr &= ~(1 << irq);
322 if (cmd == 5) {
323 s->priority_add = (irq + 1) & 7;
325 pic_update_irq(s);
327 break;
328 case 3:
329 irq = val & 7;
330 s->isr &= ~(1 << irq);
331 pic_update_irq(s);
332 break;
333 case 6:
334 s->priority_add = (val + 1) & 7;
335 pic_update_irq(s);
336 break;
337 case 7:
338 irq = val & 7;
339 s->isr &= ~(1 << irq);
340 s->priority_add = (irq + 1) & 7;
341 pic_update_irq(s);
342 break;
343 default:
344 /* no operation */
345 break;
348 } else {
349 switch (s->init_state) {
350 case 0:
351 /* normal mode */
352 s->imr = val;
353 pic_update_irq(s);
354 break;
355 case 1:
356 s->irq_base = val & 0xf8;
357 s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
358 break;
359 case 2:
360 if (s->init4) {
361 s->init_state = 3;
362 } else {
363 s->init_state = 0;
365 break;
366 case 3:
367 s->special_fully_nested_mode = (val >> 4) & 1;
368 s->auto_eoi = (val >> 1) & 1;
369 s->init_state = 0;
370 break;
375 static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr,
376 unsigned size)
378 PicState *s = opaque;
379 int ret;
381 if (s->poll) {
382 ret = pic_get_irq(s);
383 if (ret >= 0) {
384 pic_intack(s, ret);
385 ret |= 0x80;
386 } else {
387 ret = 0;
389 s->poll = 0;
390 } else {
391 if (addr == 0) {
392 if (s->read_reg_select) {
393 ret = s->isr;
394 } else {
395 ret = s->irr;
397 } else {
398 ret = s->imr;
401 DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
402 return ret;
405 int pic_get_output(DeviceState *d)
407 PicState *s = DO_UPCAST(PicState, dev.qdev, d);
409 return (pic_get_irq(s) >= 0);
412 static void elcr_ioport_write(void *opaque, target_phys_addr_t addr,
413 uint64_t val, unsigned size)
415 PicState *s = opaque;
416 s->elcr = val & s->elcr_mask;
419 static uint64_t elcr_ioport_read(void *opaque, target_phys_addr_t addr,
420 unsigned size)
422 PicState *s = opaque;
423 return s->elcr;
426 static const VMStateDescription vmstate_pic = {
427 .name = "i8259",
428 .version_id = 1,
429 .minimum_version_id = 1,
430 .minimum_version_id_old = 1,
431 .fields = (VMStateField[]) {
432 VMSTATE_UINT8(last_irr, PicState),
433 VMSTATE_UINT8(irr, PicState),
434 VMSTATE_UINT8(imr, PicState),
435 VMSTATE_UINT8(isr, PicState),
436 VMSTATE_UINT8(priority_add, PicState),
437 VMSTATE_UINT8(irq_base, PicState),
438 VMSTATE_UINT8(read_reg_select, PicState),
439 VMSTATE_UINT8(poll, PicState),
440 VMSTATE_UINT8(special_mask, PicState),
441 VMSTATE_UINT8(init_state, PicState),
442 VMSTATE_UINT8(auto_eoi, PicState),
443 VMSTATE_UINT8(rotate_on_auto_eoi, PicState),
444 VMSTATE_UINT8(special_fully_nested_mode, PicState),
445 VMSTATE_UINT8(init4, PicState),
446 VMSTATE_UINT8(single_mode, PicState),
447 VMSTATE_UINT8(elcr, PicState),
448 VMSTATE_END_OF_LIST()
452 static const MemoryRegionOps pic_base_ioport_ops = {
453 .read = pic_ioport_read,
454 .write = pic_ioport_write,
455 .impl = {
456 .min_access_size = 1,
457 .max_access_size = 1,
461 static const MemoryRegionOps pic_elcr_ioport_ops = {
462 .read = elcr_ioport_read,
463 .write = elcr_ioport_write,
464 .impl = {
465 .min_access_size = 1,
466 .max_access_size = 1,
470 static int pic_initfn(ISADevice *dev)
472 PicState *s = DO_UPCAST(PicState, dev, dev);
474 memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
475 memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
477 isa_register_ioport(dev, &s->base_io, s->iobase);
478 if (s->elcr_addr != -1) {
479 isa_register_ioport(dev, &s->elcr_io, s->elcr_addr);
482 qdev_init_gpio_out(&dev->qdev, s->int_out, ARRAY_SIZE(s->int_out));
483 qdev_init_gpio_in(&dev->qdev, pic_set_irq, 8);
485 qdev_set_legacy_instance_id(&dev->qdev, s->iobase, 1);
487 return 0;
490 void pic_info(Monitor *mon)
492 int i;
493 PicState *s;
495 if (!isa_pic) {
496 return;
498 for (i = 0; i < 2; i++) {
499 s = i == 0 ? DO_UPCAST(PicState, dev.qdev, isa_pic) : slave_pic;
500 monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
501 "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
502 i, s->irr, s->imr, s->isr, s->priority_add,
503 s->irq_base, s->read_reg_select, s->elcr,
504 s->special_fully_nested_mode);
508 void irq_info(Monitor *mon)
510 #ifndef DEBUG_IRQ_COUNT
511 monitor_printf(mon, "irq statistic code not compiled.\n");
512 #else
513 int i;
514 int64_t count;
516 monitor_printf(mon, "IRQ statistics:\n");
517 for (i = 0; i < 16; i++) {
518 count = irq_count[i];
519 if (count > 0) {
520 monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
523 #endif
526 qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
528 qemu_irq *irq_set;
529 ISADevice *dev;
530 int i;
532 irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
534 dev = isa_create(bus, "isa-i8259");
535 qdev_prop_set_uint32(&dev->qdev, "iobase", 0x20);
536 qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d0);
537 qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xf8);
538 qdev_prop_set_bit(&dev->qdev, "master", true);
539 qdev_init_nofail(&dev->qdev);
541 qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
542 for (i = 0 ; i < 8; i++) {
543 irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
546 isa_pic = &dev->qdev;
548 dev = isa_create(bus, "isa-i8259");
549 qdev_prop_set_uint32(&dev->qdev, "iobase", 0xa0);
550 qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d1);
551 qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xde);
552 qdev_init_nofail(&dev->qdev);
554 qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
555 for (i = 0 ; i < 8; i++) {
556 irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
559 slave_pic = DO_UPCAST(PicState, dev, dev);
561 return irq_set;
564 static ISADeviceInfo i8259_info = {
565 .qdev.name = "isa-i8259",
566 .qdev.size = sizeof(PicState),
567 .qdev.vmsd = &vmstate_pic,
568 .qdev.reset = pic_reset,
569 .qdev.no_user = 1,
570 .init = pic_initfn,
571 .qdev.props = (Property[]) {
572 DEFINE_PROP_HEX32("iobase", PicState, iobase, -1),
573 DEFINE_PROP_HEX32("elcr_addr", PicState, elcr_addr, -1),
574 DEFINE_PROP_HEX8("elcr_mask", PicState, elcr_mask, -1),
575 DEFINE_PROP_BIT("master", PicState, master, 0, false),
576 DEFINE_PROP_END_OF_LIST(),
580 static void pic_register(void)
582 isa_qdev_register(&i8259_info);
584 device_init(pic_register)