]>
Commit | Line | Data |
---|---|---|
cdbdb648 PB |
1 | /* |
2 | * ARM PrimeCell Timer modules. | |
3 | * | |
4 | * Copyright (c) 2005-2006 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
7 | * This code is licenced under the GPL. | |
8 | */ | |
9 | ||
10 | #include "vl.h" | |
11 | #include "arm_pic.h" | |
12 | ||
13 | /* Common timer implementation. */ | |
14 | ||
15 | #define TIMER_CTRL_ONESHOT (1 << 0) | |
16 | #define TIMER_CTRL_32BIT (1 << 1) | |
17 | #define TIMER_CTRL_DIV1 (0 << 2) | |
18 | #define TIMER_CTRL_DIV16 (1 << 2) | |
19 | #define TIMER_CTRL_DIV256 (2 << 2) | |
20 | #define TIMER_CTRL_IE (1 << 5) | |
21 | #define TIMER_CTRL_PERIODIC (1 << 6) | |
22 | #define TIMER_CTRL_ENABLE (1 << 7) | |
23 | ||
24 | typedef struct { | |
25 | int64_t next_time; | |
26 | int64_t expires; | |
27 | int64_t loaded; | |
28 | QEMUTimer *timer; | |
29 | uint32_t control; | |
30 | uint32_t count; | |
31 | uint32_t limit; | |
32 | int raw_freq; | |
33 | int freq; | |
34 | int int_level; | |
35 | void *pic; | |
36 | int irq; | |
37 | } arm_timer_state; | |
38 | ||
39 | /* Calculate the new expiry time of the given timer. */ | |
40 | ||
41 | static void arm_timer_reload(arm_timer_state *s) | |
42 | { | |
43 | int64_t delay; | |
44 | ||
45 | s->loaded = s->expires; | |
46 | delay = muldiv64(s->count, ticks_per_sec, s->freq); | |
47 | if (delay == 0) | |
48 | delay = 1; | |
49 | s->expires += delay; | |
50 | } | |
51 | ||
52 | /* Check all active timers, and schedule the next timer interrupt. */ | |
53 | ||
54 | static void arm_timer_update(arm_timer_state *s, int64_t now) | |
55 | { | |
56 | int64_t next; | |
57 | ||
58 | /* Ignore disabled timers. */ | |
59 | if ((s->control & TIMER_CTRL_ENABLE) == 0) | |
60 | return; | |
61 | /* Ignore expired one-shot timers. */ | |
62 | if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT)) | |
63 | return; | |
64 | if (s->expires - now <= 0) { | |
65 | /* Timer has expired. */ | |
66 | s->int_level = 1; | |
67 | if (s->control & TIMER_CTRL_ONESHOT) { | |
68 | /* One-shot. */ | |
69 | s->count = 0; | |
70 | } else { | |
71 | if ((s->control & TIMER_CTRL_PERIODIC) == 0) { | |
72 | /* Free running. */ | |
73 | if (s->control & TIMER_CTRL_32BIT) | |
74 | s->count = 0xffffffff; | |
75 | else | |
76 | s->count = 0xffff; | |
77 | } else { | |
78 | /* Periodic. */ | |
79 | s->count = s->limit; | |
80 | } | |
81 | } | |
82 | } | |
83 | while (s->expires - now <= 0) { | |
84 | arm_timer_reload(s); | |
85 | } | |
86 | /* Update interrupts. */ | |
87 | if (s->int_level && (s->control & TIMER_CTRL_IE)) { | |
88 | pic_set_irq_new(s->pic, s->irq, 1); | |
89 | } else { | |
90 | pic_set_irq_new(s->pic, s->irq, 0); | |
91 | } | |
92 | ||
93 | next = now; | |
94 | if (next - s->expires < 0) | |
95 | next = s->expires; | |
96 | ||
97 | /* Schedule the next timer interrupt. */ | |
98 | if (next == now) { | |
99 | qemu_del_timer(s->timer); | |
100 | s->next_time = 0; | |
101 | } else if (next != s->next_time) { | |
102 | qemu_mod_timer(s->timer, next); | |
103 | s->next_time = next; | |
104 | } | |
105 | } | |
106 | ||
107 | /* Return the current value of the timer. */ | |
108 | static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now) | |
109 | { | |
ec2db7de | 110 | int64_t left; |
cdbdb648 PB |
111 | int64_t period; |
112 | ||
113 | if (s->count == 0) | |
114 | return 0; | |
115 | if ((s->control & TIMER_CTRL_ENABLE) == 0) | |
116 | return s->count; | |
ec2db7de | 117 | left = s->expires - now; |
cdbdb648 PB |
118 | period = s->expires - s->loaded; |
119 | /* If the timer should have expired then return 0. This can happen | |
120 | when the host timer signal doesnt occur immediately. It's better to | |
121 | have a timer appear to sit at zero for a while than have it wrap | |
122 | around before the guest interrupt is raised. */ | |
123 | /* ??? Could we trigger the interrupt here? */ | |
ec2db7de | 124 | if (left < 0) |
cdbdb648 PB |
125 | return 0; |
126 | /* We need to calculate count * elapsed / period without overfowing. | |
127 | Scale both elapsed and period so they fit in a 32-bit int. */ | |
128 | while (period != (int32_t)period) { | |
129 | period >>= 1; | |
ec2db7de | 130 | left >>= 1; |
cdbdb648 | 131 | } |
ec2db7de | 132 | return ((uint64_t)s->count * (uint64_t)(int32_t)left) |
cdbdb648 PB |
133 | / (int32_t)period; |
134 | } | |
135 | ||
136 | uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset) | |
137 | { | |
138 | arm_timer_state *s = (arm_timer_state *)opaque; | |
139 | ||
140 | switch (offset >> 2) { | |
141 | case 0: /* TimerLoad */ | |
142 | case 6: /* TimerBGLoad */ | |
143 | return s->limit; | |
144 | case 1: /* TimerValue */ | |
145 | return arm_timer_getcount(s, qemu_get_clock(vm_clock)); | |
146 | case 2: /* TimerControl */ | |
147 | return s->control; | |
148 | case 4: /* TimerRIS */ | |
149 | return s->int_level; | |
150 | case 5: /* TimerMIS */ | |
151 | if ((s->control & TIMER_CTRL_IE) == 0) | |
152 | return 0; | |
153 | return s->int_level; | |
154 | default: | |
155 | cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset); | |
156 | return 0; | |
157 | } | |
158 | } | |
159 | ||
160 | static void arm_timer_write(void *opaque, target_phys_addr_t offset, | |
161 | uint32_t value) | |
162 | { | |
163 | arm_timer_state *s = (arm_timer_state *)opaque; | |
164 | int64_t now; | |
165 | ||
166 | now = qemu_get_clock(vm_clock); | |
167 | switch (offset >> 2) { | |
168 | case 0: /* TimerLoad */ | |
169 | s->limit = value; | |
170 | s->count = value; | |
171 | s->expires = now; | |
172 | arm_timer_reload(s); | |
173 | break; | |
174 | case 1: /* TimerValue */ | |
175 | /* ??? Linux seems to want to write to this readonly register. | |
176 | Ignore it. */ | |
177 | break; | |
178 | case 2: /* TimerControl */ | |
179 | if (s->control & TIMER_CTRL_ENABLE) { | |
180 | /* Pause the timer if it is running. This may cause some | |
181 | inaccuracy dure to rounding, but avoids a whole lot of other | |
182 | messyness. */ | |
183 | s->count = arm_timer_getcount(s, now); | |
184 | } | |
185 | s->control = value; | |
186 | s->freq = s->raw_freq; | |
187 | /* ??? Need to recalculate expiry time after changing divisor. */ | |
188 | switch ((value >> 2) & 3) { | |
189 | case 1: s->freq >>= 4; break; | |
190 | case 2: s->freq >>= 8; break; | |
191 | } | |
192 | if (s->control & TIMER_CTRL_ENABLE) { | |
193 | /* Restart the timer if still enabled. */ | |
194 | s->expires = now; | |
195 | arm_timer_reload(s); | |
196 | } | |
197 | break; | |
198 | case 3: /* TimerIntClr */ | |
199 | s->int_level = 0; | |
200 | break; | |
201 | case 6: /* TimerBGLoad */ | |
202 | s->limit = value; | |
203 | break; | |
204 | default: | |
205 | cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset); | |
206 | } | |
207 | arm_timer_update(s, now); | |
208 | } | |
209 | ||
210 | static void arm_timer_tick(void *opaque) | |
211 | { | |
212 | int64_t now; | |
213 | ||
214 | now = qemu_get_clock(vm_clock); | |
215 | arm_timer_update((arm_timer_state *)opaque, now); | |
216 | } | |
217 | ||
218 | static void *arm_timer_init(uint32_t freq, void *pic, int irq) | |
219 | { | |
220 | arm_timer_state *s; | |
221 | ||
222 | s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state)); | |
223 | s->pic = pic; | |
224 | s->irq = irq; | |
225 | s->raw_freq = s->freq = 1000000; | |
226 | s->control = TIMER_CTRL_IE; | |
227 | s->count = 0xffffffff; | |
228 | ||
229 | s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s); | |
230 | /* ??? Save/restore. */ | |
231 | return s; | |
232 | } | |
233 | ||
234 | /* ARM PrimeCell SP804 dual timer module. | |
235 | Docs for this device don't seem to be publicly available. This | |
236 | implementation is based on gueswork, the linux kernel sources and the | |
237 | Integrator/CP timer modules. */ | |
238 | ||
239 | typedef struct { | |
240 | /* Include a pseudo-PIC device to merge the two interrupt sources. */ | |
241 | arm_pic_handler handler; | |
242 | void *timer[2]; | |
243 | int level[2]; | |
244 | uint32_t base; | |
245 | /* The output PIC device. */ | |
246 | void *pic; | |
247 | int irq; | |
248 | } sp804_state; | |
249 | ||
250 | static void sp804_set_irq(void *opaque, int irq, int level) | |
251 | { | |
252 | sp804_state *s = (sp804_state *)opaque; | |
253 | ||
254 | s->level[irq] = level; | |
255 | pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]); | |
256 | } | |
257 | ||
258 | static uint32_t sp804_read(void *opaque, target_phys_addr_t offset) | |
259 | { | |
260 | sp804_state *s = (sp804_state *)opaque; | |
261 | ||
262 | /* ??? Don't know the PrimeCell ID for this device. */ | |
263 | offset -= s->base; | |
264 | if (offset < 0x20) { | |
265 | return arm_timer_read(s->timer[0], offset); | |
266 | } else { | |
267 | return arm_timer_read(s->timer[1], offset - 0x20); | |
268 | } | |
269 | } | |
270 | ||
271 | static void sp804_write(void *opaque, target_phys_addr_t offset, | |
272 | uint32_t value) | |
273 | { | |
274 | sp804_state *s = (sp804_state *)opaque; | |
275 | ||
276 | offset -= s->base; | |
277 | if (offset < 0x20) { | |
278 | arm_timer_write(s->timer[0], offset, value); | |
279 | } else { | |
280 | arm_timer_write(s->timer[1], offset - 0x20, value); | |
281 | } | |
282 | } | |
283 | ||
284 | static CPUReadMemoryFunc *sp804_readfn[] = { | |
285 | sp804_read, | |
286 | sp804_read, | |
287 | sp804_read | |
288 | }; | |
289 | ||
290 | static CPUWriteMemoryFunc *sp804_writefn[] = { | |
291 | sp804_write, | |
292 | sp804_write, | |
293 | sp804_write | |
294 | }; | |
295 | ||
296 | void sp804_init(uint32_t base, void *pic, int irq) | |
297 | { | |
298 | int iomemtype; | |
299 | sp804_state *s; | |
300 | ||
301 | s = (sp804_state *)qemu_mallocz(sizeof(sp804_state)); | |
302 | s->handler = sp804_set_irq; | |
303 | s->base = base; | |
304 | s->pic = pic; | |
305 | s->irq = irq; | |
306 | /* ??? The timers are actually configurable between 32kHz and 1MHz, but | |
307 | we don't implement that. */ | |
308 | s->timer[0] = arm_timer_init(1000000, s, 0); | |
309 | s->timer[1] = arm_timer_init(1000000, s, 1); | |
310 | iomemtype = cpu_register_io_memory(0, sp804_readfn, | |
311 | sp804_writefn, s); | |
312 | cpu_register_physical_memory(base, 0x00000fff, iomemtype); | |
313 | /* ??? Save/restore. */ | |
314 | } | |
315 | ||
316 | ||
317 | /* Integrator/CP timer module. */ | |
318 | ||
319 | typedef struct { | |
320 | void *timer[3]; | |
321 | uint32_t base; | |
322 | } icp_pit_state; | |
323 | ||
324 | static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset) | |
325 | { | |
326 | icp_pit_state *s = (icp_pit_state *)opaque; | |
327 | int n; | |
328 | ||
329 | /* ??? Don't know the PrimeCell ID for this device. */ | |
330 | offset -= s->base; | |
331 | n = offset >> 8; | |
332 | if (n > 3) | |
333 | cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n); | |
334 | ||
335 | return arm_timer_read(s->timer[n], offset & 0xff); | |
336 | } | |
337 | ||
338 | static void icp_pit_write(void *opaque, target_phys_addr_t offset, | |
339 | uint32_t value) | |
340 | { | |
341 | icp_pit_state *s = (icp_pit_state *)opaque; | |
342 | int n; | |
343 | ||
344 | offset -= s->base; | |
345 | n = offset >> 8; | |
346 | if (n > 3) | |
347 | cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n); | |
348 | ||
349 | arm_timer_write(s->timer[n], offset & 0xff, value); | |
350 | } | |
351 | ||
352 | ||
353 | static CPUReadMemoryFunc *icp_pit_readfn[] = { | |
354 | icp_pit_read, | |
355 | icp_pit_read, | |
356 | icp_pit_read | |
357 | }; | |
358 | ||
359 | static CPUWriteMemoryFunc *icp_pit_writefn[] = { | |
360 | icp_pit_write, | |
361 | icp_pit_write, | |
362 | icp_pit_write | |
363 | }; | |
364 | ||
365 | void icp_pit_init(uint32_t base, void *pic, int irq) | |
366 | { | |
367 | int iomemtype; | |
368 | icp_pit_state *s; | |
369 | ||
370 | s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state)); | |
371 | s->base = base; | |
372 | /* Timer 0 runs at the system clock speed (40MHz). */ | |
373 | s->timer[0] = arm_timer_init(40000000, pic, irq); | |
374 | /* The other two timers run at 1MHz. */ | |
375 | s->timer[1] = arm_timer_init(1000000, pic, irq + 1); | |
376 | s->timer[2] = arm_timer_init(1000000, pic, irq + 2); | |
377 | ||
378 | iomemtype = cpu_register_io_memory(0, icp_pit_readfn, | |
379 | icp_pit_writefn, s); | |
380 | cpu_register_physical_memory(base, 0x00000fff, iomemtype); | |
381 | /* ??? Save/restore. */ | |
382 | } | |
383 |