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