Statistics
| Branch: | Revision:

root / hw / arm_gic.c @ 8d7b0fbb

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 , (int)s->base, ##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
    s->running_priority = s->priority[irq];
139
    gic_update(s);
140
}
141

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
520
void *arm_gic_init(uint32_t base, void *parent, int parent_irq)
521
{
522
    gic_state *s;
523
    int iomemtype;
524

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