xref: /openbmc/qemu/hw/rtc/ls7a_rtc.c (revision 59e52dcff7254603f3b5bf527806a830f016bf82)
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