Statistics
| Branch: | Revision:

root / hw / i8259.c @ ba91cd80

History | View | Annotate | Download (11.4 kB)

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 "vl.h"
25

    
26
/* debug PIC */
27
//#define DEBUG_PIC
28

    
29
//#define DEBUG_IRQ_LATENCY
30

    
31
typedef 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 */
46
} PicState;
47

    
48
/* 0 is master pic, 1 is slave pic */
49
static PicState pics[2];
50
static int pic_irq_requested;
51

    
52
/* set irq level. If an edge is detected, then the IRR is set to 1 */
53
static inline void pic_set_irq1(PicState *s, int irq, int level)
54
{
55
    int mask;
56
    mask = 1 << irq;
57
    if (level) {
58
        if ((s->last_irr & mask) == 0)
59
            s->irr |= mask;
60
        s->last_irr |= mask;
61
    } else {
62
        s->last_irr &= ~mask;
63
    }
64
}
65

    
66
/* return the highest priority found in mask (highest = smallest
67
   number). Return 8 if no irq */
68
static inline int get_priority(PicState *s, int mask)
69
{
70
    int priority;
71
    if (mask == 0)
72
        return 8;
73
    priority = 0;
74
    while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
75
        priority++;
76
    return priority;
77
}
78

    
79
/* return the pic wanted interrupt. return -1 if none */
80
static int pic_get_irq(PicState *s)
81
{
82
    int mask, cur_priority, priority;
83

    
84
    mask = s->irr & ~s->imr;
85
    priority = get_priority(s, mask);
86
    if (priority == 8)
87
        return -1;
88
    /* compute current priority. If special fully nested mode on the
89
       master, the IRQ coming from the slave is not taken into account
90
       for the priority computation. */
91
    mask = s->isr;
92
    if (s->special_fully_nested_mode && s == &pics[0])
93
        mask &= ~(1 << 2);
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
/* raise irq to CPU if necessary. must be called every time the active
104
   irq may change */
105
static void pic_update_irq(void)
106
{
107
    int irq2, irq;
108

    
109
    /* first look at slave pic */
110
    irq2 = pic_get_irq(&pics[1]);
111
    if (irq2 >= 0) {
112
        /* if irq request by slave pic, signal master PIC */
113
        pic_set_irq1(&pics[0], 2, 1);
114
        pic_set_irq1(&pics[0], 2, 0);
115
    }
116
    /* look at requested irq */
117
    irq = pic_get_irq(&pics[0]);
118
    if (irq >= 0) {
119
        if (irq == 2) {
120
            /* from slave pic */
121
            pic_irq_requested = 8 + irq2;
122
        } else {
123
            /* from master pic */
124
            pic_irq_requested = irq;
125
        }
126
#if defined(DEBUG_PIC)
127
        {
128
            int i;
129
            for(i = 0; i < 2; i++) {
130
                printf("pic%d: imr=%x irr=%x padd=%d\n", 
131
                       i, pics[i].imr, pics[i].irr, pics[i].priority_add);
132
                
133
            }
134
        }
135
        printf("pic: cpu_interrupt req=%d\n", pic_irq_requested);
136
#endif
137
        cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
138
    }
139
}
140

    
141
#ifdef DEBUG_IRQ_LATENCY
142
int64_t irq_time[16];
143
#endif
144
#if defined(DEBUG_PIC)
145
int irq_level[16];
146
#endif
147

    
148
void pic_set_irq(int irq, int level)
149
{
150
#if defined(DEBUG_PIC)
151
    if (level != irq_level[irq]) {
152
        printf("pic_set_irq: irq=%d level=%d\n", irq, level);
153
        irq_level[irq] = level;
154
    }
155
#endif
156
#ifdef DEBUG_IRQ_LATENCY
157
    if (level) {
158
        irq_time[irq] = cpu_get_ticks();
159
    }
160
#endif
161
    pic_set_irq1(&pics[irq >> 3], irq & 7, level);
162
    pic_update_irq();
163
}
164

    
165
/* acknowledge interrupt 'irq' */
166
static inline void pic_intack(PicState *s, int irq)
167
{
168
    if (s->auto_eoi) {
169
        if (s->rotate_on_auto_eoi)
170
            s->priority_add = (irq + 1) & 7;
171
    } else {
172
        s->isr |= (1 << irq);
173
    }
174
    s->irr &= ~(1 << irq);
175
}
176

    
177
int cpu_get_pic_interrupt(CPUState *env)
178
{
179
    int irq, irq2, intno;
180

    
181
    /* signal the pic that the irq was acked by the CPU */
182
    irq = pic_irq_requested;
183
#ifdef DEBUG_IRQ_LATENCY
184
    printf("IRQ%d latency=%0.3fus\n", 
185
           irq, 
186
           (double)(cpu_get_ticks() - irq_time[irq]) * 1000000.0 / ticks_per_sec);
187
#endif
188
#if defined(DEBUG_PIC)
189
    printf("pic_interrupt: irq=%d\n", irq);
190
#endif
191

    
192
    if (irq >= 8) {
193
        irq2 = irq & 7;
194
        pic_intack(&pics[1], irq2);
195
        irq = 2;
196
        intno = pics[1].irq_base + irq2;
197
    } else {
198
        intno = pics[0].irq_base + irq;
199
    }
200
    pic_intack(&pics[0], irq);
201
    pic_update_irq();
202
    return intno;
203
}
204

    
205
static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
206
{
207
    PicState *s = opaque;
208
    int priority, cmd, irq;
209

    
210
#ifdef DEBUG_PIC
211
    printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val);
212
#endif
213
    addr &= 1;
214
    if (addr == 0) {
215
        if (val & 0x10) {
216
            /* init */
217
            memset(s, 0, sizeof(PicState));
218
            s->init_state = 1;
219
            s->init4 = val & 1;
220
            if (val & 0x02)
221
                hw_error("single mode not supported");
222
            if (val & 0x08)
223
                hw_error("level sensitive irq not supported");
224
        } else if (val & 0x08) {
225
            if (val & 0x04)
226
                s->poll = 1;
227
            if (val & 0x02)
228
                s->read_reg_select = val & 1;
229
            if (val & 0x40)
230
                s->special_mask = (val >> 5) & 1;
231
        } else {
232
            cmd = val >> 5;
233
            switch(cmd) {
234
            case 0:
235
            case 4:
236
                s->rotate_on_auto_eoi = cmd >> 2;
237
                break;
238
            case 1: /* end of interrupt */
239
            case 5:
240
                priority = get_priority(s, s->isr);
241
                if (priority != 8) {
242
                    irq = (priority + s->priority_add) & 7;
243
                    s->isr &= ~(1 << irq);
244
                    if (cmd == 5)
245
                        s->priority_add = (irq + 1) & 7;
246
                    pic_update_irq();
247
                }
248
                break;
249
            case 3:
250
                irq = val & 7;
251
                s->isr &= ~(1 << irq);
252
                pic_update_irq();
253
                break;
254
            case 6:
255
                s->priority_add = (val + 1) & 7;
256
                pic_update_irq();
257
                break;
258
            case 7:
259
                irq = val & 7;
260
                s->isr &= ~(1 << irq);
261
                s->priority_add = (irq + 1) & 7;
262
                pic_update_irq();
263
                break;
264
            default:
265
                /* no operation */
266
                break;
267
            }
268
        }
269
    } else {
270
        switch(s->init_state) {
271
        case 0:
272
            /* normal mode */
273
            s->imr = val;
274
            pic_update_irq();
275
            break;
276
        case 1:
277
            s->irq_base = val & 0xf8;
278
            s->init_state = 2;
279
            break;
280
        case 2:
281
            if (s->init4) {
282
                s->init_state = 3;
283
            } else {
284
                s->init_state = 0;
285
            }
286
            break;
287
        case 3:
288
            s->special_fully_nested_mode = (val >> 4) & 1;
289
            s->auto_eoi = (val >> 1) & 1;
290
            s->init_state = 0;
291
            break;
292
        }
293
    }
294
}
295

    
296
static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
297
{
298
    int ret;
299

    
300
    ret = pic_get_irq(s);
301
    if (ret >= 0) {
302
        if (addr1 >> 7) {
303
            pics[0].isr &= ~(1 << 2);
304
            pics[0].irr &= ~(1 << 2);
305
        }
306
        s->irr &= ~(1 << ret);
307
        s->isr &= ~(1 << ret);
308
        if (addr1 >> 7 || ret != 2)
309
            pic_update_irq();
310
    } else {
311
        ret = 0x07;
312
        pic_update_irq();
313
    }
314

    
315
    return ret;
316
}
317

    
318
static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
319
{
320
    PicState *s = opaque;
321
    unsigned int addr;
322
    int ret;
323

    
324
    addr = addr1;
325
    addr &= 1;
326
    if (s->poll) {
327
        ret = pic_poll_read(s, addr1);
328
        s->poll = 0;
329
    } else {
330
        if (addr == 0) {
331
            if (s->read_reg_select)
332
                ret = s->isr;
333
            else
334
                ret = s->irr;
335
        } else {
336
            ret = s->imr;
337
        }
338
    }
339
#ifdef DEBUG_PIC
340
    printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret);
341
#endif
342
    return ret;
343
}
344

    
345
/* memory mapped interrupt status */
346
uint32_t pic_intack_read(CPUState *env)
347
{
348
    int ret;
349

    
350
    ret = pic_poll_read(&pics[0], 0x00);
351
    if (ret == 2)
352
        ret = pic_poll_read(&pics[1], 0x80) + 8;
353
    /* Prepare for ISR read */
354
    pics[0].read_reg_select = 1;
355
    
356
    return ret;
357
}
358

    
359
static void pic_save(QEMUFile *f, void *opaque)
360
{
361
    PicState *s = opaque;
362
    
363
    qemu_put_8s(f, &s->last_irr);
364
    qemu_put_8s(f, &s->irr);
365
    qemu_put_8s(f, &s->imr);
366
    qemu_put_8s(f, &s->isr);
367
    qemu_put_8s(f, &s->priority_add);
368
    qemu_put_8s(f, &s->irq_base);
369
    qemu_put_8s(f, &s->read_reg_select);
370
    qemu_put_8s(f, &s->poll);
371
    qemu_put_8s(f, &s->special_mask);
372
    qemu_put_8s(f, &s->init_state);
373
    qemu_put_8s(f, &s->auto_eoi);
374
    qemu_put_8s(f, &s->rotate_on_auto_eoi);
375
    qemu_put_8s(f, &s->special_fully_nested_mode);
376
    qemu_put_8s(f, &s->init4);
377
}
378

    
379
static int pic_load(QEMUFile *f, void *opaque, int version_id)
380
{
381
    PicState *s = opaque;
382
    
383
    if (version_id != 1)
384
        return -EINVAL;
385

    
386
    qemu_get_8s(f, &s->last_irr);
387
    qemu_get_8s(f, &s->irr);
388
    qemu_get_8s(f, &s->imr);
389
    qemu_get_8s(f, &s->isr);
390
    qemu_get_8s(f, &s->priority_add);
391
    qemu_get_8s(f, &s->irq_base);
392
    qemu_get_8s(f, &s->read_reg_select);
393
    qemu_get_8s(f, &s->poll);
394
    qemu_get_8s(f, &s->special_mask);
395
    qemu_get_8s(f, &s->init_state);
396
    qemu_get_8s(f, &s->auto_eoi);
397
    qemu_get_8s(f, &s->rotate_on_auto_eoi);
398
    qemu_get_8s(f, &s->special_fully_nested_mode);
399
    qemu_get_8s(f, &s->init4);
400
    return 0;
401
}
402

    
403
/* XXX: add generic master/slave system */
404
static void pic_init1(int io_addr, PicState *s)
405
{
406
    register_ioport_write(io_addr, 2, 1, pic_ioport_write, s);
407
    register_ioport_read(io_addr, 2, 1, pic_ioport_read, s);
408

    
409
    register_savevm("i8259", io_addr, 1, pic_save, pic_load, s);
410
}
411

    
412
void pic_info(void)
413
{
414
    int i;
415
    PicState *s;
416

    
417
    for(i=0;i<2;i++) {
418
        s = &pics[i];
419
        term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d\n",
420
                    i, s->irr, s->imr, s->isr, s->priority_add, s->irq_base, s->read_reg_select);
421
    }
422
}
423

    
424

    
425
void pic_init(void)
426
{
427
    pic_init1(0x20, &pics[0]);
428
    pic_init1(0xa0, &pics[1]);
429
}
430