]> git.proxmox.com Git - mirror_qemu.git/blame - hw/arm_timer.c
Add a local copy of hpet.h.
[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 {
423f0742 25 ptimer_state *timer;
cdbdb648 26 uint32_t control;
cdbdb648 27 uint32_t limit;
cdbdb648
PB
28 int freq;
29 int int_level;
d537cf6c 30 qemu_irq irq;
cdbdb648
PB
31} arm_timer_state;
32
cdbdb648
PB
33/* Check all active timers, and schedule the next timer interrupt. */
34
423f0742 35static void arm_timer_update(arm_timer_state *s)
cdbdb648 36{
cdbdb648
PB
37 /* Update interrupts. */
38 if (s->int_level && (s->control & TIMER_CTRL_IE)) {
d537cf6c 39 qemu_irq_raise(s->irq);
cdbdb648 40 } else {
d537cf6c 41 qemu_irq_lower(s->irq);
cdbdb648 42 }
cdbdb648
PB
43}
44
45uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
46{
47 arm_timer_state *s = (arm_timer_state *)opaque;
48
49 switch (offset >> 2) {
50 case 0: /* TimerLoad */
51 case 6: /* TimerBGLoad */
52 return s->limit;
53 case 1: /* TimerValue */
423f0742 54 return ptimer_get_count(s->timer);
cdbdb648
PB
55 case 2: /* TimerControl */
56 return s->control;
57 case 4: /* TimerRIS */
58 return s->int_level;
59 case 5: /* TimerMIS */
60 if ((s->control & TIMER_CTRL_IE) == 0)
61 return 0;
62 return s->int_level;
63 default:
423f0742
PB
64 cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n",
65 (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:
423f0742
PB
132 cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n",
133 (int)offset);
cdbdb648 134 }
423f0742 135 arm_timer_update(s);
cdbdb648
PB
136}
137
138static void arm_timer_tick(void *opaque)
139{
423f0742
PB
140 arm_timer_state *s = (arm_timer_state *)opaque;
141 s->int_level = 1;
142 arm_timer_update(s);
cdbdb648
PB
143}
144
d537cf6c 145static void *arm_timer_init(uint32_t freq, qemu_irq irq)
cdbdb648
PB
146{
147 arm_timer_state *s;
423f0742 148 QEMUBH *bh;
cdbdb648
PB
149
150 s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
cdbdb648 151 s->irq = irq;
423f0742 152 s->freq = freq;
cdbdb648 153 s->control = TIMER_CTRL_IE;
cdbdb648 154
423f0742
PB
155 bh = qemu_bh_new(arm_timer_tick, s);
156 s->timer = ptimer_init(bh);
cdbdb648
PB
157 /* ??? Save/restore. */
158 return s;
159}
160
161/* ARM PrimeCell SP804 dual timer module.
162 Docs for this device don't seem to be publicly available. This
d85fb99b 163 implementation is based on guesswork, the linux kernel sources and the
cdbdb648
PB
164 Integrator/CP timer modules. */
165
166typedef struct {
cdbdb648
PB
167 void *timer[2];
168 int level[2];
169 uint32_t base;
d537cf6c 170 qemu_irq irq;
cdbdb648
PB
171} sp804_state;
172
d537cf6c 173/* Merge the IRQs from the two component devices. */
cdbdb648
PB
174static void sp804_set_irq(void *opaque, int irq, int level)
175{
176 sp804_state *s = (sp804_state *)opaque;
177
178 s->level[irq] = level;
d537cf6c 179 qemu_set_irq(s->irq, s->level[0] || s->level[1]);
cdbdb648
PB
180}
181
182static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
183{
184 sp804_state *s = (sp804_state *)opaque;
185
186 /* ??? Don't know the PrimeCell ID for this device. */
187 offset -= s->base;
188 if (offset < 0x20) {
189 return arm_timer_read(s->timer[0], offset);
190 } else {
191 return arm_timer_read(s->timer[1], offset - 0x20);
192 }
193}
194
195static void sp804_write(void *opaque, target_phys_addr_t offset,
196 uint32_t value)
197{
198 sp804_state *s = (sp804_state *)opaque;
199
200 offset -= s->base;
201 if (offset < 0x20) {
202 arm_timer_write(s->timer[0], offset, value);
203 } else {
204 arm_timer_write(s->timer[1], offset - 0x20, value);
205 }
206}
207
208static CPUReadMemoryFunc *sp804_readfn[] = {
209 sp804_read,
210 sp804_read,
211 sp804_read
212};
213
214static CPUWriteMemoryFunc *sp804_writefn[] = {
215 sp804_write,
216 sp804_write,
217 sp804_write
218};
219
d537cf6c 220void sp804_init(uint32_t base, qemu_irq irq)
cdbdb648
PB
221{
222 int iomemtype;
223 sp804_state *s;
d537cf6c 224 qemu_irq *qi;
cdbdb648
PB
225
226 s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
d537cf6c 227 qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
cdbdb648 228 s->base = base;
cdbdb648
PB
229 s->irq = irq;
230 /* ??? The timers are actually configurable between 32kHz and 1MHz, but
231 we don't implement that. */
d537cf6c
PB
232 s->timer[0] = arm_timer_init(1000000, qi[0]);
233 s->timer[1] = arm_timer_init(1000000, qi[1]);
cdbdb648
PB
234 iomemtype = cpu_register_io_memory(0, sp804_readfn,
235 sp804_writefn, s);
187337f8 236 cpu_register_physical_memory(base, 0x00001000, iomemtype);
cdbdb648
PB
237 /* ??? Save/restore. */
238}
239
240
241/* Integrator/CP timer module. */
242
243typedef struct {
244 void *timer[3];
245 uint32_t base;
246} icp_pit_state;
247
248static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
249{
250 icp_pit_state *s = (icp_pit_state *)opaque;
251 int n;
252
253 /* ??? Don't know the PrimeCell ID for this device. */
254 offset -= s->base;
255 n = offset >> 8;
256 if (n > 3)
257 cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
258
259 return arm_timer_read(s->timer[n], offset & 0xff);
260}
261
262static void icp_pit_write(void *opaque, target_phys_addr_t offset,
263 uint32_t value)
264{
265 icp_pit_state *s = (icp_pit_state *)opaque;
266 int n;
267
268 offset -= s->base;
269 n = offset >> 8;
270 if (n > 3)
271 cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
272
273 arm_timer_write(s->timer[n], offset & 0xff, value);
274}
275
276
277static CPUReadMemoryFunc *icp_pit_readfn[] = {
278 icp_pit_read,
279 icp_pit_read,
280 icp_pit_read
281};
282
283static CPUWriteMemoryFunc *icp_pit_writefn[] = {
284 icp_pit_write,
285 icp_pit_write,
286 icp_pit_write
287};
288
d537cf6c 289void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
cdbdb648
PB
290{
291 int iomemtype;
292 icp_pit_state *s;
293
294 s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
295 s->base = base;
296 /* Timer 0 runs at the system clock speed (40MHz). */
d537cf6c 297 s->timer[0] = arm_timer_init(40000000, pic[irq]);
cdbdb648 298 /* The other two timers run at 1MHz. */
d537cf6c
PB
299 s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
300 s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
cdbdb648
PB
301
302 iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
303 icp_pit_writefn, s);
187337f8 304 cpu_register_physical_memory(base, 0x00001000, iomemtype);
cdbdb648
PB
305 /* ??? Save/restore. */
306}
307