]> git.proxmox.com Git - mirror_qemu.git/blame - hw/i8259.c
i8259: Replace PicState::pics_state with master flag
[mirror_qemu.git] / hw / i8259.c
CommitLineData
80cabfad
FB
1/*
2 * QEMU 8259 interrupt controller emulation
5fafdf24 3 *
80cabfad 4 * Copyright (c) 2003-2004 Fabrice Bellard
5fafdf24 5 *
80cabfad
FB
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 */
87ecb68b
PB
24#include "hw.h"
25#include "pc.h"
26#include "isa.h"
376253ec 27#include "monitor.h"
0bf9e31a 28#include "qemu-timer.h"
80cabfad
FB
29
30/* debug PIC */
31//#define DEBUG_PIC
32
8ac02ff8
BS
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
39
b41a2cd1 40//#define DEBUG_IRQ_LATENCY
4a0fb71e 41//#define DEBUG_IRQ_COUNT
b41a2cd1 42
80cabfad
FB
43typedef struct PicState {
44 uint8_t last_irr; /* edge detection */
45 uint8_t irr; /* interrupt request register */
46 uint8_t imr; /* interrupt mask register */
47 uint8_t isr; /* interrupt service register */
48 uint8_t priority_add; /* highest irq priority */
49 uint8_t irq_base;
50 uint8_t read_reg_select;
51 uint8_t poll;
52 uint8_t special_mask;
53 uint8_t init_state;
54 uint8_t auto_eoi;
55 uint8_t rotate_on_auto_eoi;
56 uint8_t special_fully_nested_mode;
57 uint8_t init4; /* true if 4 byte init */
2053152b 58 uint8_t single_mode; /* true if slave pic is not initialized */
660de336
FB
59 uint8_t elcr; /* PIIX edge/trigger selection*/
60 uint8_t elcr_mask;
2e2b2274 61 qemu_irq int_out;
25985396 62 bool master; /* reflects /SP input pin */
098d314a
RH
63 MemoryRegion base_io;
64 MemoryRegion elcr_io;
80cabfad
FB
65} PicState;
66
3de388f6
FB
67struct PicState2 {
68 /* 0 is master pic, 1 is slave pic */
69 /* XXX: better separation between the two pics */
70 PicState pics[2];
3de388f6
FB
71 void *irq_request_opaque;
72};
80cabfad 73
4a0fb71e
FB
74#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT)
75static int irq_level[16];
76#endif
77#ifdef DEBUG_IRQ_COUNT
78static uint64_t irq_count[16];
79#endif
fbe3288d 80PicState2 *isa_pic;
4a0fb71e 81
80cabfad
FB
82/* return the highest priority found in mask (highest = smallest
83 number). Return 8 if no irq */
5dcd35e2 84static int get_priority(PicState *s, int mask)
80cabfad
FB
85{
86 int priority;
87 if (mask == 0)
88 return 8;
89 priority = 0;
90 while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
91 priority++;
92 return priority;
93}
94
95/* return the pic wanted interrupt. return -1 if none */
96static int pic_get_irq(PicState *s)
97{
98 int mask, cur_priority, priority;
99
100 mask = s->irr & ~s->imr;
101 priority = get_priority(s, mask);
102 if (priority == 8)
103 return -1;
104 /* compute current priority. If special fully nested mode on the
105 master, the IRQ coming from the slave is not taken into account
106 for the priority computation. */
107 mask = s->isr;
84678711
AZ
108 if (s->special_mask)
109 mask &= ~s->imr;
25985396 110 if (s->special_fully_nested_mode && s->master) {
80cabfad 111 mask &= ~(1 << 2);
25985396 112 }
80cabfad
FB
113 cur_priority = get_priority(s, mask);
114 if (priority < cur_priority) {
115 /* higher priority found: an irq should be generated */
116 return (priority + s->priority_add) & 7;
117 } else {
118 return -1;
119 }
120}
121
b76750c1
JK
122/* Update INT output. Must be called every time the output may have changed. */
123static void pic_update_irq(PicState *s)
80cabfad 124{
b76750c1 125 int irq;
80cabfad 126
b76750c1 127 irq = pic_get_irq(s);
80cabfad 128 if (irq >= 0) {
b76750c1 129 DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
25985396 130 s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
b76750c1 131 qemu_irq_raise(s->int_out);
d96e1737 132 } else {
b76750c1 133 qemu_irq_lower(s->int_out);
4de9b249 134 }
80cabfad
FB
135}
136
62026017
JK
137/* set irq level. If an edge is detected, then the IRR is set to 1 */
138static void pic_set_irq1(PicState *s, int irq, int level)
139{
140 int mask;
141 mask = 1 << irq;
142 if (s->elcr & mask) {
143 /* level triggered */
144 if (level) {
145 s->irr |= mask;
146 s->last_irr |= mask;
147 } else {
148 s->irr &= ~mask;
149 s->last_irr &= ~mask;
150 }
151 } else {
152 /* edge triggered */
153 if (level) {
154 if ((s->last_irr & mask) == 0) {
155 s->irr |= mask;
156 }
157 s->last_irr |= mask;
158 } else {
159 s->last_irr &= ~mask;
160 }
161 }
b76750c1 162 pic_update_irq(s);
62026017
JK
163}
164
80cabfad
FB
165#ifdef DEBUG_IRQ_LATENCY
166int64_t irq_time[16];
80cabfad 167#endif
80cabfad 168
9596ebb7 169static void i8259_set_irq(void *opaque, int irq, int level)
80cabfad 170{
3de388f6
FB
171 PicState2 *s = opaque;
172
4a0fb71e 173#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
80cabfad 174 if (level != irq_level[irq]) {
8ac02ff8 175 DPRINTF("i8259_set_irq: irq=%d level=%d\n", irq, level);
80cabfad 176 irq_level[irq] = level;
4a0fb71e
FB
177#ifdef DEBUG_IRQ_COUNT
178 if (level == 1)
179 irq_count[irq]++;
180#endif
80cabfad
FB
181 }
182#endif
183#ifdef DEBUG_IRQ_LATENCY
184 if (level) {
74475455 185 irq_time[irq] = qemu_get_clock_ns(vm_clock);
80cabfad
FB
186 }
187#endif
3de388f6 188 pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
80cabfad
FB
189}
190
191/* acknowledge interrupt 'irq' */
5dcd35e2 192static void pic_intack(PicState *s, int irq)
80cabfad
FB
193{
194 if (s->auto_eoi) {
195 if (s->rotate_on_auto_eoi)
196 s->priority_add = (irq + 1) & 7;
197 } else {
198 s->isr |= (1 << irq);
199 }
0ecf89aa
FB
200 /* We don't clear a level sensitive interrupt here */
201 if (!(s->elcr & (1 << irq)))
202 s->irr &= ~(1 << irq);
b76750c1 203 pic_update_irq(s);
80cabfad
FB
204}
205
3de388f6 206int pic_read_irq(PicState2 *s)
80cabfad
FB
207{
208 int irq, irq2, intno;
209
3de388f6 210 irq = pic_get_irq(&s->pics[0]);
15aeac38 211 if (irq >= 0) {
15aeac38 212 if (irq == 2) {
3de388f6 213 irq2 = pic_get_irq(&s->pics[1]);
15aeac38 214 if (irq2 >= 0) {
3de388f6 215 pic_intack(&s->pics[1], irq2);
15aeac38
FB
216 } else {
217 /* spurious IRQ on slave controller */
218 irq2 = 7;
219 }
3de388f6 220 intno = s->pics[1].irq_base + irq2;
15aeac38 221 } else {
3de388f6 222 intno = s->pics[0].irq_base + irq;
15aeac38 223 }
78ef2b69 224 pic_intack(&s->pics[0], irq);
15aeac38
FB
225 } else {
226 /* spurious IRQ on host controller */
227 irq = 7;
3de388f6 228 intno = s->pics[0].irq_base + irq;
15aeac38 229 }
3b46e624 230
78ef2b69
JK
231#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
232 if (irq == 2) {
233 irq = irq2 + 8;
234 }
235#endif
80cabfad 236#ifdef DEBUG_IRQ_LATENCY
5fafdf24
TS
237 printf("IRQ%d latency=%0.3fus\n",
238 irq,
74475455 239 (double)(qemu_get_clock_ns(vm_clock) -
6ee093c9 240 irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
80cabfad 241#endif
8ac02ff8 242 DPRINTF("pic_interrupt: irq=%d\n", irq);
80cabfad
FB
243 return intno;
244}
245
86fbf97c 246static void pic_init_reset(PicState *s)
d7d02e3c 247{
3de388f6
FB
248 s->last_irr = 0;
249 s->irr = 0;
250 s->imr = 0;
251 s->isr = 0;
252 s->priority_add = 0;
253 s->irq_base = 0;
254 s->read_reg_select = 0;
255 s->poll = 0;
256 s->special_mask = 0;
257 s->init_state = 0;
258 s->auto_eoi = 0;
259 s->rotate_on_auto_eoi = 0;
260 s->special_fully_nested_mode = 0;
261 s->init4 = 0;
2053152b 262 s->single_mode = 0;
4dbe19e1 263 /* Note: ELCR is not reset */
b76750c1 264 pic_update_irq(s);
d7d02e3c
FB
265}
266
86fbf97c
JK
267static void pic_reset(void *opaque)
268{
269 PicState *s = opaque;
270
271 pic_init_reset(s);
272 s->elcr = 0;
273}
274
098d314a
RH
275static void pic_ioport_write(void *opaque, target_phys_addr_t addr64,
276 uint64_t val64, unsigned size)
80cabfad 277{
b41a2cd1 278 PicState *s = opaque;
098d314a
RH
279 uint32_t addr = addr64;
280 uint32_t val = val64;
d7d02e3c 281 int priority, cmd, irq;
80cabfad 282
8ac02ff8 283 DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
80cabfad
FB
284 if (addr == 0) {
285 if (val & 0x10) {
86fbf97c 286 pic_init_reset(s);
80cabfad
FB
287 s->init_state = 1;
288 s->init4 = val & 1;
2053152b 289 s->single_mode = val & 2;
80cabfad
FB
290 if (val & 0x08)
291 hw_error("level sensitive irq not supported");
292 } else if (val & 0x08) {
293 if (val & 0x04)
294 s->poll = 1;
295 if (val & 0x02)
296 s->read_reg_select = val & 1;
297 if (val & 0x40)
298 s->special_mask = (val >> 5) & 1;
299 } else {
300 cmd = val >> 5;
301 switch(cmd) {
302 case 0:
303 case 4:
304 s->rotate_on_auto_eoi = cmd >> 2;
305 break;
306 case 1: /* end of interrupt */
307 case 5:
308 priority = get_priority(s, s->isr);
309 if (priority != 8) {
310 irq = (priority + s->priority_add) & 7;
311 s->isr &= ~(1 << irq);
312 if (cmd == 5)
313 s->priority_add = (irq + 1) & 7;
b76750c1 314 pic_update_irq(s);
80cabfad
FB
315 }
316 break;
317 case 3:
318 irq = val & 7;
319 s->isr &= ~(1 << irq);
b76750c1 320 pic_update_irq(s);
80cabfad
FB
321 break;
322 case 6:
323 s->priority_add = (val + 1) & 7;
b76750c1 324 pic_update_irq(s);
80cabfad
FB
325 break;
326 case 7:
327 irq = val & 7;
328 s->isr &= ~(1 << irq);
329 s->priority_add = (irq + 1) & 7;
b76750c1 330 pic_update_irq(s);
80cabfad
FB
331 break;
332 default:
333 /* no operation */
334 break;
335 }
336 }
337 } else {
338 switch(s->init_state) {
339 case 0:
340 /* normal mode */
341 s->imr = val;
b76750c1 342 pic_update_irq(s);
80cabfad
FB
343 break;
344 case 1:
345 s->irq_base = val & 0xf8;
2bb081f7 346 s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
80cabfad
FB
347 break;
348 case 2:
349 if (s->init4) {
350 s->init_state = 3;
351 } else {
352 s->init_state = 0;
353 }
354 break;
355 case 3:
356 s->special_fully_nested_mode = (val >> 4) & 1;
357 s->auto_eoi = (val >> 1) & 1;
358 s->init_state = 0;
359 break;
360 }
361 }
362}
363
fc1a5e0a 364static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr,
098d314a 365 unsigned size)
80cabfad 366{
b41a2cd1 367 PicState *s = opaque;
80cabfad
FB
368 int ret;
369
80cabfad 370 if (s->poll) {
8d484caa
JK
371 ret = pic_get_irq(s);
372 if (ret >= 0) {
373 pic_intack(s, ret);
374 ret |= 0x80;
375 } else {
376 ret = 0;
377 }
80cabfad
FB
378 s->poll = 0;
379 } else {
380 if (addr == 0) {
381 if (s->read_reg_select)
382 ret = s->isr;
383 else
384 ret = s->irr;
385 } else {
386 ret = s->imr;
387 }
388 }
098d314a 389 DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
80cabfad
FB
390 return ret;
391}
392
d96e1737
JK
393int pic_get_output(PicState2 *s)
394{
395 return (pic_get_irq(&s->pics[0]) >= 0);
396}
397
098d314a
RH
398static void elcr_ioport_write(void *opaque, target_phys_addr_t addr,
399 uint64_t val, unsigned size)
660de336
FB
400{
401 PicState *s = opaque;
402 s->elcr = val & s->elcr_mask;
403}
404
098d314a
RH
405static uint64_t elcr_ioport_read(void *opaque, target_phys_addr_t addr,
406 unsigned size)
660de336
FB
407{
408 PicState *s = opaque;
409 return s->elcr;
410}
411
77eea838
JQ
412static const VMStateDescription vmstate_pic = {
413 .name = "i8259",
414 .version_id = 1,
415 .minimum_version_id = 1,
416 .minimum_version_id_old = 1,
417 .fields = (VMStateField []) {
418 VMSTATE_UINT8(last_irr, PicState),
419 VMSTATE_UINT8(irr, PicState),
420 VMSTATE_UINT8(imr, PicState),
421 VMSTATE_UINT8(isr, PicState),
422 VMSTATE_UINT8(priority_add, PicState),
423 VMSTATE_UINT8(irq_base, PicState),
424 VMSTATE_UINT8(read_reg_select, PicState),
425 VMSTATE_UINT8(poll, PicState),
426 VMSTATE_UINT8(special_mask, PicState),
427 VMSTATE_UINT8(init_state, PicState),
428 VMSTATE_UINT8(auto_eoi, PicState),
429 VMSTATE_UINT8(rotate_on_auto_eoi, PicState),
430 VMSTATE_UINT8(special_fully_nested_mode, PicState),
431 VMSTATE_UINT8(init4, PicState),
432 VMSTATE_UINT8(single_mode, PicState),
433 VMSTATE_UINT8(elcr, PicState),
434 VMSTATE_END_OF_LIST()
435 }
436};
b0a21b53 437
098d314a
RH
438static const MemoryRegionOps pic_base_ioport_ops = {
439 .read = pic_ioport_read,
440 .write = pic_ioport_write,
441 .impl = {
442 .min_access_size = 1,
443 .max_access_size = 1,
444 },
445};
446
447static const MemoryRegionOps pic_elcr_ioport_ops = {
448 .read = elcr_ioport_read,
449 .write = elcr_ioport_write,
450 .impl = {
451 .min_access_size = 1,
452 .max_access_size = 1,
453 },
454};
455
b0a21b53 456/* XXX: add generic master/slave system */
25985396
JK
457static void pic_init(int io_addr, int elcr_addr, PicState *s, qemu_irq int_out,
458 bool master)
b0a21b53 459{
2e2b2274 460 s->int_out = int_out;
25985396 461 s->master = master;
2e2b2274 462
098d314a
RH
463 memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
464 memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
465
466 isa_register_ioport(NULL, &s->base_io, io_addr);
660de336 467 if (elcr_addr >= 0) {
098d314a 468 isa_register_ioport(NULL, &s->elcr_io, elcr_addr);
660de336 469 }
098d314a 470
0be71e32 471 vmstate_register(NULL, io_addr, &vmstate_pic, s);
a08d4367 472 qemu_register_reset(pic_reset, s);
b0a21b53
FB
473}
474
376253ec 475void pic_info(Monitor *mon)
ba91cd80
FB
476{
477 int i;
478 PicState *s;
3b46e624 479
3de388f6
FB
480 if (!isa_pic)
481 return;
ba91cd80
FB
482
483 for(i=0;i<2;i++) {
3de388f6 484 s = &isa_pic->pics[i];
376253ec
AL
485 monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
486 "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
487 i, s->irr, s->imr, s->isr, s->priority_add,
488 s->irq_base, s->read_reg_select, s->elcr,
489 s->special_fully_nested_mode);
ba91cd80
FB
490 }
491}
492
376253ec 493void irq_info(Monitor *mon)
4a0fb71e
FB
494{
495#ifndef DEBUG_IRQ_COUNT
376253ec 496 monitor_printf(mon, "irq statistic code not compiled.\n");
4a0fb71e
FB
497#else
498 int i;
499 int64_t count;
500
376253ec 501 monitor_printf(mon, "IRQ statistics:\n");
4a0fb71e
FB
502 for (i = 0; i < 16; i++) {
503 count = irq_count[i];
504 if (count > 0)
376253ec 505 monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
4a0fb71e
FB
506 }
507#endif
508}
ba91cd80 509
d537cf6c 510qemu_irq *i8259_init(qemu_irq parent_irq)
80cabfad 511{
2e2b2274 512 qemu_irq *irqs;
3de388f6 513 PicState2 *s;
d537cf6c 514
7267c094 515 s = g_malloc0(sizeof(PicState2));
2e2b2274 516 irqs = qemu_allocate_irqs(i8259_set_irq, s, 16);
25985396
JK
517 pic_init(0x20, 0x4d0, &s->pics[0], parent_irq, true);
518 pic_init(0xa0, 0x4d1, &s->pics[1], irqs[2], false);
3de388f6
FB
519 s->pics[0].elcr_mask = 0xf8;
520 s->pics[1].elcr_mask = 0xde;
d537cf6c 521 isa_pic = s;
2e2b2274 522 return irqs;
80cabfad 523}