qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

armv7m_systick.c (9375B)


      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 
     33 static 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 
     46 static 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 
     66 static 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 
    124 static 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_CLKSOURCE) {
    153             systick_set_period_from_clock(s);
    154         }
    155 
    156         if ((oldval ^ value) & SYSTICK_ENABLE) {
    157             if (value & SYSTICK_ENABLE) {
    158                 ptimer_run(s->ptimer, 0);
    159             } else {
    160                 ptimer_stop(s->ptimer);
    161             }
    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 
    192 static 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 
    200 static 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 
    217 static 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 
    230 static 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 
    243 static 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 
    258 static 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 
    274 static 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 
    288 static 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 
    297 static 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 
    305 static void armv7m_systick_register_types(void)
    306 {
    307     type_register_static(&armv7m_systick_info);
    308 }
    309 
    310 type_init(armv7m_systick_register_types)