qemu/hw/timer/armv7m_systick.c
<<
>>
Prefs
   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 "hw/qdev-clock.h"
  18#include "qemu/timer.h"
  19#include "qemu/log.h"
  20#include "qemu/module.h"
  21#include "qapi/error.h"
  22#include "trace.h"
  23
  24#define SYSTICK_ENABLE    (1 << 0)
  25#define SYSTICK_TICKINT   (1 << 1)
  26#define SYSTICK_CLKSOURCE (1 << 2)
  27#define SYSTICK_COUNTFLAG (1 << 16)
  28
  29#define SYSCALIB_NOREF (1U << 31)
  30#define SYSCALIB_SKEW (1U << 30)
  31#define SYSCALIB_TENMS ((1U << 24) - 1)
  32
  33static void systick_set_period_from_clock(SysTickState *s)
  34{
  35    /*
  36     * Set the ptimer period from whichever clock is selected.
  37     * Must be called from within a ptimer transaction block.
  38     */
  39    if (s->control & SYSTICK_CLKSOURCE) {
  40        ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
  41    } else {
  42        ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
  43    }
  44}
  45
  46static void systick_timer_tick(void *opaque)
  47{
  48    SysTickState *s = (SysTickState *)opaque;
  49
  50    trace_systick_timer_tick();
  51
  52    s->control |= SYSTICK_COUNTFLAG;
  53    if (s->control & SYSTICK_TICKINT) {
  54        /* Tell the NVIC to pend the SysTick exception */
  55        qemu_irq_pulse(s->irq);
  56    }
  57    if (ptimer_get_limit(s->ptimer) == 0) {
  58        /*
  59         * Timer expiry with SYST_RVR zero disables the timer
  60         * (but doesn't clear SYST_CSR.ENABLE)
  61         */
  62        ptimer_stop(s->ptimer);
  63    }
  64}
  65
  66static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
  67                                unsigned size, MemTxAttrs attrs)
  68{
  69    SysTickState *s = opaque;
  70    uint32_t val;
  71
  72    if (attrs.user) {
  73        /* Generate BusFault for unprivileged accesses */
  74        return MEMTX_ERROR;
  75    }
  76
  77    switch (addr) {
  78    case 0x0: /* SysTick Control and Status.  */
  79        val = s->control;
  80        s->control &= ~SYSTICK_COUNTFLAG;
  81        break;
  82    case 0x4: /* SysTick Reload Value.  */
  83        val = ptimer_get_limit(s->ptimer);
  84        break;
  85    case 0x8: /* SysTick Current Value.  */
  86        val = ptimer_get_count(s->ptimer);
  87        break;
  88    case 0xc: /* SysTick Calibration Value.  */
  89        /*
  90         * In real hardware it is possible to make this register report
  91         * a different value from what the reference clock is actually
  92         * running at. We don't model that (which usually happens due
  93         * to integration errors in the real hardware) and instead always
  94         * report the theoretical correct value as described in the
  95         * knowledgebase article at
  96         * https://developer.arm.com/documentation/ka001325/latest
  97         * If necessary, we could implement an extra QOM property on this
  98         * device to force the STCALIB value to something different from
  99         * the "correct" value.
 100         */
 101        if (!clock_has_source(s->refclk)) {
 102            val = SYSCALIB_NOREF;
 103            break;
 104        }
 105        val = clock_ns_to_ticks(s->refclk, 10 * SCALE_MS) - 1;
 106        val &= SYSCALIB_TENMS;
 107        if (clock_ticks_to_ns(s->refclk, val + 1) != 10 * SCALE_MS) {
 108            /* report that tick count does not yield exactly 10ms */
 109            val |= SYSCALIB_SKEW;
 110        }
 111        break;
 112    default:
 113        val = 0;
 114        qemu_log_mask(LOG_GUEST_ERROR,
 115                      "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
 116        break;
 117    }
 118
 119    trace_systick_read(addr, val, size);
 120    *data = val;
 121    return MEMTX_OK;
 122}
 123
 124static MemTxResult systick_write(void *opaque, hwaddr addr,
 125                                 uint64_t value, unsigned size,
 126                                 MemTxAttrs attrs)
 127{
 128    SysTickState *s = opaque;
 129
 130    if (attrs.user) {
 131        /* Generate BusFault for unprivileged accesses */
 132        return MEMTX_ERROR;
 133    }
 134
 135    trace_systick_write(addr, value, size);
 136
 137    switch (addr) {
 138    case 0x0: /* SysTick Control and Status.  */
 139    {
 140        uint32_t oldval;
 141
 142        if (!clock_has_source(s->refclk)) {
 143            /* This bit is always 1 if there is no external refclk */
 144            value |= SYSTICK_CLKSOURCE;
 145        }
 146
 147        ptimer_transaction_begin(s->ptimer);
 148        oldval = s->control;
 149        s->control &= 0xfffffff8;
 150        s->control |= value & 7;
 151
 152        if ((oldval ^ value) & SYSTICK_ENABLE) {
 153            if (value & SYSTICK_ENABLE) {
 154                ptimer_run(s->ptimer, 0);
 155            } else {
 156                ptimer_stop(s->ptimer);
 157            }
 158        }
 159
 160        if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
 161            systick_set_period_from_clock(s);
 162        }
 163        ptimer_transaction_commit(s->ptimer);
 164        break;
 165    }
 166    case 0x4: /* SysTick Reload Value.  */
 167        ptimer_transaction_begin(s->ptimer);
 168        ptimer_set_limit(s->ptimer, value & 0xffffff, 0);
 169        ptimer_transaction_commit(s->ptimer);
 170        break;
 171    case 0x8: /* SysTick Current Value. */
 172        /*
 173         * Writing any value clears SYST_CVR to zero and clears
 174         * SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR
 175         * on the next clock edge unless SYST_RVR is zero.
 176         */
 177        ptimer_transaction_begin(s->ptimer);
 178        if (ptimer_get_limit(s->ptimer) == 0) {
 179            ptimer_stop(s->ptimer);
 180        }
 181        ptimer_set_count(s->ptimer, 0);
 182        s->control &= ~SYSTICK_COUNTFLAG;
 183        ptimer_transaction_commit(s->ptimer);
 184        break;
 185    default:
 186        qemu_log_mask(LOG_GUEST_ERROR,
 187                      "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
 188    }
 189    return MEMTX_OK;
 190}
 191
 192static const MemoryRegionOps systick_ops = {
 193    .read_with_attrs = systick_read,
 194    .write_with_attrs = systick_write,
 195    .endianness = DEVICE_NATIVE_ENDIAN,
 196    .valid.min_access_size = 4,
 197    .valid.max_access_size = 4,
 198};
 199
 200static void systick_reset(DeviceState *dev)
 201{
 202    SysTickState *s = SYSTICK(dev);
 203
 204    ptimer_transaction_begin(s->ptimer);
 205    s->control = 0;
 206    if (!clock_has_source(s->refclk)) {
 207        /* This bit is always 1 if there is no external refclk */
 208        s->control |= SYSTICK_CLKSOURCE;
 209    }
 210    ptimer_stop(s->ptimer);
 211    ptimer_set_count(s->ptimer, 0);
 212    ptimer_set_limit(s->ptimer, 0, 0);
 213    systick_set_period_from_clock(s);
 214    ptimer_transaction_commit(s->ptimer);
 215}
 216
 217static void systick_cpuclk_update(void *opaque, ClockEvent event)
 218{
 219    SysTickState *s = SYSTICK(opaque);
 220
 221    if (!(s->control & SYSTICK_CLKSOURCE)) {
 222        /* currently using refclk, we can ignore cpuclk changes */
 223    }
 224
 225    ptimer_transaction_begin(s->ptimer);
 226    ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
 227    ptimer_transaction_commit(s->ptimer);
 228}
 229
 230static void systick_refclk_update(void *opaque, ClockEvent event)
 231{
 232    SysTickState *s = SYSTICK(opaque);
 233
 234    if (s->control & SYSTICK_CLKSOURCE) {
 235        /* currently using cpuclk, we can ignore refclk changes */
 236    }
 237
 238    ptimer_transaction_begin(s->ptimer);
 239    ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
 240    ptimer_transaction_commit(s->ptimer);
 241}
 242
 243static void systick_instance_init(Object *obj)
 244{
 245    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 246    SysTickState *s = SYSTICK(obj);
 247
 248    memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
 249    sysbus_init_mmio(sbd, &s->iomem);
 250    sysbus_init_irq(sbd, &s->irq);
 251
 252    s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
 253                                   systick_refclk_update, s, ClockUpdate);
 254    s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk",
 255                                   systick_cpuclk_update, s, ClockUpdate);
 256}
 257
 258static void systick_realize(DeviceState *dev, Error **errp)
 259{
 260    SysTickState *s = SYSTICK(dev);
 261    s->ptimer = ptimer_init(systick_timer_tick, s,
 262                            PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
 263                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
 264                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
 265                            PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
 266
 267    if (!clock_has_source(s->cpuclk)) {
 268        error_setg(errp, "systick: cpuclk must be connected");
 269        return;
 270    }
 271    /* It's OK not to connect the refclk */
 272}
 273
 274static const VMStateDescription vmstate_systick = {
 275    .name = "armv7m_systick",
 276    .version_id = 3,
 277    .minimum_version_id = 3,
 278    .fields = (VMStateField[]) {
 279        VMSTATE_CLOCK(refclk, SysTickState),
 280        VMSTATE_CLOCK(cpuclk, SysTickState),
 281        VMSTATE_UINT32(control, SysTickState),
 282        VMSTATE_INT64(tick, SysTickState),
 283        VMSTATE_PTIMER(ptimer, SysTickState),
 284        VMSTATE_END_OF_LIST()
 285    }
 286};
 287
 288static void systick_class_init(ObjectClass *klass, void *data)
 289{
 290    DeviceClass *dc = DEVICE_CLASS(klass);
 291
 292    dc->vmsd = &vmstate_systick;
 293    dc->reset = systick_reset;
 294    dc->realize = systick_realize;
 295}
 296
 297static const TypeInfo armv7m_systick_info = {
 298    .name = TYPE_SYSTICK,
 299    .parent = TYPE_SYS_BUS_DEVICE,
 300    .instance_init = systick_instance_init,
 301    .instance_size = sizeof(SysTickState),
 302    .class_init = systick_class_init,
 303};
 304
 305static void armv7m_systick_register_types(void)
 306{
 307    type_register_static(&armv7m_systick_info);
 308}
 309
 310type_init(armv7m_systick_register_types)
 311