root / hw / mpcore.c @ 1eed09cb
History | View | Annotate | Download (9.2 kB)
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 |
|
10 |
#include "sysbus.h" |
11 |
#include "qemu-timer.h" |
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 { |
43 |
gic_state gic; |
44 |
uint32_t scu_control; |
45 |
int iomemtype;
|
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;
|
108 |
default:
|
109 |
return 0; |
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 |
hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
199 |
return 0; |
200 |
} |
201 |
|
202 |
static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, |
203 |
uint32_t value) |
204 |
{ |
205 |
mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
206 |
int id;
|
207 |
offset &= 0xfff;
|
208 |
if (offset < 0x100) { |
209 |
/* SCU */
|
210 |
switch (offset) {
|
211 |
case 0: /* Control register. */ |
212 |
s->scu_control = value & 1;
|
213 |
break;
|
214 |
case 0x0c: /* Invalidate all. */ |
215 |
/* This is a no-op as cache is not emulated. */
|
216 |
break;
|
217 |
default:
|
218 |
goto bad_reg;
|
219 |
} |
220 |
} else if (offset < 0x600) { |
221 |
/* Interrupt controller. */
|
222 |
if (offset < 0x200) { |
223 |
id = gic_get_current_cpu(); |
224 |
} else {
|
225 |
id = (offset - 0x200) >> 8; |
226 |
} |
227 |
gic_cpu_write(&s->gic, id, offset & 0xff, value);
|
228 |
} else if (offset < 0xb00) { |
229 |
/* Timers. */
|
230 |
if (offset < 0x700) { |
231 |
id = gic_get_current_cpu(); |
232 |
} else {
|
233 |
id = (offset - 0x700) >> 8; |
234 |
} |
235 |
id <<= 1;
|
236 |
if (offset & 0x20) |
237 |
id++; |
238 |
mpcore_timer_write(&s->timer[id], offset & 0xf, value);
|
239 |
return;
|
240 |
} |
241 |
return;
|
242 |
bad_reg:
|
243 |
hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
244 |
} |
245 |
|
246 |
static CPUReadMemoryFunc *mpcore_priv_readfn[] = {
|
247 |
mpcore_priv_read, |
248 |
mpcore_priv_read, |
249 |
mpcore_priv_read |
250 |
}; |
251 |
|
252 |
static CPUWriteMemoryFunc *mpcore_priv_writefn[] = {
|
253 |
mpcore_priv_write, |
254 |
mpcore_priv_write, |
255 |
mpcore_priv_write |
256 |
}; |
257 |
|
258 |
static void mpcore_priv_map(SysBusDevice *dev, target_phys_addr_t base) |
259 |
{ |
260 |
mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); |
261 |
cpu_register_physical_memory(base, 0x1000, s->iomemtype);
|
262 |
cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype); |
263 |
} |
264 |
|
265 |
static void mpcore_priv_init(SysBusDevice *dev) |
266 |
{ |
267 |
mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); |
268 |
int i;
|
269 |
|
270 |
gic_init(&s->gic); |
271 |
s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn, |
272 |
mpcore_priv_writefn, s); |
273 |
sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map);
|
274 |
for (i = 0; i < 8; i++) { |
275 |
mpcore_timer_init(s, &s->timer[i], i); |
276 |
} |
277 |
} |
278 |
|
279 |
/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
|
280 |
controllers. The output of these, plus some of the raw input lines
|
281 |
are fed into a single SMP-aware interrupt controller on the CPU. */
|
282 |
typedef struct { |
283 |
SysBusDevice busdev; |
284 |
qemu_irq cpuic[32];
|
285 |
qemu_irq rvic[4][64]; |
286 |
} mpcore_rirq_state; |
287 |
|
288 |
/* Map baseboard IRQs onto CPU IRQ lines. */
|
289 |
static const int mpcore_irq_map[32] = { |
290 |
-1, -1, -1, -1, 1, 2, -1, -1, |
291 |
-1, -1, 6, -1, 4, 5, -1, -1, |
292 |
-1, 14, 15, 0, 7, 8, -1, -1, |
293 |
-1, -1, -1, -1, 9, 3, -1, -1, |
294 |
}; |
295 |
|
296 |
static void mpcore_rirq_set_irq(void *opaque, int irq, int level) |
297 |
{ |
298 |
mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; |
299 |
int i;
|
300 |
|
301 |
for (i = 0; i < 4; i++) { |
302 |
qemu_set_irq(s->rvic[i][irq], level); |
303 |
} |
304 |
if (irq < 32) { |
305 |
irq = mpcore_irq_map[irq]; |
306 |
if (irq >= 0) { |
307 |
qemu_set_irq(s->cpuic[irq], level); |
308 |
} |
309 |
} |
310 |
} |
311 |
|
312 |
static void realview_mpcore_init(SysBusDevice *dev) |
313 |
{ |
314 |
mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev); |
315 |
DeviceState *gic; |
316 |
DeviceState *priv; |
317 |
int n;
|
318 |
int i;
|
319 |
|
320 |
priv = sysbus_create_simple("arm11mpcore_priv", MPCORE_PRIV_BASE, NULL); |
321 |
sysbus_pass_irq(dev, sysbus_from_qdev(priv)); |
322 |
for (i = 0; i < 32; i++) { |
323 |
s->cpuic[i] = qdev_get_gpio_in(priv, i); |
324 |
} |
325 |
/* ??? IRQ routing is hardcoded to "normal" mode. */
|
326 |
for (n = 0; n < 4; n++) { |
327 |
gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000, |
328 |
s->cpuic[10 + n]);
|
329 |
for (i = 0; i < 64; i++) { |
330 |
s->rvic[n][i] = qdev_get_gpio_in(gic, i); |
331 |
} |
332 |
} |
333 |
qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64);
|
334 |
} |
335 |
|
336 |
static void mpcore_register_devices(void) |
337 |
{ |
338 |
sysbus_register_dev("realview_mpcore", sizeof(mpcore_rirq_state), |
339 |
realview_mpcore_init); |
340 |
sysbus_register_dev("arm11mpcore_priv", sizeof(mpcore_priv_state), |
341 |
mpcore_priv_init); |
342 |
} |
343 |
|
344 |
device_init(mpcore_register_devices) |