]> git.proxmox.com Git - qemu.git/blame - hw/i8259.c
added cpu_get_tsc()
[qemu.git] / hw / i8259.c
CommitLineData
80cabfad
FB
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 */
80cabfad
FB
24#include "vl.h"
25
26/* debug PIC */
27//#define DEBUG_PIC
28
b41a2cd1
FB
29//#define DEBUG_IRQ_LATENCY
30
80cabfad
FB
31typedef struct PicState {
32 uint8_t last_irr; /* edge detection */
33 uint8_t irr; /* interrupt request register */
34 uint8_t imr; /* interrupt mask register */
35 uint8_t isr; /* interrupt service register */
36 uint8_t priority_add; /* highest irq priority */
37 uint8_t irq_base;
38 uint8_t read_reg_select;
39 uint8_t poll;
40 uint8_t special_mask;
41 uint8_t init_state;
42 uint8_t auto_eoi;
43 uint8_t rotate_on_auto_eoi;
44 uint8_t special_fully_nested_mode;
45 uint8_t init4; /* true if 4 byte init */
660de336
FB
46 uint8_t elcr; /* PIIX edge/trigger selection*/
47 uint8_t elcr_mask;
80cabfad
FB
48} PicState;
49
50/* 0 is master pic, 1 is slave pic */
ba91cd80
FB
51static PicState pics[2];
52static int pic_irq_requested;
80cabfad
FB
53
54/* set irq level. If an edge is detected, then the IRR is set to 1 */
55static inline void pic_set_irq1(PicState *s, int irq, int level)
56{
57 int mask;
58 mask = 1 << irq;
660de336
FB
59 if (s->elcr & mask) {
60 /* level triggered */
61 if (level) {
80cabfad 62 s->irr |= mask;
660de336
FB
63 s->last_irr |= mask;
64 } else {
65 s->irr &= ~mask;
66 s->last_irr &= ~mask;
67 }
80cabfad 68 } else {
660de336
FB
69 /* edge triggered */
70 if (level) {
71 if ((s->last_irr & mask) == 0)
72 s->irr |= mask;
73 s->last_irr |= mask;
74 } else {
75 s->last_irr &= ~mask;
76 }
80cabfad
FB
77 }
78}
79
80/* return the highest priority found in mask (highest = smallest
81 number). Return 8 if no irq */
82static inline int get_priority(PicState *s, int mask)
83{
84 int priority;
85 if (mask == 0)
86 return 8;
87 priority = 0;
88 while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
89 priority++;
90 return priority;
91}
92
93/* return the pic wanted interrupt. return -1 if none */
94static int pic_get_irq(PicState *s)
95{
96 int mask, cur_priority, priority;
97
98 mask = s->irr & ~s->imr;
99 priority = get_priority(s, mask);
100 if (priority == 8)
101 return -1;
102 /* compute current priority. If special fully nested mode on the
103 master, the IRQ coming from the slave is not taken into account
104 for the priority computation. */
105 mask = s->isr;
106 if (s->special_fully_nested_mode && s == &pics[0])
107 mask &= ~(1 << 2);
108 cur_priority = get_priority(s, mask);
109 if (priority < cur_priority) {
110 /* higher priority found: an irq should be generated */
111 return (priority + s->priority_add) & 7;
112 } else {
113 return -1;
114 }
115}
116
117/* raise irq to CPU if necessary. must be called every time the active
118 irq may change */
b0a21b53 119static void pic_update_irq(void)
80cabfad
FB
120{
121 int irq2, irq;
122
123 /* first look at slave pic */
124 irq2 = pic_get_irq(&pics[1]);
125 if (irq2 >= 0) {
126 /* if irq request by slave pic, signal master PIC */
127 pic_set_irq1(&pics[0], 2, 1);
128 pic_set_irq1(&pics[0], 2, 0);
129 }
130 /* look at requested irq */
131 irq = pic_get_irq(&pics[0]);
132 if (irq >= 0) {
133 if (irq == 2) {
134 /* from slave pic */
135 pic_irq_requested = 8 + irq2;
136 } else {
137 /* from master pic */
138 pic_irq_requested = irq;
139 }
140#if defined(DEBUG_PIC)
141 {
142 int i;
143 for(i = 0; i < 2; i++) {
144 printf("pic%d: imr=%x irr=%x padd=%d\n",
145 i, pics[i].imr, pics[i].irr, pics[i].priority_add);
146
147 }
148 }
149 printf("pic: cpu_interrupt req=%d\n", pic_irq_requested);
150#endif
151 cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
152 }
153}
154
155#ifdef DEBUG_IRQ_LATENCY
156int64_t irq_time[16];
80cabfad
FB
157#endif
158#if defined(DEBUG_PIC)
159int irq_level[16];
160#endif
161
162void pic_set_irq(int irq, int level)
163{
164#if defined(DEBUG_PIC)
165 if (level != irq_level[irq]) {
166 printf("pic_set_irq: irq=%d level=%d\n", irq, level);
167 irq_level[irq] = level;
168 }
169#endif
170#ifdef DEBUG_IRQ_LATENCY
171 if (level) {
172 irq_time[irq] = cpu_get_ticks();
173 }
174#endif
175 pic_set_irq1(&pics[irq >> 3], irq & 7, level);
176 pic_update_irq();
177}
178
179/* acknowledge interrupt 'irq' */
180static inline void pic_intack(PicState *s, int irq)
181{
182 if (s->auto_eoi) {
183 if (s->rotate_on_auto_eoi)
184 s->priority_add = (irq + 1) & 7;
185 } else {
186 s->isr |= (1 << irq);
187 }
188 s->irr &= ~(1 << irq);
189}
190
a541f297 191int cpu_get_pic_interrupt(CPUState *env)
80cabfad
FB
192{
193 int irq, irq2, intno;
194
195 /* signal the pic that the irq was acked by the CPU */
196 irq = pic_irq_requested;
197#ifdef DEBUG_IRQ_LATENCY
198 printf("IRQ%d latency=%0.3fus\n",
199 irq,
200 (double)(cpu_get_ticks() - irq_time[irq]) * 1000000.0 / ticks_per_sec);
201#endif
202#if defined(DEBUG_PIC)
203 printf("pic_interrupt: irq=%d\n", irq);
204#endif
205
206 if (irq >= 8) {
207 irq2 = irq & 7;
208 pic_intack(&pics[1], irq2);
209 irq = 2;
210 intno = pics[1].irq_base + irq2;
211 } else {
212 intno = pics[0].irq_base + irq;
213 }
214 pic_intack(&pics[0], irq);
ba91cd80 215 pic_update_irq();
80cabfad
FB
216 return intno;
217}
218
b41a2cd1 219static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
80cabfad 220{
b41a2cd1 221 PicState *s = opaque;
660de336 222 int priority, cmd, irq, tmp;
80cabfad
FB
223
224#ifdef DEBUG_PIC
225 printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val);
226#endif
80cabfad
FB
227 addr &= 1;
228 if (addr == 0) {
229 if (val & 0x10) {
230 /* init */
660de336 231 tmp = s->elcr_mask;
80cabfad 232 memset(s, 0, sizeof(PicState));
660de336 233 s->elcr_mask = tmp;
b54ad049
FB
234 /* deassert a pending interrupt */
235 cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
660de336 236
80cabfad
FB
237 s->init_state = 1;
238 s->init4 = val & 1;
239 if (val & 0x02)
240 hw_error("single mode not supported");
241 if (val & 0x08)
242 hw_error("level sensitive irq not supported");
243 } else if (val & 0x08) {
244 if (val & 0x04)
245 s->poll = 1;
246 if (val & 0x02)
247 s->read_reg_select = val & 1;
248 if (val & 0x40)
249 s->special_mask = (val >> 5) & 1;
250 } else {
251 cmd = val >> 5;
252 switch(cmd) {
253 case 0:
254 case 4:
255 s->rotate_on_auto_eoi = cmd >> 2;
256 break;
257 case 1: /* end of interrupt */
258 case 5:
259 priority = get_priority(s, s->isr);
260 if (priority != 8) {
261 irq = (priority + s->priority_add) & 7;
262 s->isr &= ~(1 << irq);
263 if (cmd == 5)
264 s->priority_add = (irq + 1) & 7;
265 pic_update_irq();
266 }
267 break;
268 case 3:
269 irq = val & 7;
270 s->isr &= ~(1 << irq);
271 pic_update_irq();
272 break;
273 case 6:
274 s->priority_add = (val + 1) & 7;
275 pic_update_irq();
276 break;
277 case 7:
278 irq = val & 7;
279 s->isr &= ~(1 << irq);
280 s->priority_add = (irq + 1) & 7;
281 pic_update_irq();
282 break;
283 default:
284 /* no operation */
285 break;
286 }
287 }
288 } else {
289 switch(s->init_state) {
290 case 0:
291 /* normal mode */
292 s->imr = val;
293 pic_update_irq();
294 break;
295 case 1:
296 s->irq_base = val & 0xf8;
297 s->init_state = 2;
298 break;
299 case 2:
300 if (s->init4) {
301 s->init_state = 3;
302 } else {
303 s->init_state = 0;
304 }
305 break;
306 case 3:
307 s->special_fully_nested_mode = (val >> 4) & 1;
308 s->auto_eoi = (val >> 1) & 1;
309 s->init_state = 0;
310 break;
311 }
312 }
313}
314
315static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
316{
317 int ret;
318
319 ret = pic_get_irq(s);
320 if (ret >= 0) {
321 if (addr1 >> 7) {
322 pics[0].isr &= ~(1 << 2);
323 pics[0].irr &= ~(1 << 2);
324 }
325 s->irr &= ~(1 << ret);
326 s->isr &= ~(1 << ret);
327 if (addr1 >> 7 || ret != 2)
328 pic_update_irq();
329 } else {
330 ret = 0x07;
331 pic_update_irq();
332 }
333
334 return ret;
335}
336
b41a2cd1 337static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
80cabfad 338{
b41a2cd1 339 PicState *s = opaque;
80cabfad
FB
340 unsigned int addr;
341 int ret;
342
343 addr = addr1;
80cabfad
FB
344 addr &= 1;
345 if (s->poll) {
346 ret = pic_poll_read(s, addr1);
347 s->poll = 0;
348 } else {
349 if (addr == 0) {
350 if (s->read_reg_select)
351 ret = s->isr;
352 else
353 ret = s->irr;
354 } else {
355 ret = s->imr;
356 }
357 }
358#ifdef DEBUG_PIC
359 printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret);
360#endif
361 return ret;
362}
363
364/* memory mapped interrupt status */
365uint32_t pic_intack_read(CPUState *env)
366{
367 int ret;
368
369 ret = pic_poll_read(&pics[0], 0x00);
370 if (ret == 2)
371 ret = pic_poll_read(&pics[1], 0x80) + 8;
372 /* Prepare for ISR read */
373 pics[0].read_reg_select = 1;
374
375 return ret;
376}
377
660de336
FB
378static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
379{
380 PicState *s = opaque;
381 s->elcr = val & s->elcr_mask;
382}
383
384static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
385{
386 PicState *s = opaque;
387 return s->elcr;
388}
389
b0a21b53
FB
390static void pic_save(QEMUFile *f, void *opaque)
391{
392 PicState *s = opaque;
393
394 qemu_put_8s(f, &s->last_irr);
395 qemu_put_8s(f, &s->irr);
396 qemu_put_8s(f, &s->imr);
397 qemu_put_8s(f, &s->isr);
398 qemu_put_8s(f, &s->priority_add);
399 qemu_put_8s(f, &s->irq_base);
400 qemu_put_8s(f, &s->read_reg_select);
401 qemu_put_8s(f, &s->poll);
402 qemu_put_8s(f, &s->special_mask);
403 qemu_put_8s(f, &s->init_state);
404 qemu_put_8s(f, &s->auto_eoi);
405 qemu_put_8s(f, &s->rotate_on_auto_eoi);
406 qemu_put_8s(f, &s->special_fully_nested_mode);
407 qemu_put_8s(f, &s->init4);
660de336 408 qemu_put_8s(f, &s->elcr);
b0a21b53
FB
409}
410
411static int pic_load(QEMUFile *f, void *opaque, int version_id)
412{
413 PicState *s = opaque;
414
415 if (version_id != 1)
416 return -EINVAL;
417
418 qemu_get_8s(f, &s->last_irr);
419 qemu_get_8s(f, &s->irr);
420 qemu_get_8s(f, &s->imr);
421 qemu_get_8s(f, &s->isr);
422 qemu_get_8s(f, &s->priority_add);
423 qemu_get_8s(f, &s->irq_base);
424 qemu_get_8s(f, &s->read_reg_select);
425 qemu_get_8s(f, &s->poll);
426 qemu_get_8s(f, &s->special_mask);
427 qemu_get_8s(f, &s->init_state);
428 qemu_get_8s(f, &s->auto_eoi);
429 qemu_get_8s(f, &s->rotate_on_auto_eoi);
430 qemu_get_8s(f, &s->special_fully_nested_mode);
431 qemu_get_8s(f, &s->init4);
660de336 432 qemu_get_8s(f, &s->elcr);
b0a21b53
FB
433 return 0;
434}
435
436/* XXX: add generic master/slave system */
660de336 437static void pic_init1(int io_addr, int elcr_addr, PicState *s)
b0a21b53
FB
438{
439 register_ioport_write(io_addr, 2, 1, pic_ioport_write, s);
440 register_ioport_read(io_addr, 2, 1, pic_ioport_read, s);
660de336
FB
441 if (elcr_addr >= 0) {
442 register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s);
443 register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s);
444 }
b0a21b53
FB
445 register_savevm("i8259", io_addr, 1, pic_save, pic_load, s);
446}
447
ba91cd80
FB
448void pic_info(void)
449{
450 int i;
451 PicState *s;
452
453 for(i=0;i<2;i++) {
454 s = &pics[i];
660de336
FB
455 term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x\n",
456 i, s->irr, s->imr, s->isr, s->priority_add,
457 s->irq_base, s->read_reg_select, s->elcr);
ba91cd80
FB
458 }
459}
460
461
80cabfad
FB
462void pic_init(void)
463{
660de336
FB
464 pic_init1(0x20, 0x4d0, &pics[0]);
465 pic_init1(0xa0, 0x4d1, &pics[1]);
466 pics[0].elcr_mask = 0xf8;
467 pics[1].elcr_mask = 0xde;
80cabfad
FB
468}
469