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