]>
Commit | Line | Data |
---|---|---|
9ee6e8bb | 1 | /* |
f7c70325 | 2 | * ARM MPCore internal peripheral emulation (common code). |
9ee6e8bb PB |
3 | * |
4 | * Copyright (c) 2006-2007 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
8e31bf38 | 7 | * This code is licensed under the GPL. |
9ee6e8bb PB |
8 | */ |
9 | ||
fe7e8758 | 10 | #include "sysbus.h" |
87ecb68b | 11 | #include "qemu-timer.h" |
9ee6e8bb | 12 | |
9ee6e8bb | 13 | #define NCPU 4 |
9ee6e8bb PB |
14 | |
15 | static inline int | |
16 | gic_get_current_cpu(void) | |
17 | { | |
18 | return cpu_single_env->cpu_index; | |
19 | } | |
20 | ||
21 | #include "arm_gic.c" | |
22 | ||
23 | /* MPCore private memory region. */ | |
24 | ||
25 | typedef struct { | |
26 | uint32_t count; | |
27 | uint32_t load; | |
28 | uint32_t control; | |
29 | uint32_t status; | |
30 | uint32_t old_status; | |
31 | int64_t tick; | |
32 | QEMUTimer *timer; | |
33 | struct mpcore_priv_state *mpcore; | |
34 | int id; /* Encodes both timer/watchdog and CPU. */ | |
35 | } mpcore_timer_state; | |
36 | ||
37 | typedef struct mpcore_priv_state { | |
fe7e8758 | 38 | gic_state gic; |
9ee6e8bb | 39 | uint32_t scu_control; |
fe7e8758 | 40 | int iomemtype; |
9ee6e8bb | 41 | mpcore_timer_state timer[8]; |
c988bfad | 42 | uint32_t num_cpu; |
755c0802 AK |
43 | MemoryRegion iomem; |
44 | MemoryRegion container; | |
9ee6e8bb PB |
45 | } mpcore_priv_state; |
46 | ||
47 | /* Per-CPU Timers. */ | |
48 | ||
49 | static inline void mpcore_timer_update_irq(mpcore_timer_state *s) | |
50 | { | |
51 | if (s->status & ~s->old_status) { | |
fe7e8758 | 52 | gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1)); |
9ee6e8bb PB |
53 | } |
54 | s->old_status = s->status; | |
55 | } | |
56 | ||
57 | /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ | |
58 | static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s) | |
59 | { | |
60 | return (((s->control >> 8) & 0xff) + 1) * 10; | |
61 | } | |
62 | ||
63 | static void mpcore_timer_reload(mpcore_timer_state *s, int restart) | |
64 | { | |
65 | if (s->count == 0) | |
66 | return; | |
67 | if (restart) | |
74475455 | 68 | s->tick = qemu_get_clock_ns(vm_clock); |
9ee6e8bb PB |
69 | s->tick += (int64_t)s->count * mpcore_timer_scale(s); |
70 | qemu_mod_timer(s->timer, s->tick); | |
71 | } | |
72 | ||
73 | static void mpcore_timer_tick(void *opaque) | |
74 | { | |
75 | mpcore_timer_state *s = (mpcore_timer_state *)opaque; | |
76 | s->status = 1; | |
77 | if (s->control & 2) { | |
78 | s->count = s->load; | |
79 | mpcore_timer_reload(s, 0); | |
80 | } else { | |
81 | s->count = 0; | |
82 | } | |
83 | mpcore_timer_update_irq(s); | |
84 | } | |
85 | ||
86 | static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset) | |
87 | { | |
88 | int64_t val; | |
89 | switch (offset) { | |
90 | case 0: /* Load */ | |
91 | return s->load; | |
92 | /* Fall through. */ | |
93 | case 4: /* Counter. */ | |
94 | if (((s->control & 1) == 0) || (s->count == 0)) | |
95 | return 0; | |
96 | /* Slow and ugly, but hopefully won't happen too often. */ | |
74475455 | 97 | val = s->tick - qemu_get_clock_ns(vm_clock); |
9ee6e8bb PB |
98 | val /= mpcore_timer_scale(s); |
99 | if (val < 0) | |
100 | val = 0; | |
101 | return val; | |
102 | case 8: /* Control. */ | |
103 | return s->control; | |
104 | case 12: /* Interrupt status. */ | |
105 | return s->status; | |
a38131b6 BS |
106 | default: |
107 | return 0; | |
9ee6e8bb PB |
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; | |
74475455 | 150 | s->timer = qemu_new_timer_ns(vm_clock, mpcore_timer_tick, s); |
9ee6e8bb PB |
151 | } |
152 | ||
153 | ||
154 | /* Per-CPU private memory mapped IO. */ | |
155 | ||
755c0802 AK |
156 | static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset, |
157 | unsigned size) | |
9ee6e8bb PB |
158 | { |
159 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
160 | int id; | |
161 | offset &= 0xfff; | |
162 | if (offset < 0x100) { | |
163 | /* SCU */ | |
164 | switch (offset) { | |
165 | case 0x00: /* Control. */ | |
166 | return s->scu_control; | |
167 | case 0x04: /* Configuration. */ | |
c988bfad PB |
168 | id = ((1 << s->num_cpu) - 1) << 4; |
169 | return id | (s->num_cpu - 1); | |
9ee6e8bb PB |
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; | |
c988bfad PB |
183 | if (id >= s->num_cpu) { |
184 | return 0; | |
185 | } | |
9ee6e8bb | 186 | } |
fe7e8758 | 187 | return gic_cpu_read(&s->gic, id, offset & 0xff); |
9ee6e8bb PB |
188 | } else if (offset < 0xb00) { |
189 | /* Timers. */ | |
190 | if (offset < 0x700) { | |
191 | id = gic_get_current_cpu(); | |
192 | } else { | |
193 | id = (offset - 0x700) >> 8; | |
c988bfad PB |
194 | if (id >= s->num_cpu) { |
195 | return 0; | |
196 | } | |
9ee6e8bb PB |
197 | } |
198 | id <<= 1; | |
199 | if (offset & 0x20) | |
200 | id++; | |
201 | return mpcore_timer_read(&s->timer[id], offset & 0xf); | |
202 | } | |
203 | bad_reg: | |
2ac71179 | 204 | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
9ee6e8bb PB |
205 | return 0; |
206 | } | |
207 | ||
c227f099 | 208 | static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, |
755c0802 | 209 | uint64_t value, unsigned size) |
9ee6e8bb PB |
210 | { |
211 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
212 | int id; | |
213 | offset &= 0xfff; | |
214 | if (offset < 0x100) { | |
215 | /* SCU */ | |
216 | switch (offset) { | |
217 | case 0: /* Control register. */ | |
218 | s->scu_control = value & 1; | |
219 | break; | |
220 | case 0x0c: /* Invalidate all. */ | |
221 | /* This is a no-op as cache is not emulated. */ | |
222 | break; | |
223 | default: | |
224 | goto bad_reg; | |
225 | } | |
226 | } else if (offset < 0x600) { | |
227 | /* Interrupt controller. */ | |
228 | if (offset < 0x200) { | |
229 | id = gic_get_current_cpu(); | |
230 | } else { | |
231 | id = (offset - 0x200) >> 8; | |
232 | } | |
c988bfad PB |
233 | if (id < s->num_cpu) { |
234 | gic_cpu_write(&s->gic, id, offset & 0xff, value); | |
235 | } | |
9ee6e8bb PB |
236 | } else if (offset < 0xb00) { |
237 | /* Timers. */ | |
238 | if (offset < 0x700) { | |
239 | id = gic_get_current_cpu(); | |
240 | } else { | |
241 | id = (offset - 0x700) >> 8; | |
242 | } | |
c988bfad PB |
243 | if (id < s->num_cpu) { |
244 | id <<= 1; | |
245 | if (offset & 0x20) | |
246 | id++; | |
247 | mpcore_timer_write(&s->timer[id], offset & 0xf, value); | |
248 | } | |
9ee6e8bb PB |
249 | return; |
250 | } | |
251 | return; | |
252 | bad_reg: | |
2ac71179 | 253 | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); |
9ee6e8bb PB |
254 | } |
255 | ||
755c0802 AK |
256 | static const MemoryRegionOps mpcore_priv_ops = { |
257 | .read = mpcore_priv_read, | |
258 | .write = mpcore_priv_write, | |
259 | .endianness = DEVICE_NATIVE_ENDIAN, | |
9ee6e8bb PB |
260 | }; |
261 | ||
755c0802 | 262 | static void mpcore_priv_map_setup(mpcore_priv_state *s) |
fe7e8758 | 263 | { |
755c0802 AK |
264 | memory_region_init(&s->container, "mpcode-priv-container", 0x2000); |
265 | memory_region_init_io(&s->iomem, &mpcore_priv_ops, s, "mpcode-priv", | |
266 | 0x1000); | |
267 | memory_region_add_subregion(&s->container, 0, &s->iomem); | |
268 | memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); | |
fe7e8758 | 269 | } |
9ee6e8bb | 270 | |
81a322d4 | 271 | static int mpcore_priv_init(SysBusDevice *dev) |
9ee6e8bb | 272 | { |
fe7e8758 | 273 | mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); |
9ee6e8bb PB |
274 | int i; |
275 | ||
c988bfad | 276 | gic_init(&s->gic, s->num_cpu); |
755c0802 AK |
277 | mpcore_priv_map_setup(s); |
278 | sysbus_init_mmio_region(dev, &s->container); | |
c988bfad | 279 | for (i = 0; i < s->num_cpu * 2; i++) { |
9ee6e8bb PB |
280 | mpcore_timer_init(s, &s->timer[i], i); |
281 | } | |
81a322d4 | 282 | return 0; |
9ee6e8bb | 283 | } |