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