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_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
,
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",
47 static uint64_t bcm2835_systmr_read(void *opaque
, hwaddr offset
,
50 BCM2835SystemTimerState
*s
= BCM2835_SYSTIMER(opaque
);
57 case A_COMPARE0
... A_COMPARE3
:
58 r
= s
->reg
.compare
[(offset
- A_COMPARE0
) >> 2];
62 /* Free running counter at 1MHz */
63 r
= qemu_clock_get_us(QEMU_CLOCK_VIRTUAL
);
64 r
>>= 8 * (offset
- A_COUNTER_LOW
);
68 qemu_log_mask(LOG_GUEST_ERROR
, "%s: bad offset 0x%" HWADDR_PRIx
"\n",
72 trace_bcm2835_systmr_read(offset
, 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
);
85 s
->reg
.status
&= ~value
; /* Ack */
86 bcm2835_systmr_update_irq(s
);
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);
94 qemu_log_mask(LOG_GUEST_ERROR
, "%s: read-only ofs 0x%" HWADDR_PRIx
"\n",
98 qemu_log_mask(LOG_GUEST_ERROR
, "%s: bad offset 0x%" HWADDR_PRIx
"\n",
104 static const MemoryRegionOps bcm2835_systmr_ops
= {
105 .read
= bcm2835_systmr_read
,
106 .write
= bcm2835_systmr_write
,
107 .endianness
= DEVICE_LITTLE_ENDIAN
,
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",
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
);