Statistics
| Branch: | Revision:

root / hw / mpcore.c @ f7c70325

History | View | Annotate | Download (7.3 kB)

1
/*
2
 * ARM MPCore internal peripheral emulation (common code).
3
 *
4
 * Copyright (c) 2006-2007 CodeSourcery.
5
 * Written by Paul Brook
6
 *
7
 * This code is licenced under the GPL.
8
 */
9

    
10
#include "sysbus.h"
11
#include "qemu-timer.h"
12

    
13
#define NCPU 4
14

    
15
static inline int
16
gic_get_current_cpu(void)
17
{
18
  return cpu_single_env->cpu_index;
19
}
20

    
21
#include "arm_gic.c"
22

    
23
/* MPCore private memory region.  */
24

    
25
typedef struct {
26
    uint32_t count;
27
    uint32_t load;
28
    uint32_t control;
29
    uint32_t status;
30
    uint32_t old_status;
31
    int64_t tick;
32
    QEMUTimer *timer;
33
    struct mpcore_priv_state *mpcore;
34
    int id; /* Encodes both timer/watchdog and CPU.  */
35
} mpcore_timer_state;
36

    
37
typedef struct mpcore_priv_state {
38
    gic_state gic;
39
    uint32_t scu_control;
40
    int iomemtype;
41
    mpcore_timer_state timer[8];
42
    uint32_t num_cpu;
43
} mpcore_priv_state;
44

    
45
/* Per-CPU Timers.  */
46

    
47
static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
48
{
49
    if (s->status & ~s->old_status) {
50
        gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
51
    }
52
    s->old_status = s->status;
53
}
54

    
55
/* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
56
static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
57
{
58
    return (((s->control >> 8) & 0xff) + 1) * 10;
59
}
60

    
61
static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
62
{
63
    if (s->count == 0)
64
        return;
65
    if (restart)
66
        s->tick = qemu_get_clock(vm_clock);
67
    s->tick += (int64_t)s->count * mpcore_timer_scale(s);
68
    qemu_mod_timer(s->timer, s->tick);
69
}
70

    
71
static void mpcore_timer_tick(void *opaque)
72
{
73
    mpcore_timer_state *s = (mpcore_timer_state *)opaque;
74
    s->status = 1;
75
    if (s->control & 2) {
76
        s->count = s->load;
77
        mpcore_timer_reload(s, 0);
78
    } else {
79
        s->count = 0;
80
    }
81
    mpcore_timer_update_irq(s);
82
}
83

    
84
static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
85
{
86
    int64_t val;
87
    switch (offset) {
88
    case 0: /* Load */
89
        return s->load;
90
        /* Fall through.  */
91
    case 4: /* Counter.  */
92
        if (((s->control & 1) == 0) || (s->count == 0))
93
            return 0;
94
        /* Slow and ugly, but hopefully won't happen too often.  */
95
        val = s->tick - qemu_get_clock(vm_clock);
96
        val /= mpcore_timer_scale(s);
97
        if (val < 0)
98
            val = 0;
99
        return val;
100
    case 8: /* Control.  */
101
        return s->control;
102
    case 12: /* Interrupt status.  */
103
        return s->status;
104
    default:
105
        return 0;
106
    }
107
}
108

    
109
static void mpcore_timer_write(mpcore_timer_state *s, int offset,
110
                               uint32_t value)
111
{
112
    int64_t old;
113
    switch (offset) {
114
    case 0: /* Load */
115
        s->load = value;
116
        /* Fall through.  */
117
    case 4: /* Counter.  */
118
        if ((s->control & 1) && s->count) {
119
            /* Cancel the previous timer.  */
120
            qemu_del_timer(s->timer);
121
        }
122
        s->count = value;
123
        if (s->control & 1) {
124
            mpcore_timer_reload(s, 1);
125
        }
126
        break;
127
    case 8: /* Control.  */
128
        old = s->control;
129
        s->control = value;
130
        if (((old & 1) == 0) && (value & 1)) {
131
            if (s->count == 0 && (s->control & 2))
132
                s->count = s->load;
133
            mpcore_timer_reload(s, 1);
134
        }
135
        break;
136
    case 12: /* Interrupt status.  */
137
        s->status &= ~value;
138
        mpcore_timer_update_irq(s);
139
        break;
140
    }
141
}
142

    
143
static void mpcore_timer_init(mpcore_priv_state *mpcore,
144
                              mpcore_timer_state *s, int id)
145
{
146
    s->id = id;
147
    s->mpcore = mpcore;
148
    s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
149
}
150

    
151

    
152
/* Per-CPU private memory mapped IO.  */
153

    
154
static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
155
{
156
    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
157
    int id;
158
    offset &= 0xfff;
159
    if (offset < 0x100) {
160
        /* SCU */
161
        switch (offset) {
162
        case 0x00: /* Control.  */
163
            return s->scu_control;
164
        case 0x04: /* Configuration.  */
165
            id = ((1 << s->num_cpu) - 1) << 4;
166
            return id | (s->num_cpu - 1);
167
        case 0x08: /* CPU status.  */
168
            return 0;
169
        case 0x0c: /* Invalidate all.  */
170
            return 0;
171
        default:
172
            goto bad_reg;
173
        }
174
    } else if (offset < 0x600) {
175
        /* Interrupt controller.  */
176
        if (offset < 0x200) {
177
            id = gic_get_current_cpu();
178
        } else {
179
            id = (offset - 0x200) >> 8;
180
            if (id >= s->num_cpu) {
181
                return 0;
182
            }
183
        }
184
        return gic_cpu_read(&s->gic, id, offset & 0xff);
185
    } else if (offset < 0xb00) {
186
        /* Timers.  */
187
        if (offset < 0x700) {
188
            id = gic_get_current_cpu();
189
        } else {
190
            id = (offset - 0x700) >> 8;
191
            if (id >= s->num_cpu) {
192
                return 0;
193
            }
194
        }
195
        id <<= 1;
196
        if (offset & 0x20)
197
          id++;
198
        return mpcore_timer_read(&s->timer[id], offset & 0xf);
199
    }
200
bad_reg:
201
    hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
202
    return 0;
203
}
204

    
205
static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
206
                          uint32_t value)
207
{
208
    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
209
    int id;
210
    offset &= 0xfff;
211
    if (offset < 0x100) {
212
        /* SCU */
213
        switch (offset) {
214
        case 0: /* Control register.  */
215
            s->scu_control = value & 1;
216
            break;
217
        case 0x0c: /* Invalidate all.  */
218
            /* This is a no-op as cache is not emulated.  */
219
            break;
220
        default:
221
            goto bad_reg;
222
        }
223
    } else if (offset < 0x600) {
224
        /* Interrupt controller.  */
225
        if (offset < 0x200) {
226
            id = gic_get_current_cpu();
227
        } else {
228
            id = (offset - 0x200) >> 8;
229
        }
230
        if (id < s->num_cpu) {
231
            gic_cpu_write(&s->gic, id, offset & 0xff, value);
232
        }
233
    } else if (offset < 0xb00) {
234
        /* Timers.  */
235
        if (offset < 0x700) {
236
            id = gic_get_current_cpu();
237
        } else {
238
            id = (offset - 0x700) >> 8;
239
        }
240
        if (id < s->num_cpu) {
241
            id <<= 1;
242
            if (offset & 0x20)
243
              id++;
244
            mpcore_timer_write(&s->timer[id], offset & 0xf, value);
245
        }
246
        return;
247
    }
248
    return;
249
bad_reg:
250
    hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
251
}
252

    
253
static CPUReadMemoryFunc * const mpcore_priv_readfn[] = {
254
   mpcore_priv_read,
255
   mpcore_priv_read,
256
   mpcore_priv_read
257
};
258

    
259
static CPUWriteMemoryFunc * const mpcore_priv_writefn[] = {
260
   mpcore_priv_write,
261
   mpcore_priv_write,
262
   mpcore_priv_write
263
};
264

    
265
static void mpcore_priv_map(SysBusDevice *dev, target_phys_addr_t base)
266
{
267
    mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
268
    cpu_register_physical_memory(base, 0x1000, s->iomemtype);
269
    cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype);
270
}
271

    
272
static int mpcore_priv_init(SysBusDevice *dev)
273
{
274
    mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
275
    int i;
276

    
277
    gic_init(&s->gic, s->num_cpu);
278
    s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn,
279
                                          mpcore_priv_writefn, s);
280
    sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map);
281
    for (i = 0; i < s->num_cpu * 2; i++) {
282
        mpcore_timer_init(s, &s->timer[i], i);
283
    }
284
    return 0;
285
}