qemu-options.hx: Fix up the autogenerated rST
[qemu.git] / hw / timer / bcm2835_systmr.c
blob3387a6214a2371392f930b93e872fe6568f76366
1 /*
2 * BCM2835 SYS timer emulation
4 * Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
6 * SPDX-License-Identifier: GPL-2.0-or-later
8 * Datasheet: BCM2835 ARM Peripherals (C6357-M-1398)
9 * https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
11 * Only the free running 64-bit counter is implemented.
12 * The 4 COMPARE registers and the interruption are not implemented.
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"
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)
31 static void bcm2835_systmr_update_irq(BCM2835SystemTimerState *s)
33 bool enable = !!s->reg.status;
35 trace_bcm2835_systmr_irq(enable);
36 qemu_set_irq(s->irq, enable);
39 static void bcm2835_systmr_update_compare(BCM2835SystemTimerState *s,
40 unsigned timer_index)
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);
47 static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
48 unsigned size)
50 BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
51 uint64_t r = 0;
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;
72 trace_bcm2835_systmr_read(offset, r);
74 return r;
77 static void bcm2835_systmr_write(void *opaque, hwaddr offset,
78 uint64_t value, unsigned size)
80 BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
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;
104 static 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,
114 static void bcm2835_systmr_reset(DeviceState *dev)
116 BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
118 memset(&s->reg, 0, sizeof(s->reg));
121 static void bcm2835_systmr_realize(DeviceState *dev, Error **errp)
123 BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
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);
131 static 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()
142 static void bcm2835_systmr_class_init(ObjectClass *klass, void *data)
144 DeviceClass *dc = DEVICE_CLASS(klass);
146 dc->realize = bcm2835_systmr_realize;
147 dc->reset = bcm2835_systmr_reset;
148 dc->vmsd = &bcm2835_systmr_vmstate;
151 static 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,
158 static void bcm2835_systmr_register_types(void)
160 type_register_static(&bcm2835_systmr_info);
163 type_init(bcm2835_systmr_register_types);