qemu/hw/rtc/aspeed_rtc.c
<<
>>
Prefs
   1/*
   2 * ASPEED Real Time Clock
   3 * Joel Stanley <joel@jms.id.au>
   4 *
   5 * Copyright 2019 IBM Corp
   6 * SPDX-License-Identifier: GPL-2.0-or-later
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "qemu-common.h"
  11#include "hw/rtc/aspeed_rtc.h"
  12#include "migration/vmstate.h"
  13#include "qemu/log.h"
  14#include "qemu/timer.h"
  15
  16#include "trace.h"
  17
  18#define COUNTER1        (0x00 / 4)
  19#define COUNTER2        (0x04 / 4)
  20#define ALARM           (0x08 / 4)
  21#define CONTROL         (0x10 / 4)
  22#define ALARM_STATUS    (0x14 / 4)
  23
  24#define RTC_UNLOCKED    BIT(1)
  25#define RTC_ENABLED     BIT(0)
  26
  27static void aspeed_rtc_calc_offset(AspeedRtcState *rtc)
  28{
  29    struct tm tm;
  30    uint32_t year, cent;
  31    uint32_t reg1 = rtc->reg[COUNTER1];
  32    uint32_t reg2 = rtc->reg[COUNTER2];
  33
  34    tm.tm_mday = (reg1 >> 24) & 0x1f;
  35    tm.tm_hour = (reg1 >> 16) & 0x1f;
  36    tm.tm_min = (reg1 >> 8) & 0x3f;
  37    tm.tm_sec = (reg1 >> 0) & 0x3f;
  38
  39    cent = (reg2 >> 16) & 0x1f;
  40    year = (reg2 >> 8) & 0x7f;
  41    tm.tm_mon = ((reg2 >>  0) & 0x0f) - 1;
  42    tm.tm_year = year + (cent * 100) - 1900;
  43
  44    rtc->offset = qemu_timedate_diff(&tm);
  45}
  46
  47static uint32_t aspeed_rtc_get_counter(AspeedRtcState *rtc, int r)
  48{
  49    uint32_t year, cent;
  50    struct tm now;
  51
  52    qemu_get_timedate(&now, rtc->offset);
  53
  54    switch (r) {
  55    case COUNTER1:
  56        return (now.tm_mday << 24) | (now.tm_hour << 16) |
  57            (now.tm_min << 8) | now.tm_sec;
  58    case COUNTER2:
  59        cent = (now.tm_year + 1900) / 100;
  60        year = now.tm_year % 100;
  61        return ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) |
  62            ((now.tm_mon + 1) & 0xf);
  63    default:
  64        g_assert_not_reached();
  65    }
  66}
  67
  68static uint64_t aspeed_rtc_read(void *opaque, hwaddr addr,
  69                                unsigned size)
  70{
  71    AspeedRtcState *rtc = opaque;
  72    uint64_t val;
  73    uint32_t r = addr >> 2;
  74
  75    switch (r) {
  76    case COUNTER1:
  77    case COUNTER2:
  78        if (rtc->reg[CONTROL] & RTC_ENABLED) {
  79            rtc->reg[r] = aspeed_rtc_get_counter(rtc, r);
  80        }
  81        /* fall through */
  82    case CONTROL:
  83        val = rtc->reg[r];
  84        break;
  85    case ALARM:
  86    case ALARM_STATUS:
  87    default:
  88        qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
  89        return 0;
  90    }
  91
  92    trace_aspeed_rtc_read(addr, val);
  93
  94    return val;
  95}
  96
  97static void aspeed_rtc_write(void *opaque, hwaddr addr,
  98                             uint64_t val, unsigned size)
  99{
 100    AspeedRtcState *rtc = opaque;
 101    uint32_t r = addr >> 2;
 102
 103    switch (r) {
 104    case COUNTER1:
 105    case COUNTER2:
 106        if (!(rtc->reg[CONTROL] & RTC_UNLOCKED)) {
 107            break;
 108        }
 109        /* fall through */
 110    case CONTROL:
 111        rtc->reg[r] = val;
 112        aspeed_rtc_calc_offset(rtc);
 113        break;
 114    case ALARM:
 115    case ALARM_STATUS:
 116    default:
 117        qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
 118        break;
 119    }
 120    trace_aspeed_rtc_write(addr, val);
 121}
 122
 123static void aspeed_rtc_reset(DeviceState *d)
 124{
 125    AspeedRtcState *rtc = ASPEED_RTC(d);
 126
 127    rtc->offset = 0;
 128    memset(rtc->reg, 0, sizeof(rtc->reg));
 129}
 130
 131static const MemoryRegionOps aspeed_rtc_ops = {
 132    .read = aspeed_rtc_read,
 133    .write = aspeed_rtc_write,
 134    .endianness = DEVICE_NATIVE_ENDIAN,
 135};
 136
 137static const VMStateDescription vmstate_aspeed_rtc = {
 138    .name = TYPE_ASPEED_RTC,
 139    .version_id = 1,
 140    .fields = (VMStateField[]) {
 141        VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18),
 142        VMSTATE_INT32(offset, AspeedRtcState),
 143        VMSTATE_INT32(offset, AspeedRtcState),
 144        VMSTATE_END_OF_LIST()
 145    }
 146};
 147
 148static void aspeed_rtc_realize(DeviceState *dev, Error **errp)
 149{
 150    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 151    AspeedRtcState *s = ASPEED_RTC(dev);
 152
 153    sysbus_init_irq(sbd, &s->irq);
 154
 155    memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_rtc_ops, s,
 156                          "aspeed-rtc", 0x18ULL);
 157    sysbus_init_mmio(sbd, &s->iomem);
 158}
 159
 160static void aspeed_rtc_class_init(ObjectClass *klass, void *data)
 161{
 162    DeviceClass *dc = DEVICE_CLASS(klass);
 163
 164    dc->realize = aspeed_rtc_realize;
 165    dc->vmsd = &vmstate_aspeed_rtc;
 166    dc->reset = aspeed_rtc_reset;
 167}
 168
 169static const TypeInfo aspeed_rtc_info = {
 170    .name          = TYPE_ASPEED_RTC,
 171    .parent        = TYPE_SYS_BUS_DEVICE,
 172    .instance_size = sizeof(AspeedRtcState),
 173    .class_init    = aspeed_rtc_class_init,
 174};
 175
 176static void aspeed_rtc_register_types(void)
 177{
 178    type_register_static(&aspeed_rtc_info);
 179}
 180
 181type_init(aspeed_rtc_register_types)
 182