1 /* 2 * Global peripheral timer block for ARM A9MP 3 * 4 * (C) 2013 Xilinx Inc. 5 * 6 * Written by François LEGAL 7 * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 12 * 2 of the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, see <http://www.gnu.org/licenses/>. 21 */ 22 23 #include "qemu/osdep.h" 24 #include "hw/timer/a9gtimer.h" 25 #include "qapi/error.h" 26 #include "qemu/timer.h" 27 #include "qemu/bitops.h" 28 #include "qemu/log.h" 29 #include "qom/cpu.h" 30 31 #ifndef A9_GTIMER_ERR_DEBUG 32 #define A9_GTIMER_ERR_DEBUG 0 33 #endif 34 35 #define DB_PRINT_L(level, ...) do { \ 36 if (A9_GTIMER_ERR_DEBUG > (level)) { \ 37 fprintf(stderr, ": %s: ", __func__); \ 38 fprintf(stderr, ## __VA_ARGS__); \ 39 } \ 40 } while (0); 41 42 #define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__) 43 44 static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) 45 { 46 if (current_cpu->cpu_index >= s->num_cpu) { 47 hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n", 48 s->num_cpu, current_cpu->cpu_index); 49 } 50 return current_cpu->cpu_index; 51 } 52 53 static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s) 54 { 55 uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT, 56 R_CONTROL_PRESCALER_LEN); 57 58 return (prescale + 1) * 10; 59 } 60 61 static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s) 62 { 63 A9GTimerUpdate ret; 64 65 ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 66 ret.new = s->ref_counter + 67 (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s); 68 return ret; 69 } 70 71 static void a9_gtimer_update(A9GTimerState *s, bool sync) 72 { 73 74 A9GTimerUpdate update = a9_gtimer_get_update(s); 75 int i; 76 int64_t next_cdiff = 0; 77 78 for (i = 0; i < s->num_cpu; ++i) { 79 A9GTimerPerCPU *gtb = &s->per_cpu[i]; 80 int64_t cdiff = 0; 81 82 if ((s->control & R_CONTROL_TIMER_ENABLE) && 83 (gtb->control & R_CONTROL_COMP_ENABLE)) { 84 /* R2p0+, where the compare function is >= */ 85 while (gtb->compare < update.new) { 86 DB_PRINT("Compare event happened for CPU %d\n", i); 87 gtb->status = 1; 88 if (gtb->control & R_CONTROL_AUTO_INCREMENT) { 89 DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n", 90 gtb->inc); 91 gtb->compare += gtb->inc; 92 } else { 93 break; 94 } 95 } 96 cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1; 97 if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) { 98 next_cdiff = cdiff; 99 } 100 } 101 102 qemu_set_irq(gtb->irq, 103 gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE)); 104 } 105 106 timer_del(s->timer); 107 if (next_cdiff) { 108 DB_PRINT("scheduling qemu_timer to fire again in %" 109 PRIx64 " cycles\n", next_cdiff); 110 timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s)); 111 } 112 113 if (s->control & R_CONTROL_TIMER_ENABLE) { 114 s->counter = update.new; 115 } 116 117 if (sync) { 118 s->cpu_ref_time = update.now; 119 s->ref_counter = s->counter; 120 } 121 } 122 123 static void a9_gtimer_update_no_sync(void *opaque) 124 { 125 A9GTimerState *s = A9_GTIMER(opaque); 126 127 a9_gtimer_update(s, false); 128 } 129 130 static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size) 131 { 132 A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; 133 A9GTimerState *s = gtb->parent; 134 A9GTimerUpdate update; 135 uint64_t ret = 0; 136 int shift = 0; 137 138 switch (addr) { 139 case R_COUNTER_HI: 140 shift = 32; 141 /* fallthrough */ 142 case R_COUNTER_LO: 143 update = a9_gtimer_get_update(s); 144 ret = extract64(update.new, shift, 32); 145 break; 146 case R_CONTROL: 147 ret = s->control | gtb->control; 148 break; 149 case R_INTERRUPT_STATUS: 150 ret = gtb->status; 151 break; 152 case R_COMPARATOR_HI: 153 shift = 32; 154 /* fallthrough */ 155 case R_COMPARATOR_LO: 156 ret = extract64(gtb->compare, shift, 32); 157 break; 158 case R_AUTO_INCREMENT: 159 ret = gtb->inc; 160 break; 161 default: 162 qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n", 163 (unsigned)addr); 164 return 0; 165 } 166 167 DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret); 168 return ret; 169 } 170 171 static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value, 172 unsigned size) 173 { 174 A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; 175 A9GTimerState *s = gtb->parent; 176 int shift = 0; 177 178 DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value); 179 180 switch (addr) { 181 case R_COUNTER_HI: 182 shift = 32; 183 /* fallthrough */ 184 case R_COUNTER_LO: 185 /* 186 * Keep it simple - ARM docco explicitly says to disable timer before 187 * modding it, so don't bother trying to do all the difficult on the fly 188 * timer modifications - (if they even work in real hardware??). 189 */ 190 if (s->control & R_CONTROL_TIMER_ENABLE) { 191 qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n"); 192 return; 193 } 194 s->counter = deposit64(s->counter, shift, 32, value); 195 return; 196 case R_CONTROL: 197 a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC); 198 gtb->control = value & R_CONTROL_BANKED; 199 s->control = value & ~R_CONTROL_BANKED; 200 break; 201 case R_INTERRUPT_STATUS: 202 a9_gtimer_update(s, false); 203 gtb->status &= ~value; 204 break; 205 case R_COMPARATOR_HI: 206 shift = 32; 207 /* fallthrough */ 208 case R_COMPARATOR_LO: 209 a9_gtimer_update(s, false); 210 gtb->compare = deposit64(gtb->compare, shift, 32, value); 211 break; 212 case R_AUTO_INCREMENT: 213 gtb->inc = value; 214 return; 215 default: 216 return; 217 } 218 219 a9_gtimer_update(s, false); 220 } 221 222 /* Wrapper functions to implement the "read global timer for 223 * the current CPU" memory regions. 224 */ 225 static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr, 226 unsigned size) 227 { 228 A9GTimerState *s = A9_GTIMER(opaque); 229 int id = a9_gtimer_get_current_cpu(s); 230 231 /* no \n so concatenates with message from read fn */ 232 DB_PRINT("CPU:%d:", id); 233 234 return a9_gtimer_read(&s->per_cpu[id], addr, size); 235 } 236 237 static void a9_gtimer_this_write(void *opaque, hwaddr addr, 238 uint64_t value, unsigned size) 239 { 240 A9GTimerState *s = A9_GTIMER(opaque); 241 int id = a9_gtimer_get_current_cpu(s); 242 243 /* no \n so concatenates with message from write fn */ 244 DB_PRINT("CPU:%d:", id); 245 246 a9_gtimer_write(&s->per_cpu[id], addr, value, size); 247 } 248 249 static const MemoryRegionOps a9_gtimer_this_ops = { 250 .read = a9_gtimer_this_read, 251 .write = a9_gtimer_this_write, 252 .valid = { 253 .min_access_size = 4, 254 .max_access_size = 4, 255 }, 256 .endianness = DEVICE_NATIVE_ENDIAN, 257 }; 258 259 static const MemoryRegionOps a9_gtimer_ops = { 260 .read = a9_gtimer_read, 261 .write = a9_gtimer_write, 262 .valid = { 263 .min_access_size = 4, 264 .max_access_size = 4, 265 }, 266 .endianness = DEVICE_NATIVE_ENDIAN, 267 }; 268 269 static void a9_gtimer_reset(DeviceState *dev) 270 { 271 A9GTimerState *s = A9_GTIMER(dev); 272 int i; 273 274 s->counter = 0; 275 s->control = 0; 276 277 for (i = 0; i < s->num_cpu; i++) { 278 A9GTimerPerCPU *gtb = &s->per_cpu[i]; 279 280 gtb->control = 0; 281 gtb->status = 0; 282 gtb->compare = 0; 283 gtb->inc = 0; 284 } 285 a9_gtimer_update(s, false); 286 } 287 288 static void a9_gtimer_realize(DeviceState *dev, Error **errp) 289 { 290 A9GTimerState *s = A9_GTIMER(dev); 291 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 292 int i; 293 294 if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) { 295 error_setg(errp, "%s: num-cpu must be between 1 and %d", 296 __func__, A9_GTIMER_MAX_CPUS); 297 return; 298 } 299 300 memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s, 301 "a9gtimer shared", 0x20); 302 sysbus_init_mmio(sbd, &s->iomem); 303 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s); 304 305 for (i = 0; i < s->num_cpu; i++) { 306 A9GTimerPerCPU *gtb = &s->per_cpu[i]; 307 308 gtb->parent = s; 309 sysbus_init_irq(sbd, >b->irq); 310 memory_region_init_io(>b->iomem, OBJECT(dev), &a9_gtimer_ops, gtb, 311 "a9gtimer per cpu", 0x20); 312 sysbus_init_mmio(sbd, >b->iomem); 313 } 314 } 315 316 static const VMStateDescription vmstate_a9_gtimer_per_cpu = { 317 .name = "arm.cortex-a9-global-timer.percpu", 318 .version_id = 1, 319 .minimum_version_id = 1, 320 .fields = (VMStateField[]) { 321 VMSTATE_UINT32(control, A9GTimerPerCPU), 322 VMSTATE_UINT64(compare, A9GTimerPerCPU), 323 VMSTATE_UINT32(status, A9GTimerPerCPU), 324 VMSTATE_UINT32(inc, A9GTimerPerCPU), 325 VMSTATE_END_OF_LIST() 326 } 327 }; 328 329 static const VMStateDescription vmstate_a9_gtimer = { 330 .name = "arm.cortex-a9-global-timer", 331 .version_id = 1, 332 .minimum_version_id = 1, 333 .fields = (VMStateField[]) { 334 VMSTATE_TIMER_PTR(timer, A9GTimerState), 335 VMSTATE_UINT64(counter, A9GTimerState), 336 VMSTATE_UINT64(ref_counter, A9GTimerState), 337 VMSTATE_UINT64(cpu_ref_time, A9GTimerState), 338 VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu, 339 1, vmstate_a9_gtimer_per_cpu, 340 A9GTimerPerCPU), 341 VMSTATE_END_OF_LIST() 342 } 343 }; 344 345 static Property a9_gtimer_properties[] = { 346 DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), 347 DEFINE_PROP_END_OF_LIST() 348 }; 349 350 static void a9_gtimer_class_init(ObjectClass *klass, void *data) 351 { 352 DeviceClass *dc = DEVICE_CLASS(klass); 353 354 dc->realize = a9_gtimer_realize; 355 dc->vmsd = &vmstate_a9_gtimer; 356 dc->reset = a9_gtimer_reset; 357 dc->props = a9_gtimer_properties; 358 } 359 360 static const TypeInfo a9_gtimer_info = { 361 .name = TYPE_A9_GTIMER, 362 .parent = TYPE_SYS_BUS_DEVICE, 363 .instance_size = sizeof(A9GTimerState), 364 .class_init = a9_gtimer_class_init, 365 }; 366 367 static void a9_gtimer_register_types(void) 368 { 369 type_register_static(&a9_gtimer_info); 370 } 371 372 type_init(a9_gtimer_register_types) 373