Statistics
| Branch: | Revision:

root / hw / timer / arm_mptimer.c @ f487b677

History | View | Annotate | Download (8.5 kB)

1 b9dc07d4 Peter Maydell
/*
2 b9dc07d4 Peter Maydell
 * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
3 b9dc07d4 Peter Maydell
 *
4 b9dc07d4 Peter Maydell
 * Copyright (c) 2006-2007 CodeSourcery.
5 b9dc07d4 Peter Maydell
 * Copyright (c) 2011 Linaro Limited
6 b9dc07d4 Peter Maydell
 * Written by Paul Brook, Peter Maydell
7 b9dc07d4 Peter Maydell
 *
8 b9dc07d4 Peter Maydell
 * This program is free software; you can redistribute it and/or
9 b9dc07d4 Peter Maydell
 * modify it under the terms of the GNU General Public License
10 b9dc07d4 Peter Maydell
 * as published by the Free Software Foundation; either version
11 b9dc07d4 Peter Maydell
 * 2 of the License, or (at your option) any later version.
12 b9dc07d4 Peter Maydell
 *
13 b9dc07d4 Peter Maydell
 * This program is distributed in the hope that it will be useful,
14 b9dc07d4 Peter Maydell
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 b9dc07d4 Peter Maydell
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 b9dc07d4 Peter Maydell
 * GNU General Public License for more details.
17 b9dc07d4 Peter Maydell
 *
18 b9dc07d4 Peter Maydell
 * You should have received a copy of the GNU General Public License along
19 b9dc07d4 Peter Maydell
 * with this program; if not, see <http://www.gnu.org/licenses/>.
20 b9dc07d4 Peter Maydell
 */
21 b9dc07d4 Peter Maydell
22 83c9f4ca Paolo Bonzini
#include "hw/sysbus.h"
23 1de7afc9 Paolo Bonzini
#include "qemu/timer.h"
24 b9dc07d4 Peter Maydell
25 b9dc07d4 Peter Maydell
/* This device implements the per-cpu private timer and watchdog block
26 b9dc07d4 Peter Maydell
 * which is used in both the ARM11MPCore and Cortex-A9MP.
27 b9dc07d4 Peter Maydell
 */
28 b9dc07d4 Peter Maydell
29 b9dc07d4 Peter Maydell
#define MAX_CPUS 4
30 b9dc07d4 Peter Maydell
31 b9dc07d4 Peter Maydell
/* State of a single timer or watchdog block */
32 b9dc07d4 Peter Maydell
typedef struct {
33 b9dc07d4 Peter Maydell
    uint32_t count;
34 b9dc07d4 Peter Maydell
    uint32_t load;
35 b9dc07d4 Peter Maydell
    uint32_t control;
36 b9dc07d4 Peter Maydell
    uint32_t status;
37 b9dc07d4 Peter Maydell
    int64_t tick;
38 b9dc07d4 Peter Maydell
    QEMUTimer *timer;
39 b9dc07d4 Peter Maydell
    qemu_irq irq;
40 b9dc07d4 Peter Maydell
    MemoryRegion iomem;
41 c6205ddf Peter Crosthwaite
} TimerBlock;
42 b9dc07d4 Peter Maydell
43 b9dc07d4 Peter Maydell
typedef struct {
44 b9dc07d4 Peter Maydell
    SysBusDevice busdev;
45 b9dc07d4 Peter Maydell
    uint32_t num_cpu;
46 cde4577f Peter Crosthwaite
    TimerBlock timerblock[MAX_CPUS];
47 cde4577f Peter Crosthwaite
    MemoryRegion iomem;
48 c6205ddf Peter Crosthwaite
} ARMMPTimerState;
49 b9dc07d4 Peter Maydell
50 c6205ddf Peter Crosthwaite
static inline int get_current_cpu(ARMMPTimerState *s)
51 b9dc07d4 Peter Maydell
{
52 55e5c285 Andreas Färber
    CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
53 55e5c285 Andreas Färber
54 55e5c285 Andreas Färber
    if (cpu_single_cpu->cpu_index >= s->num_cpu) {
55 b9dc07d4 Peter Maydell
        hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
56 55e5c285 Andreas Färber
                 s->num_cpu, cpu_single_cpu->cpu_index);
57 b9dc07d4 Peter Maydell
    }
58 55e5c285 Andreas Färber
    return cpu_single_cpu->cpu_index;
59 b9dc07d4 Peter Maydell
}
60 b9dc07d4 Peter Maydell
61 c6205ddf Peter Crosthwaite
static inline void timerblock_update_irq(TimerBlock *tb)
62 b9dc07d4 Peter Maydell
{
63 b9dc07d4 Peter Maydell
    qemu_set_irq(tb->irq, tb->status);
64 b9dc07d4 Peter Maydell
}
65 b9dc07d4 Peter Maydell
66 b9dc07d4 Peter Maydell
/* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
67 c6205ddf Peter Crosthwaite
static inline uint32_t timerblock_scale(TimerBlock *tb)
68 b9dc07d4 Peter Maydell
{
69 b9dc07d4 Peter Maydell
    return (((tb->control >> 8) & 0xff) + 1) * 10;
70 b9dc07d4 Peter Maydell
}
71 b9dc07d4 Peter Maydell
72 c6205ddf Peter Crosthwaite
static void timerblock_reload(TimerBlock *tb, int restart)
73 b9dc07d4 Peter Maydell
{
74 b9dc07d4 Peter Maydell
    if (tb->count == 0) {
75 b9dc07d4 Peter Maydell
        return;
76 b9dc07d4 Peter Maydell
    }
77 b9dc07d4 Peter Maydell
    if (restart) {
78 b9dc07d4 Peter Maydell
        tb->tick = qemu_get_clock_ns(vm_clock);
79 b9dc07d4 Peter Maydell
    }
80 b9dc07d4 Peter Maydell
    tb->tick += (int64_t)tb->count * timerblock_scale(tb);
81 b9dc07d4 Peter Maydell
    qemu_mod_timer(tb->timer, tb->tick);
82 b9dc07d4 Peter Maydell
}
83 b9dc07d4 Peter Maydell
84 b9dc07d4 Peter Maydell
static void timerblock_tick(void *opaque)
85 b9dc07d4 Peter Maydell
{
86 c6205ddf Peter Crosthwaite
    TimerBlock *tb = (TimerBlock *)opaque;
87 b9dc07d4 Peter Maydell
    tb->status = 1;
88 b9dc07d4 Peter Maydell
    if (tb->control & 2) {
89 b9dc07d4 Peter Maydell
        tb->count = tb->load;
90 b9dc07d4 Peter Maydell
        timerblock_reload(tb, 0);
91 b9dc07d4 Peter Maydell
    } else {
92 b9dc07d4 Peter Maydell
        tb->count = 0;
93 b9dc07d4 Peter Maydell
    }
94 b9dc07d4 Peter Maydell
    timerblock_update_irq(tb);
95 b9dc07d4 Peter Maydell
}
96 b9dc07d4 Peter Maydell
97 a8170e5e Avi Kivity
static uint64_t timerblock_read(void *opaque, hwaddr addr,
98 b9dc07d4 Peter Maydell
                                unsigned size)
99 b9dc07d4 Peter Maydell
{
100 c6205ddf Peter Crosthwaite
    TimerBlock *tb = (TimerBlock *)opaque;
101 b9dc07d4 Peter Maydell
    int64_t val;
102 b9dc07d4 Peter Maydell
    switch (addr) {
103 b9dc07d4 Peter Maydell
    case 0: /* Load */
104 b9dc07d4 Peter Maydell
        return tb->load;
105 b9dc07d4 Peter Maydell
    case 4: /* Counter.  */
106 b9dc07d4 Peter Maydell
        if (((tb->control & 1) == 0) || (tb->count == 0)) {
107 b9dc07d4 Peter Maydell
            return 0;
108 b9dc07d4 Peter Maydell
        }
109 b9dc07d4 Peter Maydell
        /* Slow and ugly, but hopefully won't happen too often.  */
110 b9dc07d4 Peter Maydell
        val = tb->tick - qemu_get_clock_ns(vm_clock);
111 b9dc07d4 Peter Maydell
        val /= timerblock_scale(tb);
112 b9dc07d4 Peter Maydell
        if (val < 0) {
113 b9dc07d4 Peter Maydell
            val = 0;
114 b9dc07d4 Peter Maydell
        }
115 b9dc07d4 Peter Maydell
        return val;
116 b9dc07d4 Peter Maydell
    case 8: /* Control.  */
117 b9dc07d4 Peter Maydell
        return tb->control;
118 b9dc07d4 Peter Maydell
    case 12: /* Interrupt status.  */
119 b9dc07d4 Peter Maydell
        return tb->status;
120 b9dc07d4 Peter Maydell
    default:
121 b9dc07d4 Peter Maydell
        return 0;
122 b9dc07d4 Peter Maydell
    }
123 b9dc07d4 Peter Maydell
}
124 b9dc07d4 Peter Maydell
125 a8170e5e Avi Kivity
static void timerblock_write(void *opaque, hwaddr addr,
126 b9dc07d4 Peter Maydell
                             uint64_t value, unsigned size)
127 b9dc07d4 Peter Maydell
{
128 c6205ddf Peter Crosthwaite
    TimerBlock *tb = (TimerBlock *)opaque;
129 b9dc07d4 Peter Maydell
    int64_t old;
130 b9dc07d4 Peter Maydell
    switch (addr) {
131 b9dc07d4 Peter Maydell
    case 0: /* Load */
132 b9dc07d4 Peter Maydell
        tb->load = value;
133 b9dc07d4 Peter Maydell
        /* Fall through.  */
134 b9dc07d4 Peter Maydell
    case 4: /* Counter.  */
135 b9dc07d4 Peter Maydell
        if ((tb->control & 1) && tb->count) {
136 b9dc07d4 Peter Maydell
            /* Cancel the previous timer.  */
137 b9dc07d4 Peter Maydell
            qemu_del_timer(tb->timer);
138 b9dc07d4 Peter Maydell
        }
139 b9dc07d4 Peter Maydell
        tb->count = value;
140 b9dc07d4 Peter Maydell
        if (tb->control & 1) {
141 b9dc07d4 Peter Maydell
            timerblock_reload(tb, 1);
142 b9dc07d4 Peter Maydell
        }
143 b9dc07d4 Peter Maydell
        break;
144 b9dc07d4 Peter Maydell
    case 8: /* Control.  */
145 b9dc07d4 Peter Maydell
        old = tb->control;
146 b9dc07d4 Peter Maydell
        tb->control = value;
147 b9dc07d4 Peter Maydell
        if (((old & 1) == 0) && (value & 1)) {
148 b9dc07d4 Peter Maydell
            if (tb->count == 0 && (tb->control & 2)) {
149 b9dc07d4 Peter Maydell
                tb->count = tb->load;
150 b9dc07d4 Peter Maydell
            }
151 b9dc07d4 Peter Maydell
            timerblock_reload(tb, 1);
152 b9dc07d4 Peter Maydell
        }
153 b9dc07d4 Peter Maydell
        break;
154 b9dc07d4 Peter Maydell
    case 12: /* Interrupt status.  */
155 b9dc07d4 Peter Maydell
        tb->status &= ~value;
156 b9dc07d4 Peter Maydell
        timerblock_update_irq(tb);
157 b9dc07d4 Peter Maydell
        break;
158 b9dc07d4 Peter Maydell
    }
159 b9dc07d4 Peter Maydell
}
160 b9dc07d4 Peter Maydell
161 b9dc07d4 Peter Maydell
/* Wrapper functions to implement the "read timer/watchdog for
162 b9dc07d4 Peter Maydell
 * the current CPU" memory regions.
163 b9dc07d4 Peter Maydell
 */
164 a8170e5e Avi Kivity
static uint64_t arm_thistimer_read(void *opaque, hwaddr addr,
165 b9dc07d4 Peter Maydell
                                   unsigned size)
166 b9dc07d4 Peter Maydell
{
167 c6205ddf Peter Crosthwaite
    ARMMPTimerState *s = (ARMMPTimerState *)opaque;
168 b9dc07d4 Peter Maydell
    int id = get_current_cpu(s);
169 cde4577f Peter Crosthwaite
    return timerblock_read(&s->timerblock[id], addr, size);
170 b9dc07d4 Peter Maydell
}
171 b9dc07d4 Peter Maydell
172 a8170e5e Avi Kivity
static void arm_thistimer_write(void *opaque, hwaddr addr,
173 b9dc07d4 Peter Maydell
                                uint64_t value, unsigned size)
174 b9dc07d4 Peter Maydell
{
175 c6205ddf Peter Crosthwaite
    ARMMPTimerState *s = (ARMMPTimerState *)opaque;
176 b9dc07d4 Peter Maydell
    int id = get_current_cpu(s);
177 cde4577f Peter Crosthwaite
    timerblock_write(&s->timerblock[id], addr, value, size);
178 b9dc07d4 Peter Maydell
}
179 b9dc07d4 Peter Maydell
180 b9dc07d4 Peter Maydell
static const MemoryRegionOps arm_thistimer_ops = {
181 b9dc07d4 Peter Maydell
    .read = arm_thistimer_read,
182 b9dc07d4 Peter Maydell
    .write = arm_thistimer_write,
183 b9dc07d4 Peter Maydell
    .valid = {
184 b9dc07d4 Peter Maydell
        .min_access_size = 4,
185 b9dc07d4 Peter Maydell
        .max_access_size = 4,
186 b9dc07d4 Peter Maydell
    },
187 b9dc07d4 Peter Maydell
    .endianness = DEVICE_NATIVE_ENDIAN,
188 b9dc07d4 Peter Maydell
};
189 b9dc07d4 Peter Maydell
190 b9dc07d4 Peter Maydell
static const MemoryRegionOps timerblock_ops = {
191 b9dc07d4 Peter Maydell
    .read = timerblock_read,
192 b9dc07d4 Peter Maydell
    .write = timerblock_write,
193 b9dc07d4 Peter Maydell
    .valid = {
194 b9dc07d4 Peter Maydell
        .min_access_size = 4,
195 b9dc07d4 Peter Maydell
        .max_access_size = 4,
196 b9dc07d4 Peter Maydell
    },
197 b9dc07d4 Peter Maydell
    .endianness = DEVICE_NATIVE_ENDIAN,
198 b9dc07d4 Peter Maydell
};
199 b9dc07d4 Peter Maydell
200 c6205ddf Peter Crosthwaite
static void timerblock_reset(TimerBlock *tb)
201 b9dc07d4 Peter Maydell
{
202 b9dc07d4 Peter Maydell
    tb->count = 0;
203 b9dc07d4 Peter Maydell
    tb->load = 0;
204 b9dc07d4 Peter Maydell
    tb->control = 0;
205 b9dc07d4 Peter Maydell
    tb->status = 0;
206 b9dc07d4 Peter Maydell
    tb->tick = 0;
207 bdac1c1e Peter Maydell
    if (tb->timer) {
208 bdac1c1e Peter Maydell
        qemu_del_timer(tb->timer);
209 bdac1c1e Peter Maydell
    }
210 b9dc07d4 Peter Maydell
}
211 b9dc07d4 Peter Maydell
212 b9dc07d4 Peter Maydell
static void arm_mptimer_reset(DeviceState *dev)
213 b9dc07d4 Peter Maydell
{
214 c6205ddf Peter Crosthwaite
    ARMMPTimerState *s =
215 c6205ddf Peter Crosthwaite
        FROM_SYSBUS(ARMMPTimerState, SYS_BUS_DEVICE(dev));
216 b9dc07d4 Peter Maydell
    int i;
217 b9dc07d4 Peter Maydell
    for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
218 b9dc07d4 Peter Maydell
        timerblock_reset(&s->timerblock[i]);
219 b9dc07d4 Peter Maydell
    }
220 b9dc07d4 Peter Maydell
}
221 b9dc07d4 Peter Maydell
222 b9dc07d4 Peter Maydell
static int arm_mptimer_init(SysBusDevice *dev)
223 b9dc07d4 Peter Maydell
{
224 c6205ddf Peter Crosthwaite
    ARMMPTimerState *s = FROM_SYSBUS(ARMMPTimerState, dev);
225 b9dc07d4 Peter Maydell
    int i;
226 b9dc07d4 Peter Maydell
    if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) {
227 b9dc07d4 Peter Maydell
        hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS);
228 b9dc07d4 Peter Maydell
    }
229 cde4577f Peter Crosthwaite
    /* We implement one timer block per CPU, and expose multiple MMIO regions:
230 b9dc07d4 Peter Maydell
     *  * region 0 is "timer for this core"
231 cde4577f Peter Crosthwaite
     *  * region 1 is "timer for core 0"
232 cde4577f Peter Crosthwaite
     *  * region 2 is "timer for core 1"
233 b9dc07d4 Peter Maydell
     * and so on.
234 b9dc07d4 Peter Maydell
     * The outgoing interrupt lines are
235 b9dc07d4 Peter Maydell
     *  * timer for core 0
236 b9dc07d4 Peter Maydell
     *  * timer for core 1
237 b9dc07d4 Peter Maydell
     * and so on.
238 b9dc07d4 Peter Maydell
     */
239 cde4577f Peter Crosthwaite
    memory_region_init_io(&s->iomem, &arm_thistimer_ops, s,
240 b9dc07d4 Peter Maydell
                          "arm_mptimer_timer", 0x20);
241 cde4577f Peter Crosthwaite
    sysbus_init_mmio(dev, &s->iomem);
242 cde4577f Peter Crosthwaite
    for (i = 0; i < s->num_cpu; i++) {
243 c6205ddf Peter Crosthwaite
        TimerBlock *tb = &s->timerblock[i];
244 b9dc07d4 Peter Maydell
        tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb);
245 b9dc07d4 Peter Maydell
        sysbus_init_irq(dev, &tb->irq);
246 b9dc07d4 Peter Maydell
        memory_region_init_io(&tb->iomem, &timerblock_ops, tb,
247 b9dc07d4 Peter Maydell
                              "arm_mptimer_timerblock", 0x20);
248 b9dc07d4 Peter Maydell
        sysbus_init_mmio(dev, &tb->iomem);
249 b9dc07d4 Peter Maydell
    }
250 b9dc07d4 Peter Maydell
251 b9dc07d4 Peter Maydell
    return 0;
252 b9dc07d4 Peter Maydell
}
253 b9dc07d4 Peter Maydell
254 b9dc07d4 Peter Maydell
static const VMStateDescription vmstate_timerblock = {
255 b9dc07d4 Peter Maydell
    .name = "arm_mptimer_timerblock",
256 28092a23 Peter Maydell
    .version_id = 2,
257 28092a23 Peter Maydell
    .minimum_version_id = 2,
258 b9dc07d4 Peter Maydell
    .fields = (VMStateField[]) {
259 c6205ddf Peter Crosthwaite
        VMSTATE_UINT32(count, TimerBlock),
260 c6205ddf Peter Crosthwaite
        VMSTATE_UINT32(load, TimerBlock),
261 c6205ddf Peter Crosthwaite
        VMSTATE_UINT32(control, TimerBlock),
262 c6205ddf Peter Crosthwaite
        VMSTATE_UINT32(status, TimerBlock),
263 c6205ddf Peter Crosthwaite
        VMSTATE_INT64(tick, TimerBlock),
264 28092a23 Peter Maydell
        VMSTATE_TIMER(timer, TimerBlock),
265 b9dc07d4 Peter Maydell
        VMSTATE_END_OF_LIST()
266 b9dc07d4 Peter Maydell
    }
267 b9dc07d4 Peter Maydell
};
268 b9dc07d4 Peter Maydell
269 b9dc07d4 Peter Maydell
static const VMStateDescription vmstate_arm_mptimer = {
270 b9dc07d4 Peter Maydell
    .name = "arm_mptimer",
271 cde4577f Peter Crosthwaite
    .version_id = 2,
272 cde4577f Peter Crosthwaite
    .minimum_version_id = 2,
273 b9dc07d4 Peter Maydell
    .fields = (VMStateField[]) {
274 cde4577f Peter Crosthwaite
        VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
275 cde4577f Peter Crosthwaite
                                     2, vmstate_timerblock, TimerBlock),
276 b9dc07d4 Peter Maydell
        VMSTATE_END_OF_LIST()
277 b9dc07d4 Peter Maydell
    }
278 b9dc07d4 Peter Maydell
};
279 b9dc07d4 Peter Maydell
280 39bffca2 Anthony Liguori
static Property arm_mptimer_properties[] = {
281 c6205ddf Peter Crosthwaite
    DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0),
282 39bffca2 Anthony Liguori
    DEFINE_PROP_END_OF_LIST()
283 39bffca2 Anthony Liguori
};
284 39bffca2 Anthony Liguori
285 999e12bb Anthony Liguori
static void arm_mptimer_class_init(ObjectClass *klass, void *data)
286 999e12bb Anthony Liguori
{
287 39bffca2 Anthony Liguori
    DeviceClass *dc = DEVICE_CLASS(klass);
288 999e12bb Anthony Liguori
    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
289 999e12bb Anthony Liguori
290 999e12bb Anthony Liguori
    sbc->init = arm_mptimer_init;
291 39bffca2 Anthony Liguori
    dc->vmsd = &vmstate_arm_mptimer;
292 39bffca2 Anthony Liguori
    dc->reset = arm_mptimer_reset;
293 39bffca2 Anthony Liguori
    dc->no_user = 1;
294 39bffca2 Anthony Liguori
    dc->props = arm_mptimer_properties;
295 999e12bb Anthony Liguori
}
296 999e12bb Anthony Liguori
297 8c43a6f0 Andreas Färber
static const TypeInfo arm_mptimer_info = {
298 39bffca2 Anthony Liguori
    .name          = "arm_mptimer",
299 39bffca2 Anthony Liguori
    .parent        = TYPE_SYS_BUS_DEVICE,
300 c6205ddf Peter Crosthwaite
    .instance_size = sizeof(ARMMPTimerState),
301 39bffca2 Anthony Liguori
    .class_init    = arm_mptimer_class_init,
302 b9dc07d4 Peter Maydell
};
303 b9dc07d4 Peter Maydell
304 83f7d43a Andreas Färber
static void arm_mptimer_register_types(void)
305 b9dc07d4 Peter Maydell
{
306 39bffca2 Anthony Liguori
    type_register_static(&arm_mptimer_info);
307 b9dc07d4 Peter Maydell
}
308 b9dc07d4 Peter Maydell
309 83f7d43a Andreas Färber
type_init(arm_mptimer_register_types)