root / hw / mpcore.c @ 795928f6
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 | 8e31bf38 | Matthew Fernandez | * This code is licensed 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 | 755c0802 | Avi Kivity | MemoryRegion iomem; |
44 | 755c0802 | Avi Kivity | MemoryRegion container; |
45 | 9ee6e8bb | pbrook | } mpcore_priv_state; |
46 | 9ee6e8bb | pbrook | |
47 | 9ee6e8bb | pbrook | /* Per-CPU Timers. */
|
48 | 9ee6e8bb | pbrook | |
49 | 9ee6e8bb | pbrook | static inline void mpcore_timer_update_irq(mpcore_timer_state *s) |
50 | 9ee6e8bb | pbrook | { |
51 | 9ee6e8bb | pbrook | if (s->status & ~s->old_status) {
|
52 | fe7e8758 | Paul Brook | gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1)); |
53 | 9ee6e8bb | pbrook | } |
54 | 9ee6e8bb | pbrook | s->old_status = s->status; |
55 | 9ee6e8bb | pbrook | } |
56 | 9ee6e8bb | pbrook | |
57 | 9ee6e8bb | pbrook | /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
|
58 | 9ee6e8bb | pbrook | static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s) |
59 | 9ee6e8bb | pbrook | { |
60 | 9ee6e8bb | pbrook | return (((s->control >> 8) & 0xff) + 1) * 10; |
61 | 9ee6e8bb | pbrook | } |
62 | 9ee6e8bb | pbrook | |
63 | 9ee6e8bb | pbrook | static void mpcore_timer_reload(mpcore_timer_state *s, int restart) |
64 | 9ee6e8bb | pbrook | { |
65 | 9ee6e8bb | pbrook | if (s->count == 0) |
66 | 9ee6e8bb | pbrook | return;
|
67 | 9ee6e8bb | pbrook | if (restart)
|
68 | 74475455 | Paolo Bonzini | s->tick = qemu_get_clock_ns(vm_clock); |
69 | 9ee6e8bb | pbrook | s->tick += (int64_t)s->count * mpcore_timer_scale(s); |
70 | 9ee6e8bb | pbrook | qemu_mod_timer(s->timer, s->tick); |
71 | 9ee6e8bb | pbrook | } |
72 | 9ee6e8bb | pbrook | |
73 | 9ee6e8bb | pbrook | static void mpcore_timer_tick(void *opaque) |
74 | 9ee6e8bb | pbrook | { |
75 | 9ee6e8bb | pbrook | mpcore_timer_state *s = (mpcore_timer_state *)opaque; |
76 | 9ee6e8bb | pbrook | s->status = 1;
|
77 | 9ee6e8bb | pbrook | if (s->control & 2) { |
78 | 9ee6e8bb | pbrook | s->count = s->load; |
79 | 9ee6e8bb | pbrook | mpcore_timer_reload(s, 0);
|
80 | 9ee6e8bb | pbrook | } else {
|
81 | 9ee6e8bb | pbrook | s->count = 0;
|
82 | 9ee6e8bb | pbrook | } |
83 | 9ee6e8bb | pbrook | mpcore_timer_update_irq(s); |
84 | 9ee6e8bb | pbrook | } |
85 | 9ee6e8bb | pbrook | |
86 | 9ee6e8bb | pbrook | static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset) |
87 | 9ee6e8bb | pbrook | { |
88 | 9ee6e8bb | pbrook | int64_t val; |
89 | 9ee6e8bb | pbrook | switch (offset) {
|
90 | 9ee6e8bb | pbrook | case 0: /* Load */ |
91 | 9ee6e8bb | pbrook | return s->load;
|
92 | 9ee6e8bb | pbrook | /* Fall through. */
|
93 | 9ee6e8bb | pbrook | case 4: /* Counter. */ |
94 | 9ee6e8bb | pbrook | if (((s->control & 1) == 0) || (s->count == 0)) |
95 | 9ee6e8bb | pbrook | return 0; |
96 | 9ee6e8bb | pbrook | /* Slow and ugly, but hopefully won't happen too often. */
|
97 | 74475455 | Paolo Bonzini | val = s->tick - qemu_get_clock_ns(vm_clock); |
98 | 9ee6e8bb | pbrook | val /= mpcore_timer_scale(s); |
99 | 9ee6e8bb | pbrook | if (val < 0) |
100 | 9ee6e8bb | pbrook | val = 0;
|
101 | 9ee6e8bb | pbrook | return val;
|
102 | 9ee6e8bb | pbrook | case 8: /* Control. */ |
103 | 9ee6e8bb | pbrook | return s->control;
|
104 | 9ee6e8bb | pbrook | case 12: /* Interrupt status. */ |
105 | 9ee6e8bb | pbrook | return s->status;
|
106 | a38131b6 | blueswir1 | default:
|
107 | a38131b6 | blueswir1 | return 0; |
108 | 9ee6e8bb | pbrook | } |
109 | 9ee6e8bb | pbrook | } |
110 | 9ee6e8bb | pbrook | |
111 | 9ee6e8bb | pbrook | static void mpcore_timer_write(mpcore_timer_state *s, int offset, |
112 | 9ee6e8bb | pbrook | uint32_t value) |
113 | 9ee6e8bb | pbrook | { |
114 | 9ee6e8bb | pbrook | int64_t old; |
115 | 9ee6e8bb | pbrook | switch (offset) {
|
116 | 9ee6e8bb | pbrook | case 0: /* Load */ |
117 | 9ee6e8bb | pbrook | s->load = value; |
118 | 9ee6e8bb | pbrook | /* Fall through. */
|
119 | 9ee6e8bb | pbrook | case 4: /* Counter. */ |
120 | 9ee6e8bb | pbrook | if ((s->control & 1) && s->count) { |
121 | 9ee6e8bb | pbrook | /* Cancel the previous timer. */
|
122 | 9ee6e8bb | pbrook | qemu_del_timer(s->timer); |
123 | 9ee6e8bb | pbrook | } |
124 | 9ee6e8bb | pbrook | s->count = value; |
125 | 9ee6e8bb | pbrook | if (s->control & 1) { |
126 | 9ee6e8bb | pbrook | mpcore_timer_reload(s, 1);
|
127 | 9ee6e8bb | pbrook | } |
128 | 9ee6e8bb | pbrook | break;
|
129 | 9ee6e8bb | pbrook | case 8: /* Control. */ |
130 | 9ee6e8bb | pbrook | old = s->control; |
131 | 9ee6e8bb | pbrook | s->control = value; |
132 | 9ee6e8bb | pbrook | if (((old & 1) == 0) && (value & 1)) { |
133 | 9ee6e8bb | pbrook | if (s->count == 0 && (s->control & 2)) |
134 | 9ee6e8bb | pbrook | s->count = s->load; |
135 | 9ee6e8bb | pbrook | mpcore_timer_reload(s, 1);
|
136 | 9ee6e8bb | pbrook | } |
137 | 9ee6e8bb | pbrook | break;
|
138 | 9ee6e8bb | pbrook | case 12: /* Interrupt status. */ |
139 | 9ee6e8bb | pbrook | s->status &= ~value; |
140 | 9ee6e8bb | pbrook | mpcore_timer_update_irq(s); |
141 | 9ee6e8bb | pbrook | break;
|
142 | 9ee6e8bb | pbrook | } |
143 | 9ee6e8bb | pbrook | } |
144 | 9ee6e8bb | pbrook | |
145 | 9ee6e8bb | pbrook | static void mpcore_timer_init(mpcore_priv_state *mpcore, |
146 | 9ee6e8bb | pbrook | mpcore_timer_state *s, int id)
|
147 | 9ee6e8bb | pbrook | { |
148 | 9ee6e8bb | pbrook | s->id = id; |
149 | 9ee6e8bb | pbrook | s->mpcore = mpcore; |
150 | 74475455 | Paolo Bonzini | s->timer = qemu_new_timer_ns(vm_clock, mpcore_timer_tick, s); |
151 | 9ee6e8bb | pbrook | } |
152 | 9ee6e8bb | pbrook | |
153 | 9ee6e8bb | pbrook | |
154 | 9ee6e8bb | pbrook | /* Per-CPU private memory mapped IO. */
|
155 | 9ee6e8bb | pbrook | |
156 | 755c0802 | Avi Kivity | static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset, |
157 | 755c0802 | Avi Kivity | unsigned size)
|
158 | 9ee6e8bb | pbrook | { |
159 | 9ee6e8bb | pbrook | mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
160 | 9ee6e8bb | pbrook | int id;
|
161 | 9ee6e8bb | pbrook | offset &= 0xfff;
|
162 | 9ee6e8bb | pbrook | if (offset < 0x100) { |
163 | 9ee6e8bb | pbrook | /* SCU */
|
164 | 9ee6e8bb | pbrook | switch (offset) {
|
165 | 9ee6e8bb | pbrook | case 0x00: /* Control. */ |
166 | 9ee6e8bb | pbrook | return s->scu_control;
|
167 | 9ee6e8bb | pbrook | case 0x04: /* Configuration. */ |
168 | c988bfad | Paul Brook | id = ((1 << s->num_cpu) - 1) << 4; |
169 | c988bfad | Paul Brook | return id | (s->num_cpu - 1); |
170 | 9ee6e8bb | pbrook | case 0x08: /* CPU status. */ |
171 | 9ee6e8bb | pbrook | return 0; |
172 | 9ee6e8bb | pbrook | case 0x0c: /* Invalidate all. */ |
173 | 9ee6e8bb | pbrook | return 0; |
174 | 9ee6e8bb | pbrook | default:
|
175 | 9ee6e8bb | pbrook | goto bad_reg;
|
176 | 9ee6e8bb | pbrook | } |
177 | 9ee6e8bb | pbrook | } else if (offset < 0x600) { |
178 | 9ee6e8bb | pbrook | /* Interrupt controller. */
|
179 | 9ee6e8bb | pbrook | if (offset < 0x200) { |
180 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
181 | 9ee6e8bb | pbrook | } else {
|
182 | 9ee6e8bb | pbrook | id = (offset - 0x200) >> 8; |
183 | c988bfad | Paul Brook | if (id >= s->num_cpu) {
|
184 | c988bfad | Paul Brook | return 0; |
185 | c988bfad | Paul Brook | } |
186 | 9ee6e8bb | pbrook | } |
187 | fe7e8758 | Paul Brook | return gic_cpu_read(&s->gic, id, offset & 0xff); |
188 | 9ee6e8bb | pbrook | } else if (offset < 0xb00) { |
189 | 9ee6e8bb | pbrook | /* Timers. */
|
190 | 9ee6e8bb | pbrook | if (offset < 0x700) { |
191 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
192 | 9ee6e8bb | pbrook | } else {
|
193 | 9ee6e8bb | pbrook | id = (offset - 0x700) >> 8; |
194 | c988bfad | Paul Brook | if (id >= s->num_cpu) {
|
195 | c988bfad | Paul Brook | return 0; |
196 | c988bfad | Paul Brook | } |
197 | 9ee6e8bb | pbrook | } |
198 | 9ee6e8bb | pbrook | id <<= 1;
|
199 | 9ee6e8bb | pbrook | if (offset & 0x20) |
200 | 9ee6e8bb | pbrook | id++; |
201 | 9ee6e8bb | pbrook | return mpcore_timer_read(&s->timer[id], offset & 0xf); |
202 | 9ee6e8bb | pbrook | } |
203 | 9ee6e8bb | pbrook | bad_reg:
|
204 | 2ac71179 | Paul Brook | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
205 | 9ee6e8bb | pbrook | return 0; |
206 | 9ee6e8bb | pbrook | } |
207 | 9ee6e8bb | pbrook | |
208 | c227f099 | Anthony Liguori | static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, |
209 | 755c0802 | Avi Kivity | uint64_t value, unsigned size)
|
210 | 9ee6e8bb | pbrook | { |
211 | 9ee6e8bb | pbrook | mpcore_priv_state *s = (mpcore_priv_state *)opaque; |
212 | 9ee6e8bb | pbrook | int id;
|
213 | 9ee6e8bb | pbrook | offset &= 0xfff;
|
214 | 9ee6e8bb | pbrook | if (offset < 0x100) { |
215 | 9ee6e8bb | pbrook | /* SCU */
|
216 | 9ee6e8bb | pbrook | switch (offset) {
|
217 | 9ee6e8bb | pbrook | case 0: /* Control register. */ |
218 | 9ee6e8bb | pbrook | s->scu_control = value & 1;
|
219 | 9ee6e8bb | pbrook | break;
|
220 | 9ee6e8bb | pbrook | case 0x0c: /* Invalidate all. */ |
221 | 9ee6e8bb | pbrook | /* This is a no-op as cache is not emulated. */
|
222 | 9ee6e8bb | pbrook | break;
|
223 | 9ee6e8bb | pbrook | default:
|
224 | 9ee6e8bb | pbrook | goto bad_reg;
|
225 | 9ee6e8bb | pbrook | } |
226 | 9ee6e8bb | pbrook | } else if (offset < 0x600) { |
227 | 9ee6e8bb | pbrook | /* Interrupt controller. */
|
228 | 9ee6e8bb | pbrook | if (offset < 0x200) { |
229 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
230 | 9ee6e8bb | pbrook | } else {
|
231 | 9ee6e8bb | pbrook | id = (offset - 0x200) >> 8; |
232 | 9ee6e8bb | pbrook | } |
233 | c988bfad | Paul Brook | if (id < s->num_cpu) {
|
234 | c988bfad | Paul Brook | gic_cpu_write(&s->gic, id, offset & 0xff, value);
|
235 | c988bfad | Paul Brook | } |
236 | 9ee6e8bb | pbrook | } else if (offset < 0xb00) { |
237 | 9ee6e8bb | pbrook | /* Timers. */
|
238 | 9ee6e8bb | pbrook | if (offset < 0x700) { |
239 | 9ee6e8bb | pbrook | id = gic_get_current_cpu(); |
240 | 9ee6e8bb | pbrook | } else {
|
241 | 9ee6e8bb | pbrook | id = (offset - 0x700) >> 8; |
242 | 9ee6e8bb | pbrook | } |
243 | c988bfad | Paul Brook | if (id < s->num_cpu) {
|
244 | c988bfad | Paul Brook | id <<= 1;
|
245 | c988bfad | Paul Brook | if (offset & 0x20) |
246 | c988bfad | Paul Brook | id++; |
247 | c988bfad | Paul Brook | mpcore_timer_write(&s->timer[id], offset & 0xf, value);
|
248 | c988bfad | Paul Brook | } |
249 | 9ee6e8bb | pbrook | return;
|
250 | 9ee6e8bb | pbrook | } |
251 | 9ee6e8bb | pbrook | return;
|
252 | 9ee6e8bb | pbrook | bad_reg:
|
253 | 2ac71179 | Paul Brook | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
254 | 9ee6e8bb | pbrook | } |
255 | 9ee6e8bb | pbrook | |
256 | 755c0802 | Avi Kivity | static const MemoryRegionOps mpcore_priv_ops = { |
257 | 755c0802 | Avi Kivity | .read = mpcore_priv_read, |
258 | 755c0802 | Avi Kivity | .write = mpcore_priv_write, |
259 | 755c0802 | Avi Kivity | .endianness = DEVICE_NATIVE_ENDIAN, |
260 | 9ee6e8bb | pbrook | }; |
261 | 9ee6e8bb | pbrook | |
262 | 755c0802 | Avi Kivity | static void mpcore_priv_map_setup(mpcore_priv_state *s) |
263 | fe7e8758 | Paul Brook | { |
264 | 755c0802 | Avi Kivity | memory_region_init(&s->container, "mpcode-priv-container", 0x2000); |
265 | 755c0802 | Avi Kivity | memory_region_init_io(&s->iomem, &mpcore_priv_ops, s, "mpcode-priv",
|
266 | 755c0802 | Avi Kivity | 0x1000);
|
267 | 755c0802 | Avi Kivity | memory_region_add_subregion(&s->container, 0, &s->iomem);
|
268 | 755c0802 | Avi Kivity | memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
|
269 | fe7e8758 | Paul Brook | } |
270 | 9ee6e8bb | pbrook | |
271 | 81a322d4 | Gerd Hoffmann | static int mpcore_priv_init(SysBusDevice *dev) |
272 | 9ee6e8bb | pbrook | { |
273 | fe7e8758 | Paul Brook | mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); |
274 | 9ee6e8bb | pbrook | int i;
|
275 | 9ee6e8bb | pbrook | |
276 | c988bfad | Paul Brook | gic_init(&s->gic, s->num_cpu); |
277 | 755c0802 | Avi Kivity | mpcore_priv_map_setup(s); |
278 | 755c0802 | Avi Kivity | sysbus_init_mmio_region(dev, &s->container); |
279 | c988bfad | Paul Brook | for (i = 0; i < s->num_cpu * 2; i++) { |
280 | 9ee6e8bb | pbrook | mpcore_timer_init(s, &s->timer[i], i); |
281 | 9ee6e8bb | pbrook | } |
282 | 81a322d4 | Gerd Hoffmann | return 0; |
283 | 9ee6e8bb | pbrook | } |