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