Statistics
| Branch: | Revision:

root / hw / arm_gic.c @ 70705261

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
    arm_pic_handler handler;
64
    uint32_t base;
65
    void *parent;
66
    int parent_irq;
67
    int enabled;
68
    int cpu_enabled;
69

    
70
    gic_irq_state irq_state[GIC_NIRQ];
71
    int irq_target[GIC_NIRQ];
72
    int priority[GIC_NIRQ];
73
    int last_active[GIC_NIRQ];
74

    
75
    int priority_mask;
76
    int running_irq;
77
    int running_priority;
78
    int current_pending;
79
} gic_state;
80

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
523
void *arm_gic_init(uint32_t base, void *parent, int parent_irq)
524
{
525
    gic_state *s;
526
    int iomemtype;
527

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