qemu/hw/rtc/xlnx-zynqmp-rtc.c
<<
>>
Prefs
   1/*
   2 * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC).
   3 *
   4 * Copyright (c) 2017 Xilinx Inc.
   5 *
   6 * Written-by: Alistair Francis <alistair.francis@xilinx.com>
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "qemu-common.h"
  29#include "hw/sysbus.h"
  30#include "hw/register.h"
  31#include "qemu/bitops.h"
  32#include "qemu/log.h"
  33#include "qemu/module.h"
  34#include "hw/irq.h"
  35#include "qemu/cutils.h"
  36#include "sysemu/sysemu.h"
  37#include "trace.h"
  38#include "hw/rtc/xlnx-zynqmp-rtc.h"
  39#include "migration/vmstate.h"
  40
  41#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG
  42#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0
  43#endif
  44
  45static void rtc_int_update_irq(XlnxZynqMPRTC *s)
  46{
  47    bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK];
  48    qemu_set_irq(s->irq_rtc_int, pending);
  49}
  50
  51static void addr_error_int_update_irq(XlnxZynqMPRTC *s)
  52{
  53    bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK];
  54    qemu_set_irq(s->irq_addr_error_int, pending);
  55}
  56
  57static uint32_t rtc_get_count(XlnxZynqMPRTC *s)
  58{
  59    int64_t now = qemu_clock_get_ns(rtc_clock);
  60    return s->tick_offset + now / NANOSECONDS_PER_SECOND;
  61}
  62
  63static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64)
  64{
  65    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  66
  67    return rtc_get_count(s);
  68}
  69
  70static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64)
  71{
  72    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  73    rtc_int_update_irq(s);
  74}
  75
  76static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64)
  77{
  78    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  79
  80    s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64;
  81    rtc_int_update_irq(s);
  82    return 0;
  83}
  84
  85static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64)
  86{
  87    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  88
  89    s->regs[R_RTC_INT_MASK] |= (uint32_t) val64;
  90    rtc_int_update_irq(s);
  91    return 0;
  92}
  93
  94static void addr_error_postw(RegisterInfo *reg, uint64_t val64)
  95{
  96    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  97    addr_error_int_update_irq(s);
  98}
  99
 100static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64)
 101{
 102    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 103
 104    s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64;
 105    addr_error_int_update_irq(s);
 106    return 0;
 107}
 108
 109static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64)
 110{
 111    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 112
 113    s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64;
 114    addr_error_int_update_irq(s);
 115    return 0;
 116}
 117
 118static const RegisterAccessInfo rtc_regs_info[] = {
 119    {   .name = "SET_TIME_WRITE",  .addr = A_SET_TIME_WRITE,
 120        .unimp = MAKE_64BIT_MASK(0, 32),
 121    },{ .name = "SET_TIME_READ",  .addr = A_SET_TIME_READ,
 122        .ro = 0xffffffff,
 123        .post_read = current_time_postr,
 124    },{ .name = "CALIB_WRITE",  .addr = A_CALIB_WRITE,
 125        .unimp = MAKE_64BIT_MASK(0, 32),
 126    },{ .name = "CALIB_READ",  .addr = A_CALIB_READ,
 127        .ro = 0x1fffff,
 128    },{ .name = "CURRENT_TIME",  .addr = A_CURRENT_TIME,
 129        .ro = 0xffffffff,
 130        .post_read = current_time_postr,
 131    },{ .name = "CURRENT_TICK",  .addr = A_CURRENT_TICK,
 132        .ro = 0xffff,
 133    },{ .name = "ALARM",  .addr = A_ALARM,
 134    },{ .name = "RTC_INT_STATUS",  .addr = A_RTC_INT_STATUS,
 135        .w1c = 0x3,
 136        .post_write = rtc_int_status_postw,
 137    },{ .name = "RTC_INT_MASK",  .addr = A_RTC_INT_MASK,
 138        .reset = 0x3,
 139        .ro = 0x3,
 140    },{ .name = "RTC_INT_EN",  .addr = A_RTC_INT_EN,
 141        .pre_write = rtc_int_en_prew,
 142    },{ .name = "RTC_INT_DIS",  .addr = A_RTC_INT_DIS,
 143        .pre_write = rtc_int_dis_prew,
 144    },{ .name = "ADDR_ERROR",  .addr = A_ADDR_ERROR,
 145        .w1c = 0x1,
 146        .post_write = addr_error_postw,
 147    },{ .name = "ADDR_ERROR_INT_MASK",  .addr = A_ADDR_ERROR_INT_MASK,
 148        .reset = 0x1,
 149        .ro = 0x1,
 150    },{ .name = "ADDR_ERROR_INT_EN",  .addr = A_ADDR_ERROR_INT_EN,
 151        .pre_write = addr_error_int_en_prew,
 152    },{ .name = "ADDR_ERROR_INT_DIS",  .addr = A_ADDR_ERROR_INT_DIS,
 153        .pre_write = addr_error_int_dis_prew,
 154    },{ .name = "CONTROL",  .addr = A_CONTROL,
 155        .reset = 0x1000000,
 156        .rsvd = 0x70fffffe,
 157    },{ .name = "SAFETY_CHK",  .addr = A_SAFETY_CHK,
 158    }
 159};
 160
 161static void rtc_reset(DeviceState *dev)
 162{
 163    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev);
 164    unsigned int i;
 165
 166    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 167        register_reset(&s->regs_info[i]);
 168    }
 169
 170    rtc_int_update_irq(s);
 171    addr_error_int_update_irq(s);
 172}
 173
 174static const MemoryRegionOps rtc_ops = {
 175    .read = register_read_memory,
 176    .write = register_write_memory,
 177    .endianness = DEVICE_LITTLE_ENDIAN,
 178    .valid = {
 179        .min_access_size = 4,
 180        .max_access_size = 4,
 181    },
 182};
 183
 184static void rtc_init(Object *obj)
 185{
 186    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj);
 187    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 188    RegisterInfoArray *reg_array;
 189    struct tm current_tm;
 190
 191    memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC,
 192                       XLNX_ZYNQMP_RTC_R_MAX * 4);
 193    reg_array =
 194        register_init_block32(DEVICE(obj), rtc_regs_info,
 195                              ARRAY_SIZE(rtc_regs_info),
 196                              s->regs_info, s->regs,
 197                              &rtc_ops,
 198                              XLNX_ZYNQMP_RTC_ERR_DEBUG,
 199                              XLNX_ZYNQMP_RTC_R_MAX * 4);
 200    memory_region_add_subregion(&s->iomem,
 201                                0x0,
 202                                &reg_array->mem);
 203    sysbus_init_mmio(sbd, &s->iomem);
 204    sysbus_init_irq(sbd, &s->irq_rtc_int);
 205    sysbus_init_irq(sbd, &s->irq_addr_error_int);
 206
 207    qemu_get_timedate(&current_tm, 0);
 208    s->tick_offset = mktimegm(&current_tm) -
 209        qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 210
 211    trace_xlnx_zynqmp_rtc_gettime(current_tm.tm_year, current_tm.tm_mon,
 212                                  current_tm.tm_mday, current_tm.tm_hour,
 213                                  current_tm.tm_min, current_tm.tm_sec);
 214}
 215
 216static int rtc_pre_save(void *opaque)
 217{
 218    XlnxZynqMPRTC *s = opaque;
 219    int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 220
 221    /* Add the time at migration */
 222    s->tick_offset = s->tick_offset + now;
 223
 224    return 0;
 225}
 226
 227static int rtc_post_load(void *opaque, int version_id)
 228{
 229    XlnxZynqMPRTC *s = opaque;
 230    int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 231
 232    /* Subtract the time after migration. This combined with the pre_save
 233     * action results in us having subtracted the time that the guest was
 234     * stopped to the offset.
 235     */
 236    s->tick_offset = s->tick_offset - now;
 237
 238    return 0;
 239}
 240
 241static const VMStateDescription vmstate_rtc = {
 242    .name = TYPE_XLNX_ZYNQMP_RTC,
 243    .version_id = 1,
 244    .minimum_version_id = 1,
 245    .pre_save = rtc_pre_save,
 246    .post_load = rtc_post_load,
 247    .fields = (VMStateField[]) {
 248        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX),
 249        VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC),
 250        VMSTATE_END_OF_LIST(),
 251    }
 252};
 253
 254static void rtc_class_init(ObjectClass *klass, void *data)
 255{
 256    DeviceClass *dc = DEVICE_CLASS(klass);
 257
 258    dc->reset = rtc_reset;
 259    dc->vmsd = &vmstate_rtc;
 260}
 261
 262static const TypeInfo rtc_info = {
 263    .name          = TYPE_XLNX_ZYNQMP_RTC,
 264    .parent        = TYPE_SYS_BUS_DEVICE,
 265    .instance_size = sizeof(XlnxZynqMPRTC),
 266    .class_init    = rtc_class_init,
 267    .instance_init = rtc_init,
 268};
 269
 270static void rtc_register_types(void)
 271{
 272    type_register_static(&rtc_info);
 273}
 274
 275type_init(rtc_register_types)
 276