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_update_irq(BCM2835SystemTimerState *s)
  32{
  33    bool enable = !!s->reg.status;
  34
  35    trace_bcm2835_systmr_irq(enable);
  36    qemu_set_irq(s->irq, enable);
  37}
  38
  39static void bcm2835_systmr_update_compare(BCM2835SystemTimerState *s,
  40                                          unsigned timer_index)
  41{
  42    /* TODO fow now, since neither Linux nor U-boot use these timers. */
  43    qemu_log_mask(LOG_UNIMP, "COMPARE register %u not implemented\n",
  44                  timer_index);
  45}
  46
  47static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
  48                                    unsigned size)
  49{
  50    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
  51    uint64_t r = 0;
  52
  53    switch (offset) {
  54    case A_CTRL_STATUS:
  55        r = s->reg.status;
  56        break;
  57    case A_COMPARE0 ... A_COMPARE3:
  58        r = s->reg.compare[(offset - A_COMPARE0) >> 2];
  59        break;
  60    case A_COUNTER_LOW:
  61    case A_COUNTER_HIGH:
  62        /* Free running counter at 1MHz */
  63        r = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
  64        r >>= 8 * (offset - A_COUNTER_LOW);
  65        r &= UINT32_MAX;
  66        break;
  67    default:
  68        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
  69                      __func__, offset);
  70        break;
  71    }
  72    trace_bcm2835_systmr_read(offset, r);
  73
  74    return r;
  75}
  76
  77static void bcm2835_systmr_write(void *opaque, hwaddr offset,
  78                                 uint64_t value, unsigned size)
  79{
  80    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
  81
  82    trace_bcm2835_systmr_write(offset, value);
  83    switch (offset) {
  84    case A_CTRL_STATUS:
  85        s->reg.status &= ~value; /* Ack */
  86        bcm2835_systmr_update_irq(s);
  87        break;
  88    case A_COMPARE0 ... A_COMPARE3:
  89        s->reg.compare[(offset - A_COMPARE0) >> 2] = value;
  90        bcm2835_systmr_update_compare(s, (offset - A_COMPARE0) >> 2);
  91        break;
  92    case A_COUNTER_LOW:
  93    case A_COUNTER_HIGH:
  94        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only ofs 0x%" HWADDR_PRIx "\n",
  95                      __func__, offset);
  96        break;
  97    default:
  98        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
  99                      __func__, offset);
 100        break;
 101    }
 102}
 103
 104static const MemoryRegionOps bcm2835_systmr_ops = {
 105    .read = bcm2835_systmr_read,
 106    .write = bcm2835_systmr_write,
 107    .endianness = DEVICE_LITTLE_ENDIAN,
 108    .impl = {
 109        .min_access_size = 4,
 110        .max_access_size = 4,
 111    },
 112};
 113
 114static void bcm2835_systmr_reset(DeviceState *dev)
 115{
 116    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
 117
 118    memset(&s->reg, 0, sizeof(s->reg));
 119}
 120
 121static void bcm2835_systmr_realize(DeviceState *dev, Error **errp)
 122{
 123    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
 124
 125    memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_systmr_ops,
 126                          s, "bcm2835-sys-timer", 0x20);
 127    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
 128    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
 129}
 130
 131static const VMStateDescription bcm2835_systmr_vmstate = {
 132    .name = "bcm2835_sys_timer",
 133    .version_id = 1,
 134    .minimum_version_id = 1,
 135    .fields = (VMStateField[]) {
 136        VMSTATE_UINT32(reg.status, BCM2835SystemTimerState),
 137        VMSTATE_UINT32_ARRAY(reg.compare, BCM2835SystemTimerState, 4),
 138        VMSTATE_END_OF_LIST()
 139    }
 140};
 141
 142static void bcm2835_systmr_class_init(ObjectClass *klass, void *data)
 143{
 144    DeviceClass *dc = DEVICE_CLASS(klass);
 145
 146    dc->realize = bcm2835_systmr_realize;
 147    dc->reset = bcm2835_systmr_reset;
 148    dc->vmsd = &bcm2835_systmr_vmstate;
 149}
 150
 151static const TypeInfo bcm2835_systmr_info = {
 152    .name = TYPE_BCM2835_SYSTIMER,
 153    .parent = TYPE_SYS_BUS_DEVICE,
 154    .instance_size = sizeof(BCM2835SystemTimerState),
 155    .class_init = bcm2835_systmr_class_init,
 156};
 157
 158static void bcm2835_systmr_register_types(void)
 159{
 160    type_register_static(&bcm2835_systmr_info);
 161}
 162
 163type_init(bcm2835_systmr_register_types);
 164