qemu/hw/rtc/ls7a_rtc.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * Loongarch LS7A Real Time Clock emulation
   4 *
   5 * Copyright (C) 2021 Loongson Technology Corporation Limited
   6 */
   7
   8#include "qemu/osdep.h"
   9#include "hw/sysbus.h"
  10#include "hw/irq.h"
  11#include "include/hw/register.h"
  12#include "qemu/timer.h"
  13#include "sysemu/sysemu.h"
  14#include "qemu/cutils.h"
  15#include "qemu/log.h"
  16#include "migration/vmstate.h"
  17#include "hw/misc/unimp.h"
  18#include "sysemu/rtc.h"
  19#include "hw/registerfields.h"
  20
  21#define SYS_TOYTRIM        0x20
  22#define SYS_TOYWRITE0      0x24
  23#define SYS_TOYWRITE1      0x28
  24#define SYS_TOYREAD0       0x2C
  25#define SYS_TOYREAD1       0x30
  26#define SYS_TOYMATCH0      0x34
  27#define SYS_TOYMATCH1      0x38
  28#define SYS_TOYMATCH2      0x3C
  29#define SYS_RTCCTRL        0x40
  30#define SYS_RTCTRIM        0x60
  31#define SYS_RTCWRTIE0      0x64
  32#define SYS_RTCREAD0       0x68
  33#define SYS_RTCMATCH0      0x6C
  34#define SYS_RTCMATCH1      0x70
  35#define SYS_RTCMATCH2      0x74
  36
  37#define LS7A_RTC_FREQ     32768
  38#define TIMER_NUMS        3
  39/*
  40 * Shift bits and filed mask
  41 */
  42
  43FIELD(TOY, MON, 26, 6)
  44FIELD(TOY, DAY, 21, 5)
  45FIELD(TOY, HOUR, 16, 5)
  46FIELD(TOY, MIN, 10, 6)
  47FIELD(TOY, SEC, 4, 6)
  48FIELD(TOY, MSEC, 0, 4)
  49
  50FIELD(TOY_MATCH, YEAR, 26, 6)
  51FIELD(TOY_MATCH, MON, 22, 4)
  52FIELD(TOY_MATCH, DAY, 17, 5)
  53FIELD(TOY_MATCH, HOUR, 12, 5)
  54FIELD(TOY_MATCH, MIN, 6, 6)
  55FIELD(TOY_MATCH, SEC, 0, 6)
  56
  57FIELD(RTC_CTRL, RTCEN, 13, 1)
  58FIELD(RTC_CTRL, TOYEN, 11, 1)
  59FIELD(RTC_CTRL, EO, 8, 1)
  60
  61#define TYPE_LS7A_RTC "ls7a_rtc"
  62OBJECT_DECLARE_SIMPLE_TYPE(LS7ARtcState, LS7A_RTC)
  63
  64struct LS7ARtcState {
  65    SysBusDevice parent_obj;
  66
  67    MemoryRegion iomem;
  68    /*
  69     * Needed to preserve the tick_count across migration, even if the
  70     * absolute value of the rtc_clock is different on the source and
  71     * destination.
  72     */
  73    int64_t offset_toy;
  74    int64_t offset_rtc;
  75    int64_t data;
  76    int tidx;
  77    uint32_t toymatch[3];
  78    uint32_t toytrim;
  79    uint32_t cntrctl;
  80    uint32_t rtctrim;
  81    uint32_t rtccount;
  82    uint32_t rtcmatch[3];
  83    QEMUTimer *toy_timer[TIMER_NUMS];
  84    QEMUTimer *rtc_timer[TIMER_NUMS];
  85    qemu_irq irq;
  86};
  87
  88/* switch nanoseconds time to rtc ticks */
  89static uint64_t ls7a_rtc_ticks(void)
  90{
  91    return qemu_clock_get_ns(rtc_clock) * LS7A_RTC_FREQ / NANOSECONDS_PER_SECOND;
  92}
  93
  94/* switch rtc ticks to nanoseconds */
  95static uint64_t ticks_to_ns(uint64_t ticks)
  96{
  97    return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ;
  98}
  99
 100static bool toy_enabled(LS7ARtcState *s)
 101{
 102    return FIELD_EX32(s->cntrctl, RTC_CTRL, TOYEN) &&
 103           FIELD_EX32(s->cntrctl, RTC_CTRL, EO);
 104}
 105
 106static bool rtc_enabled(LS7ARtcState *s)
 107{
 108    return FIELD_EX32(s->cntrctl, RTC_CTRL, RTCEN) &&
 109           FIELD_EX32(s->cntrctl, RTC_CTRL, EO);
 110}
 111
 112/* parse struct tm to toy value */
 113static uint64_t toy_time_to_val_mon(const struct tm *tm)
 114{
 115    uint64_t val = 0;
 116
 117    val = FIELD_DP32(val, TOY, MON, tm->tm_mon + 1);
 118    val = FIELD_DP32(val, TOY, DAY, tm->tm_mday);
 119    val = FIELD_DP32(val, TOY, HOUR, tm->tm_hour);
 120    val = FIELD_DP32(val, TOY, MIN, tm->tm_min);
 121    val = FIELD_DP32(val, TOY, SEC, tm->tm_sec);
 122    return val;
 123}
 124
 125static void toymatch_val_to_time(LS7ARtcState *s, uint64_t val, struct tm *tm)
 126{
 127    qemu_get_timedate(tm, s->offset_toy);
 128    tm->tm_sec = FIELD_EX32(val, TOY_MATCH, SEC);
 129    tm->tm_min = FIELD_EX32(val, TOY_MATCH, MIN);
 130    tm->tm_hour = FIELD_EX32(val, TOY_MATCH, HOUR);
 131    tm->tm_mday = FIELD_EX32(val, TOY_MATCH, DAY);
 132    tm->tm_mon = FIELD_EX32(val, TOY_MATCH, MON) - 1;
 133    tm->tm_year += (FIELD_EX32(val, TOY_MATCH, YEAR) - (tm->tm_year & 0x3f));
 134}
 135
 136static void toymatch_write(LS7ARtcState *s, uint64_t val, int num)
 137{
 138    int64_t now, expire_time;
 139    struct tm tm = {};
 140
 141    /* it do not support write when toy disabled */
 142    if (toy_enabled(s)) {
 143        s->toymatch[num] = val;
 144        /* calculate expire time */
 145        now = qemu_clock_get_ms(rtc_clock);
 146        toymatch_val_to_time(s, val, &tm);
 147        expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
 148        timer_mod(s->toy_timer[num], expire_time);
 149    }
 150}
 151
 152static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)
 153{
 154    uint64_t expire_ns;
 155
 156    /* it do not support write when toy disabled */
 157    if (rtc_enabled(s)) {
 158        s->rtcmatch[num] = val;
 159        /* calculate expire time */
 160        expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
 161        timer_mod_ns(s->rtc_timer[num], expire_ns);
 162    }
 163}
 164
 165static void ls7a_toy_stop(LS7ARtcState *s)
 166{
 167    int i;
 168
 169    /* delete timers, and when re-enabled, recalculate expire time */
 170    for (i = 0; i < TIMER_NUMS; i++) {
 171        timer_del(s->toy_timer[i]);
 172    }
 173}
 174
 175static void ls7a_rtc_stop(LS7ARtcState *s)
 176{
 177    int i;
 178
 179    /* delete timers, and when re-enabled, recalculate expire time */
 180    for (i = 0; i < TIMER_NUMS; i++) {
 181        timer_del(s->rtc_timer[i]);
 182    }
 183}
 184
 185static void ls7a_toy_start(LS7ARtcState *s)
 186{
 187    int i;
 188    uint64_t expire_time, now;
 189    struct tm tm = {};
 190
 191    now = qemu_clock_get_ms(rtc_clock);
 192
 193    /* recalculate expire time and enable timer */
 194    for (i = 0; i < TIMER_NUMS; i++) {
 195        toymatch_val_to_time(s, s->toymatch[i], &tm);
 196        expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
 197        timer_mod(s->toy_timer[i], expire_time);
 198    }
 199}
 200
 201static void ls7a_rtc_start(LS7ARtcState *s)
 202{
 203    int i;
 204    uint64_t expire_time;
 205
 206    /* recalculate expire time and enable timer */
 207    for (i = 0; i < TIMER_NUMS; i++) {
 208        expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc);
 209        timer_mod_ns(s->rtc_timer[i], expire_time);
 210    }
 211}
 212
 213static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size)
 214{
 215    LS7ARtcState *s = LS7A_RTC(opaque);
 216    struct tm tm;
 217    int val = 0;
 218
 219    switch (addr) {
 220    case SYS_TOYREAD0:
 221        if (toy_enabled(s)) {
 222            qemu_get_timedate(&tm, s->offset_toy);
 223            val = toy_time_to_val_mon(&tm);
 224        } else {
 225            /* return 0 when toy disabled */
 226            val = 0;
 227        }
 228        break;
 229    case SYS_TOYREAD1:
 230        if (toy_enabled(s)) {
 231            qemu_get_timedate(&tm, s->offset_toy);
 232            val = tm.tm_year;
 233        } else {
 234            /* return 0 when toy disabled */
 235            val = 0;
 236        }
 237        break;
 238    case SYS_TOYMATCH0:
 239        val = s->toymatch[0];
 240        break;
 241    case SYS_TOYMATCH1:
 242        val = s->toymatch[1];
 243        break;
 244    case SYS_TOYMATCH2:
 245        val = s->toymatch[2];
 246        break;
 247    case SYS_RTCCTRL:
 248        val = s->cntrctl;
 249        break;
 250    case SYS_RTCREAD0:
 251        if (rtc_enabled(s)) {
 252            val = ls7a_rtc_ticks() + s->offset_rtc;
 253        } else {
 254            /* return 0 when rtc disabled */
 255            val = 0;
 256        }
 257        break;
 258    case SYS_RTCMATCH0:
 259        val = s->rtcmatch[0];
 260        break;
 261    case SYS_RTCMATCH1:
 262        val = s->rtcmatch[1];
 263        break;
 264    case SYS_RTCMATCH2:
 265        val = s->rtcmatch[2];
 266        break;
 267    default:
 268        val = 0;
 269        break;
 270    }
 271    return val;
 272}
 273
 274static void ls7a_rtc_write(void *opaque, hwaddr addr,
 275                           uint64_t val, unsigned size)
 276{
 277    int old_toyen, old_rtcen, new_toyen, new_rtcen;
 278    LS7ARtcState *s = LS7A_RTC(opaque);
 279    struct tm tm;
 280
 281    switch (addr) {
 282    case SYS_TOYWRITE0:
 283        /* it do not support write when toy disabled */
 284        if (toy_enabled(s)) {
 285            qemu_get_timedate(&tm, s->offset_toy);
 286            tm.tm_sec = FIELD_EX32(val, TOY, SEC);
 287            tm.tm_min = FIELD_EX32(val, TOY, MIN);
 288            tm.tm_hour = FIELD_EX32(val, TOY, HOUR);
 289            tm.tm_mday = FIELD_EX32(val, TOY, DAY);
 290            tm.tm_mon = FIELD_EX32(val, TOY, MON) - 1;
 291            s->offset_toy = qemu_timedate_diff(&tm);
 292        }
 293    break;
 294    case SYS_TOYWRITE1:
 295        if (toy_enabled(s)) {
 296            qemu_get_timedate(&tm, s->offset_toy);
 297            tm.tm_year = val;
 298            s->offset_toy = qemu_timedate_diff(&tm);
 299        }
 300        break;
 301    case SYS_TOYMATCH0:
 302        toymatch_write(s, val, 0);
 303        break;
 304    case SYS_TOYMATCH1:
 305        toymatch_write(s, val, 1);
 306        break;
 307    case SYS_TOYMATCH2:
 308        toymatch_write(s, val, 2);
 309        break;
 310    case SYS_RTCCTRL:
 311        /* get old ctrl */
 312        old_toyen = toy_enabled(s);
 313        old_rtcen = rtc_enabled(s);
 314
 315        s->cntrctl = val;
 316        /* get new ctrl */
 317        new_toyen = toy_enabled(s);
 318        new_rtcen = rtc_enabled(s);
 319
 320        /*
 321         * we do not consider if EO changed, as it always set at most time.
 322         * toy or rtc enabled should start timer. otherwise, stop timer
 323         */
 324        if (old_toyen != new_toyen) {
 325            if (new_toyen) {
 326                ls7a_toy_start(s);
 327            } else {
 328                ls7a_toy_stop(s);
 329            }
 330        }
 331        if (old_rtcen != new_rtcen) {
 332            if (new_rtcen) {
 333                ls7a_rtc_start(s);
 334            } else {
 335                ls7a_rtc_stop(s);
 336            }
 337        }
 338        break;
 339    case SYS_RTCWRTIE0:
 340        if (rtc_enabled(s)) {
 341            s->offset_rtc = val - ls7a_rtc_ticks();
 342        }
 343        break;
 344    case SYS_RTCMATCH0:
 345        rtcmatch_write(s, val, 0);
 346        break;
 347    case SYS_RTCMATCH1:
 348        rtcmatch_write(s, val, 1);
 349        break;
 350    case SYS_RTCMATCH2:
 351        rtcmatch_write(s, val, 2);
 352        break;
 353    default:
 354        break;
 355    }
 356}
 357
 358static const MemoryRegionOps ls7a_rtc_ops = {
 359    .read = ls7a_rtc_read,
 360    .write = ls7a_rtc_write,
 361    .endianness = DEVICE_LITTLE_ENDIAN,
 362    .valid = {
 363        .min_access_size = 4,
 364        .max_access_size = 4,
 365    },
 366};
 367
 368static void toy_timer_cb(void *opaque)
 369{
 370    LS7ARtcState *s = opaque;
 371
 372    if (toy_enabled(s)) {
 373        qemu_irq_raise(s->irq);
 374    }
 375}
 376
 377static void rtc_timer_cb(void *opaque)
 378{
 379    LS7ARtcState *s = opaque;
 380
 381    if (rtc_enabled(s)) {
 382        qemu_irq_raise(s->irq);
 383    }
 384}
 385
 386static void ls7a_rtc_realize(DeviceState *dev, Error **errp)
 387{
 388    int i;
 389    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 390    LS7ARtcState *d = LS7A_RTC(sbd);
 391    memory_region_init_io(&d->iomem, NULL, &ls7a_rtc_ops,
 392                         (void *)d, "ls7a_rtc", 0x100);
 393
 394    sysbus_init_irq(sbd, &d->irq);
 395
 396    sysbus_init_mmio(sbd, &d->iomem);
 397    for (i = 0; i < TIMER_NUMS; i++) {
 398        d->toymatch[i] = 0;
 399        d->rtcmatch[i] = 0;
 400        d->toy_timer[i] = timer_new_ms(rtc_clock, toy_timer_cb, d);
 401        d->rtc_timer[i] = timer_new_ms(rtc_clock, rtc_timer_cb, d);
 402    }
 403    d->offset_toy = 0;
 404    d->offset_rtc = 0;
 405
 406}
 407
 408/* delete timer and clear reg when reset */
 409static void ls7a_rtc_reset(DeviceState *dev)
 410{
 411    int i;
 412    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 413    LS7ARtcState *d = LS7A_RTC(sbd);
 414    for (i = 0; i < TIMER_NUMS; i++) {
 415        if (toy_enabled(d)) {
 416            timer_del(d->toy_timer[i]);
 417        }
 418        if (rtc_enabled(d)) {
 419            timer_del(d->rtc_timer[i]);
 420        }
 421        d->toymatch[i] = 0;
 422        d->rtcmatch[i] = 0;
 423    }
 424    d->cntrctl = 0;
 425}
 426
 427static int ls7a_rtc_pre_save(void *opaque)
 428{
 429    LS7ARtcState *s = LS7A_RTC(opaque);
 430
 431    ls7a_toy_stop(s);
 432    ls7a_rtc_stop(s);
 433
 434    return 0;
 435}
 436
 437static int ls7a_rtc_post_load(void *opaque, int version_id)
 438{
 439    LS7ARtcState *s = LS7A_RTC(opaque);
 440    if (toy_enabled(s)) {
 441        ls7a_toy_start(s);
 442    }
 443
 444    if (rtc_enabled(s)) {
 445        ls7a_rtc_start(s);
 446    }
 447
 448    return 0;
 449}
 450
 451static const VMStateDescription vmstate_ls7a_rtc = {
 452    .name = "ls7a_rtc",
 453    .version_id = 1,
 454    .minimum_version_id = 1,
 455    .pre_save = ls7a_rtc_pre_save,
 456    .post_load = ls7a_rtc_post_load,
 457    .fields = (VMStateField[]) {
 458        VMSTATE_INT64(offset_toy, LS7ARtcState),
 459        VMSTATE_INT64(offset_rtc, LS7ARtcState),
 460        VMSTATE_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS),
 461        VMSTATE_UINT32_ARRAY(rtcmatch, LS7ARtcState, TIMER_NUMS),
 462        VMSTATE_UINT32(cntrctl, LS7ARtcState),
 463        VMSTATE_END_OF_LIST()
 464    }
 465};
 466
 467static void ls7a_rtc_class_init(ObjectClass *klass, void *data)
 468{
 469    DeviceClass *dc = DEVICE_CLASS(klass);
 470    dc->vmsd = &vmstate_ls7a_rtc;
 471    dc->realize = ls7a_rtc_realize;
 472    dc->reset = ls7a_rtc_reset;
 473    dc->desc = "ls7a rtc";
 474}
 475
 476static const TypeInfo ls7a_rtc_info = {
 477    .name          = TYPE_LS7A_RTC,
 478    .parent        = TYPE_SYS_BUS_DEVICE,
 479    .instance_size = sizeof(LS7ARtcState),
 480    .class_init    = ls7a_rtc_class_init,
 481};
 482
 483static void ls7a_rtc_register_types(void)
 484{
 485    type_register_static(&ls7a_rtc_info);
 486}
 487
 488type_init(ls7a_rtc_register_types)
 489