xref: /openbmc/qemu/hw/intc/i8259.c (revision b0dc5ee6)
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/hw.h"
25 #include "hw/i386/pc.h"
26 #include "hw/isa/isa.h"
27 #include "monitor/monitor.h"
28 #include "qemu/timer.h"
29 #include "hw/isa/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 = PIC_COMMON(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 = PIC_COMMON(dev);
233 
234     s->elcr = 0;
235     pic_init_reset(s);
236 }
237 
238 static void pic_ioport_write(void *opaque, hwaddr 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, hwaddr 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 = PIC_COMMON(d);
365 
366     return (pic_get_irq(s) >= 0);
367 }
368 
369 static void elcr_ioport_write(void *opaque, hwaddr 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, hwaddr 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     DeviceState *dev = DEVICE(s);
404 
405     memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
406     memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
407 
408     qdev_init_gpio_out(dev, s->int_out, ARRAY_SIZE(s->int_out));
409     qdev_init_gpio_in(dev, pic_set_irq, 8);
410 }
411 
412 void pic_info(Monitor *mon, const QDict *qdict)
413 {
414     int i;
415     PICCommonState *s;
416 
417     if (!isa_pic) {
418         return;
419     }
420     for (i = 0; i < 2; i++) {
421         s = i == 0 ? PIC_COMMON(isa_pic) : slave_pic;
422         monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
423                        "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
424                        i, s->irr, s->imr, s->isr, s->priority_add,
425                        s->irq_base, s->read_reg_select, s->elcr,
426                        s->special_fully_nested_mode);
427     }
428 }
429 
430 void irq_info(Monitor *mon, const QDict *qdict)
431 {
432 #ifndef DEBUG_IRQ_COUNT
433     monitor_printf(mon, "irq statistic code not compiled.\n");
434 #else
435     int i;
436     int64_t count;
437 
438     monitor_printf(mon, "IRQ statistics:\n");
439     for (i = 0; i < 16; i++) {
440         count = irq_count[i];
441         if (count > 0) {
442             monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
443         }
444     }
445 #endif
446 }
447 
448 qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
449 {
450     qemu_irq *irq_set;
451     ISADevice *dev;
452     int i;
453 
454     irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
455 
456     dev = i8259_init_chip("isa-i8259", bus, true);
457 
458     qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
459     for (i = 0 ; i < 8; i++) {
460         irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
461     }
462 
463     isa_pic = &dev->qdev;
464 
465     dev = i8259_init_chip("isa-i8259", bus, false);
466 
467     qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
468     for (i = 0 ; i < 8; i++) {
469         irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
470     }
471 
472     slave_pic = PIC_COMMON(dev);
473 
474     return irq_set;
475 }
476 
477 static void i8259_class_init(ObjectClass *klass, void *data)
478 {
479     PICCommonClass *k = PIC_COMMON_CLASS(klass);
480     DeviceClass *dc = DEVICE_CLASS(klass);
481 
482     k->init = pic_init;
483     dc->reset = pic_reset;
484 }
485 
486 static const TypeInfo i8259_info = {
487     .name       = "isa-i8259",
488     .instance_size = sizeof(PICCommonState),
489     .parent     = TYPE_PIC_COMMON,
490     .class_init = i8259_class_init,
491 };
492 
493 static void pic_register_types(void)
494 {
495     type_register_static(&i8259_info);
496 }
497 
498 type_init(pic_register_types)
499