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 22 #include "qemu/osdep.h" 23 #include "hw/timer/arm_mptimer.h" 24 #include "qapi/error.h" 25 #include "qemu/timer.h" 26 #include "qom/cpu.h" 27 28 /* This device implements the per-cpu private timer and watchdog block 29 * which is used in both the ARM11MPCore and Cortex-A9MP. 30 */ 31 32 static inline int get_current_cpu(ARMMPTimerState *s) 33 { 34 if (current_cpu->cpu_index >= s->num_cpu) { 35 hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", 36 s->num_cpu, current_cpu->cpu_index); 37 } 38 return current_cpu->cpu_index; 39 } 40 41 static inline void timerblock_update_irq(TimerBlock *tb) 42 { 43 qemu_set_irq(tb->irq, tb->status && (tb->control & 4)); 44 } 45 46 /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ 47 static inline uint32_t timerblock_scale(TimerBlock *tb) 48 { 49 return (((tb->control >> 8) & 0xff) + 1) * 10; 50 } 51 52 static void timerblock_reload(TimerBlock *tb, int restart) 53 { 54 if (tb->count == 0) { 55 return; 56 } 57 if (restart) { 58 tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 59 } 60 tb->tick += (int64_t)tb->count * timerblock_scale(tb); 61 timer_mod(tb->timer, tb->tick); 62 } 63 64 static void timerblock_tick(void *opaque) 65 { 66 TimerBlock *tb = (TimerBlock *)opaque; 67 tb->status = 1; 68 if (tb->control & 2) { 69 tb->count = tb->load; 70 timerblock_reload(tb, 0); 71 } else { 72 tb->count = 0; 73 } 74 timerblock_update_irq(tb); 75 } 76 77 static uint64_t timerblock_read(void *opaque, hwaddr addr, 78 unsigned size) 79 { 80 TimerBlock *tb = (TimerBlock *)opaque; 81 int64_t val; 82 switch (addr) { 83 case 0: /* Load */ 84 return tb->load; 85 case 4: /* Counter. */ 86 if (((tb->control & 1) == 0) || (tb->count == 0)) { 87 return 0; 88 } 89 /* Slow and ugly, but hopefully won't happen too often. */ 90 val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 91 val /= timerblock_scale(tb); 92 if (val < 0) { 93 val = 0; 94 } 95 return val; 96 case 8: /* Control. */ 97 return tb->control; 98 case 12: /* Interrupt status. */ 99 return tb->status; 100 default: 101 return 0; 102 } 103 } 104 105 static void timerblock_write(void *opaque, hwaddr addr, 106 uint64_t value, unsigned size) 107 { 108 TimerBlock *tb = (TimerBlock *)opaque; 109 int64_t old; 110 switch (addr) { 111 case 0: /* Load */ 112 tb->load = value; 113 /* Fall through. */ 114 case 4: /* Counter. */ 115 if ((tb->control & 1) && tb->count) { 116 /* Cancel the previous timer. */ 117 timer_del(tb->timer); 118 } 119 tb->count = value; 120 if (tb->control & 1) { 121 timerblock_reload(tb, 1); 122 } 123 break; 124 case 8: /* Control. */ 125 old = tb->control; 126 tb->control = value; 127 if (value & 1) { 128 if ((old & 1) && (tb->count != 0)) { 129 /* Do nothing if timer is ticking right now. */ 130 break; 131 } 132 if (tb->control & 2) { 133 tb->count = tb->load; 134 } 135 timerblock_reload(tb, 1); 136 } else if (old & 1) { 137 /* Shutdown the timer. */ 138 timer_del(tb->timer); 139 } 140 break; 141 case 12: /* Interrupt status. */ 142 tb->status &= ~value; 143 timerblock_update_irq(tb); 144 break; 145 } 146 } 147 148 /* Wrapper functions to implement the "read timer/watchdog for 149 * the current CPU" memory regions. 150 */ 151 static uint64_t arm_thistimer_read(void *opaque, hwaddr addr, 152 unsigned size) 153 { 154 ARMMPTimerState *s = (ARMMPTimerState *)opaque; 155 int id = get_current_cpu(s); 156 return timerblock_read(&s->timerblock[id], addr, size); 157 } 158 159 static void arm_thistimer_write(void *opaque, hwaddr addr, 160 uint64_t value, unsigned size) 161 { 162 ARMMPTimerState *s = (ARMMPTimerState *)opaque; 163 int id = get_current_cpu(s); 164 timerblock_write(&s->timerblock[id], addr, value, size); 165 } 166 167 static const MemoryRegionOps arm_thistimer_ops = { 168 .read = arm_thistimer_read, 169 .write = arm_thistimer_write, 170 .valid = { 171 .min_access_size = 4, 172 .max_access_size = 4, 173 }, 174 .endianness = DEVICE_NATIVE_ENDIAN, 175 }; 176 177 static const MemoryRegionOps timerblock_ops = { 178 .read = timerblock_read, 179 .write = timerblock_write, 180 .valid = { 181 .min_access_size = 4, 182 .max_access_size = 4, 183 }, 184 .endianness = DEVICE_NATIVE_ENDIAN, 185 }; 186 187 static void timerblock_reset(TimerBlock *tb) 188 { 189 tb->count = 0; 190 tb->load = 0; 191 tb->control = 0; 192 tb->status = 0; 193 tb->tick = 0; 194 if (tb->timer) { 195 timer_del(tb->timer); 196 } 197 } 198 199 static void arm_mptimer_reset(DeviceState *dev) 200 { 201 ARMMPTimerState *s = ARM_MPTIMER(dev); 202 int i; 203 204 for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { 205 timerblock_reset(&s->timerblock[i]); 206 } 207 } 208 209 static void arm_mptimer_init(Object *obj) 210 { 211 ARMMPTimerState *s = ARM_MPTIMER(obj); 212 213 memory_region_init_io(&s->iomem, obj, &arm_thistimer_ops, s, 214 "arm_mptimer_timer", 0x20); 215 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); 216 } 217 218 static void arm_mptimer_realize(DeviceState *dev, Error **errp) 219 { 220 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 221 ARMMPTimerState *s = ARM_MPTIMER(dev); 222 int i; 223 224 if (s->num_cpu < 1 || s->num_cpu > ARM_MPTIMER_MAX_CPUS) { 225 error_setg(errp, "num-cpu must be between 1 and %d", 226 ARM_MPTIMER_MAX_CPUS); 227 return; 228 } 229 /* We implement one timer block per CPU, and expose multiple MMIO regions: 230 * * region 0 is "timer for this core" 231 * * region 1 is "timer for core 0" 232 * * region 2 is "timer for core 1" 233 * and so on. 234 * The outgoing interrupt lines are 235 * * timer for core 0 236 * * timer for core 1 237 * and so on. 238 */ 239 for (i = 0; i < s->num_cpu; i++) { 240 TimerBlock *tb = &s->timerblock[i]; 241 tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb); 242 sysbus_init_irq(sbd, &tb->irq); 243 memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, 244 "arm_mptimer_timerblock", 0x20); 245 sysbus_init_mmio(sbd, &tb->iomem); 246 } 247 } 248 249 static const VMStateDescription vmstate_timerblock = { 250 .name = "arm_mptimer_timerblock", 251 .version_id = 2, 252 .minimum_version_id = 2, 253 .fields = (VMStateField[]) { 254 VMSTATE_UINT32(count, TimerBlock), 255 VMSTATE_UINT32(load, TimerBlock), 256 VMSTATE_UINT32(control, TimerBlock), 257 VMSTATE_UINT32(status, TimerBlock), 258 VMSTATE_INT64(tick, TimerBlock), 259 VMSTATE_TIMER_PTR(timer, TimerBlock), 260 VMSTATE_END_OF_LIST() 261 } 262 }; 263 264 static const VMStateDescription vmstate_arm_mptimer = { 265 .name = "arm_mptimer", 266 .version_id = 2, 267 .minimum_version_id = 2, 268 .fields = (VMStateField[]) { 269 VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, 270 2, vmstate_timerblock, TimerBlock), 271 VMSTATE_END_OF_LIST() 272 } 273 }; 274 275 static Property arm_mptimer_properties[] = { 276 DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), 277 DEFINE_PROP_END_OF_LIST() 278 }; 279 280 static void arm_mptimer_class_init(ObjectClass *klass, void *data) 281 { 282 DeviceClass *dc = DEVICE_CLASS(klass); 283 284 dc->realize = arm_mptimer_realize; 285 dc->vmsd = &vmstate_arm_mptimer; 286 dc->reset = arm_mptimer_reset; 287 dc->props = arm_mptimer_properties; 288 } 289 290 static const TypeInfo arm_mptimer_info = { 291 .name = TYPE_ARM_MPTIMER, 292 .parent = TYPE_SYS_BUS_DEVICE, 293 .instance_size = sizeof(ARMMPTimerState), 294 .instance_init = arm_mptimer_init, 295 .class_init = arm_mptimer_class_init, 296 }; 297 298 static void arm_mptimer_register_types(void) 299 { 300 type_register_static(&arm_mptimer_info); 301 } 302 303 type_init(arm_mptimer_register_types) 304