1 /* 2 * ARMv7M SysTick timer 3 * 4 * Copyright (c) 2006-2007 CodeSourcery. 5 * Written by Paul Brook 6 * Copyright (c) 2017 Linaro Ltd 7 * Written by Peter Maydell 8 * 9 * This code is licensed under the GPL (version 2 or later). 10 */ 11 12 #include "qemu/osdep.h" 13 #include "hw/timer/armv7m_systick.h" 14 #include "migration/vmstate.h" 15 #include "hw/irq.h" 16 #include "hw/sysbus.h" 17 #include "qemu/timer.h" 18 #include "qemu/log.h" 19 #include "qemu/module.h" 20 #include "trace.h" 21 22 /* qemu timers run at 1GHz. We want something closer to 1MHz. */ 23 #define SYSTICK_SCALE 1000ULL 24 25 #define SYSTICK_ENABLE (1 << 0) 26 #define SYSTICK_TICKINT (1 << 1) 27 #define SYSTICK_CLKSOURCE (1 << 2) 28 #define SYSTICK_COUNTFLAG (1 << 16) 29 30 int system_clock_scale; 31 32 /* Conversion factor from qemu timer to SysTick frequencies. */ 33 static inline int64_t systick_scale(SysTickState *s) 34 { 35 if (s->control & SYSTICK_CLKSOURCE) { 36 return system_clock_scale; 37 } else { 38 return 1000; 39 } 40 } 41 42 static void systick_reload(SysTickState *s, int reset) 43 { 44 /* The Cortex-M3 Devices Generic User Guide says that "When the 45 * ENABLE bit is set to 1, the counter loads the RELOAD value from the 46 * SYST RVR register and then counts down". So, we need to check the 47 * ENABLE bit before reloading the value. 48 */ 49 trace_systick_reload(); 50 51 if ((s->control & SYSTICK_ENABLE) == 0) { 52 return; 53 } 54 55 if (reset) { 56 s->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 57 } 58 s->tick += (s->reload + 1) * systick_scale(s); 59 timer_mod(s->timer, s->tick); 60 } 61 62 static void systick_timer_tick(void *opaque) 63 { 64 SysTickState *s = (SysTickState *)opaque; 65 66 trace_systick_timer_tick(); 67 68 s->control |= SYSTICK_COUNTFLAG; 69 if (s->control & SYSTICK_TICKINT) { 70 /* Tell the NVIC to pend the SysTick exception */ 71 qemu_irq_pulse(s->irq); 72 } 73 if (s->reload == 0) { 74 s->control &= ~SYSTICK_ENABLE; 75 } else { 76 systick_reload(s, 0); 77 } 78 } 79 80 static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data, 81 unsigned size, MemTxAttrs attrs) 82 { 83 SysTickState *s = opaque; 84 uint32_t val; 85 86 if (attrs.user) { 87 /* Generate BusFault for unprivileged accesses */ 88 return MEMTX_ERROR; 89 } 90 91 switch (addr) { 92 case 0x0: /* SysTick Control and Status. */ 93 val = s->control; 94 s->control &= ~SYSTICK_COUNTFLAG; 95 break; 96 case 0x4: /* SysTick Reload Value. */ 97 val = s->reload; 98 break; 99 case 0x8: /* SysTick Current Value. */ 100 { 101 int64_t t; 102 103 if ((s->control & SYSTICK_ENABLE) == 0) { 104 val = 0; 105 break; 106 } 107 t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 108 if (t >= s->tick) { 109 val = 0; 110 break; 111 } 112 val = ((s->tick - (t + 1)) / systick_scale(s)) + 1; 113 /* The interrupt in triggered when the timer reaches zero. 114 However the counter is not reloaded until the next clock 115 tick. This is a hack to return zero during the first tick. */ 116 if (val > s->reload) { 117 val = 0; 118 } 119 break; 120 } 121 case 0xc: /* SysTick Calibration Value. */ 122 val = 10000; 123 break; 124 default: 125 val = 0; 126 qemu_log_mask(LOG_GUEST_ERROR, 127 "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr); 128 break; 129 } 130 131 trace_systick_read(addr, val, size); 132 *data = val; 133 return MEMTX_OK; 134 } 135 136 static MemTxResult systick_write(void *opaque, hwaddr addr, 137 uint64_t value, unsigned size, 138 MemTxAttrs attrs) 139 { 140 SysTickState *s = opaque; 141 142 if (attrs.user) { 143 /* Generate BusFault for unprivileged accesses */ 144 return MEMTX_ERROR; 145 } 146 147 trace_systick_write(addr, value, size); 148 149 switch (addr) { 150 case 0x0: /* SysTick Control and Status. */ 151 { 152 uint32_t oldval = s->control; 153 154 s->control &= 0xfffffff8; 155 s->control |= value & 7; 156 if ((oldval ^ value) & SYSTICK_ENABLE) { 157 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 158 if (value & SYSTICK_ENABLE) { 159 if (s->tick) { 160 s->tick += now; 161 timer_mod(s->timer, s->tick); 162 } else { 163 systick_reload(s, 1); 164 } 165 } else { 166 timer_del(s->timer); 167 s->tick -= now; 168 if (s->tick < 0) { 169 s->tick = 0; 170 } 171 } 172 } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { 173 /* This is a hack. Force the timer to be reloaded 174 when the reference clock is changed. */ 175 systick_reload(s, 1); 176 } 177 break; 178 } 179 case 0x4: /* SysTick Reload Value. */ 180 s->reload = value; 181 break; 182 case 0x8: /* SysTick Current Value. Writes reload the timer. */ 183 systick_reload(s, 1); 184 s->control &= ~SYSTICK_COUNTFLAG; 185 break; 186 default: 187 qemu_log_mask(LOG_GUEST_ERROR, 188 "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr); 189 } 190 return MEMTX_OK; 191 } 192 193 static const MemoryRegionOps systick_ops = { 194 .read_with_attrs = systick_read, 195 .write_with_attrs = systick_write, 196 .endianness = DEVICE_NATIVE_ENDIAN, 197 .valid.min_access_size = 4, 198 .valid.max_access_size = 4, 199 }; 200 201 static void systick_reset(DeviceState *dev) 202 { 203 SysTickState *s = SYSTICK(dev); 204 205 s->control = 0; 206 s->reload = 0; 207 s->tick = 0; 208 timer_del(s->timer); 209 } 210 211 static void systick_instance_init(Object *obj) 212 { 213 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 214 SysTickState *s = SYSTICK(obj); 215 216 memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0); 217 sysbus_init_mmio(sbd, &s->iomem); 218 sysbus_init_irq(sbd, &s->irq); 219 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s); 220 } 221 222 static const VMStateDescription vmstate_systick = { 223 .name = "armv7m_systick", 224 .version_id = 1, 225 .minimum_version_id = 1, 226 .fields = (VMStateField[]) { 227 VMSTATE_UINT32(control, SysTickState), 228 VMSTATE_UINT32(reload, SysTickState), 229 VMSTATE_INT64(tick, SysTickState), 230 VMSTATE_TIMER_PTR(timer, SysTickState), 231 VMSTATE_END_OF_LIST() 232 } 233 }; 234 235 static void systick_class_init(ObjectClass *klass, void *data) 236 { 237 DeviceClass *dc = DEVICE_CLASS(klass); 238 239 dc->vmsd = &vmstate_systick; 240 dc->reset = systick_reset; 241 } 242 243 static const TypeInfo armv7m_systick_info = { 244 .name = TYPE_SYSTICK, 245 .parent = TYPE_SYS_BUS_DEVICE, 246 .instance_init = systick_instance_init, 247 .instance_size = sizeof(SysTickState), 248 .class_init = systick_class_init, 249 }; 250 251 static void armv7m_systick_register_types(void) 252 { 253 type_register_static(&armv7m_systick_info); 254 } 255 256 type_init(armv7m_systick_register_types) 257