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