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"
17 #include "qemu/timer.h"
18 #include "hw/timer/bcm2835_systmr.h"
19 #include "hw/registerfields.h"
20 #include "migration/vmstate.h"
23 REG32(CTRL_STATUS
, 0x00)
24 REG32(COUNTER_LOW
, 0x04)
25 REG32(COUNTER_HIGH
, 0x08)
31 static void bcm2835_systmr_timer_expire(void *opaque
)
33 BCM2835SystemTimerCompare
*tmr
= opaque
;
35 trace_bcm2835_systmr_timer_expired(tmr
->id
);
36 tmr
->state
->reg
.ctrl_status
|= 1 << tmr
->id
;
37 qemu_set_irq(tmr
->irq
, 1);
40 static uint64_t bcm2835_systmr_read(void *opaque
, hwaddr offset
,
43 BCM2835SystemTimerState
*s
= BCM2835_SYSTIMER(opaque
);
48 r
= s
->reg
.ctrl_status
;
50 case A_COMPARE0
... A_COMPARE3
:
51 r
= s
->reg
.compare
[(offset
- A_COMPARE0
) >> 2];
55 /* Free running counter at 1MHz */
56 r
= qemu_clock_get_us(QEMU_CLOCK_VIRTUAL
);
57 r
>>= 8 * (offset
- A_COUNTER_LOW
);
61 qemu_log_mask(LOG_GUEST_ERROR
, "%s: bad offset 0x%" HWADDR_PRIx
"\n",
65 trace_bcm2835_systmr_read(offset
, r
);
70 static void bcm2835_systmr_write(void *opaque
, hwaddr offset
,
71 uint64_t value64
, unsigned size
)
73 BCM2835SystemTimerState
*s
= BCM2835_SYSTIMER(opaque
);
75 uint32_t value
= value64
;
76 uint32_t triggers_delay_us
;
79 trace_bcm2835_systmr_write(offset
, value
);
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);
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
);
101 qemu_log_mask(LOG_GUEST_ERROR
, "%s: read-only ofs 0x%" HWADDR_PRIx
"\n",
105 qemu_log_mask(LOG_GUEST_ERROR
, "%s: bad offset 0x%" HWADDR_PRIx
"\n",
111 static const MemoryRegionOps bcm2835_systmr_ops
= {
112 .read
= bcm2835_systmr_read
,
113 .write
= bcm2835_systmr_write
,
114 .endianness
= DEVICE_LITTLE_ENDIAN
,
116 .min_access_size
= 4,
117 .max_access_size
= 4,
121 static void bcm2835_systmr_reset(DeviceState
*dev
)
123 BCM2835SystemTimerState
*s
= BCM2835_SYSTIMER(dev
);
125 memset(&s
->reg
, 0, sizeof(s
->reg
));
128 static void bcm2835_systmr_realize(DeviceState
*dev
, Error
**errp
)
130 BCM2835SystemTimerState
*s
= BCM2835_SYSTIMER(dev
);
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
);
136 for (size_t i
= 0; i
< ARRAY_SIZE(s
->tmr
); i
++) {
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
]);
145 static const VMStateDescription bcm2835_systmr_vmstate
= {
146 .name
= "bcm2835_sys_timer",
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()
157 static void bcm2835_systmr_class_init(ObjectClass
*klass
, void *data
)
159 DeviceClass
*dc
= DEVICE_CLASS(klass
);
161 dc
->realize
= bcm2835_systmr_realize
;
162 dc
->reset
= bcm2835_systmr_reset
;
163 dc
->vmsd
= &bcm2835_systmr_vmstate
;
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
,
173 static void bcm2835_systmr_register_types(void)
175 type_register_static(&bcm2835_systmr_info
);
178 type_init(bcm2835_systmr_register_types
);