]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/blob - hw/i8259.c
isa: pic: convert to QEMU Object Model
[lisovros/qemu_apohw.git] / hw / i8259.c
1 /*
2  * QEMU 8259 interrupt controller emulation
3  *
4  * Copyright (c) 2003-2004 Fabrice Bellard
5  *
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  */
24 #include "hw.h"
25 #include "pc.h"
26 #include "isa.h"
27 #include "monitor.h"
28 #include "qemu-timer.h"
29 #include "i8259_internal.h"
30
31 /* debug PIC */
32 //#define DEBUG_PIC
33
34 #ifdef DEBUG_PIC
35 #define DPRINTF(fmt, ...)                                       \
36     do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
37 #else
38 #define DPRINTF(fmt, ...)
39 #endif
40
41 //#define DEBUG_IRQ_LATENCY
42 //#define DEBUG_IRQ_COUNT
43
44 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
45 static int irq_level[16];
46 #endif
47 #ifdef DEBUG_IRQ_COUNT
48 static uint64_t irq_count[16];
49 #endif
50 #ifdef DEBUG_IRQ_LATENCY
51 static int64_t irq_time[16];
52 #endif
53 DeviceState *isa_pic;
54 static PICCommonState *slave_pic;
55
56 /* return the highest priority found in mask (highest = smallest
57    number). Return 8 if no irq */
58 static int get_priority(PICCommonState *s, int mask)
59 {
60     int priority;
61
62     if (mask == 0) {
63         return 8;
64     }
65     priority = 0;
66     while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
67         priority++;
68     }
69     return priority;
70 }
71
72 /* return the pic wanted interrupt. return -1 if none */
73 static int pic_get_irq(PICCommonState *s)
74 {
75     int mask, cur_priority, priority;
76
77     mask = s->irr & ~s->imr;
78     priority = get_priority(s, mask);
79     if (priority == 8) {
80         return -1;
81     }
82     /* compute current priority. If special fully nested mode on the
83        master, the IRQ coming from the slave is not taken into account
84        for the priority computation. */
85     mask = s->isr;
86     if (s->special_mask) {
87         mask &= ~s->imr;
88     }
89     if (s->special_fully_nested_mode && s->master) {
90         mask &= ~(1 << 2);
91     }
92     cur_priority = get_priority(s, mask);
93     if (priority < cur_priority) {
94         /* higher priority found: an irq should be generated */
95         return (priority + s->priority_add) & 7;
96     } else {
97         return -1;
98     }
99 }
100
101 /* Update INT output. Must be called every time the output may have changed. */
102 static void pic_update_irq(PICCommonState *s)
103 {
104     int irq;
105
106     irq = pic_get_irq(s);
107     if (irq >= 0) {
108         DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
109                 s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
110         qemu_irq_raise(s->int_out[0]);
111     } else {
112         qemu_irq_lower(s->int_out[0]);
113     }
114 }
115
116 /* set irq level. If an edge is detected, then the IRR is set to 1 */
117 static void pic_set_irq(void *opaque, int irq, int level)
118 {
119     PICCommonState *s = opaque;
120     int mask = 1 << irq;
121
122 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
123     defined(DEBUG_IRQ_LATENCY)
124     int irq_index = s->master ? irq : irq + 8;
125 #endif
126 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
127     if (level != irq_level[irq_index]) {
128         DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
129         irq_level[irq_index] = level;
130 #ifdef DEBUG_IRQ_COUNT
131         if (level == 1) {
132             irq_count[irq_index]++;
133         }
134 #endif
135     }
136 #endif
137 #ifdef DEBUG_IRQ_LATENCY
138     if (level) {
139         irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
140     }
141 #endif
142
143     if (s->elcr & mask) {
144         /* level triggered */
145         if (level) {
146             s->irr |= mask;
147             s->last_irr |= mask;
148         } else {
149             s->irr &= ~mask;
150             s->last_irr &= ~mask;
151         }
152     } else {
153         /* edge triggered */
154         if (level) {
155             if ((s->last_irr & mask) == 0) {
156                 s->irr |= mask;
157             }
158             s->last_irr |= mask;
159         } else {
160             s->last_irr &= ~mask;
161         }
162     }
163     pic_update_irq(s);
164 }
165
166 /* acknowledge interrupt 'irq' */
167 static void pic_intack(PICCommonState *s, int irq)
168 {
169     if (s->auto_eoi) {
170         if (s->rotate_on_auto_eoi) {
171             s->priority_add = (irq + 1) & 7;
172         }
173     } else {
174         s->isr |= (1 << irq);
175     }
176     /* We don't clear a level sensitive interrupt here */
177     if (!(s->elcr & (1 << irq))) {
178         s->irr &= ~(1 << irq);
179     }
180     pic_update_irq(s);
181 }
182
183 int pic_read_irq(DeviceState *d)
184 {
185     PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
186     int irq, irq2, intno;
187
188     irq = pic_get_irq(s);
189     if (irq >= 0) {
190         if (irq == 2) {
191             irq2 = pic_get_irq(slave_pic);
192             if (irq2 >= 0) {
193                 pic_intack(slave_pic, irq2);
194             } else {
195                 /* spurious IRQ on slave controller */
196                 irq2 = 7;
197             }
198             intno = slave_pic->irq_base + irq2;
199         } else {
200             intno = s->irq_base + irq;
201         }
202         pic_intack(s, irq);
203     } else {
204         /* spurious IRQ on host controller */
205         irq = 7;
206         intno = s->irq_base + irq;
207     }
208
209 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
210     if (irq == 2) {
211         irq = irq2 + 8;
212     }
213 #endif
214 #ifdef DEBUG_IRQ_LATENCY
215     printf("IRQ%d latency=%0.3fus\n",
216            irq,
217            (double)(qemu_get_clock_ns(vm_clock) -
218                     irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
219 #endif
220     DPRINTF("pic_interrupt: irq=%d\n", irq);
221     return intno;
222 }
223
224 static void pic_init_reset(PICCommonState *s)
225 {
226     pic_reset_common(s);
227     pic_update_irq(s);
228 }
229
230 static void pic_reset(DeviceState *dev)
231 {
232     PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev);
233
234     pic_init_reset(s);
235     s->elcr = 0;
236 }
237
238 static void pic_ioport_write(void *opaque, target_phys_addr_t addr64,
239                              uint64_t val64, unsigned size)
240 {
241     PICCommonState *s = opaque;
242     uint32_t addr = addr64;
243     uint32_t val = val64;
244     int priority, cmd, irq;
245
246     DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
247     if (addr == 0) {
248         if (val & 0x10) {
249             pic_init_reset(s);
250             s->init_state = 1;
251             s->init4 = val & 1;
252             s->single_mode = val & 2;
253             if (val & 0x08) {
254                 hw_error("level sensitive irq not supported");
255             }
256         } else if (val & 0x08) {
257             if (val & 0x04) {
258                 s->poll = 1;
259             }
260             if (val & 0x02) {
261                 s->read_reg_select = val & 1;
262             }
263             if (val & 0x40) {
264                 s->special_mask = (val >> 5) & 1;
265             }
266         } else {
267             cmd = val >> 5;
268             switch (cmd) {
269             case 0:
270             case 4:
271                 s->rotate_on_auto_eoi = cmd >> 2;
272                 break;
273             case 1: /* end of interrupt */
274             case 5:
275                 priority = get_priority(s, s->isr);
276                 if (priority != 8) {
277                     irq = (priority + s->priority_add) & 7;
278                     s->isr &= ~(1 << irq);
279                     if (cmd == 5) {
280                         s->priority_add = (irq + 1) & 7;
281                     }
282                     pic_update_irq(s);
283                 }
284                 break;
285             case 3:
286                 irq = val & 7;
287                 s->isr &= ~(1 << irq);
288                 pic_update_irq(s);
289                 break;
290             case 6:
291                 s->priority_add = (val + 1) & 7;
292                 pic_update_irq(s);
293                 break;
294             case 7:
295                 irq = val & 7;
296                 s->isr &= ~(1 << irq);
297                 s->priority_add = (irq + 1) & 7;
298                 pic_update_irq(s);
299                 break;
300             default:
301                 /* no operation */
302                 break;
303             }
304         }
305     } else {
306         switch (s->init_state) {
307         case 0:
308             /* normal mode */
309             s->imr = val;
310             pic_update_irq(s);
311             break;
312         case 1:
313             s->irq_base = val & 0xf8;
314             s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
315             break;
316         case 2:
317             if (s->init4) {
318                 s->init_state = 3;
319             } else {
320                 s->init_state = 0;
321             }
322             break;
323         case 3:
324             s->special_fully_nested_mode = (val >> 4) & 1;
325             s->auto_eoi = (val >> 1) & 1;
326             s->init_state = 0;
327             break;
328         }
329     }
330 }
331
332 static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr,
333                                 unsigned size)
334 {
335     PICCommonState *s = opaque;
336     int ret;
337
338     if (s->poll) {
339         ret = pic_get_irq(s);
340         if (ret >= 0) {
341             pic_intack(s, ret);
342             ret |= 0x80;
343         } else {
344             ret = 0;
345         }
346         s->poll = 0;
347     } else {
348         if (addr == 0) {
349             if (s->read_reg_select) {
350                 ret = s->isr;
351             } else {
352                 ret = s->irr;
353             }
354         } else {
355             ret = s->imr;
356         }
357     }
358     DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
359     return ret;
360 }
361
362 int pic_get_output(DeviceState *d)
363 {
364     PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
365
366     return (pic_get_irq(s) >= 0);
367 }
368
369 static void elcr_ioport_write(void *opaque, target_phys_addr_t addr,
370                               uint64_t val, unsigned size)
371 {
372     PICCommonState *s = opaque;
373     s->elcr = val & s->elcr_mask;
374 }
375
376 static uint64_t elcr_ioport_read(void *opaque, target_phys_addr_t addr,
377                                  unsigned size)
378 {
379     PICCommonState *s = opaque;
380     return s->elcr;
381 }
382
383 static const MemoryRegionOps pic_base_ioport_ops = {
384     .read = pic_ioport_read,
385     .write = pic_ioport_write,
386     .impl = {
387         .min_access_size = 1,
388         .max_access_size = 1,
389     },
390 };
391
392 static const MemoryRegionOps pic_elcr_ioport_ops = {
393     .read = elcr_ioport_read,
394     .write = elcr_ioport_write,
395     .impl = {
396         .min_access_size = 1,
397         .max_access_size = 1,
398     },
399 };
400
401 static void pic_init(PICCommonState *s)
402 {
403     memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
404     memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
405
406     qdev_init_gpio_out(&s->dev.qdev, s->int_out, ARRAY_SIZE(s->int_out));
407     qdev_init_gpio_in(&s->dev.qdev, pic_set_irq, 8);
408 }
409
410 void pic_info(Monitor *mon)
411 {
412     int i;
413     PICCommonState *s;
414
415     if (!isa_pic) {
416         return;
417     }
418     for (i = 0; i < 2; i++) {
419         s = i == 0 ? DO_UPCAST(PICCommonState, dev.qdev, isa_pic) : slave_pic;
420         monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
421                        "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
422                        i, s->irr, s->imr, s->isr, s->priority_add,
423                        s->irq_base, s->read_reg_select, s->elcr,
424                        s->special_fully_nested_mode);
425     }
426 }
427
428 void irq_info(Monitor *mon)
429 {
430 #ifndef DEBUG_IRQ_COUNT
431     monitor_printf(mon, "irq statistic code not compiled.\n");
432 #else
433     int i;
434     int64_t count;
435
436     monitor_printf(mon, "IRQ statistics:\n");
437     for (i = 0; i < 16; i++) {
438         count = irq_count[i];
439         if (count > 0) {
440             monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
441         }
442     }
443 #endif
444 }
445
446 qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
447 {
448     qemu_irq *irq_set;
449     ISADevice *dev;
450     int i;
451
452     irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
453
454     dev = i8259_init_chip("isa-i8259", bus, true);
455
456     qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
457     for (i = 0 ; i < 8; i++) {
458         irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
459     }
460
461     isa_pic = &dev->qdev;
462
463     dev = i8259_init_chip("isa-i8259", bus, false);
464
465     qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
466     for (i = 0 ; i < 8; i++) {
467         irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
468     }
469
470     slave_pic = DO_UPCAST(PICCommonState, dev, dev);
471
472     return irq_set;
473 }
474
475 static void i8259_class_init(ObjectClass *klass, void *data)
476 {
477     PICCommonClass *k = PIC_COMMON_CLASS(klass);
478
479     k->init = pic_init;
480 }
481
482 static DeviceInfo i8259_info = {
483     .name  = "isa-i8259",
484     .reset = pic_reset,
485     .class_init = i8259_class_init,
486 };
487
488 static void pic_register(void)
489 {
490     pic_qdev_register(&i8259_info);
491 }
492
493 device_init(pic_register)