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