2
* ARM Nested Vectored Interrupt Controller
4
* Copyright (c) 2006-2007 CodeSourcery.
5
* Written by Paul Brook
7
* This code is licenced under the GPL.
9
* The ARMv7M System controller is fairly tightly tied in with the
10
* NVIC. Much of that is also implemented here.
14
#include "qemu-timer.h"
17
/* 32 internal lines (16 used for system exceptions) plus 64 external
23
/* Only a single "CPU" interface is present. */
25
gic_get_current_cpu(void)
30
static uint32_t nvic_readl(void *opaque, uint32_t offset);
31
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
45
/* qemu timers run at 1GHz. We want something closer to 1MHz. */
46
#define SYSTICK_SCALE 1000ULL
48
#define SYSTICK_ENABLE (1 << 0)
49
#define SYSTICK_TICKINT (1 << 1)
50
#define SYSTICK_CLKSOURCE (1 << 2)
51
#define SYSTICK_COUNTFLAG (1 << 16)
53
int system_clock_scale;
55
/* Conversion factor from qemu timer to SysTick frequencies. */
56
static inline int64_t systick_scale(nvic_state *s)
58
if (s->systick.control & SYSTICK_CLKSOURCE)
59
return system_clock_scale;
64
static void systick_reload(nvic_state *s, int 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);
72
static void systick_timer_tick(void * opaque)
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);
80
if (s->systick.reload == 0) {
81
s->systick.control &= ~SYSTICK_ENABLE;
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)
91
nvic_state *s = (nvic_state *)opaque;
94
gic_set_pending_private(s->gic, 0, irq);
97
/* Make pending IRQ active. */
98
int armv7m_nvic_acknowledge_irq(void *opaque)
100
nvic_state *s = (nvic_state *)opaque;
103
irq = gic_acknowledge_irq(s->gic, 0);
105
cpu_abort(cpu_single_env, "Interrupt but no vector\n");
111
void armv7m_nvic_complete_irq(void *opaque, int irq)
113
nvic_state *s = (nvic_state *)opaque;
116
gic_complete_irq(s->gic, 0, irq);
119
static uint32_t nvic_readl(void *opaque, uint32_t offset)
121
nvic_state *s = (nvic_state *)opaque;
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;
132
case 0x14: /* SysTick Reload Value. */
133
return s->systick.reload;
134
case 0x18: /* SysTick Current Value. */
137
if ((s->systick.control & SYSTICK_ENABLE) == 0)
139
t = qemu_get_clock(vm_clock);
140
if (t >= s->systick.tick)
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)
150
case 0x1c: /* SysTick Calibration Value. */
152
case 0xd00: /* CPUID Base. */
153
return cpu_single_env->cp15.c0_cpuid;
154
case 0xd04: /* Interrypt Control State. */
156
val = s->gic->running_irq[0];
159
} else if (val >= 32) {
163
if (s->gic->running_irq[0] == 1023
164
|| s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
168
if (s->gic->current_pending[0] != 1023)
169
val |= (s->gic->current_pending[0] << 12);
171
for (irq = 32; irq < GIC_NIRQ; irq++) {
172
if (s->gic->irq_state[irq].pending) {
178
if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
181
if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
184
if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
187
case 0xd08: /* Vector Table Offset. */
188
return cpu_single_env->v7m.vecbase;
189
case 0xd0c: /* Application Interrupt/Reset Control. */
191
case 0xd10: /* System Control. */
192
/* TODO: Implement SLEEPONEXIT. */
194
case 0xd14: /* Configuration Control. */
195
/* TODO: Implement Configuration Control bits. */
197
case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
198
irq = offset - 0xd14;
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;
205
case 0xd24: /* System Handler Status. */
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);
222
case 0xd28: /* Configurable Fault Status. */
223
/* TODO: Implement Fault Status. */
224
cpu_abort(cpu_single_env,
225
"Not implemented: Configurable Fault Status.");
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. */
234
case 0xd40: /* PFR0. */
236
case 0xd44: /* PRF1. */
238
case 0xd48: /* DFR0. */
240
case 0xd4c: /* AFR0. */
242
case 0xd50: /* MMFR0. */
244
case 0xd54: /* MMFR1. */
246
case 0xd58: /* MMFR2. */
248
case 0xd5c: /* MMFR3. */
250
case 0xd60: /* ISAR0. */
252
case 0xd64: /* ISAR1. */
254
case 0xd68: /* ISAR2. */
256
case 0xd6c: /* ISAR3. */
258
case 0xd70: /* ISAR4. */
260
/* TODO: Implement debug registers. */
263
cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
267
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
269
nvic_state *s = (nvic_state *)opaque;
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);
283
systick_reload(s, 1);
286
qemu_del_timer(s->systick.timer);
287
s->systick.tick -= now;
288
if (s->systick.tick < 0)
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);
297
case 0x14: /* SysTick Reload Value. */
298
s->systick.reload = value;
300
case 0x18: /* SysTick Current Value. Writes reload the timer. */
301
systick_reload(s, 1);
302
s->systick.control &= ~SYSTICK_COUNTFLAG;
304
case 0xd04: /* Interrupt Control State. */
305
if (value & (1 << 31)) {
306
armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
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;
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;
321
case 0xd08: /* Vector Table Offset. */
322
cpu_single_env->v7m.vecbase = value & 0xffffff80;
324
case 0xd0c: /* Application Interrupt/Reset Control. */
325
if ((value >> 16) == 0x05fa) {
327
cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
330
cpu_abort(cpu_single_env, "System reset");
334
case 0xd10: /* System Control. */
335
case 0xd14: /* Configuration Control. */
336
/* TODO: Implement control registers. */
338
case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
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;
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;
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. */
365
cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
369
static void nvic_save(QEMUFile *f, void *opaque)
371
nvic_state *s = (nvic_state *)opaque;
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);
379
static int nvic_load(QEMUFile *f, void *opaque, int version_id)
381
nvic_state *s = (nvic_state *)opaque;
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);
394
qemu_irq *armv7m_nvic_init(CPUState *env)
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]);
403
s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
405
cpu_abort(env, "CPU can only have one NVIC\n");
407
register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s);