Statistics
| Branch: | Revision:

root / hw / arm_gic.c @ 5fafdf24

History | View | Annotate | Download (15.3 kB)

1
/*
2
 * ARM AMBA Generic/Distributed Interrupt Controller
3
 *
4
 * Copyright (c) 2006 CodeSourcery.
5
 * Written by Paul Brook
6
 *
7
 * This code is licenced under the GPL.
8
 */
9

    
10
/* TODO: Some variants of this controller can handle multiple CPUs.
11
   Currently only single CPU operation is implemented.  */
12

    
13
#include "vl.h"
14
#include "arm_pic.h"
15

    
16
//#define DEBUG_GIC
17

    
18
#ifdef DEBUG_GIC
19
#define DPRINTF(fmt, args...) \
20
do { printf("arm_gic: " fmt , ##args); } while (0)
21
#else
22
#define DPRINTF(fmt, args...) do {} while(0)
23
#endif
24

    
25
/* Distributed interrupt controller.  */
26

    
27
static const uint8_t gic_id[] =
28
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
29

    
30
#define GIC_NIRQ 96
31

    
32
typedef struct gic_irq_state
33
{
34
    unsigned enabled:1;
35
    unsigned pending:1;
36
    unsigned active:1;
37
    unsigned level:1;
38
    unsigned model:1; /* 0 = 1:N, 1 = N:N */
39
    unsigned trigger:1; /* nonzero = edge triggered.  */
40
} gic_irq_state;
41

    
42
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
43
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
44
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
45
#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
46
#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
47
#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
48
#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
49
#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
50
#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
51
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
52
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
53
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
54
#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
55
#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
56
#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
57
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
58
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
59
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
60

    
61
typedef struct gic_state
62
{
63
    uint32_t base;
64
    qemu_irq parent_irq;
65
    int enabled;
66
    int cpu_enabled;
67

    
68
    gic_irq_state irq_state[GIC_NIRQ];
69
    int irq_target[GIC_NIRQ];
70
    int priority[GIC_NIRQ];
71
    int last_active[GIC_NIRQ];
72

    
73
    int priority_mask;
74
    int running_irq;
75
    int running_priority;
76
    int current_pending;
77
} gic_state;
78

    
79
/* TODO: Many places that call this routine could be optimized.  */
80
/* Update interrupt status after enabled or pending bits have been changed.  */
81
static void gic_update(gic_state *s)
82
{
83
    int best_irq;
84
    int best_prio;
85
    int irq;
86

    
87
    s->current_pending = 1023;
88
    if (!s->enabled || !s->cpu_enabled) {
89
        qemu_irq_lower(s->parent_irq);
90
        return;
91
    }
92
    best_prio = 0x100;
93
    best_irq = 1023;
94
    for (irq = 0; irq < 96; irq++) {
95
        if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
96
            if (s->priority[irq] < best_prio) {
97
                best_prio = s->priority[irq];
98
                best_irq = irq;
99
            }
100
        }
101
    }
102
    if (best_prio > s->priority_mask) {
103
        qemu_irq_lower(s->parent_irq);
104
    } else {
105
        s->current_pending = best_irq;
106
        if (best_prio < s->running_priority) {
107
            DPRINTF("Raised pending IRQ %d\n", best_irq);
108
            qemu_irq_raise(s->parent_irq);
109
        }
110
    }
111
}
112

    
113
static void gic_set_irq(void *opaque, int irq, int level)
114
{
115
    gic_state *s = (gic_state *)opaque;
116
    /* The first external input line is internal interrupt 32.  */
117
    irq += 32;
118
    if (level == GIC_TEST_LEVEL(irq))
119
        return;
120

    
121
    if (level) {
122
        GIC_SET_LEVEL(irq);
123
        if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
124
            DPRINTF("Set %d pending\n", irq);
125
            GIC_SET_PENDING(irq);
126
        }
127
    } else {
128
        GIC_CLEAR_LEVEL(irq);
129
    }
130
    gic_update(s);
131
}
132

    
133
static void gic_set_running_irq(gic_state *s, int irq)
134
{
135
    s->running_irq = irq;
136
    if (irq == 1023)
137
        s->running_priority = 0x100;
138
    else
139
        s->running_priority = s->priority[irq];
140
    gic_update(s);
141
}
142

    
143
static uint32_t gic_acknowledge_irq(gic_state *s)
144
{
145
    int new_irq;
146
    new_irq = s->current_pending;
147
    if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
148
        DPRINTF("ACK no pending IRQ\n");
149
        return 1023;
150
    }
151
    qemu_irq_lower(s->parent_irq);
152
    s->last_active[new_irq] = s->running_irq;
153
    /* For level triggered interrupts we clear the pending bit while
154
       the interrupt is active.  */
155
    GIC_CLEAR_PENDING(new_irq);
156
    gic_set_running_irq(s, new_irq);
157
    DPRINTF("ACK %d\n", new_irq);
158
    return new_irq;
159
}
160

    
161
static void gic_complete_irq(gic_state * s, int irq)
162
{
163
    int update = 0;
164
    DPRINTF("EOI %d\n", irq);
165
    if (s->running_irq == 1023)
166
        return; /* No active IRQ.  */
167
    if (irq != 1023) {
168
        /* Mark level triggered interrupts as pending if they are still
169
           raised.  */
170
        if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
171
                && GIC_TEST_LEVEL(irq)) {
172
            GIC_SET_PENDING(irq);
173
            update = 1;
174
        }
175
    }
176
    if (irq != s->running_irq) {
177
        /* Complete an IRQ that is not currently running.  */
178
        int tmp = s->running_irq;
179
        while (s->last_active[tmp] != 1023) {
180
            if (s->last_active[tmp] == irq) {
181
                s->last_active[tmp] = s->last_active[irq];
182
                break;
183
            }
184
            tmp = s->last_active[tmp];
185
        }
186
        if (update) {
187
            gic_update(s);
188
        }
189
    } else {
190
        /* Complete the current running IRQ.  */
191
        gic_set_running_irq(s, s->last_active[s->running_irq]);
192
    }
193
}
194

    
195
static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
196
{
197
    gic_state *s = (gic_state *)opaque;
198
    uint32_t res;
199
    int irq;
200
    int i;
201

    
202
    offset -= s->base + 0x1000;
203
    if (offset < 0x100) {
204
        if (offset == 0)
205
            return s->enabled;
206
        if (offset == 4)
207
            return (GIC_NIRQ / 32) - 1;
208
        if (offset < 0x08)
209
            return 0;
210
        goto bad_reg;
211
    } else if (offset < 0x200) {
212
        /* Interrupt Set/Clear Enable.  */
213
        if (offset < 0x180)
214
            irq = (offset - 0x100) * 8;
215
        else
216
            irq = (offset - 0x180) * 8;
217
        if (irq >= GIC_NIRQ)
218
            goto bad_reg;
219
        res = 0;
220
        for (i = 0; i < 8; i++) {
221
            if (GIC_TEST_ENABLED(irq + i)) {
222
                res |= (1 << i);
223
            }
224
        }
225
    } else if (offset < 0x300) {
226
        /* Interrupt Set/Clear Pending.  */
227
        if (offset < 0x280)
228
            irq = (offset - 0x200) * 8;
229
        else
230
            irq = (offset - 0x280) * 8;
231
        if (irq >= GIC_NIRQ)
232
            goto bad_reg;
233
        res = 0;
234
        for (i = 0; i < 8; i++) {
235
            if (GIC_TEST_PENDING(irq + i)) {
236
                res |= (1 << i);
237
            }
238
        }
239
    } else if (offset < 0x400) {
240
        /* Interrupt Active.  */
241
        irq = (offset - 0x300) * 8;
242
        if (irq >= GIC_NIRQ)
243
            goto bad_reg;
244
        res = 0;
245
        for (i = 0; i < 8; i++) {
246
            if (GIC_TEST_ACTIVE(irq + i)) {
247
                res |= (1 << i);
248
            }
249
        }
250
    } else if (offset < 0x800) {
251
        /* Interrupt Priority.  */
252
        irq = offset - 0x400;
253
        if (irq >= GIC_NIRQ)
254
            goto bad_reg;
255
        res = s->priority[irq];
256
    } else if (offset < 0xc00) {
257
        /* Interrupt CPU Target.  */
258
        irq = offset - 0x800;
259
        if (irq >= GIC_NIRQ)
260
            goto bad_reg;
261
        res = s->irq_target[irq];
262
    } else if (offset < 0xf00) {
263
        /* Interrupt Configuration.  */
264
        irq = (offset - 0xc00) * 2;
265
        if (irq >= GIC_NIRQ)
266
            goto bad_reg;
267
        res = 0;
268
        for (i = 0; i < 4; i++) {
269
            if (GIC_TEST_MODEL(irq + i))
270
                res |= (1 << (i * 2));
271
            if (GIC_TEST_TRIGGER(irq + i))
272
                res |= (2 << (i * 2));
273
        }
274
    } else if (offset < 0xfe0) {
275
        goto bad_reg;
276
    } else /* offset >= 0xfe0 */ {
277
        if (offset & 3) {
278
            res = 0;
279
        } else {
280
            res = gic_id[(offset - 0xfe0) >> 2];
281
        }
282
    }
283
    return res;
284
bad_reg:
285
    cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
286
    return 0;
287
}
288

    
289
static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
290
{
291
    uint32_t val;
292
    val = gic_dist_readb(opaque, offset);
293
    val |= gic_dist_readb(opaque, offset + 1) << 8;
294
    return val;
295
}
296

    
297
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
298
{
299
    uint32_t val;
300
    val = gic_dist_readw(opaque, offset);
301
    val |= gic_dist_readw(opaque, offset + 2) << 16;
302
    return val;
303
}
304

    
305
static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
306
                            uint32_t value)
307
{
308
    gic_state *s = (gic_state *)opaque;
309
    int irq;
310
    int i;
311

    
312
    offset -= s->base + 0x1000;
313
    if (offset < 0x100) {
314
        if (offset == 0) {
315
            s->enabled = (value & 1);
316
            DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
317
        } else if (offset < 4) {
318
            /* ignored.  */
319
        } else {
320
            goto bad_reg;
321
        }
322
    } else if (offset < 0x180) {
323
        /* Interrupt Set Enable.  */
324
        irq = (offset - 0x100) * 8;
325
        if (irq >= GIC_NIRQ)
326
            goto bad_reg;
327
        for (i = 0; i < 8; i++) {
328
            if (value & (1 << i)) {
329
                if (!GIC_TEST_ENABLED(irq + i))
330
                    DPRINTF("Enabled IRQ %d\n", irq + i);
331
                GIC_SET_ENABLED(irq + i);
332
                /* If a raised level triggered IRQ enabled then mark
333
                   is as pending.  */
334
                if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
335
                    GIC_SET_PENDING(irq + i);
336
            }
337
        }
338
    } else if (offset < 0x200) {
339
        /* Interrupt Clear Enable.  */
340
        irq = (offset - 0x180) * 8;
341
        if (irq >= GIC_NIRQ)
342
            goto bad_reg;
343
        for (i = 0; i < 8; i++) {
344
            if (value & (1 << i)) {
345
                if (GIC_TEST_ENABLED(irq + i))
346
                    DPRINTF("Disabled IRQ %d\n", irq + i);
347
                GIC_CLEAR_ENABLED(irq + i);
348
            }
349
        }
350
    } else if (offset < 0x280) {
351
        /* Interrupt Set Pending.  */
352
        irq = (offset - 0x200) * 8;
353
        if (irq >= GIC_NIRQ)
354
            goto bad_reg;
355
        for (i = 0; i < 8; i++) {
356
            if (value & (1 << i)) {
357
                GIC_SET_PENDING(irq + i);
358
            }
359
        }
360
    } else if (offset < 0x300) {
361
        /* Interrupt Clear Pending.  */
362
        irq = (offset - 0x280) * 8;
363
        if (irq >= GIC_NIRQ)
364
            goto bad_reg;
365
        for (i = 0; i < 8; i++) {
366
            if (value & (1 << i)) {
367
                GIC_CLEAR_PENDING(irq + i);
368
            }
369
        }
370
    } else if (offset < 0x400) {
371
        /* Interrupt Active.  */
372
        goto bad_reg;
373
    } else if (offset < 0x800) {
374
        /* Interrupt Priority.  */
375
        irq = offset - 0x400;
376
        if (irq >= GIC_NIRQ)
377
            goto bad_reg;
378
        s->priority[irq] = value;
379
    } else if (offset < 0xc00) {
380
        /* Interrupt CPU Target.  */
381
        irq = offset - 0x800;
382
        if (irq >= GIC_NIRQ)
383
            goto bad_reg;
384
        s->irq_target[irq] = value;
385
    } else if (offset < 0xf00) {
386
        /* Interrupt Configuration.  */
387
        irq = (offset - 0xc00) * 4;
388
        if (irq >= GIC_NIRQ)
389
            goto bad_reg;
390
        for (i = 0; i < 4; i++) {
391
            if (value & (1 << (i * 2))) {
392
                GIC_SET_MODEL(irq + i);
393
            } else {
394
                GIC_CLEAR_MODEL(irq + i);
395
            }
396
            if (value & (2 << (i * 2))) {
397
                GIC_SET_TRIGGER(irq + i);
398
            } else {
399
                GIC_CLEAR_TRIGGER(irq + i);
400
            }
401
        }
402
    } else {
403
        /* 0xf00 is only handled for word writes.  */
404
        goto bad_reg;
405
    }
406
    gic_update(s);
407
    return;
408
bad_reg:
409
    cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
410
}
411

    
412
static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
413
                            uint32_t value)
414
{
415
    gic_state *s = (gic_state *)opaque;
416
    if (offset - s->base == 0xf00) {
417
        GIC_SET_PENDING(value & 0x3ff);
418
        gic_update(s);
419
        return;
420
    }
421
    gic_dist_writeb(opaque, offset, value & 0xff);
422
    gic_dist_writeb(opaque, offset + 1, value >> 8);
423
}
424

    
425
static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
426
                            uint32_t value)
427
{
428
    gic_dist_writew(opaque, offset, value & 0xffff);
429
    gic_dist_writew(opaque, offset + 2, value >> 16);
430
}
431

    
432
static CPUReadMemoryFunc *gic_dist_readfn[] = {
433
   gic_dist_readb,
434
   gic_dist_readw,
435
   gic_dist_readl
436
};
437

    
438
static CPUWriteMemoryFunc *gic_dist_writefn[] = {
439
   gic_dist_writeb,
440
   gic_dist_writew,
441
   gic_dist_writel
442
};
443

    
444
static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
445
{
446
    gic_state *s = (gic_state *)opaque;
447
    offset -= s->base;
448
    switch (offset) {
449
    case 0x00: /* Control */
450
        return s->cpu_enabled;
451
    case 0x04: /* Priority mask */
452
        return s->priority_mask;
453
    case 0x08: /* Binary Point */
454
        /* ??? Not implemented.  */
455
        return 0;
456
    case 0x0c: /* Acknowledge */
457
        return gic_acknowledge_irq(s);
458
    case 0x14: /* Runing Priority */
459
        return s->running_priority;
460
    case 0x18: /* Highest Pending Interrupt */
461
        return s->current_pending;
462
    default:
463
        cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset);
464
        return 0;
465
    }
466
}
467

    
468
static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
469
                          uint32_t value)
470
{
471
    gic_state *s = (gic_state *)opaque;
472
    offset -= s->base;
473
    switch (offset) {
474
    case 0x00: /* Control */
475
        s->cpu_enabled = (value & 1);
476
        DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
477
        break;
478
    case 0x04: /* Priority mask */
479
        s->priority_mask = (value & 0x3ff);
480
        break;
481
    case 0x08: /* Binary Point */
482
        /* ??? Not implemented.  */
483
        break;
484
    case 0x10: /* End Of Interrupt */
485
        return gic_complete_irq(s, value & 0x3ff);
486
    default:
487
        cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset);
488
        return;
489
    }
490
    gic_update(s);
491
}
492

    
493
static CPUReadMemoryFunc *gic_cpu_readfn[] = {
494
   gic_cpu_read,
495
   gic_cpu_read,
496
   gic_cpu_read
497
};
498

    
499
static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
500
   gic_cpu_write,
501
   gic_cpu_write,
502
   gic_cpu_write
503
};
504

    
505
static void gic_reset(gic_state *s)
506
{
507
    int i;
508
    memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
509
    s->priority_mask = 0xf0;
510
    s->current_pending = 1023;
511
    s->running_irq = 1023;
512
    s->running_priority = 0x100;
513
    for (i = 0; i < 15; i++) {
514
        GIC_SET_ENABLED(i);
515
        GIC_SET_TRIGGER(i);
516
    }
517
    s->enabled = 0;
518
    s->cpu_enabled = 0;
519
}
520

    
521
qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq)
522
{
523
    gic_state *s;
524
    qemu_irq *qi;
525
    int iomemtype;
526

    
527
    s = (gic_state *)qemu_mallocz(sizeof(gic_state));
528
    if (!s)
529
        return NULL;
530
    qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
531
    s->parent_irq = parent_irq;
532
    if (base != 0xffffffff) {
533
        iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
534
                                           gic_cpu_writefn, s);
535
        cpu_register_physical_memory(base, 0x00001000, iomemtype);
536
        iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
537
                                           gic_dist_writefn, s);
538
        cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype);
539
        s->base = base;
540
    } else {
541
        s->base = 0;
542
    }
543
    gic_reset(s);
544
    return qi;
545
}