]>
Commit | Line | Data |
---|---|---|
9ee6e8bb PB |
1 | /* |
2 | * ARM MPCore internal peripheral emulation. | |
3 | * | |
4 | * Copyright (c) 2006-2007 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
7 | * This code is licenced under the GPL. | |
8 | */ | |
9 | ||
87ecb68b PB |
10 | #include "hw.h" |
11 | #include "qemu-timer.h" | |
12 | #include "primecell.h" | |
9ee6e8bb PB |
13 | |
14 | #define MPCORE_PRIV_BASE 0x10100000 | |
15 | #define NCPU 4 | |
16 | /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines | |
17 | (+ 32 internal). However my test chip only exposes/reports 32. | |
18 | More importantly Linux falls over if more than 32 are present! */ | |
19 | #define GIC_NIRQ 64 | |
20 | ||
21 | static inline int | |
22 | gic_get_current_cpu(void) | |
23 | { | |
24 | return cpu_single_env->cpu_index; | |
25 | } | |
26 | ||
27 | #include "arm_gic.c" | |
28 | ||
29 | /* MPCore private memory region. */ | |
30 | ||
31 | typedef struct { | |
32 | uint32_t count; | |
33 | uint32_t load; | |
34 | uint32_t control; | |
35 | uint32_t status; | |
36 | uint32_t old_status; | |
37 | int64_t tick; | |
38 | QEMUTimer *timer; | |
39 | struct mpcore_priv_state *mpcore; | |
40 | int id; /* Encodes both timer/watchdog and CPU. */ | |
41 | } mpcore_timer_state; | |
42 | ||
43 | typedef struct mpcore_priv_state { | |
44 | gic_state *gic; | |
45 | uint32_t scu_control; | |
46 | mpcore_timer_state timer[8]; | |
47 | } mpcore_priv_state; | |
48 | ||
49 | /* Per-CPU Timers. */ | |
50 | ||
51 | static inline void mpcore_timer_update_irq(mpcore_timer_state *s) | |
52 | { | |
53 | if (s->status & ~s->old_status) { | |
54 | gic_set_pending_private(s->mpcore->gic, s->id >> 1, 29 + (s->id & 1)); | |
55 | } | |
56 | s->old_status = s->status; | |
57 | } | |
58 | ||
59 | /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ | |
60 | static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s) | |
61 | { | |
62 | return (((s->control >> 8) & 0xff) + 1) * 10; | |
63 | } | |
64 | ||
65 | static void mpcore_timer_reload(mpcore_timer_state *s, int restart) | |
66 | { | |
67 | if (s->count == 0) | |
68 | return; | |
69 | if (restart) | |
70 | s->tick = qemu_get_clock(vm_clock); | |
71 | s->tick += (int64_t)s->count * mpcore_timer_scale(s); | |
72 | qemu_mod_timer(s->timer, s->tick); | |
73 | } | |
74 | ||
75 | static void mpcore_timer_tick(void *opaque) | |
76 | { | |
77 | mpcore_timer_state *s = (mpcore_timer_state *)opaque; | |
78 | s->status = 1; | |
79 | if (s->control & 2) { | |
80 | s->count = s->load; | |
81 | mpcore_timer_reload(s, 0); | |
82 | } else { | |
83 | s->count = 0; | |
84 | } | |
85 | mpcore_timer_update_irq(s); | |
86 | } | |
87 | ||
88 | static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset) | |
89 | { | |
90 | int64_t val; | |
91 | switch (offset) { | |
92 | case 0: /* Load */ | |
93 | return s->load; | |
94 | /* Fall through. */ | |
95 | case 4: /* Counter. */ | |
96 | if (((s->control & 1) == 0) || (s->count == 0)) | |
97 | return 0; | |
98 | /* Slow and ugly, but hopefully won't happen too often. */ | |
99 | val = s->tick - qemu_get_clock(vm_clock); | |
100 | val /= mpcore_timer_scale(s); | |
101 | if (val < 0) | |
102 | val = 0; | |
103 | return val; | |
104 | case 8: /* Control. */ | |
105 | return s->control; | |
106 | case 12: /* Interrupt status. */ | |
107 | return s->status; | |
a38131b6 BS |
108 | default: |
109 | return 0; | |
9ee6e8bb PB |
110 | } |
111 | } | |
112 | ||
113 | static void mpcore_timer_write(mpcore_timer_state *s, int offset, | |
114 | uint32_t value) | |
115 | { | |
116 | int64_t old; | |
117 | switch (offset) { | |
118 | case 0: /* Load */ | |
119 | s->load = value; | |
120 | /* Fall through. */ | |
121 | case 4: /* Counter. */ | |
122 | if ((s->control & 1) && s->count) { | |
123 | /* Cancel the previous timer. */ | |
124 | qemu_del_timer(s->timer); | |
125 | } | |
126 | s->count = value; | |
127 | if (s->control & 1) { | |
128 | mpcore_timer_reload(s, 1); | |
129 | } | |
130 | break; | |
131 | case 8: /* Control. */ | |
132 | old = s->control; | |
133 | s->control = value; | |
134 | if (((old & 1) == 0) && (value & 1)) { | |
135 | if (s->count == 0 && (s->control & 2)) | |
136 | s->count = s->load; | |
137 | mpcore_timer_reload(s, 1); | |
138 | } | |
139 | break; | |
140 | case 12: /* Interrupt status. */ | |
141 | s->status &= ~value; | |
142 | mpcore_timer_update_irq(s); | |
143 | break; | |
144 | } | |
145 | } | |
146 | ||
147 | static void mpcore_timer_init(mpcore_priv_state *mpcore, | |
148 | mpcore_timer_state *s, int id) | |
149 | { | |
150 | s->id = id; | |
151 | s->mpcore = mpcore; | |
152 | s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s); | |
153 | } | |
154 | ||
155 | ||
156 | /* Per-CPU private memory mapped IO. */ | |
157 | ||
158 | static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset) | |
159 | { | |
160 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
161 | int id; | |
162 | offset &= 0xfff; | |
163 | if (offset < 0x100) { | |
164 | /* SCU */ | |
165 | switch (offset) { | |
166 | case 0x00: /* Control. */ | |
167 | return s->scu_control; | |
168 | case 0x04: /* Configuration. */ | |
169 | return 0xf3; | |
170 | case 0x08: /* CPU status. */ | |
171 | return 0; | |
172 | case 0x0c: /* Invalidate all. */ | |
173 | return 0; | |
174 | default: | |
175 | goto bad_reg; | |
176 | } | |
177 | } else if (offset < 0x600) { | |
178 | /* Interrupt controller. */ | |
179 | if (offset < 0x200) { | |
180 | id = gic_get_current_cpu(); | |
181 | } else { | |
182 | id = (offset - 0x200) >> 8; | |
183 | } | |
184 | return gic_cpu_read(s->gic, id, offset & 0xff); | |
185 | } else if (offset < 0xb00) { | |
186 | /* Timers. */ | |
187 | if (offset < 0x700) { | |
188 | id = gic_get_current_cpu(); | |
189 | } else { | |
190 | id = (offset - 0x700) >> 8; | |
191 | } | |
192 | id <<= 1; | |
193 | if (offset & 0x20) | |
194 | id++; | |
195 | return mpcore_timer_read(&s->timer[id], offset & 0xf); | |
196 | } | |
197 | bad_reg: | |
198 | cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n", | |
199 | (int)offset); | |
200 | return 0; | |
201 | } | |
202 | ||
203 | static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, | |
204 | uint32_t value) | |
205 | { | |
206 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
207 | int id; | |
208 | offset &= 0xfff; | |
209 | if (offset < 0x100) { | |
210 | /* SCU */ | |
211 | switch (offset) { | |
212 | case 0: /* Control register. */ | |
213 | s->scu_control = value & 1; | |
214 | break; | |
215 | case 0x0c: /* Invalidate all. */ | |
216 | /* This is a no-op as cache is not emulated. */ | |
217 | break; | |
218 | default: | |
219 | goto bad_reg; | |
220 | } | |
221 | } else if (offset < 0x600) { | |
222 | /* Interrupt controller. */ | |
223 | if (offset < 0x200) { | |
224 | id = gic_get_current_cpu(); | |
225 | } else { | |
226 | id = (offset - 0x200) >> 8; | |
227 | } | |
228 | gic_cpu_write(s->gic, id, offset & 0xff, value); | |
229 | } else if (offset < 0xb00) { | |
230 | /* Timers. */ | |
231 | if (offset < 0x700) { | |
232 | id = gic_get_current_cpu(); | |
233 | } else { | |
234 | id = (offset - 0x700) >> 8; | |
235 | } | |
236 | id <<= 1; | |
237 | if (offset & 0x20) | |
238 | id++; | |
239 | mpcore_timer_write(&s->timer[id], offset & 0xf, value); | |
240 | return; | |
241 | } | |
242 | return; | |
243 | bad_reg: | |
244 | cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n", | |
245 | (int)offset); | |
246 | } | |
247 | ||
248 | static CPUReadMemoryFunc *mpcore_priv_readfn[] = { | |
249 | mpcore_priv_read, | |
250 | mpcore_priv_read, | |
251 | mpcore_priv_read | |
252 | }; | |
253 | ||
254 | static CPUWriteMemoryFunc *mpcore_priv_writefn[] = { | |
255 | mpcore_priv_write, | |
256 | mpcore_priv_write, | |
257 | mpcore_priv_write | |
258 | }; | |
259 | ||
260 | ||
261 | static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq) | |
262 | { | |
263 | mpcore_priv_state *s; | |
264 | int iomemtype; | |
265 | int i; | |
266 | ||
267 | s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state)); | |
268 | if (!s) | |
269 | return NULL; | |
8da3ff18 | 270 | s->gic = gic_init(base + 0x1000, pic_irq); |
9ee6e8bb PB |
271 | if (!s->gic) |
272 | return NULL; | |
273 | iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn, | |
274 | mpcore_priv_writefn, s); | |
275 | cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
276 | for (i = 0; i < 8; i++) { | |
277 | mpcore_timer_init(s, &s->timer[i], i); | |
278 | } | |
279 | return s->gic->in; | |
280 | } | |
281 | ||
282 | /* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ | |
283 | controllers. The output of these, plus some of the raw input lines | |
284 | are fed into a single SMP-aware interrupt controller on the CPU. */ | |
285 | typedef struct { | |
286 | qemu_irq *cpuic; | |
287 | qemu_irq *rvic[4]; | |
288 | } mpcore_rirq_state; | |
289 | ||
290 | /* Map baseboard IRQs onto CPU IRQ lines. */ | |
291 | static const int mpcore_irq_map[32] = { | |
292 | -1, -1, -1, -1, 1, 2, -1, -1, | |
293 | -1, -1, 6, -1, 4, 5, -1, -1, | |
294 | -1, 14, 15, 0, 7, 8, -1, -1, | |
295 | -1, -1, -1, -1, 9, 3, -1, -1, | |
296 | }; | |
297 | ||
298 | static void mpcore_rirq_set_irq(void *opaque, int irq, int level) | |
299 | { | |
300 | mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; | |
301 | int i; | |
302 | ||
303 | for (i = 0; i < 4; i++) { | |
304 | qemu_set_irq(s->rvic[i][irq], level); | |
305 | } | |
306 | if (irq < 32) { | |
307 | irq = mpcore_irq_map[irq]; | |
308 | if (irq >= 0) { | |
309 | qemu_set_irq(s->cpuic[irq], level); | |
310 | } | |
311 | } | |
312 | } | |
313 | ||
314 | qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq) | |
315 | { | |
316 | mpcore_rirq_state *s; | |
317 | int n; | |
318 | ||
319 | /* ??? IRQ routing is hardcoded to "normal" mode. */ | |
320 | s = qemu_mallocz(sizeof(mpcore_rirq_state)); | |
321 | s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq); | |
322 | for (n = 0; n < 4; n++) { | |
323 | s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000, | |
324 | s->cpuic[10 + n]); | |
325 | } | |
326 | return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64); | |
327 | } |