xref: /openbmc/qemu/hw/timer/bcm2835_systmr.c (revision f7160f32)
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_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 
39 static 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 
47 static 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 
77 static 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 
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,
111     },
112 };
113 
114 static void bcm2835_systmr_reset(DeviceState *dev)
115 {
116     BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
117 
118     memset(&s->reg, 0, sizeof(s->reg));
119 }
120 
121 static 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 
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()
139     }
140 };
141 
142 static 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 
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,
156 };
157 
158 static void bcm2835_systmr_register_types(void)
159 {
160     type_register_static(&bcm2835_systmr_info);
161 }
162 
163 type_init(bcm2835_systmr_register_types);
164