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