qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

ls7a_rtc.c (12507B)


      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 
     43 FIELD(TOY, MON, 26, 6)
     44 FIELD(TOY, DAY, 21, 5)
     45 FIELD(TOY, HOUR, 16, 5)
     46 FIELD(TOY, MIN, 10, 6)
     47 FIELD(TOY, SEC, 4, 6)
     48 FIELD(TOY, MSEC, 0, 4)
     49 
     50 FIELD(TOY_MATCH, YEAR, 26, 6)
     51 FIELD(TOY_MATCH, MON, 22, 4)
     52 FIELD(TOY_MATCH, DAY, 17, 5)
     53 FIELD(TOY_MATCH, HOUR, 12, 5)
     54 FIELD(TOY_MATCH, MIN, 6, 6)
     55 FIELD(TOY_MATCH, SEC, 0, 6)
     56 
     57 FIELD(RTC_CTRL, RTCEN, 13, 1)
     58 FIELD(RTC_CTRL, TOYEN, 11, 1)
     59 FIELD(RTC_CTRL, EO, 8, 1)
     60 
     61 #define TYPE_LS7A_RTC "ls7a_rtc"
     62 OBJECT_DECLARE_SIMPLE_TYPE(LS7ARtcState, LS7A_RTC)
     63 
     64 struct 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 */
     89 static 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 */
     95 static uint64_t ticks_to_ns(uint64_t ticks)
     96 {
     97     return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ;
     98 }
     99 
    100 static 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 
    106 static 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 */
    113 static 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 
    125 static 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 
    136 static 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 
    152 static 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 
    165 static 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 
    175 static 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 
    185 static 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 
    201 static 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 
    213 static 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 
    274 static 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 
    358 static 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 
    368 static 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 
    377 static 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 
    386 static 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 */
    409 static 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 
    427 static 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 
    437 static 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 
    451 static 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 
    467 static 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 
    476 static 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 
    483 static void ls7a_rtc_register_types(void)
    484 {
    485     type_register_static(&ls7a_rtc_info);
    486 }
    487 
    488 type_init(ls7a_rtc_register_types)