goldfish_rtc.c (8620B)
1/* 2 * Goldfish virtual platform RTC 3 * 4 * Copyright (C) 2019 Western Digital Corporation or its affiliates. 5 * 6 * For more details on Google Goldfish virtual platform refer: 7 * https://android.googlesource.com/platform/external/qemu/+/refs/heads/emu-2.0-release/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms and conditions of the GNU General Public License, 11 * version 2 or later, as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22#include "qemu/osdep.h" 23#include "qemu-common.h" 24#include "hw/rtc/goldfish_rtc.h" 25#include "migration/vmstate.h" 26#include "hw/irq.h" 27#include "hw/qdev-properties.h" 28#include "hw/sysbus.h" 29#include "qemu/bitops.h" 30#include "qemu/timer.h" 31#include "sysemu/sysemu.h" 32#include "qemu/cutils.h" 33#include "qemu/log.h" 34 35#include "trace.h" 36 37#define RTC_TIME_LOW 0x00 38#define RTC_TIME_HIGH 0x04 39#define RTC_ALARM_LOW 0x08 40#define RTC_ALARM_HIGH 0x0c 41#define RTC_IRQ_ENABLED 0x10 42#define RTC_CLEAR_ALARM 0x14 43#define RTC_ALARM_STATUS 0x18 44#define RTC_CLEAR_INTERRUPT 0x1c 45 46static void goldfish_rtc_update(GoldfishRTCState *s) 47{ 48 qemu_set_irq(s->irq, (s->irq_pending & s->irq_enabled) ? 1 : 0); 49} 50 51static void goldfish_rtc_interrupt(void *opaque) 52{ 53 GoldfishRTCState *s = (GoldfishRTCState *)opaque; 54 55 s->alarm_running = 0; 56 s->irq_pending = 1; 57 goldfish_rtc_update(s); 58} 59 60static uint64_t goldfish_rtc_get_count(GoldfishRTCState *s) 61{ 62 return s->tick_offset + (uint64_t)qemu_clock_get_ns(rtc_clock); 63} 64 65static void goldfish_rtc_clear_alarm(GoldfishRTCState *s) 66{ 67 timer_del(s->timer); 68 s->alarm_running = 0; 69} 70 71static void goldfish_rtc_set_alarm(GoldfishRTCState *s) 72{ 73 uint64_t ticks = goldfish_rtc_get_count(s); 74 uint64_t event = s->alarm_next; 75 76 if (event <= ticks) { 77 goldfish_rtc_clear_alarm(s); 78 goldfish_rtc_interrupt(s); 79 } else { 80 /* 81 * We should be setting timer expiry to: 82 * qemu_clock_get_ns(rtc_clock) + (event - ticks) 83 * but this is equivalent to: 84 * event - s->tick_offset 85 */ 86 timer_mod(s->timer, event - s->tick_offset); 87 s->alarm_running = 1; 88 } 89} 90 91static uint64_t goldfish_rtc_read(void *opaque, hwaddr offset, 92 unsigned size) 93{ 94 GoldfishRTCState *s = opaque; 95 uint64_t r = 0; 96 97 /* 98 * From the documentation linked at the top of the file: 99 * 100 * To read the value, the kernel must perform an IO_READ(TIME_LOW), which 101 * returns an unsigned 32-bit value, before an IO_READ(TIME_HIGH), which 102 * returns a signed 32-bit value, corresponding to the higher half of the 103 * full value. 104 */ 105 switch (offset) { 106 case RTC_TIME_LOW: 107 r = goldfish_rtc_get_count(s); 108 s->time_high = r >> 32; 109 r &= 0xffffffff; 110 break; 111 case RTC_TIME_HIGH: 112 r = s->time_high; 113 break; 114 case RTC_ALARM_LOW: 115 r = s->alarm_next & 0xffffffff; 116 break; 117 case RTC_ALARM_HIGH: 118 r = s->alarm_next >> 32; 119 break; 120 case RTC_IRQ_ENABLED: 121 r = s->irq_enabled; 122 break; 123 case RTC_ALARM_STATUS: 124 r = s->alarm_running; 125 break; 126 default: 127 qemu_log_mask(LOG_GUEST_ERROR, 128 "%s: offset 0x%x is UNIMP.\n", __func__, (uint32_t)offset); 129 break; 130 } 131 132 trace_goldfish_rtc_read(offset, r); 133 134 return r; 135} 136 137static void goldfish_rtc_write(void *opaque, hwaddr offset, 138 uint64_t value, unsigned size) 139{ 140 GoldfishRTCState *s = opaque; 141 uint64_t current_tick, new_tick; 142 143 switch (offset) { 144 case RTC_TIME_LOW: 145 current_tick = goldfish_rtc_get_count(s); 146 new_tick = deposit64(current_tick, 0, 32, value); 147 s->tick_offset += new_tick - current_tick; 148 break; 149 case RTC_TIME_HIGH: 150 current_tick = goldfish_rtc_get_count(s); 151 new_tick = deposit64(current_tick, 32, 32, value); 152 s->tick_offset += new_tick - current_tick; 153 break; 154 case RTC_ALARM_LOW: 155 s->alarm_next = deposit64(s->alarm_next, 0, 32, value); 156 goldfish_rtc_set_alarm(s); 157 break; 158 case RTC_ALARM_HIGH: 159 s->alarm_next = deposit64(s->alarm_next, 32, 32, value); 160 break; 161 case RTC_IRQ_ENABLED: 162 s->irq_enabled = (uint32_t)(value & 0x1); 163 goldfish_rtc_update(s); 164 break; 165 case RTC_CLEAR_ALARM: 166 goldfish_rtc_clear_alarm(s); 167 break; 168 case RTC_CLEAR_INTERRUPT: 169 s->irq_pending = 0; 170 goldfish_rtc_update(s); 171 break; 172 default: 173 qemu_log_mask(LOG_GUEST_ERROR, 174 "%s: offset 0x%x is UNIMP.\n", __func__, (uint32_t)offset); 175 break; 176 } 177 178 trace_goldfish_rtc_write(offset, value); 179} 180 181static int goldfish_rtc_pre_save(void *opaque) 182{ 183 uint64_t delta; 184 GoldfishRTCState *s = opaque; 185 186 /* 187 * We want to migrate this offset, which sounds straightforward. 188 * Unfortunately, we cannot directly pass tick_offset because 189 * rtc_clock on destination Host might not be same source Host. 190 * 191 * To tackle, this we pass tick_offset relative to vm_clock from 192 * source Host and make it relative to rtc_clock at destination Host. 193 */ 194 delta = qemu_clock_get_ns(rtc_clock) - 195 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 196 s->tick_offset_vmstate = s->tick_offset + delta; 197 198 return 0; 199} 200 201static int goldfish_rtc_post_load(void *opaque, int version_id) 202{ 203 uint64_t delta; 204 GoldfishRTCState *s = opaque; 205 206 /* 207 * We extract tick_offset from tick_offset_vmstate by doing 208 * reverse math compared to pre_save() function. 209 */ 210 delta = qemu_clock_get_ns(rtc_clock) - 211 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 212 s->tick_offset = s->tick_offset_vmstate - delta; 213 214 goldfish_rtc_set_alarm(s); 215 216 return 0; 217} 218 219static const MemoryRegionOps goldfish_rtc_ops = { 220 .read = goldfish_rtc_read, 221 .write = goldfish_rtc_write, 222 .endianness = DEVICE_NATIVE_ENDIAN, 223 .valid = { 224 .min_access_size = 4, 225 .max_access_size = 4 226 } 227}; 228 229static const VMStateDescription goldfish_rtc_vmstate = { 230 .name = TYPE_GOLDFISH_RTC, 231 .version_id = 2, 232 .pre_save = goldfish_rtc_pre_save, 233 .post_load = goldfish_rtc_post_load, 234 .fields = (VMStateField[]) { 235 VMSTATE_UINT64(tick_offset_vmstate, GoldfishRTCState), 236 VMSTATE_UINT64(alarm_next, GoldfishRTCState), 237 VMSTATE_UINT32(alarm_running, GoldfishRTCState), 238 VMSTATE_UINT32(irq_pending, GoldfishRTCState), 239 VMSTATE_UINT32(irq_enabled, GoldfishRTCState), 240 VMSTATE_UINT32(time_high, GoldfishRTCState), 241 VMSTATE_END_OF_LIST() 242 } 243}; 244 245static void goldfish_rtc_reset(DeviceState *dev) 246{ 247 GoldfishRTCState *s = GOLDFISH_RTC(dev); 248 struct tm tm; 249 250 timer_del(s->timer); 251 252 qemu_get_timedate(&tm, 0); 253 s->tick_offset = mktimegm(&tm); 254 s->tick_offset *= NANOSECONDS_PER_SECOND; 255 s->tick_offset -= qemu_clock_get_ns(rtc_clock); 256 s->tick_offset_vmstate = 0; 257 s->alarm_next = 0; 258 s->alarm_running = 0; 259 s->irq_pending = 0; 260 s->irq_enabled = 0; 261} 262 263static void goldfish_rtc_realize(DeviceState *d, Error **errp) 264{ 265 SysBusDevice *dev = SYS_BUS_DEVICE(d); 266 GoldfishRTCState *s = GOLDFISH_RTC(d); 267 268 memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_rtc_ops, s, 269 "goldfish_rtc", 0x24); 270 sysbus_init_mmio(dev, &s->iomem); 271 272 sysbus_init_irq(dev, &s->irq); 273 274 s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); 275} 276 277static void goldfish_rtc_class_init(ObjectClass *klass, void *data) 278{ 279 DeviceClass *dc = DEVICE_CLASS(klass); 280 281 dc->realize = goldfish_rtc_realize; 282 dc->reset = goldfish_rtc_reset; 283 dc->vmsd = &goldfish_rtc_vmstate; 284} 285 286static const TypeInfo goldfish_rtc_info = { 287 .name = TYPE_GOLDFISH_RTC, 288 .parent = TYPE_SYS_BUS_DEVICE, 289 .instance_size = sizeof(GoldfishRTCState), 290 .class_init = goldfish_rtc_class_init, 291}; 292 293static void goldfish_rtc_register_types(void) 294{ 295 type_register_static(&goldfish_rtc_info); 296} 297 298type_init(goldfish_rtc_register_types)