Statistics
| Branch: | Revision:

root / hw / arm_timer.c @ 5fafdf24

History | View | Annotate | Download (7.9 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 "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

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

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

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

    
45
uint32_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 */
54
        return ptimer_get_count(s->timer);
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:
64
        cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n",
65
                   (int)offset);
66
        return 0;
67
    }
68
}
69

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

    
88
static 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;
92
    int freq;
93

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

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

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

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

    
155
    bh = qemu_bh_new(arm_timer_tick, s);
156
    s->timer = ptimer_init(bh);
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
163
   implementation is based on guesswork, the linux kernel sources and the
164
   Integrator/CP timer modules.  */
165

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

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

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

    
182
static 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

    
195
static 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

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

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

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

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

    
240

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

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

    
248
static 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

    
262
static 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

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

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

    
289
void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
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).  */
297
    s->timer[0] = arm_timer_init(40000000, pic[irq]);
298
    /* The other two timers run at 1MHz.  */
299
    s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
300
    s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
301

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