blob: c93f333d7a3239a6b74f1a01e60eb27ec595f12d [file] [log] [blame]
ths5fafdf22007-09-16 21:08:06 +00001/*
pbrookcdbdb642006-04-09 01:32:52 +00002 * 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
pbrook87ecb682007-11-17 17:14:51 +000010#include "hw.h"
pbrook87ecb682007-11-17 17:14:51 +000011#include "qemu-timer.h"
pbrook9596ebb2007-11-18 01:44:38 +000012#include "primecell.h"
pbrookcdbdb642006-04-09 01:32:52 +000013
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 {
pbrook423f0742007-05-23 00:06:54 +000026 ptimer_state *timer;
pbrookcdbdb642006-04-09 01:32:52 +000027 uint32_t control;
pbrookcdbdb642006-04-09 01:32:52 +000028 uint32_t limit;
pbrookcdbdb642006-04-09 01:32:52 +000029 int freq;
30 int int_level;
pbrookd537cf62007-04-07 18:14:41 +000031 qemu_irq irq;
pbrookcdbdb642006-04-09 01:32:52 +000032} arm_timer_state;
33
pbrookcdbdb642006-04-09 01:32:52 +000034/* Check all active timers, and schedule the next timer interrupt. */
35
pbrook423f0742007-05-23 00:06:54 +000036static void arm_timer_update(arm_timer_state *s)
pbrookcdbdb642006-04-09 01:32:52 +000037{
pbrookcdbdb642006-04-09 01:32:52 +000038 /* Update interrupts. */
39 if (s->int_level && (s->control & TIMER_CTRL_IE)) {
pbrookd537cf62007-04-07 18:14:41 +000040 qemu_irq_raise(s->irq);
pbrookcdbdb642006-04-09 01:32:52 +000041 } else {
pbrookd537cf62007-04-07 18:14:41 +000042 qemu_irq_lower(s->irq);
pbrookcdbdb642006-04-09 01:32:52 +000043 }
pbrookcdbdb642006-04-09 01:32:52 +000044}
45
pbrook9596ebb2007-11-18 01:44:38 +000046static uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
pbrookcdbdb642006-04-09 01:32:52 +000047{
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 */
pbrook423f0742007-05-23 00:06:54 +000055 return ptimer_get_count(s->timer);
pbrookcdbdb642006-04-09 01:32:52 +000056 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:
pbrook423f0742007-05-23 00:06:54 +000065 cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n",
66 (int)offset);
pbrookcdbdb642006-04-09 01:32:52 +000067 return 0;
68 }
69}
70
pbrook423f0742007-05-23 00:06:54 +000071/* Reset the timer limit after settings have changed. */
72static void arm_timer_recalibrate(arm_timer_state *s, int reload)
73{
74 uint32_t limit;
75
76 if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
77 /* Free running. */
78 if (s->control & TIMER_CTRL_32BIT)
79 limit = 0xffffffff;
80 else
81 limit = 0xffff;
82 } else {
83 /* Periodic. */
84 limit = s->limit;
85 }
86 ptimer_set_limit(s->timer, limit, reload);
87}
88
pbrookcdbdb642006-04-09 01:32:52 +000089static void arm_timer_write(void *opaque, target_phys_addr_t offset,
90 uint32_t value)
91{
92 arm_timer_state *s = (arm_timer_state *)opaque;
pbrook423f0742007-05-23 00:06:54 +000093 int freq;
pbrookcdbdb642006-04-09 01:32:52 +000094
pbrookcdbdb642006-04-09 01:32:52 +000095 switch (offset >> 2) {
96 case 0: /* TimerLoad */
97 s->limit = value;
pbrook423f0742007-05-23 00:06:54 +000098 arm_timer_recalibrate(s, 1);
pbrookcdbdb642006-04-09 01:32:52 +000099 break;
100 case 1: /* TimerValue */
101 /* ??? Linux seems to want to write to this readonly register.
102 Ignore it. */
103 break;
104 case 2: /* TimerControl */
105 if (s->control & TIMER_CTRL_ENABLE) {
106 /* Pause the timer if it is running. This may cause some
107 inaccuracy dure to rounding, but avoids a whole lot of other
108 messyness. */
pbrook423f0742007-05-23 00:06:54 +0000109 ptimer_stop(s->timer);
pbrookcdbdb642006-04-09 01:32:52 +0000110 }
111 s->control = value;
pbrook423f0742007-05-23 00:06:54 +0000112 freq = s->freq;
pbrookcdbdb642006-04-09 01:32:52 +0000113 /* ??? Need to recalculate expiry time after changing divisor. */
114 switch ((value >> 2) & 3) {
pbrook423f0742007-05-23 00:06:54 +0000115 case 1: freq >>= 4; break;
116 case 2: freq >>= 8; break;
pbrookcdbdb642006-04-09 01:32:52 +0000117 }
pbrook423f0742007-05-23 00:06:54 +0000118 arm_timer_recalibrate(s, 0);
119 ptimer_set_freq(s->timer, freq);
pbrookcdbdb642006-04-09 01:32:52 +0000120 if (s->control & TIMER_CTRL_ENABLE) {
121 /* Restart the timer if still enabled. */
pbrook423f0742007-05-23 00:06:54 +0000122 ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
pbrookcdbdb642006-04-09 01:32:52 +0000123 }
124 break;
125 case 3: /* TimerIntClr */
126 s->int_level = 0;
127 break;
128 case 6: /* TimerBGLoad */
129 s->limit = value;
pbrook423f0742007-05-23 00:06:54 +0000130 arm_timer_recalibrate(s, 0);
pbrookcdbdb642006-04-09 01:32:52 +0000131 break;
132 default:
pbrook423f0742007-05-23 00:06:54 +0000133 cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n",
134 (int)offset);
pbrookcdbdb642006-04-09 01:32:52 +0000135 }
pbrook423f0742007-05-23 00:06:54 +0000136 arm_timer_update(s);
pbrookcdbdb642006-04-09 01:32:52 +0000137}
138
139static void arm_timer_tick(void *opaque)
140{
pbrook423f0742007-05-23 00:06:54 +0000141 arm_timer_state *s = (arm_timer_state *)opaque;
142 s->int_level = 1;
143 arm_timer_update(s);
pbrookcdbdb642006-04-09 01:32:52 +0000144}
145
pbrook23e39292008-07-02 16:48:32 +0000146static void arm_timer_save(QEMUFile *f, void *opaque)
147{
148 arm_timer_state *s = (arm_timer_state *)opaque;
149 qemu_put_be32(f, s->control);
150 qemu_put_be32(f, s->limit);
151 qemu_put_be32(f, s->int_level);
152 qemu_put_ptimer(f, s->timer);
153}
154
155static int arm_timer_load(QEMUFile *f, void *opaque, int version_id)
156{
157 arm_timer_state *s = (arm_timer_state *)opaque;
158
159 if (version_id != 1)
160 return -EINVAL;
161
162 s->control = qemu_get_be32(f);
163 s->limit = qemu_get_be32(f);
164 s->int_level = qemu_get_be32(f);
165 qemu_get_ptimer(f, s->timer);
166 return 0;
167}
168
pbrookd537cf62007-04-07 18:14:41 +0000169static void *arm_timer_init(uint32_t freq, qemu_irq irq)
pbrookcdbdb642006-04-09 01:32:52 +0000170{
171 arm_timer_state *s;
pbrook423f0742007-05-23 00:06:54 +0000172 QEMUBH *bh;
pbrookcdbdb642006-04-09 01:32:52 +0000173
174 s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
pbrookcdbdb642006-04-09 01:32:52 +0000175 s->irq = irq;
pbrook423f0742007-05-23 00:06:54 +0000176 s->freq = freq;
pbrookcdbdb642006-04-09 01:32:52 +0000177 s->control = TIMER_CTRL_IE;
pbrookcdbdb642006-04-09 01:32:52 +0000178
pbrook423f0742007-05-23 00:06:54 +0000179 bh = qemu_bh_new(arm_timer_tick, s);
180 s->timer = ptimer_init(bh);
pbrook23e39292008-07-02 16:48:32 +0000181 register_savevm("arm_timer", -1, 1, arm_timer_save, arm_timer_load, s);
pbrookcdbdb642006-04-09 01:32:52 +0000182 return s;
183}
184
185/* ARM PrimeCell SP804 dual timer module.
186 Docs for this device don't seem to be publicly available. This
pbrookd85fb992007-04-06 20:58:25 +0000187 implementation is based on guesswork, the linux kernel sources and the
pbrookcdbdb642006-04-09 01:32:52 +0000188 Integrator/CP timer modules. */
189
190typedef struct {
pbrookcdbdb642006-04-09 01:32:52 +0000191 void *timer[2];
192 int level[2];
pbrookd537cf62007-04-07 18:14:41 +0000193 qemu_irq irq;
pbrookcdbdb642006-04-09 01:32:52 +0000194} sp804_state;
195
pbrookd537cf62007-04-07 18:14:41 +0000196/* Merge the IRQs from the two component devices. */
pbrookcdbdb642006-04-09 01:32:52 +0000197static void sp804_set_irq(void *opaque, int irq, int level)
198{
199 sp804_state *s = (sp804_state *)opaque;
200
201 s->level[irq] = level;
pbrookd537cf62007-04-07 18:14:41 +0000202 qemu_set_irq(s->irq, s->level[0] || s->level[1]);
pbrookcdbdb642006-04-09 01:32:52 +0000203}
204
205static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
206{
207 sp804_state *s = (sp804_state *)opaque;
208
209 /* ??? Don't know the PrimeCell ID for this device. */
pbrookcdbdb642006-04-09 01:32:52 +0000210 if (offset < 0x20) {
211 return arm_timer_read(s->timer[0], offset);
212 } else {
213 return arm_timer_read(s->timer[1], offset - 0x20);
214 }
215}
216
217static void sp804_write(void *opaque, target_phys_addr_t offset,
218 uint32_t value)
219{
220 sp804_state *s = (sp804_state *)opaque;
221
pbrookcdbdb642006-04-09 01:32:52 +0000222 if (offset < 0x20) {
223 arm_timer_write(s->timer[0], offset, value);
224 } else {
225 arm_timer_write(s->timer[1], offset - 0x20, value);
226 }
227}
228
229static CPUReadMemoryFunc *sp804_readfn[] = {
230 sp804_read,
231 sp804_read,
232 sp804_read
233};
234
235static CPUWriteMemoryFunc *sp804_writefn[] = {
236 sp804_write,
237 sp804_write,
238 sp804_write
239};
240
pbrook23e39292008-07-02 16:48:32 +0000241static void sp804_save(QEMUFile *f, void *opaque)
242{
243 sp804_state *s = (sp804_state *)opaque;
244 qemu_put_be32(f, s->level[0]);
245 qemu_put_be32(f, s->level[1]);
246}
247
248static int sp804_load(QEMUFile *f, void *opaque, int version_id)
249{
250 sp804_state *s = (sp804_state *)opaque;
251
252 if (version_id != 1)
253 return -EINVAL;
254
255 s->level[0] = qemu_get_be32(f);
256 s->level[1] = qemu_get_be32(f);
257 return 0;
258}
259
pbrookd537cf62007-04-07 18:14:41 +0000260void sp804_init(uint32_t base, qemu_irq irq)
pbrookcdbdb642006-04-09 01:32:52 +0000261{
262 int iomemtype;
263 sp804_state *s;
pbrookd537cf62007-04-07 18:14:41 +0000264 qemu_irq *qi;
pbrookcdbdb642006-04-09 01:32:52 +0000265
266 s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
pbrookd537cf62007-04-07 18:14:41 +0000267 qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
pbrookcdbdb642006-04-09 01:32:52 +0000268 s->irq = irq;
269 /* ??? The timers are actually configurable between 32kHz and 1MHz, but
270 we don't implement that. */
pbrookd537cf62007-04-07 18:14:41 +0000271 s->timer[0] = arm_timer_init(1000000, qi[0]);
272 s->timer[1] = arm_timer_init(1000000, qi[1]);
pbrookcdbdb642006-04-09 01:32:52 +0000273 iomemtype = cpu_register_io_memory(0, sp804_readfn,
274 sp804_writefn, s);
pbrook187337f2007-06-03 15:19:33 +0000275 cpu_register_physical_memory(base, 0x00001000, iomemtype);
pbrook23e39292008-07-02 16:48:32 +0000276 register_savevm("sp804", -1, 1, sp804_save, sp804_load, s);
pbrookcdbdb642006-04-09 01:32:52 +0000277}
278
279
280/* Integrator/CP timer module. */
281
282typedef struct {
283 void *timer[3];
pbrookcdbdb642006-04-09 01:32:52 +0000284} icp_pit_state;
285
286static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
287{
288 icp_pit_state *s = (icp_pit_state *)opaque;
289 int n;
290
291 /* ??? Don't know the PrimeCell ID for this device. */
pbrookcdbdb642006-04-09 01:32:52 +0000292 n = offset >> 8;
293 if (n > 3)
294 cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
295
296 return arm_timer_read(s->timer[n], offset & 0xff);
297}
298
299static void icp_pit_write(void *opaque, target_phys_addr_t offset,
300 uint32_t value)
301{
302 icp_pit_state *s = (icp_pit_state *)opaque;
303 int n;
304
pbrookcdbdb642006-04-09 01:32:52 +0000305 n = offset >> 8;
306 if (n > 3)
307 cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
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
pbrookd537cf62007-04-07 18:14:41 +0000325void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
pbrookcdbdb642006-04-09 01:32:52 +0000326{
327 int iomemtype;
328 icp_pit_state *s;
329
330 s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
pbrookcdbdb642006-04-09 01:32:52 +0000331 /* Timer 0 runs at the system clock speed (40MHz). */
pbrookd537cf62007-04-07 18:14:41 +0000332 s->timer[0] = arm_timer_init(40000000, pic[irq]);
pbrookcdbdb642006-04-09 01:32:52 +0000333 /* The other two timers run at 1MHz. */
pbrookd537cf62007-04-07 18:14:41 +0000334 s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
335 s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
pbrookcdbdb642006-04-09 01:32:52 +0000336
337 iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
338 icp_pit_writefn, s);
pbrook187337f2007-06-03 15:19:33 +0000339 cpu_register_physical_memory(base, 0x00001000, iomemtype);
pbrook23e39292008-07-02 16:48:32 +0000340 /* This device has no state to save/restore. The component timers will
341 save themselves. */
pbrookcdbdb642006-04-09 01:32:52 +0000342}