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