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 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);