~ubuntu-branches/ubuntu/hardy/qemu/hardy

« back to all changes in this revision

Viewing changes to hw/arm_gic.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Vogt
  • Date: 2007-05-02 11:55:16 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20070502115516-rf8z0ba1ojbncx10
Tags: 0.9.0-1ubuntu1
* merged from Debian/experimental
* Merge from debian unstable, remaining changes:
   - Remove 34_syscalls_types.patch from debian/patches/series: add an
     unnecessary kernel header breaking compilation of linux-user/syscall.c.
   - Move proll and openhackware from Depends to Recommends.
   - set Maintainer field to MOTU

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}