root / hw / mpcore.c @ 5439779e
History | View | Annotate | Download (8.4 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 "hw.h" |
11 |
#include "qemu-timer.h" |
12 |
#include "primecell.h" |
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;
|
108 |
} |
109 |
} |
110 |
|
111 |
static void mpcore_timer_write(mpcore_timer_state *s, int offset, |
112 |
uint32_t value) |
113 |
{ |
114 |
int64_t old; |
115 |
switch (offset) {
|
116 |
case 0: /* Load */ |
117 |
s->load = value; |
118 |
/* Fall through. */
|
119 |
case 4: /* Counter. */ |
120 |
if ((s->control & 1) && s->count) { |
121 |
/* Cancel the previous timer. */
|
122 |
qemu_del_timer(s->timer); |
123 |
} |
124 |
s->count = value; |
125 |
if (s->control & 1) { |
126 |
mpcore_timer_reload(s, 1);
|
127 |
} |
128 |
break;
|
129 |
case 8: /* Control. */ |
130 |
old = s->control; |
131 |
s->control = value; |
132 |
if (((old & 1) == 0) && (value & 1)) { |
133 |
if (s->count == 0 && (s->control & 2)) |
134 |
s->count = s->load; |
135 |
mpcore_timer_reload(s, 1);
|
136 |
} |
137 |
break;
|
138 |
case 12: /* Interrupt status. */ |
139 |
s->status &= ~value; |
140 |
mpcore_timer_update_irq(s); |
141 |
break;
|
142 |
} |
143 |
} |
144 |
|
145 |
static void mpcore_timer_init(mpcore_priv_state *mpcore, |
146 |
mpcore_timer_state *s, int id)
|
147 |
{ |
148 |
s->id = id; |
149 |
s->mpcore = mpcore; |
150 |
s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s); |
151 |
} |
152 |
|
153 |
|
154 |
/* Per-CPU private memory mapped IO. */
|
155 |
|
156 |
static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset) |
157 |
{ |
158 |
mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
159 |
int id;
|
160 |
offset &= 0xfff;
|
161 |
if (offset < 0x100) { |
162 |
/* SCU */
|
163 |
switch (offset) {
|
164 |
case 0x00: /* Control. */ |
165 |
return s->scu_control;
|
166 |
case 0x04: /* Configuration. */ |
167 |
return 0xf3; |
168 |
case 0x08: /* CPU status. */ |
169 |
return 0; |
170 |
case 0x0c: /* Invalidate all. */ |
171 |
return 0; |
172 |
default:
|
173 |
goto bad_reg;
|
174 |
} |
175 |
} else if (offset < 0x600) { |
176 |
/* Interrupt controller. */
|
177 |
if (offset < 0x200) { |
178 |
id = gic_get_current_cpu(); |
179 |
} else {
|
180 |
id = (offset - 0x200) >> 8; |
181 |
} |
182 |
return gic_cpu_read(s->gic, id, offset & 0xff); |
183 |
} else if (offset < 0xb00) { |
184 |
/* Timers. */
|
185 |
if (offset < 0x700) { |
186 |
id = gic_get_current_cpu(); |
187 |
} else {
|
188 |
id = (offset - 0x700) >> 8; |
189 |
} |
190 |
id <<= 1;
|
191 |
if (offset & 0x20) |
192 |
id++; |
193 |
return mpcore_timer_read(&s->timer[id], offset & 0xf); |
194 |
} |
195 |
bad_reg:
|
196 |
cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
|
197 |
(int)offset);
|
198 |
return 0; |
199 |
} |
200 |
|
201 |
static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, |
202 |
uint32_t value) |
203 |
{ |
204 |
mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
205 |
int id;
|
206 |
offset &= 0xfff;
|
207 |
if (offset < 0x100) { |
208 |
/* SCU */
|
209 |
switch (offset) {
|
210 |
case 0: /* Control register. */ |
211 |
s->scu_control = value & 1;
|
212 |
break;
|
213 |
case 0x0c: /* Invalidate all. */ |
214 |
/* This is a no-op as cache is not emulated. */
|
215 |
break;
|
216 |
default:
|
217 |
goto bad_reg;
|
218 |
} |
219 |
} else if (offset < 0x600) { |
220 |
/* Interrupt controller. */
|
221 |
if (offset < 0x200) { |
222 |
id = gic_get_current_cpu(); |
223 |
} else {
|
224 |
id = (offset - 0x200) >> 8; |
225 |
} |
226 |
gic_cpu_write(s->gic, id, offset & 0xff, value);
|
227 |
} else if (offset < 0xb00) { |
228 |
/* Timers. */
|
229 |
if (offset < 0x700) { |
230 |
id = gic_get_current_cpu(); |
231 |
} else {
|
232 |
id = (offset - 0x700) >> 8; |
233 |
} |
234 |
id <<= 1;
|
235 |
if (offset & 0x20) |
236 |
id++; |
237 |
mpcore_timer_write(&s->timer[id], offset & 0xf, value);
|
238 |
return;
|
239 |
} |
240 |
return;
|
241 |
bad_reg:
|
242 |
cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
|
243 |
(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 |
|
259 |
static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq)
|
260 |
{ |
261 |
mpcore_priv_state *s; |
262 |
int iomemtype;
|
263 |
int i;
|
264 |
|
265 |
s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state));
|
266 |
if (!s)
|
267 |
return NULL; |
268 |
s->gic = gic_init(base, pic_irq); |
269 |
if (!s->gic)
|
270 |
return NULL; |
271 |
iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn,
|
272 |
mpcore_priv_writefn, s); |
273 |
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
274 |
for (i = 0; i < 8; i++) { |
275 |
mpcore_timer_init(s, &s->timer[i], i); |
276 |
} |
277 |
return s->gic->in;
|
278 |
} |
279 |
|
280 |
/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
|
281 |
controllers. The output of these, plus some of the raw input lines
|
282 |
are fed into a single SMP-aware interrupt controller on the CPU. */
|
283 |
typedef struct { |
284 |
qemu_irq *cpuic; |
285 |
qemu_irq *rvic[4];
|
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 |
qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq) |
313 |
{ |
314 |
mpcore_rirq_state *s; |
315 |
int n;
|
316 |
|
317 |
/* ??? IRQ routing is hardcoded to "normal" mode. */
|
318 |
s = qemu_mallocz(sizeof(mpcore_rirq_state));
|
319 |
s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq); |
320 |
for (n = 0; n < 4; n++) { |
321 |
s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000, |
322 |
s->cpuic[10 + n]);
|
323 |
} |
324 |
return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64); |
325 |
} |