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