Statistics
| Branch: | Revision:

root / hw / arm_timer.c @ 9596ebb7

History | View | Annotate | Download (8 kB)

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 "hw.h"
11
#include "qemu-timer.h"
12
#include "primecell.h"
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

    
25
typedef struct {
26
    ptimer_state *timer;
27
    uint32_t control;
28
    uint32_t limit;
29
    int freq;
30
    int int_level;
31
    qemu_irq irq;
32
} arm_timer_state;
33

    
34
/* Check all active timers, and schedule the next timer interrupt.  */
35

    
36
static void arm_timer_update(arm_timer_state *s)
37
{
38
    /* Update interrupts.  */
39
    if (s->int_level && (s->control & TIMER_CTRL_IE)) {
40
        qemu_irq_raise(s->irq);
41
    } else {
42
        qemu_irq_lower(s->irq);
43
    }
44
}
45

    
46
static uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
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 */
55
        return ptimer_get_count(s->timer);
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:
65
        cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n",
66
                   (int)offset);
67
        return 0;
68
    }
69
}
70

    
71
/* Reset the timer limit after settings have changed.  */
72
static 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

    
89
static 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;
93
    int freq;
94

    
95
    switch (offset >> 2) {
96
    case 0: /* TimerLoad */
97
        s->limit = value;
98
        arm_timer_recalibrate(s, 1);
99
        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.  */
109
            ptimer_stop(s->timer);
110
        }
111
        s->control = value;
112
        freq = s->freq;
113
        /* ??? Need to recalculate expiry time after changing divisor.  */
114
        switch ((value >> 2) & 3) {
115
        case 1: freq >>= 4; break;
116
        case 2: freq >>= 8; break;
117
        }
118
        arm_timer_recalibrate(s, 0);
119
        ptimer_set_freq(s->timer, freq);
120
        if (s->control & TIMER_CTRL_ENABLE) {
121
            /* Restart the timer if still enabled.  */
122
            ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
123
        }
124
        break;
125
    case 3: /* TimerIntClr */
126
        s->int_level = 0;
127
        break;
128
    case 6: /* TimerBGLoad */
129
        s->limit = value;
130
        arm_timer_recalibrate(s, 0);
131
        break;
132
    default:
133
        cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n",
134
                   (int)offset);
135
    }
136
    arm_timer_update(s);
137
}
138

    
139
static void arm_timer_tick(void *opaque)
140
{
141
    arm_timer_state *s = (arm_timer_state *)opaque;
142
    s->int_level = 1;
143
    arm_timer_update(s);
144
}
145

    
146
static void *arm_timer_init(uint32_t freq, qemu_irq irq)
147
{
148
    arm_timer_state *s;
149
    QEMUBH *bh;
150

    
151
    s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
152
    s->irq = irq;
153
    s->freq = freq;
154
    s->control = TIMER_CTRL_IE;
155

    
156
    bh = qemu_bh_new(arm_timer_tick, s);
157
    s->timer = ptimer_init(bh);
158
    /* ??? Save/restore.  */
159
    return s;
160
}
161

    
162
/* ARM PrimeCell SP804 dual timer module.
163
   Docs for this device don't seem to be publicly available.  This
164
   implementation is based on guesswork, the linux kernel sources and the
165
   Integrator/CP timer modules.  */
166

    
167
typedef struct {
168
    void *timer[2];
169
    int level[2];
170
    uint32_t base;
171
    qemu_irq irq;
172
} sp804_state;
173

    
174
/* Merge the IRQs from the two component devices.  */
175
static void sp804_set_irq(void *opaque, int irq, int level)
176
{
177
    sp804_state *s = (sp804_state *)opaque;
178

    
179
    s->level[irq] = level;
180
    qemu_set_irq(s->irq, s->level[0] || s->level[1]);
181
}
182

    
183
static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
184
{
185
    sp804_state *s = (sp804_state *)opaque;
186

    
187
    /* ??? Don't know the PrimeCell ID for this device.  */
188
    offset -= s->base;
189
    if (offset < 0x20) {
190
        return arm_timer_read(s->timer[0], offset);
191
    } else {
192
        return arm_timer_read(s->timer[1], offset - 0x20);
193
    }
194
}
195

    
196
static void sp804_write(void *opaque, target_phys_addr_t offset,
197
                        uint32_t value)
198
{
199
    sp804_state *s = (sp804_state *)opaque;
200

    
201
    offset -= s->base;
202
    if (offset < 0x20) {
203
        arm_timer_write(s->timer[0], offset, value);
204
    } else {
205
        arm_timer_write(s->timer[1], offset - 0x20, value);
206
    }
207
}
208

    
209
static CPUReadMemoryFunc *sp804_readfn[] = {
210
   sp804_read,
211
   sp804_read,
212
   sp804_read
213
};
214

    
215
static CPUWriteMemoryFunc *sp804_writefn[] = {
216
   sp804_write,
217
   sp804_write,
218
   sp804_write
219
};
220

    
221
void sp804_init(uint32_t base, qemu_irq irq)
222
{
223
    int iomemtype;
224
    sp804_state *s;
225
    qemu_irq *qi;
226

    
227
    s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
228
    qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
229
    s->base = base;
230
    s->irq = irq;
231
    /* ??? The timers are actually configurable between 32kHz and 1MHz, but
232
       we don't implement that.  */
233
    s->timer[0] = arm_timer_init(1000000, qi[0]);
234
    s->timer[1] = arm_timer_init(1000000, qi[1]);
235
    iomemtype = cpu_register_io_memory(0, sp804_readfn,
236
                                       sp804_writefn, s);
237
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
238
    /* ??? Save/restore.  */
239
}
240

    
241

    
242
/* Integrator/CP timer module.  */
243

    
244
typedef struct {
245
    void *timer[3];
246
    uint32_t base;
247
} icp_pit_state;
248

    
249
static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
250
{
251
    icp_pit_state *s = (icp_pit_state *)opaque;
252
    int n;
253

    
254
    /* ??? Don't know the PrimeCell ID for this device.  */
255
    offset -= s->base;
256
    n = offset >> 8;
257
    if (n > 3)
258
        cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
259

    
260
    return arm_timer_read(s->timer[n], offset & 0xff);
261
}
262

    
263
static void icp_pit_write(void *opaque, target_phys_addr_t offset,
264
                          uint32_t value)
265
{
266
    icp_pit_state *s = (icp_pit_state *)opaque;
267
    int n;
268

    
269
    offset -= s->base;
270
    n = offset >> 8;
271
    if (n > 3)
272
        cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
273

    
274
    arm_timer_write(s->timer[n], offset & 0xff, value);
275
}
276

    
277

    
278
static CPUReadMemoryFunc *icp_pit_readfn[] = {
279
   icp_pit_read,
280
   icp_pit_read,
281
   icp_pit_read
282
};
283

    
284
static CPUWriteMemoryFunc *icp_pit_writefn[] = {
285
   icp_pit_write,
286
   icp_pit_write,
287
   icp_pit_write
288
};
289

    
290
void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
291
{
292
    int iomemtype;
293
    icp_pit_state *s;
294

    
295
    s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
296
    s->base = base;
297
    /* Timer 0 runs at the system clock speed (40MHz).  */
298
    s->timer[0] = arm_timer_init(40000000, pic[irq]);
299
    /* The other two timers run at 1MHz.  */
300
    s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
301
    s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
302

    
303
    iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
304
                                       icp_pit_writefn, s);
305
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
306
    /* ??? Save/restore.  */
307
}
308