blob: 068d0a0f7a9add5421ecba88712f372ab8112858 [file] [log] [blame]
pbrookcdbdb642006-04-09 01:32:52 +00001/*
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
24typedef 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
41static 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
54static 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. */
108static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now)
109{
pbrookec2db7d2006-11-14 21:13:53 +0000110 int64_t left;
pbrookcdbdb642006-04-09 01:32:52 +0000111 int64_t period;
112
113 if (s->count == 0)
114 return 0;
115 if ((s->control & TIMER_CTRL_ENABLE) == 0)
116 return s->count;
pbrookec2db7d2006-11-14 21:13:53 +0000117 left = s->expires - now;
pbrookcdbdb642006-04-09 01:32:52 +0000118 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? */
pbrookec2db7d2006-11-14 21:13:53 +0000124 if (left < 0)
pbrookcdbdb642006-04-09 01:32:52 +0000125 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;
pbrookec2db7d2006-11-14 21:13:53 +0000130 left >>= 1;
pbrookcdbdb642006-04-09 01:32:52 +0000131 }
pbrookec2db7d2006-11-14 21:13:53 +0000132 return ((uint64_t)s->count * (uint64_t)(int32_t)left)
pbrookcdbdb642006-04-09 01:32:52 +0000133 / (int32_t)period;
134}
135
136uint32_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
160static 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
210static 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
218static 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
pbrookd85fb992007-04-06 20:58:25 +0000236 implementation is based on guesswork, the linux kernel sources and the
pbrookcdbdb642006-04-09 01:32:52 +0000237 Integrator/CP timer modules. */
238
239typedef 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
250static 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
258static 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
271static 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
284static CPUReadMemoryFunc *sp804_readfn[] = {
285 sp804_read,
286 sp804_read,
287 sp804_read
288};
289
290static CPUWriteMemoryFunc *sp804_writefn[] = {
291 sp804_write,
292 sp804_write,
293 sp804_write
294};
295
296void 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
319typedef struct {
320 void *timer[3];
321 uint32_t base;
322} icp_pit_state;
323
324static 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
338static 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
353static CPUReadMemoryFunc *icp_pit_readfn[] = {
354 icp_pit_read,
355 icp_pit_read,
356 icp_pit_read
357};
358
359static CPUWriteMemoryFunc *icp_pit_writefn[] = {
360 icp_pit_write,
361 icp_pit_write,
362 icp_pit_write
363};
364
365void 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