bcm2835_st: misc cleanup and bugfixes
[qemu/ar7.git] / hw / timer / bcm2835_st.c
blob0b6348bebd282c0dffa9da1f905fd93465b1afc5
1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
4 */
6 /* Based on several timers code found in various QEMU source files. */
8 #include "hw/timer/bcm2835_st.h"
10 static void bcm2835_st_update(BCM2835StState *s)
12 int64_t now = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
13 uint32_t clo = (uint32_t)now;
14 uint32_t delta;
15 bool set = false;
16 int i;
18 /* Calculate new "next" value and reschedule */
19 for (i = 0; i < 4; i++) {
20 if (!(s->match & (1 << i))) {
21 if (!set || s->compare[i] - clo < delta) {
22 set = true;
23 s->next = s->compare[i];
24 delta = s->next - clo;
29 if (set) {
30 timer_mod(s->timer, now + delta);
31 } else {
32 timer_del(s->timer);
36 static void bcm2835_st_tick(void *opaque)
38 BCM2835StState *s = (BCM2835StState *)opaque;
39 int i;
41 /* Trigger irqs for current "next" value */
42 for (i = 0; i < 4; i++) {
43 if (!(s->match & (1 << i)) && (s->next == s->compare[i])) {
44 s->match |= (1 << i);
45 qemu_set_irq(s->irq[i], 1);
49 bcm2835_st_update(s);
52 static uint64_t bcm2835_st_read(void *opaque, hwaddr offset,
53 unsigned size)
55 BCM2835StState *s = (BCM2835StState *)opaque;
56 uint32_t res = 0;
57 uint64_t now = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
59 assert(size == 4);
61 switch (offset) {
62 case 0x00:
63 res = s->match;
64 break;
65 case 0x04:
66 res = (uint32_t)now;
67 /* Ugly temporary hack to get Plan9 to boot... */
68 /* see http://plan9.bell-labs.com/sources/contrib/ \
69 * miller/rpi/sys/src/9/bcm/clock.c */
70 /* res = (now / 10000) * 10000; */
71 break;
72 case 0x08:
73 res = (now >> 32);
74 break;
75 case 0x0c:
76 res = s->compare[0];
77 break;
78 case 0x10:
79 res = s->compare[1];
80 break;
81 case 0x14:
82 res = s->compare[2];
83 break;
84 case 0x18:
85 res = s->compare[3];
86 break;
87 default:
88 qemu_log_mask(LOG_GUEST_ERROR,
89 "bcm2835_st_read: Bad offset %x\n", (int)offset);
90 return 0;
93 return res;
96 static void bcm2835_st_write(void *opaque, hwaddr offset,
97 uint64_t value, unsigned size)
99 BCM2835StState *s = (BCM2835StState *)opaque;
100 int i;
102 assert(size == 4);
104 switch (offset) {
105 case 0x00:
106 s->match &= ~value & 0x0f;
107 for (i = 0; i < 4; i++) {
108 if (value & (1 << i)) {
109 qemu_set_irq(s->irq[i], 0);
112 break;
113 case 0x0c:
114 s->compare[0] = value;
115 break;
116 case 0x10:
117 s->compare[1] = value;
118 break;
119 case 0x14:
120 s->compare[2] = value;
121 break;
122 case 0x18:
123 s->compare[3] = value;
124 break;
125 default:
126 qemu_log_mask(LOG_GUEST_ERROR,
127 "bcm2835_st_write: Bad offset %x\n", (int)offset);
128 return;
130 bcm2835_st_update(s);
133 static const MemoryRegionOps bcm2835_st_ops = {
134 .read = bcm2835_st_read,
135 .write = bcm2835_st_write,
136 .endianness = DEVICE_NATIVE_ENDIAN,
139 static const VMStateDescription vmstate_bcm2835_st = {
140 .name = TYPE_BCM2835_ST,
141 .version_id = 1,
142 .minimum_version_id = 1,
143 .minimum_version_id_old = 1,
144 .fields = (VMStateField[]) {
145 VMSTATE_UINT32_ARRAY(compare, BCM2835StState, 4),
146 VMSTATE_UINT32(match, BCM2835StState),
147 VMSTATE_END_OF_LIST()
151 static void bcm2835_st_init(Object *obj)
153 BCM2835StState *s = BCM2835_ST(obj);
154 int i;
156 for (i = 0; i < 4; i++) {
157 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq[i]);
160 memory_region_init_io(&s->iomem, obj, &bcm2835_st_ops, s,
161 TYPE_BCM2835_ST, 0x20);
162 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
165 static void bcm2835_st_realize(DeviceState *dev, Error **errp)
167 BCM2835StState *s = BCM2835_ST(dev);
168 int i;
170 for (i = 0; i < 4; i++) {
171 s->compare[i] = 0;
173 s->match = 0;
174 s->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, bcm2835_st_tick, s);
176 bcm2835_st_update(s);
179 static void bcm2835_st_class_init(ObjectClass *klass, void *data)
181 DeviceClass *dc = DEVICE_CLASS(klass);
183 dc->realize = bcm2835_st_realize;
184 dc->vmsd = &vmstate_bcm2835_st;
187 static TypeInfo bcm2835_st_info = {
188 .name = TYPE_BCM2835_ST,
189 .parent = TYPE_SYS_BUS_DEVICE,
190 .instance_size = sizeof(BCM2835StState),
191 .class_init = bcm2835_st_class_init,
192 .instance_init = bcm2835_st_init,
195 static void bcm2835_st_register_types(void)
197 type_register_static(&bcm2835_st_info);
200 type_init(bcm2835_st_register_types)