~vcs-imports/qemu/git

« back to all changes in this revision

Viewing changes to hw/armv7m_nvic.c

  • Committer: ths
  • Date: 2007-10-08 12:45:38 UTC
  • Revision ID: git-v1:450d4ff553af32fc9d83fef20d7106b0151526b8
CRIS disassembler, originally from binutils, by Edgar E. Iglesias.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3356 c046a42c-6fe2-441c-8c8c-71466251a162

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * ARM Nested Vectored Interrupt Controller
3
 
 *
4
 
 * Copyright (c) 2006-2007 CodeSourcery.
5
 
 * Written by Paul Brook
6
 
 *
7
 
 * This code is licenced under the GPL.
8
 
 *
9
 
 * The ARMv7M System controller is fairly tightly tied in with the
10
 
 * NVIC.  Much of that is also implemented here.
11
 
 */
12
 
 
13
 
#include "hw.h"
14
 
#include "qemu-timer.h"
15
 
#include "arm-misc.h"
16
 
 
17
 
/* 32 internal lines (16 used for system exceptions) plus 64 external
18
 
   interrupt lines.  */
19
 
#define GIC_NIRQ 96
20
 
#define NCPU 1
21
 
#define NVIC 1
22
 
 
23
 
/* Only a single "CPU" interface is present.  */
24
 
static inline int
25
 
gic_get_current_cpu(void)
26
 
{
27
 
    return 0;
28
 
}
29
 
 
30
 
static uint32_t nvic_readl(void *opaque, uint32_t offset);
31
 
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
32
 
 
33
 
#include "arm_gic.c"
34
 
 
35
 
typedef struct {
36
 
    struct {
37
 
        uint32_t control;
38
 
        uint32_t reload;
39
 
        int64_t tick;
40
 
        QEMUTimer *timer;
41
 
    } systick;
42
 
    gic_state *gic;
43
 
} nvic_state;
44
 
 
45
 
/* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
46
 
#define SYSTICK_SCALE 1000ULL
47
 
 
48
 
#define SYSTICK_ENABLE    (1 << 0)
49
 
#define SYSTICK_TICKINT   (1 << 1)
50
 
#define SYSTICK_CLKSOURCE (1 << 2)
51
 
#define SYSTICK_COUNTFLAG (1 << 16)
52
 
 
53
 
int system_clock_scale;
54
 
 
55
 
/* Conversion factor from qemu timer to SysTick frequencies.  */
56
 
static inline int64_t systick_scale(nvic_state *s)
57
 
{
58
 
    if (s->systick.control & SYSTICK_CLKSOURCE)
59
 
        return system_clock_scale;
60
 
    else
61
 
        return 1000;
62
 
}
63
 
 
64
 
static void systick_reload(nvic_state *s, int reset)
65
 
{
66
 
    if (reset)
67
 
        s->systick.tick = qemu_get_clock(vm_clock);
68
 
    s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
69
 
    qemu_mod_timer(s->systick.timer, s->systick.tick);
70
 
}
71
 
 
72
 
static void systick_timer_tick(void * opaque)
73
 
{
74
 
    nvic_state *s = (nvic_state *)opaque;
75
 
    s->systick.control |= SYSTICK_COUNTFLAG;
76
 
    if (s->systick.control & SYSTICK_TICKINT) {
77
 
        /* Trigger the interrupt.  */
78
 
        armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
79
 
    }
80
 
    if (s->systick.reload == 0) {
81
 
        s->systick.control &= ~SYSTICK_ENABLE;
82
 
    } else {
83
 
        systick_reload(s, 0);
84
 
    }
85
 
}
86
 
 
87
 
/* The external routines use the hardware vector numbering, ie. the first
88
 
   IRQ is #16.  The internal GIC routines use #32 as the first IRQ.  */
89
 
void armv7m_nvic_set_pending(void *opaque, int irq)
90
 
{
91
 
    nvic_state *s = (nvic_state *)opaque;
92
 
    if (irq >= 16)
93
 
        irq += 16;
94
 
    gic_set_pending_private(s->gic, 0, irq);
95
 
}
96
 
 
97
 
/* Make pending IRQ active.  */
98
 
int armv7m_nvic_acknowledge_irq(void *opaque)
99
 
{
100
 
    nvic_state *s = (nvic_state *)opaque;
101
 
    uint32_t irq;
102
 
 
103
 
    irq = gic_acknowledge_irq(s->gic, 0);
104
 
    if (irq == 1023)
105
 
        cpu_abort(cpu_single_env, "Interrupt but no vector\n");
106
 
    if (irq >= 32)
107
 
        irq -= 16;
108
 
    return irq;
109
 
}
110
 
 
111
 
void armv7m_nvic_complete_irq(void *opaque, int irq)
112
 
{
113
 
    nvic_state *s = (nvic_state *)opaque;
114
 
    if (irq >= 16)
115
 
        irq += 16;
116
 
    gic_complete_irq(s->gic, 0, irq);
117
 
}
118
 
 
119
 
static uint32_t nvic_readl(void *opaque, uint32_t offset)
120
 
{
121
 
    nvic_state *s = (nvic_state *)opaque;
122
 
    uint32_t val;
123
 
    int irq;
124
 
 
125
 
    switch (offset) {
126
 
    case 4: /* Interrupt Control Type.  */
127
 
        return (GIC_NIRQ / 32) - 1;
128
 
    case 0x10: /* SysTick Control and Status.  */
129
 
        val = s->systick.control;
130
 
        s->systick.control &= ~SYSTICK_COUNTFLAG;
131
 
        return val;
132
 
    case 0x14: /* SysTick Reload Value.  */
133
 
        return s->systick.reload;
134
 
    case 0x18: /* SysTick Current Value.  */
135
 
        {
136
 
            int64_t t;
137
 
            if ((s->systick.control & SYSTICK_ENABLE) == 0)
138
 
                return 0;
139
 
            t = qemu_get_clock(vm_clock);
140
 
            if (t >= s->systick.tick)
141
 
                return 0;
142
 
            val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
143
 
            /* The interrupt in triggered when the timer reaches zero.
144
 
               However the counter is not reloaded until the next clock
145
 
               tick.  This is a hack to return zero during the first tick.  */
146
 
            if (val > s->systick.reload)
147
 
                val = 0;
148
 
            return val;
149
 
        }
150
 
    case 0x1c: /* SysTick Calibration Value.  */
151
 
        return 10000;
152
 
    case 0xd00: /* CPUID Base.  */
153
 
        return cpu_single_env->cp15.c0_cpuid;
154
 
    case 0xd04: /* Interrypt Control State.  */
155
 
        /* VECTACTIVE */
156
 
        val = s->gic->running_irq[0];
157
 
        if (val == 1023) {
158
 
            val = 0;
159
 
        } else if (val >= 32) {
160
 
            val -= 16;
161
 
        }
162
 
        /* RETTOBASE */
163
 
        if (s->gic->running_irq[0] == 1023
164
 
                || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
165
 
            val |= (1 << 11);
166
 
        }
167
 
        /* VECTPENDING */
168
 
        if (s->gic->current_pending[0] != 1023)
169
 
            val |= (s->gic->current_pending[0] << 12);
170
 
        /* ISRPENDING */
171
 
        for (irq = 32; irq < GIC_NIRQ; irq++) {
172
 
            if (s->gic->irq_state[irq].pending) {
173
 
                val |= (1 << 22);
174
 
                break;
175
 
            }
176
 
        }
177
 
        /* PENDSTSET */
178
 
        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
179
 
            val |= (1 << 26);
180
 
        /* PENDSVSET */
181
 
        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
182
 
            val |= (1 << 28);
183
 
        /* NMIPENDSET */
184
 
        if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
185
 
            val |= (1 << 31);
186
 
        return val;
187
 
    case 0xd08: /* Vector Table Offset.  */
188
 
        return cpu_single_env->v7m.vecbase;
189
 
    case 0xd0c: /* Application Interrupt/Reset Control.  */
190
 
        return 0xfa05000;
191
 
    case 0xd10: /* System Control.  */
192
 
        /* TODO: Implement SLEEPONEXIT.  */
193
 
        return 0;
194
 
    case 0xd14: /* Configuration Control.  */
195
 
        /* TODO: Implement Configuration Control bits.  */
196
 
        return 0;
197
 
    case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
198
 
        irq = offset - 0xd14;
199
 
        val = 0;
200
 
        val = s->gic->priority1[irq++][0];
201
 
        val = s->gic->priority1[irq++][0] << 8;
202
 
        val = s->gic->priority1[irq++][0] << 16;
203
 
        val = s->gic->priority1[irq][0] << 24;
204
 
        return val;
205
 
    case 0xd24: /* System Handler Status.  */
206
 
        val = 0;
207
 
        if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
208
 
        if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
209
 
        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
210
 
        if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
211
 
        if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
212
 
        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
213
 
        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
214
 
        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
215
 
        if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
216
 
        if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
217
 
        if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
218
 
        if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
219
 
        if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
220
 
        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
221
 
        return val;
222
 
    case 0xd28: /* Configurable Fault Status.  */
223
 
        /* TODO: Implement Fault Status.  */
224
 
        cpu_abort(cpu_single_env,
225
 
                  "Not implemented: Configurable Fault Status.");
226
 
        return 0;
227
 
    case 0xd2c: /* Hard Fault Status.  */
228
 
    case 0xd30: /* Debug Fault Status.  */
229
 
    case 0xd34: /* Mem Manage Address.  */
230
 
    case 0xd38: /* Bus Fault Address.  */
231
 
    case 0xd3c: /* Aux Fault Status.  */
232
 
        /* TODO: Implement fault status registers.  */
233
 
        goto bad_reg;
234
 
    case 0xd40: /* PFR0.  */
235
 
        return 0x00000030;
236
 
    case 0xd44: /* PRF1.  */
237
 
        return 0x00000200;
238
 
    case 0xd48: /* DFR0.  */
239
 
        return 0x00100000;
240
 
    case 0xd4c: /* AFR0.  */
241
 
        return 0x00000000;
242
 
    case 0xd50: /* MMFR0.  */
243
 
        return 0x00000030;
244
 
    case 0xd54: /* MMFR1.  */
245
 
        return 0x00000000;
246
 
    case 0xd58: /* MMFR2.  */
247
 
        return 0x00000000;
248
 
    case 0xd5c: /* MMFR3.  */
249
 
        return 0x00000000;
250
 
    case 0xd60: /* ISAR0.  */
251
 
        return 0x01141110;
252
 
    case 0xd64: /* ISAR1.  */
253
 
        return 0x02111000;
254
 
    case 0xd68: /* ISAR2.  */
255
 
        return 0x21112231;
256
 
    case 0xd6c: /* ISAR3.  */
257
 
        return 0x01111110;
258
 
    case 0xd70: /* ISAR4.  */
259
 
        return 0x01310102;
260
 
    /* TODO: Implement debug registers.  */
261
 
    default:
262
 
    bad_reg:
263
 
        cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
264
 
    }
265
 
}
266
 
 
267
 
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
268
 
{
269
 
    nvic_state *s = (nvic_state *)opaque;
270
 
    uint32_t oldval;
271
 
    switch (offset) {
272
 
    case 0x10: /* SysTick Control and Status.  */
273
 
        oldval = s->systick.control;
274
 
        s->systick.control &= 0xfffffff8;
275
 
        s->systick.control |= value & 7;
276
 
        if ((oldval ^ value) & SYSTICK_ENABLE) {
277
 
            int64_t now = qemu_get_clock(vm_clock);
278
 
            if (value & SYSTICK_ENABLE) {
279
 
                if (s->systick.tick) {
280
 
                    s->systick.tick += now;
281
 
                    qemu_mod_timer(s->systick.timer, s->systick.tick);
282
 
                } else {
283
 
                    systick_reload(s, 1);
284
 
                }
285
 
            } else {
286
 
                qemu_del_timer(s->systick.timer);
287
 
                s->systick.tick -= now;
288
 
                if (s->systick.tick < 0)
289
 
                  s->systick.tick = 0;
290
 
            }
291
 
        } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
292
 
            /* This is a hack. Force the timer to be reloaded
293
 
               when the reference clock is changed.  */
294
 
            systick_reload(s, 1);
295
 
        }
296
 
        break;
297
 
    case 0x14: /* SysTick Reload Value.  */
298
 
        s->systick.reload = value;
299
 
        break;
300
 
    case 0x18: /* SysTick Current Value.  Writes reload the timer.  */
301
 
        systick_reload(s, 1);
302
 
        s->systick.control &= ~SYSTICK_COUNTFLAG;
303
 
        break;
304
 
    case 0xd04: /* Interrupt Control State.  */
305
 
        if (value & (1 << 31)) {
306
 
            armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
307
 
        }
308
 
        if (value & (1 << 28)) {
309
 
            armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
310
 
        } else if (value & (1 << 27)) {
311
 
            s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
312
 
            gic_update(s->gic);
313
 
        }
314
 
        if (value & (1 << 26)) {
315
 
            armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
316
 
        } else if (value & (1 << 25)) {
317
 
            s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
318
 
            gic_update(s->gic);
319
 
        }
320
 
        break;
321
 
    case 0xd08: /* Vector Table Offset.  */
322
 
        cpu_single_env->v7m.vecbase = value & 0xffffff80;
323
 
        break;
324
 
    case 0xd0c: /* Application Interrupt/Reset Control.  */
325
 
        if ((value >> 16) == 0x05fa) {
326
 
            if (value & 2) {
327
 
                cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
328
 
            }
329
 
            if (value & 5) {
330
 
                cpu_abort(cpu_single_env, "System reset");
331
 
            }
332
 
        }
333
 
        break;
334
 
    case 0xd10: /* System Control.  */
335
 
    case 0xd14: /* Configuration Control.  */
336
 
        /* TODO: Implement control registers.  */
337
 
        goto bad_reg;
338
 
    case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
339
 
        {
340
 
            int irq;
341
 
            irq = offset - 0xd14;
342
 
            s->gic->priority1[irq++][0] = value & 0xff;
343
 
            s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
344
 
            s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
345
 
            s->gic->priority1[irq][0] = (value >> 24) & 0xff;
346
 
            gic_update(s->gic);
347
 
        }
348
 
        break;
349
 
    case 0xd24: /* System Handler Control.  */
350
 
        /* TODO: Real hardware allows you to set/clear the active bits
351
 
           under some circumstances.  We don't implement this.  */
352
 
        s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
353
 
        s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
354
 
        s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
355
 
        break;
356
 
    case 0xd28: /* Configurable Fault Status.  */
357
 
    case 0xd2c: /* Hard Fault Status.  */
358
 
    case 0xd30: /* Debug Fault Status.  */
359
 
    case 0xd34: /* Mem Manage Address.  */
360
 
    case 0xd38: /* Bus Fault Address.  */
361
 
    case 0xd3c: /* Aux Fault Status.  */
362
 
        goto bad_reg;
363
 
    default:
364
 
    bad_reg:
365
 
        cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
366
 
    }
367
 
}
368
 
 
369
 
static void nvic_save(QEMUFile *f, void *opaque)
370
 
{
371
 
    nvic_state *s = (nvic_state *)opaque;
372
 
 
373
 
    qemu_put_be32(f, s->systick.control);
374
 
    qemu_put_be32(f, s->systick.reload);
375
 
    qemu_put_be64(f, s->systick.tick);
376
 
    qemu_put_timer(f, s->systick.timer);
377
 
}
378
 
 
379
 
static int nvic_load(QEMUFile *f, void *opaque, int version_id)
380
 
{
381
 
    nvic_state *s = (nvic_state *)opaque;
382
 
 
383
 
    if (version_id != 1)
384
 
        return -EINVAL;
385
 
 
386
 
    s->systick.control = qemu_get_be32(f);
387
 
    s->systick.reload = qemu_get_be32(f);
388
 
    s->systick.tick = qemu_get_be64(f);
389
 
    qemu_get_timer(f, s->systick.timer);
390
 
 
391
 
    return 0;
392
 
}
393
 
 
394
 
qemu_irq *armv7m_nvic_init(CPUState *env)
395
 
{
396
 
    nvic_state *s;
397
 
    qemu_irq *parent;
398
 
 
399
 
    parent = arm_pic_init_cpu(env);
400
 
    s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
401
 
    s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
402
 
    s->gic->nvic = s;
403
 
    s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
404
 
    if (env->v7m.nvic)
405
 
        cpu_abort(env, "CPU can only have one NVIC\n");
406
 
    env->v7m.nvic = s;
407
 
    register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s);
408
 
    return s->gic->in;
409
 
}