root / hw / mpcore.c @ cc9f28bc
History | View | Annotate | Download (7.3 kB)
1 | 9ee6e8bb | pbrook | /*
|
---|---|---|---|
2 | f7c70325 | Paul Brook | * ARM MPCore internal peripheral emulation (common code).
|
3 | 9ee6e8bb | pbrook | *
|
4 | 9ee6e8bb | pbrook | * Copyright (c) 2006-2007 CodeSourcery.
|
5 | 9ee6e8bb | pbrook | * Written by Paul Brook
|
6 | 9ee6e8bb | pbrook | *
|
7 | 9ee6e8bb | pbrook | * This code is licenced under the GPL.
|
8 | 9ee6e8bb | pbrook | */
|
9 | 9ee6e8bb | pbrook | |
10 | fe7e8758 | Paul Brook | #include "sysbus.h" |
11 | 87ecb68b | pbrook | #include "qemu-timer.h" |
12 | 9ee6e8bb | pbrook | |
13 | 9ee6e8bb | pbrook | #define NCPU 4 |
14 | 9ee6e8bb | pbrook | |
15 | 9ee6e8bb | pbrook | static inline int |
16 | 9ee6e8bb | pbrook | gic_get_current_cpu(void)
|
17 | 9ee6e8bb | pbrook | { |
18 | 9ee6e8bb | pbrook | return cpu_single_env->cpu_index;
|
19 | 9ee6e8bb | pbrook | } |
20 | 9ee6e8bb | pbrook | |
21 | 9ee6e8bb | pbrook | #include "arm_gic.c" |
22 | 9ee6e8bb | pbrook | |
23 | 9ee6e8bb | pbrook | /* MPCore private memory region. */
|
24 | 9ee6e8bb | pbrook | |
25 | 9ee6e8bb | pbrook | typedef struct { |
26 | 9ee6e8bb | pbrook | uint32_t count; |
27 | 9ee6e8bb | pbrook | uint32_t load; |
28 | 9ee6e8bb | pbrook | uint32_t control; |
29 | 9ee6e8bb | pbrook | uint32_t status; |
30 | 9ee6e8bb | pbrook | uint32_t old_status; |
31 | 9ee6e8bb | pbrook | int64_t tick; |
32 | 9ee6e8bb | pbrook | QEMUTimer *timer; |
33 | 9ee6e8bb | pbrook | struct mpcore_priv_state *mpcore;
|
34 | 9ee6e8bb | pbrook | int id; /* Encodes both timer/watchdog and CPU. */ |
35 | 9ee6e8bb | pbrook | } mpcore_timer_state; |
36 | 9ee6e8bb | pbrook | |
37 | 9ee6e8bb | pbrook | typedef struct mpcore_priv_state { |
38 | fe7e8758 | Paul Brook | gic_state gic; |
39 | 9ee6e8bb | pbrook | uint32_t scu_control; |
40 | fe7e8758 | Paul Brook | int iomemtype;
|
41 | 9ee6e8bb | pbrook | mpcore_timer_state timer[8];
|
42 | c988bfad | Paul Brook | uint32_t num_cpu; |
43 | 9ee6e8bb | pbrook | } mpcore_priv_state; |
44 | 9ee6e8bb | pbrook | |
45 | 9ee6e8bb | pbrook | /* Per-CPU Timers. */
|
46 | 9ee6e8bb | pbrook | |
47 | 9ee6e8bb | pbrook | static inline void mpcore_timer_update_irq(mpcore_timer_state *s) |
48 | 9ee6e8bb | pbrook | { |
49 | 9ee6e8bb | pbrook | if (s->status & ~s->old_status) {
|
50 | fe7e8758 | Paul Brook | gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1)); |
51 | 9ee6e8bb | pbrook | } |
52 | 9ee6e8bb | pbrook | s->old_status = s->status; |
53 | 9ee6e8bb | pbrook | } |
54 | 9ee6e8bb | pbrook | |
55 | 9ee6e8bb | pbrook | /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
|
56 | 9ee6e8bb | pbrook | static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s) |
57 | 9ee6e8bb | pbrook | { |
58 | 9ee6e8bb | pbrook | return (((s->control >> 8) & 0xff) + 1) * 10; |
59 | 9ee6e8bb | pbrook | } |
60 | 9ee6e8bb | pbrook | |
61 | 9ee6e8bb | pbrook | static void mpcore_timer_reload(mpcore_timer_state *s, int restart) |
62 | 9ee6e8bb | pbrook | { |
63 | 9ee6e8bb | pbrook | if (s->count == 0) |
64 | 9ee6e8bb | pbrook | return;
|
65 | 9ee6e8bb | pbrook | if (restart)
|
66 | 9ee6e8bb | pbrook | s->tick = qemu_get_clock(vm_clock); |
67 | 9ee6e8bb | pbrook | s->tick += (int64_t)s->count * mpcore_timer_scale(s); |
68 | 9ee6e8bb | pbrook | qemu_mod_timer(s->timer, s->tick); |
69 | 9ee6e8bb | pbrook | } |
70 | 9ee6e8bb | pbrook | |
71 | 9ee6e8bb | pbrook | static void mpcore_timer_tick(void *opaque) |
72 | 9ee6e8bb | pbrook | { |
73 | 9ee6e8bb | pbrook | mpcore_timer_state *s = (mpcore_timer_state *)opaque; |
74 | 9ee6e8bb | pbrook | s->status = 1;
|
75 | 9ee6e8bb | pbrook | if (s->control & 2) { |
76 | 9ee6e8bb | pbrook | s->count = s->load; |
77 | 9ee6e8bb | pbrook | mpcore_timer_reload(s, 0);
|
78 | 9ee6e8bb | pbrook | } else {
|
79 | 9ee6e8bb | pbrook | s->count = 0;
|
80 | 9ee6e8bb | pbrook | } |
81 | 9ee6e8bb | pbrook | mpcore_timer_update_irq(s); |
82 | 9ee6e8bb | pbrook | } |
83 | 9ee6e8bb | pbrook | |
84 | 9ee6e8bb | pbrook | static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset) |
85 | 9ee6e8bb | pbrook | { |
86 | 9ee6e8bb | pbrook | int64_t val; |
87 | 9ee6e8bb | pbrook | switch (offset) {
|
88 | 9ee6e8bb | pbrook | case 0: /* Load */ |
89 | 9ee6e8bb | pbrook | return s->load;
|
90 | 9ee6e8bb | pbrook | /* Fall through. */
|
91 | 9ee6e8bb | pbrook | case 4: /* Counter. */ |
92 | 9ee6e8bb | pbrook | if (((s->control & 1) == 0) || (s->count == 0)) |
93 | 9ee6e8bb | pbrook | return 0; |
94 | 9ee6e8bb | pbrook | /* Slow and ugly, but hopefully won't happen too often. */
|
95 | 9ee6e8bb | pbrook | val = s->tick - qemu_get_clock(vm_clock); |
96 | 9ee6e8bb | pbrook | val /= mpcore_timer_scale(s); |
97 | 9ee6e8bb | pbrook | if (val < 0) |
98 | 9ee6e8bb | pbrook | val = 0;
|
99 | 9ee6e8bb | pbrook | return val;
|
100 | 9ee6e8bb | pbrook | case 8: /* Control. */ |
101 | 9ee6e8bb | pbrook | return s->control;
|
102 | 9ee6e8bb | pbrook | case 12: /* Interrupt status. */ |
103 | 9ee6e8bb | pbrook | return s->status;
|
104 | a38131b6 | blueswir1 | default:
|
105 | a38131b6 | blueswir1 | return 0; |
106 | 9ee6e8bb | pbrook | } |
107 | 9ee6e8bb | pbrook | } |
108 | 9ee6e8bb | pbrook | |
109 | 9ee6e8bb | pbrook | static void mpcore_timer_write(mpcore_timer_state *s, int offset, |
110 | 9ee6e8bb | pbrook | uint32_t value) |
111 | 9ee6e8bb | pbrook | { |
112 | 9ee6e8bb | pbrook | int64_t old; |
113 | 9ee6e8bb | pbrook | switch (offset) {
|
114 | 9ee6e8bb | pbrook | case 0: /* Load */ |
115 | 9ee6e8bb | pbrook | s->load = value; |
116 | 9ee6e8bb | pbrook | /* Fall through. */
|
117 | 9ee6e8bb | pbrook | case 4: /* Counter. */ |
118 | 9ee6e8bb | pbrook | if ((s->control & 1) && s->count) { |
119 | 9ee6e8bb | pbrook | /* Cancel the previous timer. */
|
120 | 9ee6e8bb | pbrook | qemu_del_timer(s->timer); |
121 | 9ee6e8bb | pbrook | } |
122 | 9ee6e8bb | pbrook | s->count = value; |
123 | 9ee6e8bb | pbrook | if (s->control & 1) { |
124 | 9ee6e8bb | pbrook | mpcore_timer_reload(s, 1);
|
125 | 9ee6e8bb | pbrook | } |
126 | 9ee6e8bb | pbrook | break;
|
127 | 9ee6e8bb | pbrook | case 8: /* Control. */ |
128 | 9ee6e8bb | pbrook | old = s->control; |
129 | 9ee6e8bb | pbrook | s->control = value; |
130 | 9ee6e8bb | pbrook | if (((old & 1) == 0) && (value & 1)) { |
131 | 9ee6e8bb | pbrook | if (s->count == 0 && (s->control & 2)) |
132 | 9ee6e8bb | pbrook | s->count = s->load; |
133 | 9ee6e8bb | pbrook | mpcore_timer_reload(s, 1);
|
134 | 9ee6e8bb | pbrook | } |
135 | 9ee6e8bb | pbrook | break;
|
136 | 9ee6e8bb | pbrook | case 12: /* Interrupt status. */ |
137 | 9ee6e8bb | pbrook | s->status &= ~value; |
138 | 9ee6e8bb | pbrook | mpcore_timer_update_irq(s); |
139 | 9ee6e8bb | pbrook | break;
|
140 | 9ee6e8bb | pbrook | } |
141 | 9ee6e8bb | pbrook | } |
142 | 9ee6e8bb | pbrook | |
143 | 9ee6e8bb | pbrook | static void mpcore_timer_init(mpcore_priv_state *mpcore, |
144 | 9ee6e8bb | pbrook | mpcore_timer_state *s, int id)
|
145 | 9ee6e8bb | pbrook | { |
146 | 9ee6e8bb | pbrook | s->id = id; |
147 | 9ee6e8bb | pbrook | s->mpcore = mpcore; |
148 | 9ee6e8bb | pbrook | s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s); |
149 | 9ee6e8bb | pbrook | } |
150 | 9ee6e8bb | pbrook | |
151 | 9ee6e8bb | pbrook | |
152 | 9ee6e8bb | pbrook | /* Per-CPU private memory mapped IO. */
|
153 | 9ee6e8bb | pbrook | |
154 | c227f099 | Anthony Liguori | static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset) |
155 | 9ee6e8bb | pbrook | { |
156 | 9ee6e8bb | pbrook | mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
157 | 9ee6e8bb | pbrook | int id;
|
158 | 9ee6e8bb | pbrook | offset &= 0xfff;
|
159 | 9ee6e8bb | pbrook | if (offset < 0x100) { |
160 | 9ee6e8bb | pbrook | /* SCU */
|
161 | 9ee6e8bb | pbrook | switch (offset) {
|
162 | 9ee6e8bb | pbrook | case 0x00: /* Control. */ |
163 | 9ee6e8bb | pbrook | return s->scu_control;
|
164 | 9ee6e8bb | pbrook | case 0x04: /* Configuration. */ |
165 | c988bfad | Paul Brook | id = ((1 << s->num_cpu) - 1) << 4; |
166 | c988bfad | Paul Brook | return id | (s->num_cpu - 1); |
167 | 9ee6e8bb | pbrook | case 0x08: /* CPU status. */ |
168 | 9ee6e8bb | pbrook | return 0; |
169 | 9ee6e8bb | pbrook | case 0x0c: /* Invalidate all. */ |
170 | 9ee6e8bb | pbrook | return 0; |
171 | 9ee6e8bb | pbrook | default:
|
172 | 9ee6e8bb | pbrook | goto bad_reg;
|
173 | 9ee6e8bb | pbrook | } |
174 | 9ee6e8bb | pbrook | } else if (offset < 0x600) { |
175 | 9ee6e8bb | pbrook | /* Interrupt controller. */
|
176 | 9ee6e8bb | pbrook | if (offset < 0x200) { |
177 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
178 | 9ee6e8bb | pbrook | } else {
|
179 | 9ee6e8bb | pbrook | id = (offset - 0x200) >> 8; |
180 | c988bfad | Paul Brook | if (id >= s->num_cpu) {
|
181 | c988bfad | Paul Brook | return 0; |
182 | c988bfad | Paul Brook | } |
183 | 9ee6e8bb | pbrook | } |
184 | fe7e8758 | Paul Brook | return gic_cpu_read(&s->gic, id, offset & 0xff); |
185 | 9ee6e8bb | pbrook | } else if (offset < 0xb00) { |
186 | 9ee6e8bb | pbrook | /* Timers. */
|
187 | 9ee6e8bb | pbrook | if (offset < 0x700) { |
188 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
189 | 9ee6e8bb | pbrook | } else {
|
190 | 9ee6e8bb | pbrook | id = (offset - 0x700) >> 8; |
191 | c988bfad | Paul Brook | if (id >= s->num_cpu) {
|
192 | c988bfad | Paul Brook | return 0; |
193 | c988bfad | Paul Brook | } |
194 | 9ee6e8bb | pbrook | } |
195 | 9ee6e8bb | pbrook | id <<= 1;
|
196 | 9ee6e8bb | pbrook | if (offset & 0x20) |
197 | 9ee6e8bb | pbrook | id++; |
198 | 9ee6e8bb | pbrook | return mpcore_timer_read(&s->timer[id], offset & 0xf); |
199 | 9ee6e8bb | pbrook | } |
200 | 9ee6e8bb | pbrook | bad_reg:
|
201 | 2ac71179 | Paul Brook | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
202 | 9ee6e8bb | pbrook | return 0; |
203 | 9ee6e8bb | pbrook | } |
204 | 9ee6e8bb | pbrook | |
205 | c227f099 | Anthony Liguori | static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, |
206 | 9ee6e8bb | pbrook | uint32_t value) |
207 | 9ee6e8bb | pbrook | { |
208 | 9ee6e8bb | pbrook | mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
209 | 9ee6e8bb | pbrook | int id;
|
210 | 9ee6e8bb | pbrook | offset &= 0xfff;
|
211 | 9ee6e8bb | pbrook | if (offset < 0x100) { |
212 | 9ee6e8bb | pbrook | /* SCU */
|
213 | 9ee6e8bb | pbrook | switch (offset) {
|
214 | 9ee6e8bb | pbrook | case 0: /* Control register. */ |
215 | 9ee6e8bb | pbrook | s->scu_control = value & 1;
|
216 | 9ee6e8bb | pbrook | break;
|
217 | 9ee6e8bb | pbrook | case 0x0c: /* Invalidate all. */ |
218 | 9ee6e8bb | pbrook | /* This is a no-op as cache is not emulated. */
|
219 | 9ee6e8bb | pbrook | break;
|
220 | 9ee6e8bb | pbrook | default:
|
221 | 9ee6e8bb | pbrook | goto bad_reg;
|
222 | 9ee6e8bb | pbrook | } |
223 | 9ee6e8bb | pbrook | } else if (offset < 0x600) { |
224 | 9ee6e8bb | pbrook | /* Interrupt controller. */
|
225 | 9ee6e8bb | pbrook | if (offset < 0x200) { |
226 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
227 | 9ee6e8bb | pbrook | } else {
|
228 | 9ee6e8bb | pbrook | id = (offset - 0x200) >> 8; |
229 | 9ee6e8bb | pbrook | } |
230 | c988bfad | Paul Brook | if (id < s->num_cpu) {
|
231 | c988bfad | Paul Brook | gic_cpu_write(&s->gic, id, offset & 0xff, value);
|
232 | c988bfad | Paul Brook | } |
233 | 9ee6e8bb | pbrook | } else if (offset < 0xb00) { |
234 | 9ee6e8bb | pbrook | /* Timers. */
|
235 | 9ee6e8bb | pbrook | if (offset < 0x700) { |
236 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
237 | 9ee6e8bb | pbrook | } else {
|
238 | 9ee6e8bb | pbrook | id = (offset - 0x700) >> 8; |
239 | 9ee6e8bb | pbrook | } |
240 | c988bfad | Paul Brook | if (id < s->num_cpu) {
|
241 | c988bfad | Paul Brook | id <<= 1;
|
242 | c988bfad | Paul Brook | if (offset & 0x20) |
243 | c988bfad | Paul Brook | id++; |
244 | c988bfad | Paul Brook | mpcore_timer_write(&s->timer[id], offset & 0xf, value);
|
245 | c988bfad | Paul Brook | } |
246 | 9ee6e8bb | pbrook | return;
|
247 | 9ee6e8bb | pbrook | } |
248 | 9ee6e8bb | pbrook | return;
|
249 | 9ee6e8bb | pbrook | bad_reg:
|
250 | 2ac71179 | Paul Brook | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
251 | 9ee6e8bb | pbrook | } |
252 | 9ee6e8bb | pbrook | |
253 | d60efc6b | Blue Swirl | static CPUReadMemoryFunc * const mpcore_priv_readfn[] = { |
254 | 9ee6e8bb | pbrook | mpcore_priv_read, |
255 | 9ee6e8bb | pbrook | mpcore_priv_read, |
256 | 9ee6e8bb | pbrook | mpcore_priv_read |
257 | 9ee6e8bb | pbrook | }; |
258 | 9ee6e8bb | pbrook | |
259 | d60efc6b | Blue Swirl | static CPUWriteMemoryFunc * const mpcore_priv_writefn[] = { |
260 | 9ee6e8bb | pbrook | mpcore_priv_write, |
261 | 9ee6e8bb | pbrook | mpcore_priv_write, |
262 | 9ee6e8bb | pbrook | mpcore_priv_write |
263 | 9ee6e8bb | pbrook | }; |
264 | 9ee6e8bb | pbrook | |
265 | c227f099 | Anthony Liguori | static void mpcore_priv_map(SysBusDevice *dev, target_phys_addr_t base) |
266 | fe7e8758 | Paul Brook | { |
267 | fe7e8758 | Paul Brook | mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); |
268 | fe7e8758 | Paul Brook | cpu_register_physical_memory(base, 0x1000, s->iomemtype);
|
269 | fe7e8758 | Paul Brook | cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype); |
270 | fe7e8758 | Paul Brook | } |
271 | 9ee6e8bb | pbrook | |
272 | 81a322d4 | Gerd Hoffmann | static int mpcore_priv_init(SysBusDevice *dev) |
273 | 9ee6e8bb | pbrook | { |
274 | fe7e8758 | Paul Brook | mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); |
275 | 9ee6e8bb | pbrook | int i;
|
276 | 9ee6e8bb | pbrook | |
277 | c988bfad | Paul Brook | gic_init(&s->gic, s->num_cpu); |
278 | 1eed09cb | Avi Kivity | s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn, |
279 | fe7e8758 | Paul Brook | mpcore_priv_writefn, s); |
280 | fe7e8758 | Paul Brook | sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map);
|
281 | c988bfad | Paul Brook | for (i = 0; i < s->num_cpu * 2; i++) { |
282 | 9ee6e8bb | pbrook | mpcore_timer_init(s, &s->timer[i], i); |
283 | 9ee6e8bb | pbrook | } |
284 | 81a322d4 | Gerd Hoffmann | return 0; |
285 | 9ee6e8bb | pbrook | } |