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 <stdint.h>
  29#include "qemu-common.h"
  30#include "hw/sysbus.h"
  31#include "hw/register.h"
  32#include "qemu/bitops.h"
  33#include "qemu/log.h"
  34#include "qemu/module.h"
  35#include "hw/irq.h"
  36#include "qemu/cutils.h"
  37#include "sysemu/sysemu.h"
  38#include "trace.h"
  39#include "hw/rtc/xlnx-zynqmp-rtc.h"
  40#include "migration/vmstate.h"
  41#include "hw/qdev-properties.h"
  42#include "qemu/timer.h"
  43
  44#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG
  45#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0
  46#endif
  47
  48#if XLNX_ZYNQMP_RTC_ERR_DEBUG
  49    #define DPRINT(fmt, args...) do { \
  50            if (XLNX_ZYNQMP_RTC_ERR_DEBUG) { \
  51                qemu_log(fmt, ##args); \
  52            } \
  53        } while (0)
  54
  55    #define DPRINT_TM(fmt, args...) do { \
  56            if (XLNX_ZYNQMP_RTC_ERR_DEBUG) { \
  57                qemu_log("[%lld] -> " fmt,\
  58                        qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND,\
  59                        ##args);\
  60            } \
  61        } while (0)
  62#else
  63    #define DPRINT(fmt, args...) do {} while (0)
  64    #define DPRINT_TM(fmt, args...) do {} while (0)
  65#endif
  66
  67enum version_id {
  68    IP_VERSION_1_0_1 = 0,
  69    IP_VERSION_2_0_0 = 1
  70};
  71
  72struct version_item_lookup {
  73    enum version_id id;
  74    const char *str;
  75};
  76
  77static struct version_item_lookup version_table_lookup[] = {
  78    { IP_VERSION_1_0_1, "1.0.1" },
  79    { IP_VERSION_2_0_0, "2.0.0" }
  80};
  81
  82static Property xlnx_rtc_properties[] = {
  83    DEFINE_PROP_STRING("version", XlnxZynqMPRTC, cfg.version),
  84    DEFINE_PROP_END_OF_LIST(),
  85};
  86
  87/* Returns the current host time in seconds. */
  88static uint32_t get_host_time_now(void)
  89{
  90    int64_t host_time_now = qemu_clock_get_ns(rtc_clock);
  91    return host_time_now / NANOSECONDS_PER_SECOND;
  92}
  93
  94/* Returns the qemu time (time set with the -rtc command line) in seconds. */
  95static uint32_t get_qemu_time_now(XlnxZynqMPRTC *s)
  96{
  97    return get_host_time_now() - s->tick_offset;
  98}
  99
 100/* Return the guest time in seconds. */
 101static uint32_t get_guest_time_now(XlnxZynqMPRTC *s)
 102{
 103    return get_qemu_time_now(s) - s->guest_offset;
 104}
 105
 106/*Return the designated Guest Time in Host time. */
 107static uint32_t host_time_from_guest(XlnxZynqMPRTC *s, uint32_t guest_time)
 108{
 109    return s->tick_offset + s->guest_offset + guest_time;
 110}
 111
 112static void rtc_int_update_irq(XlnxZynqMPRTC *s)
 113{
 114    uint32_t pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK];
 115    qemu_set_irq(s->irq_rtc_int[0],
 116                 !!(pending & R_RTC_INT_STATUS_ALARM_MASK));
 117    qemu_set_irq(s->irq_rtc_int[1],
 118                 !!(pending & R_RTC_INT_STATUS_SECONDS_MASK));
 119
 120}
 121
 122static void addr_error_int_update_irq(XlnxZynqMPRTC *s)
 123{
 124    bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK];
 125    qemu_set_irq(s->irq_addr_error_int, pending);
 126}
 127
 128static void update_alarm(XlnxZynqMPRTC *s)
 129{
 130    uint32_t alarm;
 131    uint32_t host_time_now;
 132
 133    timer_del(s->alarm);
 134    /*
 135     * Converts the guest alarm time to a host alarm time as all internal
 136     * QEMUTimers are based on host time, this will also take care of all
 137     * overflows.
 138     */
 139    alarm = host_time_from_guest(s, s->regs[R_ALARM]);
 140    host_time_now = get_host_time_now();
 141
 142    /*
 143     * If the alarm time is earlier than the current host time the timer
 144     * callback will be called instantaneously. To avoid this we will only
 145     * arm the timer if the alarm value is at a time later than the current
 146     * host time. Conversion from Guest Time to Host time is taken care of
 147     * by the call to host_time_from_guest().
 148     */
 149    if (alarm > host_time_now) {
 150        timer_mod(s->alarm, alarm * NANOSECONDS_PER_SECOND);
 151    } else if (alarm == host_time_now) {
 152        s->regs[R_RTC_INT_STATUS] = FIELD_DP32(s->regs[R_RTC_INT_STATUS],
 153                                               RTC_INT_STATUS, ALARM, 1);
 154        /* Raise the interrupt if conditions are met. */
 155        rtc_int_update_irq(s);
 156    } else {
 157        qemu_log_mask(LOG_GUEST_ERROR, "%06d : %s() attempting to arm the "
 158                      "alarm timer with a timestamp that is earlier than "
 159                      "the current time: \talarm=%u,\n\tguest time=%u,\n",
 160                      __LINE__, __func__, alarm, get_guest_time_now(s));
 161    }
 162}
 163
 164static void update_seconds(XlnxZynqMPRTC *s)
 165{
 166    uint32_t next_sec;
 167
 168    timer_del(s->sec_tick);
 169    /* Re-arm the seconds tick and go. */
 170    next_sec = get_host_time_now() + 1;
 171    timer_mod(s->sec_tick, next_sec * NANOSECONDS_PER_SECOND);
 172}
 173
 174static void rtc_set_time_write_postw(RegisterInfo *reg, uint64_t val64)
 175{
 176    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 177
 178    s->regs[R_SET_TIME_WRITE] = val64;
 179    /* Will force to read the last value written as per controller spec.*/
 180    s->regs[R_SET_TIME_READ] = val64;
 181    /* Update the guest offset to reflect the new time set. */
 182    s->guest_offset = get_qemu_time_now(s) - s->regs[R_SET_TIME_READ];
 183    DPRINT_TM("%06d : %s()\n", __LINE__, __func__);
 184    DPRINT_TM("Time Marks:\n\tQEMU Time = %u,\n \tHost Time = %u,\n"
 185              " \ts->tick_offset = %u,\n",
 186              get_qemu_time_now(s), get_host_time_now(), s->tick_offset);
 187    DPRINT("\tguest_offset = %010u\n", s->guest_offset);
 188}
 189
 190static void rtc_calib_write_postw(RegisterInfo *reg, uint64_t val64)
 191{
 192    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 193
 194    s->regs[R_CALIB_READ] = val64;
 195    /*
 196     * Since we are not simulating calibration, force CURRENT_TICK
 197     * to always read the MAX_TICK.
 198     */
 199    s->regs[R_CURRENT_TICK] = FIELD_EX32(s->regs[R_CALIB_READ],
 200                                       CALIB_WRITE, MAX_TICK);
 201}
 202
 203static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64)
 204{
 205    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 206    uint32_t guest_time_now = get_guest_time_now(s);
 207    return guest_time_now;
 208}
 209
 210static void alarm_postw(RegisterInfo *reg, uint64_t val64)
 211{
 212    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 213
 214    s->regs[R_ALARM] = val64;
 215    update_alarm(s);
 216}
 217
 218static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64)
 219{
 220    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 221
 222    s->regs[R_RTC_INT_MASK] &= ~val64;
 223    if (FIELD_EX32(s->regs[R_RTC_INT_MASK], RTC_INT_MASK, SECONDS) == 1) {
 224        update_seconds(s);
 225    }
 226    rtc_int_update_irq(s);
 227    return 0;
 228}
 229
 230static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64)
 231{
 232    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 233
 234    s->regs[R_RTC_INT_MASK] |= val64;
 235    rtc_int_update_irq(s);
 236    return 0;
 237}
 238
 239static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64)
 240{
 241    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 242
 243    if (FIELD_EX32(s->regs[R_RTC_INT_STATUS], RTC_INT_STATUS, SECONDS) == 0) {
 244        update_seconds(s);
 245    }
 246    rtc_int_update_irq(s);
 247}
 248
 249static void addr_error_set_status(void *opaque)
 250{
 251    RegisterInfo *ri = *((RegisterInfo **)((RegisterInfoArray*)opaque)->r);
 252    XlnxZynqMPRTC *s = (XlnxZynqMPRTC *)ri->opaque;
 253
 254    s->regs[R_ADDR_ERROR] = FIELD_DP32(s->regs[R_ADDR_ERROR],
 255                                       ADDR_ERROR, STATUS, 1);
 256    addr_error_int_update_irq(s);
 257}
 258
 259static void addr_error_postw(RegisterInfo *reg, uint64_t val64)
 260{
 261    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 262    addr_error_int_update_irq(s);
 263}
 264
 265static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64)
 266{
 267    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 268
 269    s->regs[R_ADDR_ERROR_INT_MASK] &= ~val64;
 270    addr_error_int_update_irq(s);
 271    return 0;
 272}
 273
 274static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64)
 275{
 276    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 277
 278    s->regs[R_ADDR_ERROR_INT_MASK] |= val64;;
 279    addr_error_int_update_irq(s);
 280    return 0;
 281}
 282
 283static const RegisterAccessInfo rtc_regs_info[] = {
 284    {   .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE,
 285        .post_write = rtc_set_time_write_postw,
 286    },{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ,
 287        .ro = 0xffffffff,
 288    },{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE,
 289        .post_write = rtc_calib_write_postw,
 290    },{ .name = "CALIB_READ", .addr = A_CALIB_READ,
 291        .ro = 0x1fffff,
 292    },{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME,
 293        .ro = 0xffffffff,
 294        .post_read = current_time_postr,
 295    },{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK,
 296        .ro = 0xffff,
 297    },{ .name = "ALARM", .addr = A_ALARM,
 298        .post_write = alarm_postw,
 299        .reset = 0x00000000,
 300    },{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS,
 301        .w1c = 0x3,
 302        .post_write = rtc_int_status_postw,
 303    },{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK,
 304        .reset = 0x3,
 305        .ro = 0x3,
 306    },{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN,
 307        .pre_write = rtc_int_en_prew,
 308    },{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS,
 309        .pre_write = rtc_int_dis_prew,
 310    },{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR,
 311        .w1c = 0x1,
 312        .post_write = addr_error_postw,
 313    },{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK,
 314        .reset = 0x1,
 315        .ro = 0x1,
 316    },{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN,
 317        .pre_write = addr_error_int_en_prew,
 318    },{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS,
 319        .pre_write = addr_error_int_dis_prew,
 320    },{ .name = "CONTROL", .addr = A_CONTROL,
 321        .reset = 0x1000000,
 322        .rsvd = 0x70fffffe,
 323    },{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK,
 324    }
 325};
 326
 327static const RegisterAccessInfo rtc_regs_control_v2_info = {
 328    .name = "CONTROL",   .addr = A_CONTROL,
 329    .reset = 0x2000000,  .rsvd = 0x70fffffe,
 330};
 331
 332static enum version_id version_id_lookup(const char *str)
 333{
 334    uint32_t i;
 335    enum version_id version;
 336
 337    version = IP_VERSION_1_0_1;
 338
 339    if (str) {
 340        for (i = 0; i < ARRAY_SIZE(version_table_lookup); ++i) {
 341            if (!strcmp(str, version_table_lookup[i].str)) {
 342                version = version_table_lookup[i].id;
 343                break;
 344            }
 345        }
 346    }
 347
 348    return version;
 349}
 350
 351static void clear_time(XlnxZynqMPRTC *s)
 352{
 353    time_t qemu_time;
 354    /*
 355     * Host Time is determined by the host clock, the QEMU RTC clock ticks
 356     * off from.
 357     */
 358    uint32_t host_time = get_host_time_now();
 359    /*
 360     * QEMU Time is determined by the ISO8601 value passed to QEMU
 361     * in the command line using the -rtc command line option. If
 362     * the user omits the -rtc command line option then QEMU Time
 363     * is equal to the Host Time.
 364     */
 365    struct tm qemu_tm = {0};
 366    qemu_get_timedate(&qemu_tm, 0);
 367    qemu_time = mktimegm(&qemu_tm);
 368    /*
 369     * tick_offset tracks the delta in seconds between the Host Time and
 370     * QEMU Time.
 371     */
 372    s->tick_offset = host_time - qemu_time;
 373    /*
 374     * The Guest Time is the time set by the guest, to begin with we'll use
 375     * the QEMU Time as the Guest Time as this is what was passed at command
 376     * line. We'll apply the QEMU Time to the Guest Set Time Read/Write
 377     * registers. The Guest can change that by writing to the Set Time Write
 378     * Register.
 379     */
 380    s->regs[R_SET_TIME_WRITE] = qemu_time;
 381    s->regs[R_SET_TIME_READ] = qemu_time;
 382    s->guest_offset = qemu_time - s->regs[R_SET_TIME_READ];
 383
 384    DPRINT_TM("%06d : %s()\n", __LINE__, __func__);
 385    DPRINT_TM("Time Marks:\n\tQEMU Time = %lu,\n\tHost Time = %u,\n"
 386              " \ts->tick_offset = %u,\n",
 387              qemu_time, host_time, s->tick_offset);
 388    DPRINT("\tguest_offset = %010u\n", s->guest_offset);
 389    DPRINT("\t%04u-%02u-%02u-T%02u:%02u:%02u (yyyy-mm-ddThh:mm:ss ISO-8601)\n",
 390           qemu_tm.tm_year + 1900, qemu_tm.tm_mon + 1, qemu_tm.tm_mday,
 391           qemu_tm.tm_hour, qemu_tm.tm_min, qemu_tm.tm_sec);
 392}
 393
 394static void rtc_reset(DeviceState *dev)
 395{
 396    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev);
 397    unsigned int i;
 398
 399    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 400        register_reset(&s->regs_info[i]);
 401    }
 402
 403    if (version_id_lookup(s->cfg.version) == IP_VERSION_2_0_0) {
 404        s->regs_info[R_CONTROL].access = &rtc_regs_control_v2_info;
 405        register_reset(&s->regs_info[R_CONTROL]);
 406    }
 407
 408    clear_time(s);
 409}
 410
 411static void alarm_timeout_cb(void *opaque)
 412{
 413    XlnxZynqMPRTC *s = (XlnxZynqMPRTC *)opaque;
 414
 415    s->regs[R_RTC_INT_STATUS] = FIELD_DP32(s->regs[R_RTC_INT_STATUS],
 416        RTC_INT_STATUS, ALARM, 1);
 417    /* Raise Alarm Interrupt Level If Unmasked. */
 418    rtc_int_update_irq(s);
 419}
 420
 421static void second_timeout_cb(void *opaque)
 422{
 423    XlnxZynqMPRTC *s = (XlnxZynqMPRTC *)opaque;
 424
 425    s->regs[R_RTC_INT_STATUS] = FIELD_DP32(s->regs[R_RTC_INT_STATUS],
 426        RTC_INT_STATUS, SECONDS, 1);
 427    /* Raise Seconds Interrupt Level If Unmasked. */
 428    rtc_int_update_irq(s);
 429}
 430
 431static uint64_t rtc_register_read_memory(void *opaque, hwaddr addr,
 432                                         unsigned size)
 433{
 434    if (addr >= (XLNX_ZYNQMP_RTC_R_MAX * 4)) {
 435        DPRINT_TM("%06d : %s()\n", __LINE__, __func__);
 436        DPRINT_TM("\tAttempting to Read from invalid RTC Memory"
 437                  " Space 0x%08lx\n", addr);
 438        addr_error_set_status(opaque);
 439        return (uint64_t)0;
 440    }
 441
 442    return register_read_memory(opaque, addr, size);
 443}
 444
 445static void rtc_register_write_memory(void *opaque, hwaddr addr, uint64_t value,
 446                                      unsigned size)
 447{
 448    if (addr >= (XLNX_ZYNQMP_RTC_R_MAX * 4)) {
 449        DPRINT_TM("%06d : %s()\n", __LINE__, __func__);
 450        DPRINT_TM("\tAttempting to Write to invalid RTC Memory Space 0x%08lx\n",
 451                  addr);
 452        addr_error_set_status(opaque);
 453        return;
 454    }
 455
 456    register_write_memory(opaque, addr, value, size);
 457}
 458
 459static const MemoryRegionOps rtc_ops = {
 460    .read = rtc_register_read_memory,
 461    .write = rtc_register_write_memory,
 462    .endianness = DEVICE_LITTLE_ENDIAN,
 463    .valid = {
 464        .min_access_size = 4,
 465        .max_access_size = 4,
 466    },
 467};
 468
 469static void rtc_init(Object *obj)
 470{
 471    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj);
 472    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 473    RegisterInfoArray *reg_array;
 474
 475    memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC,
 476                       XLNX_ZYNQMP_RTC_IO_REGION_SZ);
 477
 478    reg_array =
 479        register_init_block32(DEVICE(obj), rtc_regs_info,
 480                              ARRAY_SIZE(rtc_regs_info),
 481                              s->regs_info, s->regs,
 482                              &rtc_ops,
 483                              XLNX_ZYNQMP_RTC_ERR_DEBUG,
 484                              XLNX_ZYNQMP_RTC_IO_REGION_SZ);
 485    memory_region_add_subregion(&s->iomem,
 486                                0x0,
 487                                &reg_array->mem);
 488    sysbus_init_mmio(sbd, &s->iomem);
 489    /* Error irq */
 490    sysbus_init_irq(sbd, &s->irq_addr_error_int);
 491    /* Alarm irq */
 492    sysbus_init_irq(sbd, &s->irq_rtc_int[0]);
 493    /* Seconds irq */
 494    sysbus_init_irq(sbd, &s->irq_rtc_int[1]);
 495
 496    DPRINT_TM("%06d : %s()\n", __LINE__, __func__);
 497    clear_time(s);
 498    s->alarm = timer_new_ns(rtc_clock, alarm_timeout_cb, s);
 499    s->sec_tick = timer_new_ns(rtc_clock, second_timeout_cb, s);
 500}
 501
 502static int rtc_pre_save(void *opaque)
 503{
 504    XlnxZynqMPRTC *s = opaque;
 505    int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 506
 507    /* Add the time at migration. */
 508    s->tick_offset = s->tick_offset + now;
 509
 510    return 0;
 511}
 512
 513static int rtc_post_load(void *opaque, int version_id)
 514{
 515    XlnxZynqMPRTC *s = opaque;
 516    int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 517
 518    /* Subtract the time after migration. This combined with the pre_save
 519     * action results in us having subtracted the time that the guest was
 520     * stopped to the offset.
 521     */
 522    s->tick_offset = s->tick_offset - now;
 523
 524    return 0;
 525}
 526
 527static const VMStateDescription vmstate_rtc = {
 528    .name = TYPE_XLNX_ZYNQMP_RTC,
 529    .version_id = 1,
 530    .minimum_version_id = 1,
 531    .pre_save = rtc_pre_save,
 532    .post_load = rtc_post_load,
 533    .fields = (VMStateField[]) {
 534        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX),
 535        VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC),
 536        VMSTATE_END_OF_LIST(),
 537    }
 538};
 539
 540static void rtc_class_init(ObjectClass *klass, void *data)
 541{
 542    DeviceClass *dc = DEVICE_CLASS(klass);
 543
 544    dc->reset = rtc_reset;
 545    dc->props = xlnx_rtc_properties;
 546    dc->vmsd = &vmstate_rtc;
 547}
 548
 549static const TypeInfo rtc_info = {
 550    .name          = TYPE_XLNX_ZYNQMP_RTC,
 551    .parent        = TYPE_SYS_BUS_DEVICE,
 552    .instance_size = sizeof(XlnxZynqMPRTC),
 553    .class_init    = rtc_class_init,
 554    .instance_init = rtc_init,
 555};
 556
 557static const TypeInfo rtc_alias_info = {
 558    .name           = TYPE_XLNX_ZYNQMP_ALIAS_RTC,
 559    .parent         = TYPE_XLNX_ZYNQMP_RTC,
 560};
 561
 562static void rtc_register_types(void)
 563{
 564    type_register_static(&rtc_info);
 565    type_register_static(&rtc_alias_info);
 566}
 567
 568type_init(rtc_register_types)
 569