root / hw / armv7m_nvic.c @ bf3bc4c4
History | View | Annotate | Download (17.9 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 | 8e31bf38 | Matthew Fernandez | * This code is licensed 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 | fe7e8758 | Paul Brook | #include "sysbus.h" |
14 | 87ecb68b | pbrook | #include "qemu-timer.h" |
15 | 87ecb68b | pbrook | #include "arm-misc.h" |
16 | 755c0802 | Avi Kivity | #include "exec-memory.h" |
17 | 1e8cae4d | Peter Maydell | #include "arm_gic_internal.h" |
18 | 9ee6e8bb | pbrook | |
19 | 9ee6e8bb | pbrook | typedef struct { |
20 | fae15286 | Peter Maydell | GICState gic; |
21 | 9ee6e8bb | pbrook | struct {
|
22 | 9ee6e8bb | pbrook | uint32_t control; |
23 | 9ee6e8bb | pbrook | uint32_t reload; |
24 | 9ee6e8bb | pbrook | int64_t tick; |
25 | 9ee6e8bb | pbrook | QEMUTimer *timer; |
26 | 9ee6e8bb | pbrook | } systick; |
27 | 2a29ddee | Peter Maydell | MemoryRegion sysregmem; |
28 | 2a29ddee | Peter Maydell | MemoryRegion gic_iomem_alias; |
29 | 2a29ddee | Peter Maydell | MemoryRegion container; |
30 | a32134aa | Mark Langsdorf | uint32_t num_irq; |
31 | 9ee6e8bb | pbrook | } nvic_state; |
32 | 9ee6e8bb | pbrook | |
33 | 1e8cae4d | Peter Maydell | #define TYPE_NVIC "armv7m_nvic" |
34 | 1e8cae4d | Peter Maydell | /**
|
35 | 1e8cae4d | Peter Maydell | * NVICClass:
|
36 | 1e8cae4d | Peter Maydell | * @parent_reset: the parent class' reset handler.
|
37 | 1e8cae4d | Peter Maydell | *
|
38 | 1e8cae4d | Peter Maydell | * A model of the v7M NVIC and System Controller
|
39 | 1e8cae4d | Peter Maydell | */
|
40 | 1e8cae4d | Peter Maydell | typedef struct NVICClass { |
41 | 1e8cae4d | Peter Maydell | /*< private >*/
|
42 | 1e8cae4d | Peter Maydell | ARMGICClass parent_class; |
43 | 1e8cae4d | Peter Maydell | /*< public >*/
|
44 | 1e8cae4d | Peter Maydell | int (*parent_init)(SysBusDevice *dev);
|
45 | 1e8cae4d | Peter Maydell | void (*parent_reset)(DeviceState *dev);
|
46 | 1e8cae4d | Peter Maydell | } NVICClass; |
47 | 1e8cae4d | Peter Maydell | |
48 | 1e8cae4d | Peter Maydell | #define NVIC_CLASS(klass) \
|
49 | 1e8cae4d | Peter Maydell | OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC) |
50 | 1e8cae4d | Peter Maydell | #define NVIC_GET_CLASS(obj) \
|
51 | 1e8cae4d | Peter Maydell | OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC) |
52 | 1e8cae4d | Peter Maydell | #define NVIC(obj) \
|
53 | 1e8cae4d | Peter Maydell | OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC) |
54 | 1e8cae4d | Peter Maydell | |
55 | 2a29ddee | Peter Maydell | static const uint8_t nvic_id[] = { |
56 | 2a29ddee | Peter Maydell | 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 |
57 | 2a29ddee | Peter Maydell | }; |
58 | 2a29ddee | Peter Maydell | |
59 | 9ee6e8bb | pbrook | /* qemu timers run at 1GHz. We want something closer to 1MHz. */
|
60 | 9ee6e8bb | pbrook | #define SYSTICK_SCALE 1000ULL |
61 | 9ee6e8bb | pbrook | |
62 | 9ee6e8bb | pbrook | #define SYSTICK_ENABLE (1 << 0) |
63 | 9ee6e8bb | pbrook | #define SYSTICK_TICKINT (1 << 1) |
64 | 9ee6e8bb | pbrook | #define SYSTICK_CLKSOURCE (1 << 2) |
65 | 9ee6e8bb | pbrook | #define SYSTICK_COUNTFLAG (1 << 16) |
66 | 9ee6e8bb | pbrook | |
67 | 7ee930d0 | blueswir1 | int system_clock_scale;
|
68 | 7ee930d0 | blueswir1 | |
69 | e57ec016 | pbrook | /* Conversion factor from qemu timer to SysTick frequencies. */
|
70 | 9ee6e8bb | pbrook | static inline int64_t systick_scale(nvic_state *s) |
71 | 9ee6e8bb | pbrook | { |
72 | 9ee6e8bb | pbrook | if (s->systick.control & SYSTICK_CLKSOURCE)
|
73 | e57ec016 | pbrook | return system_clock_scale;
|
74 | 9ee6e8bb | pbrook | else
|
75 | 9ee6e8bb | pbrook | return 1000; |
76 | 9ee6e8bb | pbrook | } |
77 | 9ee6e8bb | pbrook | |
78 | 9ee6e8bb | pbrook | static void systick_reload(nvic_state *s, int reset) |
79 | 9ee6e8bb | pbrook | { |
80 | 9ee6e8bb | pbrook | if (reset)
|
81 | 74475455 | Paolo Bonzini | s->systick.tick = qemu_get_clock_ns(vm_clock); |
82 | 9ee6e8bb | pbrook | s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
|
83 | 9ee6e8bb | pbrook | qemu_mod_timer(s->systick.timer, s->systick.tick); |
84 | 9ee6e8bb | pbrook | } |
85 | 9ee6e8bb | pbrook | |
86 | 9ee6e8bb | pbrook | static void systick_timer_tick(void * opaque) |
87 | 9ee6e8bb | pbrook | { |
88 | 9ee6e8bb | pbrook | nvic_state *s = (nvic_state *)opaque; |
89 | 9ee6e8bb | pbrook | s->systick.control |= SYSTICK_COUNTFLAG; |
90 | 9ee6e8bb | pbrook | if (s->systick.control & SYSTICK_TICKINT) {
|
91 | 9ee6e8bb | pbrook | /* Trigger the interrupt. */
|
92 | 9ee6e8bb | pbrook | armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); |
93 | 9ee6e8bb | pbrook | } |
94 | 9ee6e8bb | pbrook | if (s->systick.reload == 0) { |
95 | 9ee6e8bb | pbrook | s->systick.control &= ~SYSTICK_ENABLE; |
96 | 9ee6e8bb | pbrook | } else {
|
97 | 9ee6e8bb | pbrook | systick_reload(s, 0);
|
98 | 9ee6e8bb | pbrook | } |
99 | 9ee6e8bb | pbrook | } |
100 | 9ee6e8bb | pbrook | |
101 | aecff692 | Peter Maydell | static void systick_reset(nvic_state *s) |
102 | aecff692 | Peter Maydell | { |
103 | aecff692 | Peter Maydell | s->systick.control = 0;
|
104 | aecff692 | Peter Maydell | s->systick.reload = 0;
|
105 | aecff692 | Peter Maydell | s->systick.tick = 0;
|
106 | aecff692 | Peter Maydell | qemu_del_timer(s->systick.timer); |
107 | aecff692 | Peter Maydell | } |
108 | aecff692 | Peter Maydell | |
109 | 9ee6e8bb | pbrook | /* The external routines use the hardware vector numbering, ie. the first
|
110 | 9ee6e8bb | pbrook | IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
|
111 | 9ee6e8bb | pbrook | void armv7m_nvic_set_pending(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 | fe7e8758 | Paul Brook | gic_set_pending_private(&s->gic, 0, irq);
|
117 | 9ee6e8bb | pbrook | } |
118 | 9ee6e8bb | pbrook | |
119 | 9ee6e8bb | pbrook | /* Make pending IRQ active. */
|
120 | 9ee6e8bb | pbrook | int armv7m_nvic_acknowledge_irq(void *opaque) |
121 | 9ee6e8bb | pbrook | { |
122 | 9ee6e8bb | pbrook | nvic_state *s = (nvic_state *)opaque; |
123 | 9ee6e8bb | pbrook | uint32_t irq; |
124 | 9ee6e8bb | pbrook | |
125 | fe7e8758 | Paul Brook | irq = gic_acknowledge_irq(&s->gic, 0);
|
126 | 9ee6e8bb | pbrook | if (irq == 1023) |
127 | 2ac71179 | Paul Brook | hw_error("Interrupt but no vector\n");
|
128 | 9ee6e8bb | pbrook | if (irq >= 32) |
129 | 9ee6e8bb | pbrook | irq -= 16;
|
130 | 9ee6e8bb | pbrook | return irq;
|
131 | 9ee6e8bb | pbrook | } |
132 | 9ee6e8bb | pbrook | |
133 | 9ee6e8bb | pbrook | void armv7m_nvic_complete_irq(void *opaque, int irq) |
134 | 9ee6e8bb | pbrook | { |
135 | 9ee6e8bb | pbrook | nvic_state *s = (nvic_state *)opaque; |
136 | 9ee6e8bb | pbrook | if (irq >= 16) |
137 | 9ee6e8bb | pbrook | irq += 16;
|
138 | fe7e8758 | Paul Brook | gic_complete_irq(&s->gic, 0, irq);
|
139 | 9ee6e8bb | pbrook | } |
140 | 9ee6e8bb | pbrook | |
141 | 0e8153dd | Andre Beckus | static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
|
142 | 9ee6e8bb | pbrook | { |
143 | 9ee6e8bb | pbrook | uint32_t val; |
144 | 9ee6e8bb | pbrook | int irq;
|
145 | 9ee6e8bb | pbrook | |
146 | 9ee6e8bb | pbrook | switch (offset) {
|
147 | 9ee6e8bb | pbrook | case 4: /* Interrupt Control Type. */ |
148 | a32134aa | Mark Langsdorf | return (s->num_irq / 32) - 1; |
149 | 9ee6e8bb | pbrook | case 0x10: /* SysTick Control and Status. */ |
150 | 9ee6e8bb | pbrook | val = s->systick.control; |
151 | 9ee6e8bb | pbrook | s->systick.control &= ~SYSTICK_COUNTFLAG; |
152 | 9ee6e8bb | pbrook | return val;
|
153 | 9ee6e8bb | pbrook | case 0x14: /* SysTick Reload Value. */ |
154 | 9ee6e8bb | pbrook | return s->systick.reload;
|
155 | 9ee6e8bb | pbrook | case 0x18: /* SysTick Current Value. */ |
156 | 9ee6e8bb | pbrook | { |
157 | 9ee6e8bb | pbrook | int64_t t; |
158 | 9ee6e8bb | pbrook | if ((s->systick.control & SYSTICK_ENABLE) == 0) |
159 | 9ee6e8bb | pbrook | return 0; |
160 | 74475455 | Paolo Bonzini | t = qemu_get_clock_ns(vm_clock); |
161 | 9ee6e8bb | pbrook | if (t >= s->systick.tick)
|
162 | 9ee6e8bb | pbrook | return 0; |
163 | 9ee6e8bb | pbrook | val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1; |
164 | 9ee6e8bb | pbrook | /* The interrupt in triggered when the timer reaches zero.
|
165 | 9ee6e8bb | pbrook | However the counter is not reloaded until the next clock
|
166 | 9ee6e8bb | pbrook | tick. This is a hack to return zero during the first tick. */
|
167 | 9ee6e8bb | pbrook | if (val > s->systick.reload)
|
168 | 9ee6e8bb | pbrook | val = 0;
|
169 | 9ee6e8bb | pbrook | return val;
|
170 | 9ee6e8bb | pbrook | } |
171 | 9ee6e8bb | pbrook | case 0x1c: /* SysTick Calibration Value. */ |
172 | 9ee6e8bb | pbrook | return 10000; |
173 | 9ee6e8bb | pbrook | case 0xd00: /* CPUID Base. */ |
174 | 9ee6e8bb | pbrook | return cpu_single_env->cp15.c0_cpuid;
|
175 | 9ee6e8bb | pbrook | case 0xd04: /* Interrypt Control State. */ |
176 | 9ee6e8bb | pbrook | /* VECTACTIVE */
|
177 | fe7e8758 | Paul Brook | val = s->gic.running_irq[0];
|
178 | 9ee6e8bb | pbrook | if (val == 1023) { |
179 | 9ee6e8bb | pbrook | val = 0;
|
180 | 9ee6e8bb | pbrook | } else if (val >= 32) { |
181 | 9ee6e8bb | pbrook | val -= 16;
|
182 | 9ee6e8bb | pbrook | } |
183 | 9ee6e8bb | pbrook | /* RETTOBASE */
|
184 | fe7e8758 | Paul Brook | if (s->gic.running_irq[0] == 1023 |
185 | fe7e8758 | Paul Brook | || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) { |
186 | 9ee6e8bb | pbrook | val |= (1 << 11); |
187 | 9ee6e8bb | pbrook | } |
188 | 9ee6e8bb | pbrook | /* VECTPENDING */
|
189 | fe7e8758 | Paul Brook | if (s->gic.current_pending[0] != 1023) |
190 | fe7e8758 | Paul Brook | val |= (s->gic.current_pending[0] << 12); |
191 | 9ee6e8bb | pbrook | /* ISRPENDING */
|
192 | a32134aa | Mark Langsdorf | for (irq = 32; irq < s->num_irq; irq++) { |
193 | fe7e8758 | Paul Brook | if (s->gic.irq_state[irq].pending) {
|
194 | 9ee6e8bb | pbrook | val |= (1 << 22); |
195 | 9ee6e8bb | pbrook | break;
|
196 | 9ee6e8bb | pbrook | } |
197 | 9ee6e8bb | pbrook | } |
198 | 9ee6e8bb | pbrook | /* PENDSTSET */
|
199 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
|
200 | 9ee6e8bb | pbrook | val |= (1 << 26); |
201 | 9ee6e8bb | pbrook | /* PENDSVSET */
|
202 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending)
|
203 | 9ee6e8bb | pbrook | val |= (1 << 28); |
204 | 9ee6e8bb | pbrook | /* NMIPENDSET */
|
205 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending)
|
206 | 9ee6e8bb | pbrook | val |= (1 << 31); |
207 | 9ee6e8bb | pbrook | return val;
|
208 | 9ee6e8bb | pbrook | case 0xd08: /* Vector Table Offset. */ |
209 | 9ee6e8bb | pbrook | return cpu_single_env->v7m.vecbase;
|
210 | 9ee6e8bb | pbrook | case 0xd0c: /* Application Interrupt/Reset Control. */ |
211 | 9ee6e8bb | pbrook | return 0xfa05000; |
212 | 9ee6e8bb | pbrook | case 0xd10: /* System Control. */ |
213 | 9ee6e8bb | pbrook | /* TODO: Implement SLEEPONEXIT. */
|
214 | 9ee6e8bb | pbrook | return 0; |
215 | 9ee6e8bb | pbrook | case 0xd14: /* Configuration Control. */ |
216 | 9ee6e8bb | pbrook | /* TODO: Implement Configuration Control bits. */
|
217 | 9ee6e8bb | pbrook | return 0; |
218 | 9ee6e8bb | pbrook | case 0xd24: /* System Handler Status. */ |
219 | 9ee6e8bb | pbrook | val = 0;
|
220 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0); |
221 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1); |
222 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3); |
223 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7); |
224 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8); |
225 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10); |
226 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11); |
227 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12); |
228 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13); |
229 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14); |
230 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15); |
231 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16); |
232 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17); |
233 | fe7e8758 | Paul Brook | if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); |
234 | 9ee6e8bb | pbrook | return val;
|
235 | 9ee6e8bb | pbrook | case 0xd28: /* Configurable Fault Status. */ |
236 | 9ee6e8bb | pbrook | /* TODO: Implement Fault Status. */
|
237 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_UNIMP, "Configurable Fault Status unimplemented\n");
|
238 | 9ee6e8bb | pbrook | return 0; |
239 | 9ee6e8bb | pbrook | case 0xd2c: /* Hard Fault Status. */ |
240 | 9ee6e8bb | pbrook | case 0xd30: /* Debug Fault Status. */ |
241 | 9ee6e8bb | pbrook | case 0xd34: /* Mem Manage Address. */ |
242 | 9ee6e8bb | pbrook | case 0xd38: /* Bus Fault Address. */ |
243 | 9ee6e8bb | pbrook | case 0xd3c: /* Aux Fault Status. */ |
244 | 9ee6e8bb | pbrook | /* TODO: Implement fault status registers. */
|
245 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_UNIMP, "Fault status registers unimplemented\n");
|
246 | e72e3ffc | Peter Maydell | return 0; |
247 | 9ee6e8bb | pbrook | case 0xd40: /* PFR0. */ |
248 | 9ee6e8bb | pbrook | return 0x00000030; |
249 | 9ee6e8bb | pbrook | case 0xd44: /* PRF1. */ |
250 | 9ee6e8bb | pbrook | return 0x00000200; |
251 | 9ee6e8bb | pbrook | case 0xd48: /* DFR0. */ |
252 | 9ee6e8bb | pbrook | return 0x00100000; |
253 | 9ee6e8bb | pbrook | case 0xd4c: /* AFR0. */ |
254 | 9ee6e8bb | pbrook | return 0x00000000; |
255 | 9ee6e8bb | pbrook | case 0xd50: /* MMFR0. */ |
256 | 9ee6e8bb | pbrook | return 0x00000030; |
257 | 9ee6e8bb | pbrook | case 0xd54: /* MMFR1. */ |
258 | 9ee6e8bb | pbrook | return 0x00000000; |
259 | 9ee6e8bb | pbrook | case 0xd58: /* MMFR2. */ |
260 | 9ee6e8bb | pbrook | return 0x00000000; |
261 | 9ee6e8bb | pbrook | case 0xd5c: /* MMFR3. */ |
262 | 9ee6e8bb | pbrook | return 0x00000000; |
263 | 9ee6e8bb | pbrook | case 0xd60: /* ISAR0. */ |
264 | 9ee6e8bb | pbrook | return 0x01141110; |
265 | 9ee6e8bb | pbrook | case 0xd64: /* ISAR1. */ |
266 | 9ee6e8bb | pbrook | return 0x02111000; |
267 | 9ee6e8bb | pbrook | case 0xd68: /* ISAR2. */ |
268 | 9ee6e8bb | pbrook | return 0x21112231; |
269 | 9ee6e8bb | pbrook | case 0xd6c: /* ISAR3. */ |
270 | 9ee6e8bb | pbrook | return 0x01111110; |
271 | 9ee6e8bb | pbrook | case 0xd70: /* ISAR4. */ |
272 | 9ee6e8bb | pbrook | return 0x01310102; |
273 | 9ee6e8bb | pbrook | /* TODO: Implement debug registers. */
|
274 | 9ee6e8bb | pbrook | default:
|
275 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
|
276 | e72e3ffc | Peter Maydell | return 0; |
277 | 9ee6e8bb | pbrook | } |
278 | 9ee6e8bb | pbrook | } |
279 | 9ee6e8bb | pbrook | |
280 | 0e8153dd | Andre Beckus | static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) |
281 | 9ee6e8bb | pbrook | { |
282 | 9ee6e8bb | pbrook | uint32_t oldval; |
283 | 9ee6e8bb | pbrook | switch (offset) {
|
284 | 9ee6e8bb | pbrook | case 0x10: /* SysTick Control and Status. */ |
285 | 9ee6e8bb | pbrook | oldval = s->systick.control; |
286 | 9ee6e8bb | pbrook | s->systick.control &= 0xfffffff8;
|
287 | 9ee6e8bb | pbrook | s->systick.control |= value & 7;
|
288 | 9ee6e8bb | pbrook | if ((oldval ^ value) & SYSTICK_ENABLE) {
|
289 | 74475455 | Paolo Bonzini | int64_t now = qemu_get_clock_ns(vm_clock); |
290 | 9ee6e8bb | pbrook | if (value & SYSTICK_ENABLE) {
|
291 | 9ee6e8bb | pbrook | if (s->systick.tick) {
|
292 | 9ee6e8bb | pbrook | s->systick.tick += now; |
293 | 9ee6e8bb | pbrook | qemu_mod_timer(s->systick.timer, s->systick.tick); |
294 | 9ee6e8bb | pbrook | } else {
|
295 | 9ee6e8bb | pbrook | systick_reload(s, 1);
|
296 | 9ee6e8bb | pbrook | } |
297 | 9ee6e8bb | pbrook | } else {
|
298 | 9ee6e8bb | pbrook | qemu_del_timer(s->systick.timer); |
299 | 9ee6e8bb | pbrook | s->systick.tick -= now; |
300 | 9ee6e8bb | pbrook | if (s->systick.tick < 0) |
301 | 9ee6e8bb | pbrook | s->systick.tick = 0;
|
302 | 9ee6e8bb | pbrook | } |
303 | 9ee6e8bb | pbrook | } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { |
304 | 9ee6e8bb | pbrook | /* This is a hack. Force the timer to be reloaded
|
305 | 9ee6e8bb | pbrook | when the reference clock is changed. */
|
306 | 9ee6e8bb | pbrook | systick_reload(s, 1);
|
307 | 9ee6e8bb | pbrook | } |
308 | 9ee6e8bb | pbrook | break;
|
309 | 9ee6e8bb | pbrook | case 0x14: /* SysTick Reload Value. */ |
310 | 9ee6e8bb | pbrook | s->systick.reload = value; |
311 | 9ee6e8bb | pbrook | break;
|
312 | 9ee6e8bb | pbrook | case 0x18: /* SysTick Current Value. Writes reload the timer. */ |
313 | 9ee6e8bb | pbrook | systick_reload(s, 1);
|
314 | 9ee6e8bb | pbrook | s->systick.control &= ~SYSTICK_COUNTFLAG; |
315 | 9ee6e8bb | pbrook | break;
|
316 | 9ee6e8bb | pbrook | case 0xd04: /* Interrupt Control State. */ |
317 | 9ee6e8bb | pbrook | if (value & (1 << 31)) { |
318 | 9ee6e8bb | pbrook | armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI); |
319 | 9ee6e8bb | pbrook | } |
320 | 9ee6e8bb | pbrook | if (value & (1 << 28)) { |
321 | 9ee6e8bb | pbrook | armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV); |
322 | 9ee6e8bb | pbrook | } else if (value & (1 << 27)) { |
323 | fe7e8758 | Paul Brook | s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
|
324 | fe7e8758 | Paul Brook | gic_update(&s->gic); |
325 | 9ee6e8bb | pbrook | } |
326 | 9ee6e8bb | pbrook | if (value & (1 << 26)) { |
327 | 9ee6e8bb | pbrook | armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); |
328 | 9ee6e8bb | pbrook | } else if (value & (1 << 25)) { |
329 | fe7e8758 | Paul Brook | s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
|
330 | fe7e8758 | Paul Brook | gic_update(&s->gic); |
331 | 9ee6e8bb | pbrook | } |
332 | 9ee6e8bb | pbrook | break;
|
333 | 9ee6e8bb | pbrook | case 0xd08: /* Vector Table Offset. */ |
334 | 9ee6e8bb | pbrook | cpu_single_env->v7m.vecbase = value & 0xffffff80;
|
335 | 9ee6e8bb | pbrook | break;
|
336 | 9ee6e8bb | pbrook | case 0xd0c: /* Application Interrupt/Reset Control. */ |
337 | 9ee6e8bb | pbrook | if ((value >> 16) == 0x05fa) { |
338 | 9ee6e8bb | pbrook | if (value & 2) { |
339 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n");
|
340 | 9ee6e8bb | pbrook | } |
341 | 9ee6e8bb | pbrook | if (value & 5) { |
342 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n");
|
343 | 9ee6e8bb | pbrook | } |
344 | 9ee6e8bb | pbrook | } |
345 | 9ee6e8bb | pbrook | break;
|
346 | 9ee6e8bb | pbrook | case 0xd10: /* System Control. */ |
347 | 9ee6e8bb | pbrook | case 0xd14: /* Configuration Control. */ |
348 | 9ee6e8bb | pbrook | /* TODO: Implement control registers. */
|
349 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_UNIMP, "NVIC: SCR and CCR unimplemented\n");
|
350 | e72e3ffc | Peter Maydell | break;
|
351 | 9ee6e8bb | pbrook | case 0xd24: /* System Handler Control. */ |
352 | 9ee6e8bb | pbrook | /* TODO: Real hardware allows you to set/clear the active bits
|
353 | 9ee6e8bb | pbrook | under some circumstances. We don't implement this. */
|
354 | fe7e8758 | Paul Brook | s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; |
355 | fe7e8758 | Paul Brook | s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; |
356 | fe7e8758 | Paul Brook | s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; |
357 | 9ee6e8bb | pbrook | break;
|
358 | 9ee6e8bb | pbrook | case 0xd28: /* Configurable Fault Status. */ |
359 | 9ee6e8bb | pbrook | case 0xd2c: /* Hard Fault Status. */ |
360 | 9ee6e8bb | pbrook | case 0xd30: /* Debug Fault Status. */ |
361 | 9ee6e8bb | pbrook | case 0xd34: /* Mem Manage Address. */ |
362 | 9ee6e8bb | pbrook | case 0xd38: /* Bus Fault Address. */ |
363 | 9ee6e8bb | pbrook | case 0xd3c: /* Aux Fault Status. */ |
364 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_UNIMP, |
365 | e72e3ffc | Peter Maydell | "NVIC: fault status registers unimplemented\n");
|
366 | e72e3ffc | Peter Maydell | break;
|
367 | 2a29ddee | Peter Maydell | case 0xf00: /* Software Triggered Interrupt Register */ |
368 | 2a29ddee | Peter Maydell | if ((value & 0x1ff) < s->num_irq) { |
369 | 2a29ddee | Peter Maydell | gic_set_pending_private(&s->gic, 0, value & 0x1ff); |
370 | 2a29ddee | Peter Maydell | } |
371 | 2a29ddee | Peter Maydell | break;
|
372 | 9ee6e8bb | pbrook | default:
|
373 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_GUEST_ERROR, |
374 | e72e3ffc | Peter Maydell | "NVIC: Bad write offset 0x%x\n", offset);
|
375 | 9ee6e8bb | pbrook | } |
376 | 9ee6e8bb | pbrook | } |
377 | 9ee6e8bb | pbrook | |
378 | a8170e5e | Avi Kivity | static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr, |
379 | 2a29ddee | Peter Maydell | unsigned size)
|
380 | 2a29ddee | Peter Maydell | { |
381 | 0e8153dd | Andre Beckus | nvic_state *s = (nvic_state *)opaque; |
382 | 2a29ddee | Peter Maydell | uint32_t offset = addr; |
383 | 0e8153dd | Andre Beckus | int i;
|
384 | 0e8153dd | Andre Beckus | uint32_t val; |
385 | 0e8153dd | Andre Beckus | |
386 | 0e8153dd | Andre Beckus | switch (offset) {
|
387 | 0e8153dd | Andre Beckus | case 0xd18 ... 0xd23: /* System Handler Priority. */ |
388 | 0e8153dd | Andre Beckus | val = 0;
|
389 | 0e8153dd | Andre Beckus | for (i = 0; i < size; i++) { |
390 | 0e8153dd | Andre Beckus | val |= s->gic.priority1[(offset - 0xd14) + i][0] << (i * 8); |
391 | 0e8153dd | Andre Beckus | } |
392 | 0e8153dd | Andre Beckus | return val;
|
393 | 0e8153dd | Andre Beckus | case 0xfe0 ... 0xfff: /* ID. */ |
394 | 2a29ddee | Peter Maydell | if (offset & 3) { |
395 | 2a29ddee | Peter Maydell | return 0; |
396 | 2a29ddee | Peter Maydell | } |
397 | 2a29ddee | Peter Maydell | return nvic_id[(offset - 0xfe0) >> 2]; |
398 | 2a29ddee | Peter Maydell | } |
399 | 2a29ddee | Peter Maydell | if (size == 4) { |
400 | 0e8153dd | Andre Beckus | return nvic_readl(s, offset);
|
401 | 2a29ddee | Peter Maydell | } |
402 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_GUEST_ERROR, |
403 | e72e3ffc | Peter Maydell | "NVIC: Bad read of size %d at offset 0x%x\n", size, offset);
|
404 | e72e3ffc | Peter Maydell | return 0; |
405 | 2a29ddee | Peter Maydell | } |
406 | 2a29ddee | Peter Maydell | |
407 | a8170e5e | Avi Kivity | static void nvic_sysreg_write(void *opaque, hwaddr addr, |
408 | 2a29ddee | Peter Maydell | uint64_t value, unsigned size)
|
409 | 2a29ddee | Peter Maydell | { |
410 | 0e8153dd | Andre Beckus | nvic_state *s = (nvic_state *)opaque; |
411 | 2a29ddee | Peter Maydell | uint32_t offset = addr; |
412 | 0e8153dd | Andre Beckus | int i;
|
413 | 0e8153dd | Andre Beckus | |
414 | 0e8153dd | Andre Beckus | switch (offset) {
|
415 | 0e8153dd | Andre Beckus | case 0xd18 ... 0xd23: /* System Handler Priority. */ |
416 | 0e8153dd | Andre Beckus | for (i = 0; i < size; i++) { |
417 | 0e8153dd | Andre Beckus | s->gic.priority1[(offset - 0xd14) + i][0] = |
418 | 0e8153dd | Andre Beckus | (value >> (i * 8)) & 0xff; |
419 | 0e8153dd | Andre Beckus | } |
420 | 0e8153dd | Andre Beckus | gic_update(&s->gic); |
421 | 0e8153dd | Andre Beckus | return;
|
422 | 0e8153dd | Andre Beckus | } |
423 | 2a29ddee | Peter Maydell | if (size == 4) { |
424 | 0e8153dd | Andre Beckus | nvic_writel(s, offset, value); |
425 | 2a29ddee | Peter Maydell | return;
|
426 | 2a29ddee | Peter Maydell | } |
427 | e72e3ffc | Peter Maydell | qemu_log_mask(LOG_GUEST_ERROR, |
428 | e72e3ffc | Peter Maydell | "NVIC: Bad write of size %d at offset 0x%x\n", size, offset);
|
429 | 2a29ddee | Peter Maydell | } |
430 | 2a29ddee | Peter Maydell | |
431 | 2a29ddee | Peter Maydell | static const MemoryRegionOps nvic_sysreg_ops = { |
432 | 2a29ddee | Peter Maydell | .read = nvic_sysreg_read, |
433 | 2a29ddee | Peter Maydell | .write = nvic_sysreg_write, |
434 | 2a29ddee | Peter Maydell | .endianness = DEVICE_NATIVE_ENDIAN, |
435 | 2a29ddee | Peter Maydell | }; |
436 | 2a29ddee | Peter Maydell | |
437 | 0797226c | Juan Quintela | static const VMStateDescription vmstate_nvic = { |
438 | 0797226c | Juan Quintela | .name = "armv7m_nvic",
|
439 | 0797226c | Juan Quintela | .version_id = 1,
|
440 | 0797226c | Juan Quintela | .minimum_version_id = 1,
|
441 | 0797226c | Juan Quintela | .minimum_version_id_old = 1,
|
442 | 0797226c | Juan Quintela | .fields = (VMStateField[]) { |
443 | 0797226c | Juan Quintela | VMSTATE_UINT32(systick.control, nvic_state), |
444 | 0797226c | Juan Quintela | VMSTATE_UINT32(systick.reload, nvic_state), |
445 | 0797226c | Juan Quintela | VMSTATE_INT64(systick.tick, nvic_state), |
446 | 0797226c | Juan Quintela | VMSTATE_TIMER(systick.timer, nvic_state), |
447 | 0797226c | Juan Quintela | VMSTATE_END_OF_LIST() |
448 | 0797226c | Juan Quintela | } |
449 | 0797226c | Juan Quintela | }; |
450 | 23e39294 | pbrook | |
451 | aecff692 | Peter Maydell | static void armv7m_nvic_reset(DeviceState *dev) |
452 | aecff692 | Peter Maydell | { |
453 | 1e8cae4d | Peter Maydell | nvic_state *s = NVIC(dev); |
454 | 1e8cae4d | Peter Maydell | NVICClass *nc = NVIC_GET_CLASS(s); |
455 | 1e8cae4d | Peter Maydell | nc->parent_reset(dev); |
456 | b3387ede | Peter Maydell | /* Common GIC reset resets to disabled; the NVIC doesn't have
|
457 | b3387ede | Peter Maydell | * per-CPU interfaces so mark our non-existent CPU interface
|
458 | b3387ede | Peter Maydell | * as enabled by default.
|
459 | b3387ede | Peter Maydell | */
|
460 | b3387ede | Peter Maydell | s->gic.cpu_enabled[0] = 1; |
461 | b3387ede | Peter Maydell | /* The NVIC as a whole is always enabled. */
|
462 | b3387ede | Peter Maydell | s->gic.enabled = 1;
|
463 | aecff692 | Peter Maydell | systick_reset(s); |
464 | aecff692 | Peter Maydell | } |
465 | aecff692 | Peter Maydell | |
466 | 81a322d4 | Gerd Hoffmann | static int armv7m_nvic_init(SysBusDevice *dev) |
467 | 9ee6e8bb | pbrook | { |
468 | 1e8cae4d | Peter Maydell | nvic_state *s = NVIC(dev); |
469 | 1e8cae4d | Peter Maydell | NVICClass *nc = NVIC_GET_CLASS(s); |
470 | 9ee6e8bb | pbrook | |
471 | c48c6522 | Peter Maydell | /* The NVIC always has only one CPU */
|
472 | c48c6522 | Peter Maydell | s->gic.num_cpu = 1;
|
473 | 306a571a | Peter Maydell | /* Tell the common code we're an NVIC */
|
474 | 306a571a | Peter Maydell | s->gic.revision = 0xffffffff;
|
475 | 55e00a19 | Peter Maydell | s->num_irq = s->gic.num_irq; |
476 | 1e8cae4d | Peter Maydell | nc->parent_init(dev); |
477 | 2b518c56 | Peter Maydell | gic_init_irqs_and_distributor(&s->gic, s->num_irq); |
478 | 2a29ddee | Peter Maydell | /* The NVIC and system controller register area looks like this:
|
479 | 2a29ddee | Peter Maydell | * 0..0xff : system control registers, including systick
|
480 | 2a29ddee | Peter Maydell | * 0x100..0xcff : GIC-like registers
|
481 | 2a29ddee | Peter Maydell | * 0xd00..0xfff : system control registers
|
482 | 2a29ddee | Peter Maydell | * We use overlaying to put the GIC like registers
|
483 | 2a29ddee | Peter Maydell | * over the top of the system control register region.
|
484 | 2a29ddee | Peter Maydell | */
|
485 | 2a29ddee | Peter Maydell | memory_region_init(&s->container, "nvic", 0x1000); |
486 | 2a29ddee | Peter Maydell | /* The system register region goes at the bottom of the priority
|
487 | 2a29ddee | Peter Maydell | * stack as it covers the whole page.
|
488 | 2a29ddee | Peter Maydell | */
|
489 | 2a29ddee | Peter Maydell | memory_region_init_io(&s->sysregmem, &nvic_sysreg_ops, s, |
490 | 2a29ddee | Peter Maydell | "nvic_sysregs", 0x1000); |
491 | 2a29ddee | Peter Maydell | memory_region_add_subregion(&s->container, 0, &s->sysregmem);
|
492 | 2a29ddee | Peter Maydell | /* Alias the GIC region so we can get only the section of it
|
493 | 2a29ddee | Peter Maydell | * we need, and layer it on top of the system register region.
|
494 | 2a29ddee | Peter Maydell | */
|
495 | 2a29ddee | Peter Maydell | memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem,
|
496 | 2a29ddee | Peter Maydell | 0x100, 0xc00); |
497 | 9892cae3 | Meador Inge | memory_region_add_subregion_overlap(&s->container, 0x100,
|
498 | 9892cae3 | Meador Inge | &s->gic_iomem_alias, 1);
|
499 | 2a29ddee | Peter Maydell | /* Map the whole thing into system memory at the location required
|
500 | 2a29ddee | Peter Maydell | * by the v7M architecture.
|
501 | 2a29ddee | Peter Maydell | */
|
502 | 2a29ddee | Peter Maydell | memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container);
|
503 | 74475455 | Paolo Bonzini | s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); |
504 | 81a322d4 | Gerd Hoffmann | return 0; |
505 | 9ee6e8bb | pbrook | } |
506 | fe7e8758 | Paul Brook | |
507 | 55e00a19 | Peter Maydell | static void armv7m_nvic_instance_init(Object *obj) |
508 | 55e00a19 | Peter Maydell | { |
509 | 55e00a19 | Peter Maydell | /* We have a different default value for the num-irq property
|
510 | 55e00a19 | Peter Maydell | * than our superclass. This function runs after qdev init
|
511 | 55e00a19 | Peter Maydell | * has set the defaults from the Property array and before
|
512 | 55e00a19 | Peter Maydell | * any user-specified property setting, so just modify the
|
513 | fae15286 | Peter Maydell | * value in the GICState struct.
|
514 | 55e00a19 | Peter Maydell | */
|
515 | fae15286 | Peter Maydell | GICState *s = ARM_GIC_COMMON(obj); |
516 | 39bffca2 | Anthony Liguori | /* The ARM v7m may have anything from 0 to 496 external interrupt
|
517 | 39bffca2 | Anthony Liguori | * IRQ lines. We default to 64. Other boards may differ and should
|
518 | 55e00a19 | Peter Maydell | * set the num-irq property appropriately.
|
519 | 39bffca2 | Anthony Liguori | */
|
520 | 55e00a19 | Peter Maydell | s->num_irq = 64;
|
521 | 55e00a19 | Peter Maydell | } |
522 | 39bffca2 | Anthony Liguori | |
523 | 999e12bb | Anthony Liguori | static void armv7m_nvic_class_init(ObjectClass *klass, void *data) |
524 | 999e12bb | Anthony Liguori | { |
525 | 1e8cae4d | Peter Maydell | NVICClass *nc = NVIC_CLASS(klass); |
526 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
527 | 999e12bb | Anthony Liguori | SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); |
528 | 999e12bb | Anthony Liguori | |
529 | 1e8cae4d | Peter Maydell | nc->parent_reset = dc->reset; |
530 | 1e8cae4d | Peter Maydell | nc->parent_init = sdc->init; |
531 | 999e12bb | Anthony Liguori | sdc->init = armv7m_nvic_init; |
532 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_nvic; |
533 | aecff692 | Peter Maydell | dc->reset = armv7m_nvic_reset; |
534 | 999e12bb | Anthony Liguori | } |
535 | 999e12bb | Anthony Liguori | |
536 | 39bffca2 | Anthony Liguori | static TypeInfo armv7m_nvic_info = {
|
537 | 1e8cae4d | Peter Maydell | .name = TYPE_NVIC, |
538 | 1e8cae4d | Peter Maydell | .parent = TYPE_ARM_GIC_COMMON, |
539 | 55e00a19 | Peter Maydell | .instance_init = armv7m_nvic_instance_init, |
540 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(nvic_state),
|
541 | 39bffca2 | Anthony Liguori | .class_init = armv7m_nvic_class_init, |
542 | 1e8cae4d | Peter Maydell | .class_size = sizeof(NVICClass),
|
543 | a32134aa | Mark Langsdorf | }; |
544 | a32134aa | Mark Langsdorf | |
545 | 83f7d43a | Andreas Färber | static void armv7m_nvic_register_types(void) |
546 | fe7e8758 | Paul Brook | { |
547 | 39bffca2 | Anthony Liguori | type_register_static(&armv7m_nvic_info); |
548 | fe7e8758 | Paul Brook | } |
549 | fe7e8758 | Paul Brook | |
550 | 83f7d43a | Andreas Färber | type_init(armv7m_nvic_register_types) |