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