qemu/hw/timer/bcm2835_systmr.c
<<
>>
Prefs
   1/*
   2 * BCM2835 SYS timer emulation
   3 *
   4 * Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
   5 *
   6 * SPDX-License-Identifier: GPL-2.0-or-later
   7 *
   8 * Datasheet: BCM2835 ARM Peripherals (C6357-M-1398)
   9 * https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
  10 *
  11 * Only the free running 64-bit counter is implemented.
  12 * The 4 COMPARE registers and the interruption are not implemented.
  13 */
  14
  15#include "qemu/osdep.h"
  16#include "qemu/log.h"
  17#include "qemu/timer.h"
  18#include "hw/timer/bcm2835_systmr.h"
  19#include "hw/registerfields.h"
  20#include "migration/vmstate.h"
  21#include "trace.h"
  22
  23REG32(CTRL_STATUS,  0x00)
  24REG32(COUNTER_LOW,  0x04)
  25REG32(COUNTER_HIGH, 0x08)
  26REG32(COMPARE0,     0x0c)
  27REG32(COMPARE1,     0x10)
  28REG32(COMPARE2,     0x14)
  29REG32(COMPARE3,     0x18)
  30
  31static void bcm2835_systmr_timer_expire(void *opaque)
  32{
  33    BCM2835SystemTimerCompare *tmr = opaque;
  34
  35    trace_bcm2835_systmr_timer_expired(tmr->id);
  36    tmr->state->reg.ctrl_status |= 1 << tmr->id;
  37    qemu_set_irq(tmr->irq, 1);
  38}
  39
  40static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
  41                                    unsigned size)
  42{
  43    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
  44    uint64_t r = 0;
  45
  46    switch (offset) {
  47    case A_CTRL_STATUS:
  48        r = s->reg.ctrl_status;
  49        break;
  50    case A_COMPARE0 ... A_COMPARE3:
  51        r = s->reg.compare[(offset - A_COMPARE0) >> 2];
  52        break;
  53    case A_COUNTER_LOW:
  54    case A_COUNTER_HIGH:
  55        /* Free running counter at 1MHz */
  56        r = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
  57        r >>= 8 * (offset - A_COUNTER_LOW);
  58        r &= UINT32_MAX;
  59        break;
  60    default:
  61        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
  62                      __func__, offset);
  63        break;
  64    }
  65    trace_bcm2835_systmr_read(offset, r);
  66
  67    return r;
  68}
  69
  70static void bcm2835_systmr_write(void *opaque, hwaddr offset,
  71                                 uint64_t value64, unsigned size)
  72{
  73    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
  74    int index;
  75    uint32_t value = value64;
  76    uint32_t triggers_delay_us;
  77    uint64_t now;
  78
  79    trace_bcm2835_systmr_write(offset, value);
  80    switch (offset) {
  81    case A_CTRL_STATUS:
  82        s->reg.ctrl_status &= ~value; /* Ack */
  83        for (index = 0; index < ARRAY_SIZE(s->tmr); index++) {
  84            if (extract32(value, index, 1)) {
  85                trace_bcm2835_systmr_irq_ack(index);
  86                qemu_set_irq(s->tmr[index].irq, 0);
  87            }
  88        }
  89        break;
  90    case A_COMPARE0 ... A_COMPARE3:
  91        index = (offset - A_COMPARE0) >> 2;
  92        s->reg.compare[index] = value;
  93        now = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
  94        /* Compare lower 32-bits of the free-running counter. */
  95        triggers_delay_us = value - now;
  96        trace_bcm2835_systmr_run(index, triggers_delay_us);
  97        timer_mod(&s->tmr[index].timer, now + triggers_delay_us);
  98        break;
  99    case A_COUNTER_LOW:
 100    case A_COUNTER_HIGH:
 101        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only ofs 0x%" HWADDR_PRIx "\n",
 102                      __func__, offset);
 103        break;
 104    default:
 105        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
 106                      __func__, offset);
 107        break;
 108    }
 109}
 110
 111static const MemoryRegionOps bcm2835_systmr_ops = {
 112    .read = bcm2835_systmr_read,
 113    .write = bcm2835_systmr_write,
 114    .endianness = DEVICE_LITTLE_ENDIAN,
 115    .impl = {
 116        .min_access_size = 4,
 117        .max_access_size = 4,
 118    },
 119};
 120
 121static void bcm2835_systmr_reset(DeviceState *dev)
 122{
 123    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
 124
 125    memset(&s->reg, 0, sizeof(s->reg));
 126}
 127
 128static void bcm2835_systmr_realize(DeviceState *dev, Error **errp)
 129{
 130    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
 131
 132    memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_systmr_ops,
 133                          s, "bcm2835-sys-timer", 0x20);
 134    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
 135
 136    for (size_t i = 0; i < ARRAY_SIZE(s->tmr); i++) {
 137        s->tmr[i].id = i;
 138        s->tmr[i].state = s;
 139        sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->tmr[i].irq);
 140        timer_init_us(&s->tmr[i].timer, QEMU_CLOCK_VIRTUAL,
 141                      bcm2835_systmr_timer_expire, &s->tmr[i]);
 142    }
 143}
 144
 145static const VMStateDescription bcm2835_systmr_vmstate = {
 146    .name = "bcm2835_sys_timer",
 147    .version_id = 1,
 148    .minimum_version_id = 1,
 149    .fields = (VMStateField[]) {
 150        VMSTATE_UINT32(reg.ctrl_status, BCM2835SystemTimerState),
 151        VMSTATE_UINT32_ARRAY(reg.compare, BCM2835SystemTimerState,
 152                             BCM2835_SYSTIMER_COUNT),
 153        VMSTATE_END_OF_LIST()
 154    }
 155};
 156
 157static void bcm2835_systmr_class_init(ObjectClass *klass, void *data)
 158{
 159    DeviceClass *dc = DEVICE_CLASS(klass);
 160
 161    dc->realize = bcm2835_systmr_realize;
 162    dc->reset = bcm2835_systmr_reset;
 163    dc->vmsd = &bcm2835_systmr_vmstate;
 164}
 165
 166static const TypeInfo bcm2835_systmr_info = {
 167    .name = TYPE_BCM2835_SYSTIMER,
 168    .parent = TYPE_SYS_BUS_DEVICE,
 169    .instance_size = sizeof(BCM2835SystemTimerState),
 170    .class_init = bcm2835_systmr_class_init,
 171};
 172
 173static void bcm2835_systmr_register_types(void)
 174{
 175    type_register_static(&bcm2835_systmr_info);
 176}
 177
 178type_init(bcm2835_systmr_register_types);
 179