]> git.proxmox.com Git - mirror_qemu.git/blame - hw/arm_timer.c
Integrator/CP core qdev conversion
[mirror_qemu.git] / hw / arm_timer.c
CommitLineData
5fafdf24 1/*
cdbdb648
PB
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
87ecb68b 10#include "hw.h"
87ecb68b 11#include "qemu-timer.h"
9596ebb7 12#include "primecell.h"
cdbdb648
PB
13
14/* Common timer implementation. */
15
16#define TIMER_CTRL_ONESHOT (1 << 0)
17#define TIMER_CTRL_32BIT (1 << 1)
18#define TIMER_CTRL_DIV1 (0 << 2)
19#define TIMER_CTRL_DIV16 (1 << 2)
20#define TIMER_CTRL_DIV256 (2 << 2)
21#define TIMER_CTRL_IE (1 << 5)
22#define TIMER_CTRL_PERIODIC (1 << 6)
23#define TIMER_CTRL_ENABLE (1 << 7)
24
25typedef struct {
423f0742 26 ptimer_state *timer;
cdbdb648 27 uint32_t control;
cdbdb648 28 uint32_t limit;
cdbdb648
PB
29 int freq;
30 int int_level;
d537cf6c 31 qemu_irq irq;
cdbdb648
PB
32} arm_timer_state;
33
cdbdb648
PB
34/* Check all active timers, and schedule the next timer interrupt. */
35
423f0742 36static void arm_timer_update(arm_timer_state *s)
cdbdb648 37{
cdbdb648
PB
38 /* Update interrupts. */
39 if (s->int_level && (s->control & TIMER_CTRL_IE)) {
d537cf6c 40 qemu_irq_raise(s->irq);
cdbdb648 41 } else {
d537cf6c 42 qemu_irq_lower(s->irq);
cdbdb648 43 }
cdbdb648
PB
44}
45
9596ebb7 46static uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
cdbdb648
PB
47{
48 arm_timer_state *s = (arm_timer_state *)opaque;
49
50 switch (offset >> 2) {
51 case 0: /* TimerLoad */
52 case 6: /* TimerBGLoad */
53 return s->limit;
54 case 1: /* TimerValue */
423f0742 55 return ptimer_get_count(s->timer);
cdbdb648
PB
56 case 2: /* TimerControl */
57 return s->control;
58 case 4: /* TimerRIS */
59 return s->int_level;
60 case 5: /* TimerMIS */
61 if ((s->control & TIMER_CTRL_IE) == 0)
62 return 0;
63 return s->int_level;
64 default:
2ac71179 65 hw_error("arm_timer_read: Bad offset %x\n", (int)offset);
cdbdb648
PB
66 return 0;
67 }
68}
69
423f0742
PB
70/* Reset the timer limit after settings have changed. */
71static void arm_timer_recalibrate(arm_timer_state *s, int reload)
72{
73 uint32_t limit;
74
75 if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
76 /* Free running. */
77 if (s->control & TIMER_CTRL_32BIT)
78 limit = 0xffffffff;
79 else
80 limit = 0xffff;
81 } else {
82 /* Periodic. */
83 limit = s->limit;
84 }
85 ptimer_set_limit(s->timer, limit, reload);
86}
87
cdbdb648
PB
88static void arm_timer_write(void *opaque, target_phys_addr_t offset,
89 uint32_t value)
90{
91 arm_timer_state *s = (arm_timer_state *)opaque;
423f0742 92 int freq;
cdbdb648 93
cdbdb648
PB
94 switch (offset >> 2) {
95 case 0: /* TimerLoad */
96 s->limit = value;
423f0742 97 arm_timer_recalibrate(s, 1);
cdbdb648
PB
98 break;
99 case 1: /* TimerValue */
100 /* ??? Linux seems to want to write to this readonly register.
101 Ignore it. */
102 break;
103 case 2: /* TimerControl */
104 if (s->control & TIMER_CTRL_ENABLE) {
105 /* Pause the timer if it is running. This may cause some
106 inaccuracy dure to rounding, but avoids a whole lot of other
107 messyness. */
423f0742 108 ptimer_stop(s->timer);
cdbdb648
PB
109 }
110 s->control = value;
423f0742 111 freq = s->freq;
cdbdb648
PB
112 /* ??? Need to recalculate expiry time after changing divisor. */
113 switch ((value >> 2) & 3) {
423f0742
PB
114 case 1: freq >>= 4; break;
115 case 2: freq >>= 8; break;
cdbdb648 116 }
423f0742
PB
117 arm_timer_recalibrate(s, 0);
118 ptimer_set_freq(s->timer, freq);
cdbdb648
PB
119 if (s->control & TIMER_CTRL_ENABLE) {
120 /* Restart the timer if still enabled. */
423f0742 121 ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
cdbdb648
PB
122 }
123 break;
124 case 3: /* TimerIntClr */
125 s->int_level = 0;
126 break;
127 case 6: /* TimerBGLoad */
128 s->limit = value;
423f0742 129 arm_timer_recalibrate(s, 0);
cdbdb648
PB
130 break;
131 default:
2ac71179 132 hw_error("arm_timer_write: Bad offset %x\n", (int)offset);
cdbdb648 133 }
423f0742 134 arm_timer_update(s);
cdbdb648
PB
135}
136
137static void arm_timer_tick(void *opaque)
138{
423f0742
PB
139 arm_timer_state *s = (arm_timer_state *)opaque;
140 s->int_level = 1;
141 arm_timer_update(s);
cdbdb648
PB
142}
143
23e39294
PB
144static void arm_timer_save(QEMUFile *f, void *opaque)
145{
146 arm_timer_state *s = (arm_timer_state *)opaque;
147 qemu_put_be32(f, s->control);
148 qemu_put_be32(f, s->limit);
149 qemu_put_be32(f, s->int_level);
150 qemu_put_ptimer(f, s->timer);
151}
152
153static int arm_timer_load(QEMUFile *f, void *opaque, int version_id)
154{
155 arm_timer_state *s = (arm_timer_state *)opaque;
156
157 if (version_id != 1)
158 return -EINVAL;
159
160 s->control = qemu_get_be32(f);
161 s->limit = qemu_get_be32(f);
162 s->int_level = qemu_get_be32(f);
163 qemu_get_ptimer(f, s->timer);
164 return 0;
165}
166
d537cf6c 167static void *arm_timer_init(uint32_t freq, qemu_irq irq)
cdbdb648
PB
168{
169 arm_timer_state *s;
423f0742 170 QEMUBH *bh;
cdbdb648
PB
171
172 s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
cdbdb648 173 s->irq = irq;
423f0742 174 s->freq = freq;
cdbdb648 175 s->control = TIMER_CTRL_IE;
cdbdb648 176
423f0742
PB
177 bh = qemu_bh_new(arm_timer_tick, s);
178 s->timer = ptimer_init(bh);
23e39294 179 register_savevm("arm_timer", -1, 1, arm_timer_save, arm_timer_load, s);
cdbdb648
PB
180 return s;
181}
182
183/* ARM PrimeCell SP804 dual timer module.
184 Docs for this device don't seem to be publicly available. This
d85fb99b 185 implementation is based on guesswork, the linux kernel sources and the
cdbdb648
PB
186 Integrator/CP timer modules. */
187
188typedef struct {
cdbdb648
PB
189 void *timer[2];
190 int level[2];
d537cf6c 191 qemu_irq irq;
cdbdb648
PB
192} sp804_state;
193
d537cf6c 194/* Merge the IRQs from the two component devices. */
cdbdb648
PB
195static void sp804_set_irq(void *opaque, int irq, int level)
196{
197 sp804_state *s = (sp804_state *)opaque;
198
199 s->level[irq] = level;
d537cf6c 200 qemu_set_irq(s->irq, s->level[0] || s->level[1]);
cdbdb648
PB
201}
202
203static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
204{
205 sp804_state *s = (sp804_state *)opaque;
206
207 /* ??? Don't know the PrimeCell ID for this device. */
cdbdb648
PB
208 if (offset < 0x20) {
209 return arm_timer_read(s->timer[0], offset);
210 } else {
211 return arm_timer_read(s->timer[1], offset - 0x20);
212 }
213}
214
215static void sp804_write(void *opaque, target_phys_addr_t offset,
216 uint32_t value)
217{
218 sp804_state *s = (sp804_state *)opaque;
219
cdbdb648
PB
220 if (offset < 0x20) {
221 arm_timer_write(s->timer[0], offset, value);
222 } else {
223 arm_timer_write(s->timer[1], offset - 0x20, value);
224 }
225}
226
227static CPUReadMemoryFunc *sp804_readfn[] = {
228 sp804_read,
229 sp804_read,
230 sp804_read
231};
232
233static CPUWriteMemoryFunc *sp804_writefn[] = {
234 sp804_write,
235 sp804_write,
236 sp804_write
237};
238
23e39294
PB
239static void sp804_save(QEMUFile *f, void *opaque)
240{
241 sp804_state *s = (sp804_state *)opaque;
242 qemu_put_be32(f, s->level[0]);
243 qemu_put_be32(f, s->level[1]);
244}
245
246static int sp804_load(QEMUFile *f, void *opaque, int version_id)
247{
248 sp804_state *s = (sp804_state *)opaque;
249
250 if (version_id != 1)
251 return -EINVAL;
252
253 s->level[0] = qemu_get_be32(f);
254 s->level[1] = qemu_get_be32(f);
255 return 0;
256}
257
d537cf6c 258void sp804_init(uint32_t base, qemu_irq irq)
cdbdb648
PB
259{
260 int iomemtype;
261 sp804_state *s;
d537cf6c 262 qemu_irq *qi;
cdbdb648
PB
263
264 s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
d537cf6c 265 qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
cdbdb648
PB
266 s->irq = irq;
267 /* ??? The timers are actually configurable between 32kHz and 1MHz, but
268 we don't implement that. */
d537cf6c
PB
269 s->timer[0] = arm_timer_init(1000000, qi[0]);
270 s->timer[1] = arm_timer_init(1000000, qi[1]);
cdbdb648
PB
271 iomemtype = cpu_register_io_memory(0, sp804_readfn,
272 sp804_writefn, s);
187337f8 273 cpu_register_physical_memory(base, 0x00001000, iomemtype);
23e39294 274 register_savevm("sp804", -1, 1, sp804_save, sp804_load, s);
cdbdb648
PB
275}
276
277
278/* Integrator/CP timer module. */
279
280typedef struct {
281 void *timer[3];
cdbdb648
PB
282} icp_pit_state;
283
284static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
285{
286 icp_pit_state *s = (icp_pit_state *)opaque;
287 int n;
288
289 /* ??? Don't know the PrimeCell ID for this device. */
cdbdb648 290 n = offset >> 8;
2ac71179
PB
291 if (n > 3) {
292 hw_error("sp804_read: Bad timer %d\n", n);
293 }
cdbdb648
PB
294
295 return arm_timer_read(s->timer[n], offset & 0xff);
296}
297
298static void icp_pit_write(void *opaque, target_phys_addr_t offset,
299 uint32_t value)
300{
301 icp_pit_state *s = (icp_pit_state *)opaque;
302 int n;
303
cdbdb648 304 n = offset >> 8;
2ac71179
PB
305 if (n > 3) {
306 hw_error("sp804_write: Bad timer %d\n", n);
307 }
cdbdb648
PB
308
309 arm_timer_write(s->timer[n], offset & 0xff, value);
310}
311
312
313static CPUReadMemoryFunc *icp_pit_readfn[] = {
314 icp_pit_read,
315 icp_pit_read,
316 icp_pit_read
317};
318
319static CPUWriteMemoryFunc *icp_pit_writefn[] = {
320 icp_pit_write,
321 icp_pit_write,
322 icp_pit_write
323};
324
d537cf6c 325void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
cdbdb648
PB
326{
327 int iomemtype;
328 icp_pit_state *s;
329
330 s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
cdbdb648 331 /* Timer 0 runs at the system clock speed (40MHz). */
d537cf6c 332 s->timer[0] = arm_timer_init(40000000, pic[irq]);
cdbdb648 333 /* The other two timers run at 1MHz. */
d537cf6c
PB
334 s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
335 s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
cdbdb648
PB
336
337 iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
338 icp_pit_writefn, s);
187337f8 339 cpu_register_physical_memory(base, 0x00001000, iomemtype);
23e39294
PB
340 /* This device has no state to save/restore. The component timers will
341 save themselves. */
cdbdb648 342}