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 #include "qom/cpu.h" 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 */ 33 typedef 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; 42 } TimerBlock; 43 44 #define TYPE_ARM_MPTIMER "arm_mptimer" 45 #define ARM_MPTIMER(obj) \ 46 OBJECT_CHECK(ARMMPTimerState, (obj), TYPE_ARM_MPTIMER) 47 48 typedef struct { 49 /*< private >*/ 50 SysBusDevice parent_obj; 51 /*< public >*/ 52 53 uint32_t num_cpu; 54 TimerBlock timerblock[MAX_CPUS]; 55 MemoryRegion iomem; 56 } ARMMPTimerState; 57 58 static inline int get_current_cpu(ARMMPTimerState *s) 59 { 60 if (current_cpu->cpu_index >= s->num_cpu) { 61 hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", 62 s->num_cpu, current_cpu->cpu_index); 63 } 64 return current_cpu->cpu_index; 65 } 66 67 static inline void timerblock_update_irq(TimerBlock *tb) 68 { 69 qemu_set_irq(tb->irq, tb->status); 70 } 71 72 /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ 73 static inline uint32_t timerblock_scale(TimerBlock *tb) 74 { 75 return (((tb->control >> 8) & 0xff) + 1) * 10; 76 } 77 78 static void timerblock_reload(TimerBlock *tb, int restart) 79 { 80 if (tb->count == 0) { 81 return; 82 } 83 if (restart) { 84 tb->tick = qemu_get_clock_ns(vm_clock); 85 } 86 tb->tick += (int64_t)tb->count * timerblock_scale(tb); 87 qemu_mod_timer(tb->timer, tb->tick); 88 } 89 90 static void timerblock_tick(void *opaque) 91 { 92 TimerBlock *tb = (TimerBlock *)opaque; 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 103 static uint64_t timerblock_read(void *opaque, hwaddr addr, 104 unsigned size) 105 { 106 TimerBlock *tb = (TimerBlock *)opaque; 107 int64_t val; 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. */ 116 val = tb->tick - qemu_get_clock_ns(vm_clock); 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 131 static void timerblock_write(void *opaque, hwaddr addr, 132 uint64_t value, unsigned size) 133 { 134 TimerBlock *tb = (TimerBlock *)opaque; 135 int64_t old; 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. */ 143 qemu_del_timer(tb->timer); 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 */ 170 static uint64_t arm_thistimer_read(void *opaque, hwaddr addr, 171 unsigned size) 172 { 173 ARMMPTimerState *s = (ARMMPTimerState *)opaque; 174 int id = get_current_cpu(s); 175 return timerblock_read(&s->timerblock[id], addr, size); 176 } 177 178 static void arm_thistimer_write(void *opaque, hwaddr addr, 179 uint64_t value, unsigned size) 180 { 181 ARMMPTimerState *s = (ARMMPTimerState *)opaque; 182 int id = get_current_cpu(s); 183 timerblock_write(&s->timerblock[id], addr, value, size); 184 } 185 186 static 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 196 static 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 206 static void timerblock_reset(TimerBlock *tb) 207 { 208 tb->count = 0; 209 tb->load = 0; 210 tb->control = 0; 211 tb->status = 0; 212 tb->tick = 0; 213 if (tb->timer) { 214 qemu_del_timer(tb->timer); 215 } 216 } 217 218 static void arm_mptimer_reset(DeviceState *dev) 219 { 220 ARMMPTimerState *s = ARM_MPTIMER(dev); 221 int i; 222 223 for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { 224 timerblock_reset(&s->timerblock[i]); 225 } 226 } 227 228 static int arm_mptimer_init(SysBusDevice *dev) 229 { 230 ARMMPTimerState *s = ARM_MPTIMER(dev); 231 int i; 232 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 } 236 /* We implement one timer block per CPU, and expose multiple MMIO regions: 237 * * region 0 is "timer for this core" 238 * * region 1 is "timer for core 0" 239 * * region 2 is "timer for core 1" 240 * and so on. 241 * The outgoing interrupt lines are 242 * * timer for core 0 243 * * timer for core 1 244 * and so on. 245 */ 246 memory_region_init_io(&s->iomem, OBJECT(s), &arm_thistimer_ops, s, 247 "arm_mptimer_timer", 0x20); 248 sysbus_init_mmio(dev, &s->iomem); 249 for (i = 0; i < s->num_cpu; i++) { 250 TimerBlock *tb = &s->timerblock[i]; 251 tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb); 252 sysbus_init_irq(dev, &tb->irq); 253 memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, 254 "arm_mptimer_timerblock", 0x20); 255 sysbus_init_mmio(dev, &tb->iomem); 256 } 257 258 return 0; 259 } 260 261 static const VMStateDescription vmstate_timerblock = { 262 .name = "arm_mptimer_timerblock", 263 .version_id = 2, 264 .minimum_version_id = 2, 265 .fields = (VMStateField[]) { 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), 271 VMSTATE_TIMER(timer, TimerBlock), 272 VMSTATE_END_OF_LIST() 273 } 274 }; 275 276 static const VMStateDescription vmstate_arm_mptimer = { 277 .name = "arm_mptimer", 278 .version_id = 2, 279 .minimum_version_id = 2, 280 .fields = (VMStateField[]) { 281 VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, 282 2, vmstate_timerblock, TimerBlock), 283 VMSTATE_END_OF_LIST() 284 } 285 }; 286 287 static Property arm_mptimer_properties[] = { 288 DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), 289 DEFINE_PROP_END_OF_LIST() 290 }; 291 292 static void arm_mptimer_class_init(ObjectClass *klass, void *data) 293 { 294 DeviceClass *dc = DEVICE_CLASS(klass); 295 SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); 296 297 sbc->init = arm_mptimer_init; 298 dc->vmsd = &vmstate_arm_mptimer; 299 dc->reset = arm_mptimer_reset; 300 dc->no_user = 1; 301 dc->props = arm_mptimer_properties; 302 } 303 304 static const TypeInfo arm_mptimer_info = { 305 .name = TYPE_ARM_MPTIMER, 306 .parent = TYPE_SYS_BUS_DEVICE, 307 .instance_size = sizeof(ARMMPTimerState), 308 .class_init = arm_mptimer_class_init, 309 }; 310 311 static void arm_mptimer_register_types(void) 312 { 313 type_register_static(&arm_mptimer_info); 314 } 315 316 type_init(arm_mptimer_register_types) 317