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