blob: 5150fe9a5a8ad6f7d197515322114a02796cc976 [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];
193 uint32_t base;
pbrookd537cf62007-04-07 18:14:41 +0000194 qemu_irq irq;
pbrookcdbdb642006-04-09 01:32:52 +0000195} sp804_state;
196
pbrookd537cf62007-04-07 18:14:41 +0000197/* Merge the IRQs from the two component devices. */
pbrookcdbdb642006-04-09 01:32:52 +0000198static void sp804_set_irq(void *opaque, int irq, int level)
199{
200 sp804_state *s = (sp804_state *)opaque;
201
202 s->level[irq] = level;
pbrookd537cf62007-04-07 18:14:41 +0000203 qemu_set_irq(s->irq, s->level[0] || s->level[1]);
pbrookcdbdb642006-04-09 01:32:52 +0000204}
205
206static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
207{
208 sp804_state *s = (sp804_state *)opaque;
209
210 /* ??? Don't know the PrimeCell ID for this device. */
211 offset -= s->base;
212 if (offset < 0x20) {
213 return arm_timer_read(s->timer[0], offset);
214 } else {
215 return arm_timer_read(s->timer[1], offset - 0x20);
216 }
217}
218
219static void sp804_write(void *opaque, target_phys_addr_t offset,
220 uint32_t value)
221{
222 sp804_state *s = (sp804_state *)opaque;
223
224 offset -= s->base;
225 if (offset < 0x20) {
226 arm_timer_write(s->timer[0], offset, value);
227 } else {
228 arm_timer_write(s->timer[1], offset - 0x20, value);
229 }
230}
231
232static CPUReadMemoryFunc *sp804_readfn[] = {
233 sp804_read,
234 sp804_read,
235 sp804_read
236};
237
238static CPUWriteMemoryFunc *sp804_writefn[] = {
239 sp804_write,
240 sp804_write,
241 sp804_write
242};
243
pbrook23e39292008-07-02 16:48:32 +0000244static void sp804_save(QEMUFile *f, void *opaque)
245{
246 sp804_state *s = (sp804_state *)opaque;
247 qemu_put_be32(f, s->level[0]);
248 qemu_put_be32(f, s->level[1]);
249}
250
251static int sp804_load(QEMUFile *f, void *opaque, int version_id)
252{
253 sp804_state *s = (sp804_state *)opaque;
254
255 if (version_id != 1)
256 return -EINVAL;
257
258 s->level[0] = qemu_get_be32(f);
259 s->level[1] = qemu_get_be32(f);
260 return 0;
261}
262
pbrookd537cf62007-04-07 18:14:41 +0000263void sp804_init(uint32_t base, qemu_irq irq)
pbrookcdbdb642006-04-09 01:32:52 +0000264{
265 int iomemtype;
266 sp804_state *s;
pbrookd537cf62007-04-07 18:14:41 +0000267 qemu_irq *qi;
pbrookcdbdb642006-04-09 01:32:52 +0000268
269 s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
pbrookd537cf62007-04-07 18:14:41 +0000270 qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
pbrookcdbdb642006-04-09 01:32:52 +0000271 s->base = base;
pbrookcdbdb642006-04-09 01:32:52 +0000272 s->irq = irq;
273 /* ??? The timers are actually configurable between 32kHz and 1MHz, but
274 we don't implement that. */
pbrookd537cf62007-04-07 18:14:41 +0000275 s->timer[0] = arm_timer_init(1000000, qi[0]);
276 s->timer[1] = arm_timer_init(1000000, qi[1]);
pbrookcdbdb642006-04-09 01:32:52 +0000277 iomemtype = cpu_register_io_memory(0, sp804_readfn,
278 sp804_writefn, s);
pbrook187337f2007-06-03 15:19:33 +0000279 cpu_register_physical_memory(base, 0x00001000, iomemtype);
pbrook23e39292008-07-02 16:48:32 +0000280 register_savevm("sp804", -1, 1, sp804_save, sp804_load, s);
pbrookcdbdb642006-04-09 01:32:52 +0000281}
282
283
284/* Integrator/CP timer module. */
285
286typedef struct {
287 void *timer[3];
288 uint32_t base;
289} icp_pit_state;
290
291static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
292{
293 icp_pit_state *s = (icp_pit_state *)opaque;
294 int n;
295
296 /* ??? Don't know the PrimeCell ID for this device. */
297 offset -= s->base;
298 n = offset >> 8;
299 if (n > 3)
300 cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
301
302 return arm_timer_read(s->timer[n], offset & 0xff);
303}
304
305static void icp_pit_write(void *opaque, target_phys_addr_t offset,
306 uint32_t value)
307{
308 icp_pit_state *s = (icp_pit_state *)opaque;
309 int n;
310
311 offset -= s->base;
312 n = offset >> 8;
313 if (n > 3)
314 cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
315
316 arm_timer_write(s->timer[n], offset & 0xff, value);
317}
318
319
320static CPUReadMemoryFunc *icp_pit_readfn[] = {
321 icp_pit_read,
322 icp_pit_read,
323 icp_pit_read
324};
325
326static CPUWriteMemoryFunc *icp_pit_writefn[] = {
327 icp_pit_write,
328 icp_pit_write,
329 icp_pit_write
330};
331
pbrookd537cf62007-04-07 18:14:41 +0000332void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
pbrookcdbdb642006-04-09 01:32:52 +0000333{
334 int iomemtype;
335 icp_pit_state *s;
336
337 s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
338 s->base = base;
339 /* Timer 0 runs at the system clock speed (40MHz). */
pbrookd537cf62007-04-07 18:14:41 +0000340 s->timer[0] = arm_timer_init(40000000, pic[irq]);
pbrookcdbdb642006-04-09 01:32:52 +0000341 /* The other two timers run at 1MHz. */
pbrookd537cf62007-04-07 18:14:41 +0000342 s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
343 s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
pbrookcdbdb642006-04-09 01:32:52 +0000344
345 iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
346 icp_pit_writefn, s);
pbrook187337f2007-06-03 15:19:33 +0000347 cpu_register_physical_memory(base, 0x00001000, iomemtype);
pbrook23e39292008-07-02 16:48:32 +0000348 /* This device has no state to save/restore. The component timers will
349 save themselves. */
pbrookcdbdb642006-04-09 01:32:52 +0000350}
351