qemu/hw/timer/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 "hw/sysbus.h"
  29#include "hw/register.h"
  30#include "qemu/bitops.h"
  31#include "qemu/log.h"
  32#include "hw/ptimer.h"
  33#include "qemu/cutils.h"
  34#include "sysemu/sysemu.h"
  35#include "trace.h"
  36#include "hw/timer/xlnx-zynqmp-rtc.h"
  37
  38#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG
  39#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0
  40#endif
  41
  42enum version_id {
  43    IP_VERSION_1_0_1 = 0,
  44    IP_VERSION_2_0_0 = 1
  45};
  46
  47struct version_item_lookup {
  48    enum version_id id;
  49    const char *str;
  50};
  51
  52static struct version_item_lookup  version_table_lookup[] = {
  53    { IP_VERSION_1_0_1, "1.0.1" },
  54    { IP_VERSION_2_0_0, "2.0.0" }
  55};
  56
  57static Property xlnx_rtc_properties[] = {
  58    DEFINE_PROP_STRING("version", XlnxZynqMPRTC, cfg.version),
  59    DEFINE_PROP_END_OF_LIST(),
  60};
  61
  62static void rtc_int_update_irq(XlnxZynqMPRTC *s)
  63{
  64    bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK];
  65    qemu_set_irq(s->irq_rtc_int, pending);
  66}
  67
  68static void addr_error_int_update_irq(XlnxZynqMPRTC *s)
  69{
  70    bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK];
  71    qemu_set_irq(s->irq_addr_error_int, pending);
  72}
  73
  74static uint32_t rtc_get_count(XlnxZynqMPRTC *s)
  75{
  76    int64_t now = qemu_clock_get_ns(rtc_clock);
  77    return s->tick_offset + now / NANOSECONDS_PER_SECOND;
  78}
  79
  80static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64)
  81{
  82    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  83
  84    return rtc_get_count(s);
  85}
  86
  87static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64)
  88{
  89    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  90    rtc_int_update_irq(s);
  91}
  92
  93static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64)
  94{
  95    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
  96
  97    s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64;
  98    rtc_int_update_irq(s);
  99    return 0;
 100}
 101
 102static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64)
 103{
 104    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 105
 106    s->regs[R_RTC_INT_MASK] |= (uint32_t) val64;
 107    rtc_int_update_irq(s);
 108    return 0;
 109}
 110
 111static void addr_error_postw(RegisterInfo *reg, uint64_t val64)
 112{
 113    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 114    addr_error_int_update_irq(s);
 115}
 116
 117static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64)
 118{
 119    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 120
 121    s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64;
 122    addr_error_int_update_irq(s);
 123    return 0;
 124}
 125
 126static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64)
 127{
 128    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 129
 130    s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64;
 131    addr_error_int_update_irq(s);
 132    return 0;
 133}
 134
 135static void rtc_set_timer_postw(RegisterInfo *reg, uint64_t val64)
 136{
 137    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 138    s->tick_offset = (uint32_t) val64;
 139}
 140
 141static void rtc_calib_write_postw(RegisterInfo *reg, uint64_t val64)
 142{
 143    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
 144    s->regs[R_CALIB_READ] = (uint32_t) val64;
 145}
 146
 147static const RegisterAccessInfo rtc_regs_info[] = {
 148    {   .name = "SET_TIME_WRITE",  .addr = A_SET_TIME_WRITE,
 149        .post_write = rtc_set_timer_postw,
 150    },{ .name = "SET_TIME_READ",  .addr = A_SET_TIME_READ,
 151        .ro = 0xffffffff,
 152        .post_read = current_time_postr,
 153    },{ .name = "CALIB_WRITE",  .addr = A_CALIB_WRITE,
 154        .post_write = rtc_calib_write_postw,
 155    },{ .name = "CALIB_READ",  .addr = A_CALIB_READ,
 156        .ro = 0x1fffff,
 157    },{ .name = "CURRENT_TIME",  .addr = A_CURRENT_TIME,
 158        .ro = 0xffffffff,
 159        .post_read = current_time_postr,
 160    },{ .name = "CURRENT_TICK",  .addr = A_CURRENT_TICK,
 161        .ro = 0xffff,
 162    },{ .name = "ALARM",  .addr = A_ALARM,
 163    },{ .name = "RTC_INT_STATUS",  .addr = A_RTC_INT_STATUS,
 164        .w1c = 0x3,
 165        .post_write = rtc_int_status_postw,
 166    },{ .name = "RTC_INT_MASK",  .addr = A_RTC_INT_MASK,
 167        .reset = 0x3,
 168        .ro = 0x3,
 169    },{ .name = "RTC_INT_EN",  .addr = A_RTC_INT_EN,
 170        .pre_write = rtc_int_en_prew,
 171    },{ .name = "RTC_INT_DIS",  .addr = A_RTC_INT_DIS,
 172        .pre_write = rtc_int_dis_prew,
 173    },{ .name = "ADDR_ERROR",  .addr = A_ADDR_ERROR,
 174        .w1c = 0x1,
 175        .post_write = addr_error_postw,
 176    },{ .name = "ADDR_ERROR_INT_MASK",  .addr = A_ADDR_ERROR_INT_MASK,
 177        .reset = 0x1,
 178        .ro = 0x1,
 179    },{ .name = "ADDR_ERROR_INT_EN",  .addr = A_ADDR_ERROR_INT_EN,
 180        .pre_write = addr_error_int_en_prew,
 181    },{ .name = "ADDR_ERROR_INT_DIS",  .addr = A_ADDR_ERROR_INT_DIS,
 182        .pre_write = addr_error_int_dis_prew,
 183    },{ .name = "CONTROL",  .addr = A_CONTROL,
 184        .reset = 0x1000000,
 185        .rsvd = 0x70fffffe,
 186    },{ .name = "SAFETY_CHK",  .addr = A_SAFETY_CHK,
 187    }
 188};
 189
 190static const RegisterAccessInfo rtc_regs_control_v2_info = {
 191    .name = "CONTROL",   .addr = A_CONTROL,
 192    .reset = 0x2000000,  .rsvd = 0x70fffffe,
 193};
 194
 195static enum version_id version_id_lookup(const char *str)
 196{
 197    uint32_t i;
 198    enum version_id version;
 199
 200    version = IP_VERSION_1_0_1;
 201
 202    if (str) {
 203        for (i = 0; i < ARRAY_SIZE(version_table_lookup); ++i) {
 204            if (!strcmp(str, version_table_lookup[i].str)) {
 205                version =  version_table_lookup[i].id;
 206                break;
 207            }
 208        }
 209    }
 210
 211    return version;
 212}
 213
 214static void rtc_reset(DeviceState *dev)
 215{
 216    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev);
 217    unsigned int i;
 218
 219    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 220        register_reset(&s->regs_info[i]);
 221    }
 222
 223    if (version_id_lookup(s->cfg.version) == IP_VERSION_2_0_0) {
 224        s->regs_info[R_CONTROL].access = &rtc_regs_control_v2_info;
 225        register_reset(&s->regs_info[R_CONTROL]);
 226    }
 227
 228    trace_xlnx_zynqmp_rtc_gettime(s->current_tm.tm_year, s->current_tm.tm_mon,
 229                                  s->current_tm.tm_mday, s->current_tm.tm_hour,
 230                                  s->current_tm.tm_min, s->current_tm.tm_sec);
 231
 232    rtc_int_update_irq(s);
 233    addr_error_int_update_irq(s);
 234}
 235
 236static const MemoryRegionOps rtc_ops = {
 237    .read = register_read_memory,
 238    .write = register_write_memory,
 239    .endianness = DEVICE_LITTLE_ENDIAN,
 240    .valid = {
 241        .min_access_size = 4,
 242        .max_access_size = 4,
 243    },
 244};
 245
 246static void rtc_init(Object *obj)
 247{
 248    XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj);
 249    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 250    RegisterInfoArray *reg_array;
 251
 252    memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC,
 253                       XLNX_ZYNQMP_RTC_R_MAX * 4);
 254    reg_array =
 255        register_init_block32(DEVICE(obj), rtc_regs_info,
 256                              ARRAY_SIZE(rtc_regs_info),
 257                              s->regs_info, s->regs,
 258                              &rtc_ops,
 259                              XLNX_ZYNQMP_RTC_ERR_DEBUG,
 260                              XLNX_ZYNQMP_RTC_R_MAX * 4);
 261    memory_region_add_subregion(&s->iomem,
 262                                0x0,
 263                                &reg_array->mem);
 264    sysbus_init_mmio(sbd, &s->iomem);
 265    sysbus_init_irq(sbd, &s->irq_rtc_int);
 266    sysbus_init_irq(sbd, &s->irq_addr_error_int);
 267
 268    qemu_get_timedate(&s->current_tm, 0);
 269    s->tick_offset = mktimegm(&s->current_tm) -
 270        qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 271}
 272
 273static int rtc_post_load(void *opaque, int version_id)
 274{
 275    XlnxZynqMPRTC *s = opaque;
 276
 277    /* The tick_offset is added to the current time to determine the guest
 278     * time. After migration we don't want to use the original time as that
 279     * will indicate to the guest that time has passed, so we need to
 280     * recalculate the tick_offset here.
 281     */
 282    s->tick_offset = mktimegm(&s->current_tm) -
 283        qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
 284
 285    return 0;
 286}
 287
 288static const VMStateDescription vmstate_rtc = {
 289    .name = TYPE_XLNX_ZYNQMP_RTC,
 290    .version_id = 1,
 291    .minimum_version_id = 1,
 292    .post_load = rtc_post_load,
 293    .fields = (VMStateField[]) {
 294        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX),
 295        VMSTATE_INT32(current_tm.tm_sec, XlnxZynqMPRTC),
 296        VMSTATE_INT32(current_tm.tm_min, XlnxZynqMPRTC),
 297        VMSTATE_INT32(current_tm.tm_hour, XlnxZynqMPRTC),
 298        VMSTATE_INT32(current_tm.tm_wday, XlnxZynqMPRTC),
 299        VMSTATE_INT32(current_tm.tm_mday, XlnxZynqMPRTC),
 300        VMSTATE_INT32(current_tm.tm_mon, XlnxZynqMPRTC),
 301        VMSTATE_INT32(current_tm.tm_year, XlnxZynqMPRTC),
 302        VMSTATE_END_OF_LIST(),
 303    }
 304};
 305
 306static void rtc_class_init(ObjectClass *klass, void *data)
 307{
 308    DeviceClass *dc = DEVICE_CLASS(klass);
 309
 310    dc->reset = rtc_reset;
 311    dc->props = xlnx_rtc_properties;
 312    dc->vmsd = &vmstate_rtc;
 313}
 314
 315static const TypeInfo rtc_info = {
 316    .name          = TYPE_XLNX_ZYNQMP_RTC,
 317    .parent        = TYPE_SYS_BUS_DEVICE,
 318    .instance_size = sizeof(XlnxZynqMPRTC),
 319    .class_init    = rtc_class_init,
 320    .instance_init = rtc_init,
 321};
 322
 323static const TypeInfo rtc_alias_info = {
 324    .name           = TYPE_XLNX_ZYNQMP_ALIAS_RTC,
 325    .parent         = TYPE_XLNX_ZYNQMP_RTC,
 326};
 327
 328static void rtc_register_types(void)
 329{
 330    type_register_static(&rtc_info);
 331    type_register_static(&rtc_alias_info);
 332}
 333
 334type_init(rtc_register_types)
 335