qemu

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

bcm2835_systmr.c (5315B)


      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 
     23 REG32(CTRL_STATUS,  0x00)
     24 REG32(COUNTER_LOW,  0x04)
     25 REG32(COUNTER_HIGH, 0x08)
     26 REG32(COMPARE0,     0x0c)
     27 REG32(COMPARE1,     0x10)
     28 REG32(COMPARE2,     0x14)
     29 REG32(COMPARE3,     0x18)
     30 
     31 static 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 
     40 static 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 
     70 static 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 
    111 static 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 
    121 static void bcm2835_systmr_reset(DeviceState *dev)
    122 {
    123     BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
    124 
    125     memset(&s->reg, 0, sizeof(s->reg));
    126 }
    127 
    128 static 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 
    145 static 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 
    157 static 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 
    166 static 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 
    173 static void bcm2835_systmr_register_types(void)
    174 {
    175     type_register_static(&bcm2835_systmr_info);
    176 }
    177 
    178 type_init(bcm2835_systmr_register_types);