xref: /qemu/hw/rtc/ls7a_rtc.c (revision 8b4d80bb)
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"
118b4d80bbSPhilippe Mathieu-Daudé #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 */
ls7a_rtc_ticks(void)89c1ca312aSRichard Henderson static 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 */
ticks_to_ns(uint64_t ticks)95c1ca312aSRichard Henderson static uint64_t ticks_to_ns(uint64_t ticks)
96c117f68aSXiaojuan Yang {
97c117f68aSXiaojuan Yang     return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ;
98c117f68aSXiaojuan Yang }
99c117f68aSXiaojuan Yang 
toy_enabled(LS7ARtcState * s)100c1ca312aSRichard Henderson static 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 
rtc_enabled(LS7ARtcState * s)106c1ca312aSRichard Henderson static 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 struct tm to toy value */
toy_time_to_val_mon(const struct tm * tm)113c1ca312aSRichard Henderson static uint64_t toy_time_to_val_mon(const struct tm *tm)
114c117f68aSXiaojuan Yang {
115c117f68aSXiaojuan Yang     uint64_t val = 0;
116c117f68aSXiaojuan Yang 
117582788c3SXiaojuan Yang     val = FIELD_DP32(val, TOY, MON, tm->tm_mon + 1);
118582788c3SXiaojuan Yang     val = FIELD_DP32(val, TOY, DAY, tm->tm_mday);
119582788c3SXiaojuan Yang     val = FIELD_DP32(val, TOY, HOUR, tm->tm_hour);
120582788c3SXiaojuan Yang     val = FIELD_DP32(val, TOY, MIN, tm->tm_min);
121582788c3SXiaojuan Yang     val = FIELD_DP32(val, TOY, SEC, tm->tm_sec);
122c117f68aSXiaojuan Yang     return val;
123c117f68aSXiaojuan Yang }
124c117f68aSXiaojuan Yang 
toymatch_val_to_time(LS7ARtcState * s,uint64_t val,struct tm * tm)125c1ca312aSRichard Henderson static void toymatch_val_to_time(LS7ARtcState *s, uint64_t val, struct tm *tm)
126c117f68aSXiaojuan Yang {
1274f2c6587SXiaojuan Yang     qemu_get_timedate(tm, s->offset_toy);
128c117f68aSXiaojuan Yang     tm->tm_sec = FIELD_EX32(val, TOY_MATCH, SEC);
129c117f68aSXiaojuan Yang     tm->tm_min = FIELD_EX32(val, TOY_MATCH, MIN);
130c117f68aSXiaojuan Yang     tm->tm_hour = FIELD_EX32(val, TOY_MATCH, HOUR);
131c117f68aSXiaojuan Yang     tm->tm_mday = FIELD_EX32(val, TOY_MATCH, DAY);
132c117f68aSXiaojuan Yang     tm->tm_mon = FIELD_EX32(val, TOY_MATCH, MON) - 1;
133c117f68aSXiaojuan Yang     tm->tm_year += (FIELD_EX32(val, TOY_MATCH, YEAR) - (tm->tm_year & 0x3f));
134c117f68aSXiaojuan Yang }
135c117f68aSXiaojuan Yang 
toymatch_write(LS7ARtcState * s,uint64_t val,int num)1364f2c6587SXiaojuan Yang static void toymatch_write(LS7ARtcState *s, uint64_t val, int num)
137c117f68aSXiaojuan Yang {
138c117f68aSXiaojuan Yang     int64_t now, expire_time;
1394f2c6587SXiaojuan Yang     struct tm tm = {};
140c117f68aSXiaojuan Yang 
141c117f68aSXiaojuan Yang     /* it do not support write when toy disabled */
142c117f68aSXiaojuan Yang     if (toy_enabled(s)) {
143c117f68aSXiaojuan Yang         s->toymatch[num] = val;
14459e52dcfSXiaojuan Yang         /* calculate expire time */
145c117f68aSXiaojuan Yang         now = qemu_clock_get_ms(rtc_clock);
1464f2c6587SXiaojuan Yang         toymatch_val_to_time(s, val, &tm);
1474f2c6587SXiaojuan Yang         expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
148c117f68aSXiaojuan Yang         timer_mod(s->toy_timer[num], expire_time);
149c117f68aSXiaojuan Yang     }
150c117f68aSXiaojuan Yang }
151c117f68aSXiaojuan Yang 
rtcmatch_write(LS7ARtcState * s,uint64_t val,int num)152c117f68aSXiaojuan Yang static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)
153c117f68aSXiaojuan Yang {
154c117f68aSXiaojuan Yang     uint64_t expire_ns;
155c117f68aSXiaojuan Yang 
156c117f68aSXiaojuan Yang     /* it do not support write when toy disabled */
157c117f68aSXiaojuan Yang     if (rtc_enabled(s)) {
158c117f68aSXiaojuan Yang         s->rtcmatch[num] = val;
15959e52dcfSXiaojuan Yang         /* calculate expire time */
160c117f68aSXiaojuan Yang         expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
161c117f68aSXiaojuan Yang         timer_mod_ns(s->rtc_timer[num], expire_ns);
162c117f68aSXiaojuan Yang     }
163c117f68aSXiaojuan Yang }
164c117f68aSXiaojuan Yang 
ls7a_toy_stop(LS7ARtcState * s)165c117f68aSXiaojuan Yang static void ls7a_toy_stop(LS7ARtcState *s)
166c117f68aSXiaojuan Yang {
167c117f68aSXiaojuan Yang     int i;
168c117f68aSXiaojuan Yang 
16959e52dcfSXiaojuan Yang     /* delete timers, and when re-enabled, recalculate expire time */
170c117f68aSXiaojuan Yang     for (i = 0; i < TIMER_NUMS; i++) {
171c117f68aSXiaojuan Yang         timer_del(s->toy_timer[i]);
172c117f68aSXiaojuan Yang     }
173c117f68aSXiaojuan Yang }
174c117f68aSXiaojuan Yang 
ls7a_rtc_stop(LS7ARtcState * s)175c117f68aSXiaojuan Yang static void ls7a_rtc_stop(LS7ARtcState *s)
176c117f68aSXiaojuan Yang {
177c117f68aSXiaojuan Yang     int i;
178c117f68aSXiaojuan Yang 
17959e52dcfSXiaojuan Yang     /* delete timers, and when re-enabled, recalculate expire time */
180c117f68aSXiaojuan Yang     for (i = 0; i < TIMER_NUMS; i++) {
181c117f68aSXiaojuan Yang         timer_del(s->rtc_timer[i]);
182c117f68aSXiaojuan Yang     }
183c117f68aSXiaojuan Yang }
184c117f68aSXiaojuan Yang 
ls7a_toy_start(LS7ARtcState * s)185c117f68aSXiaojuan Yang static void ls7a_toy_start(LS7ARtcState *s)
186c117f68aSXiaojuan Yang {
187c117f68aSXiaojuan Yang     int i;
188c117f68aSXiaojuan Yang     uint64_t expire_time, now;
1894f2c6587SXiaojuan Yang     struct tm tm = {};
190c117f68aSXiaojuan Yang 
191c117f68aSXiaojuan Yang     now = qemu_clock_get_ms(rtc_clock);
192c117f68aSXiaojuan Yang 
19359e52dcfSXiaojuan Yang     /* recalculate expire time and enable timer */
194c117f68aSXiaojuan Yang     for (i = 0; i < TIMER_NUMS; i++) {
1954f2c6587SXiaojuan Yang         toymatch_val_to_time(s, s->toymatch[i], &tm);
196c117f68aSXiaojuan Yang         expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
197c117f68aSXiaojuan Yang         timer_mod(s->toy_timer[i], expire_time);
198c117f68aSXiaojuan Yang     }
199c117f68aSXiaojuan Yang }
200c117f68aSXiaojuan Yang 
ls7a_rtc_start(LS7ARtcState * s)201c117f68aSXiaojuan Yang static void ls7a_rtc_start(LS7ARtcState *s)
202c117f68aSXiaojuan Yang {
203c117f68aSXiaojuan Yang     int i;
2046935f132SXiaojuan Yang     uint64_t expire_time;
205c117f68aSXiaojuan Yang 
20659e52dcfSXiaojuan Yang     /* recalculate expire time and enable timer */
207c117f68aSXiaojuan Yang     for (i = 0; i < TIMER_NUMS; i++) {
208c117f68aSXiaojuan Yang         expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc);
209c117f68aSXiaojuan Yang         timer_mod_ns(s->rtc_timer[i], expire_time);
210c117f68aSXiaojuan Yang     }
211c117f68aSXiaojuan Yang }
212c117f68aSXiaojuan Yang 
ls7a_rtc_read(void * opaque,hwaddr addr,unsigned size)213c117f68aSXiaojuan Yang static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size)
214c117f68aSXiaojuan Yang {
215c117f68aSXiaojuan Yang     LS7ARtcState *s = LS7A_RTC(opaque);
216c117f68aSXiaojuan Yang     struct tm tm;
217c117f68aSXiaojuan Yang     int val = 0;
218c117f68aSXiaojuan Yang 
219c117f68aSXiaojuan Yang     switch (addr) {
220c117f68aSXiaojuan Yang     case SYS_TOYREAD0:
221c117f68aSXiaojuan Yang         if (toy_enabled(s)) {
222c117f68aSXiaojuan Yang             qemu_get_timedate(&tm, s->offset_toy);
223582788c3SXiaojuan Yang             val = toy_time_to_val_mon(&tm);
224c117f68aSXiaojuan Yang         } else {
2256935f132SXiaojuan Yang             /* return 0 when toy disabled */
2266935f132SXiaojuan Yang             val = 0;
227c117f68aSXiaojuan Yang         }
228c117f68aSXiaojuan Yang         break;
229c117f68aSXiaojuan Yang     case SYS_TOYREAD1:
230c117f68aSXiaojuan Yang         if (toy_enabled(s)) {
231c117f68aSXiaojuan Yang             qemu_get_timedate(&tm, s->offset_toy);
232c117f68aSXiaojuan Yang             val = tm.tm_year;
233c117f68aSXiaojuan Yang         } else {
2346935f132SXiaojuan Yang             /* return 0 when toy disabled */
2356935f132SXiaojuan Yang             val = 0;
236c117f68aSXiaojuan Yang         }
237c117f68aSXiaojuan Yang         break;
238c117f68aSXiaojuan Yang     case SYS_TOYMATCH0:
239c117f68aSXiaojuan Yang         val = s->toymatch[0];
240c117f68aSXiaojuan Yang         break;
241c117f68aSXiaojuan Yang     case SYS_TOYMATCH1:
242c117f68aSXiaojuan Yang         val = s->toymatch[1];
243c117f68aSXiaojuan Yang         break;
244c117f68aSXiaojuan Yang     case SYS_TOYMATCH2:
245c117f68aSXiaojuan Yang         val = s->toymatch[2];
246c117f68aSXiaojuan Yang         break;
247c117f68aSXiaojuan Yang     case SYS_RTCCTRL:
248c117f68aSXiaojuan Yang         val = s->cntrctl;
249c117f68aSXiaojuan Yang         break;
250c117f68aSXiaojuan Yang     case SYS_RTCREAD0:
251c117f68aSXiaojuan Yang         if (rtc_enabled(s)) {
252c117f68aSXiaojuan Yang             val = ls7a_rtc_ticks() + s->offset_rtc;
253c117f68aSXiaojuan Yang         } else {
2546935f132SXiaojuan Yang             /* return 0 when rtc disabled */
2556935f132SXiaojuan Yang             val = 0;
256c117f68aSXiaojuan Yang         }
257c117f68aSXiaojuan Yang         break;
258c117f68aSXiaojuan Yang     case SYS_RTCMATCH0:
259c117f68aSXiaojuan Yang         val = s->rtcmatch[0];
260c117f68aSXiaojuan Yang         break;
261c117f68aSXiaojuan Yang     case SYS_RTCMATCH1:
262c117f68aSXiaojuan Yang         val = s->rtcmatch[1];
263c117f68aSXiaojuan Yang         break;
264c117f68aSXiaojuan Yang     case SYS_RTCMATCH2:
265c117f68aSXiaojuan Yang         val = s->rtcmatch[2];
266c117f68aSXiaojuan Yang         break;
267c117f68aSXiaojuan Yang     default:
268c117f68aSXiaojuan Yang         val = 0;
269c117f68aSXiaojuan Yang         break;
270c117f68aSXiaojuan Yang     }
271c117f68aSXiaojuan Yang     return val;
272c117f68aSXiaojuan Yang }
273c117f68aSXiaojuan Yang 
ls7a_rtc_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)274c117f68aSXiaojuan Yang static void ls7a_rtc_write(void *opaque, hwaddr addr,
275c117f68aSXiaojuan Yang                            uint64_t val, unsigned size)
276c117f68aSXiaojuan Yang {
277c117f68aSXiaojuan Yang     int old_toyen, old_rtcen, new_toyen, new_rtcen;
278c117f68aSXiaojuan Yang     LS7ARtcState *s = LS7A_RTC(opaque);
279c117f68aSXiaojuan Yang     struct tm tm;
280c117f68aSXiaojuan Yang 
281c117f68aSXiaojuan Yang     switch (addr) {
282c117f68aSXiaojuan Yang     case SYS_TOYWRITE0:
283c117f68aSXiaojuan Yang         /* it do not support write when toy disabled */
284c117f68aSXiaojuan Yang         if (toy_enabled(s)) {
285c117f68aSXiaojuan Yang             qemu_get_timedate(&tm, s->offset_toy);
286c117f68aSXiaojuan Yang             tm.tm_sec = FIELD_EX32(val, TOY, SEC);
287c117f68aSXiaojuan Yang             tm.tm_min = FIELD_EX32(val, TOY, MIN);
288c117f68aSXiaojuan Yang             tm.tm_hour = FIELD_EX32(val, TOY, HOUR);
289c117f68aSXiaojuan Yang             tm.tm_mday = FIELD_EX32(val, TOY, DAY);
290c117f68aSXiaojuan Yang             tm.tm_mon = FIELD_EX32(val, TOY, MON) - 1;
291c117f68aSXiaojuan Yang             s->offset_toy = qemu_timedate_diff(&tm);
292c117f68aSXiaojuan Yang         }
293c117f68aSXiaojuan Yang     break;
294c117f68aSXiaojuan Yang     case SYS_TOYWRITE1:
295c117f68aSXiaojuan Yang         if (toy_enabled(s)) {
296c117f68aSXiaojuan Yang             qemu_get_timedate(&tm, s->offset_toy);
297c117f68aSXiaojuan Yang             tm.tm_year = val;
298c117f68aSXiaojuan Yang             s->offset_toy = qemu_timedate_diff(&tm);
299c117f68aSXiaojuan Yang         }
300c117f68aSXiaojuan Yang         break;
301c117f68aSXiaojuan Yang     case SYS_TOYMATCH0:
3024f2c6587SXiaojuan Yang         toymatch_write(s, val, 0);
303c117f68aSXiaojuan Yang         break;
304c117f68aSXiaojuan Yang     case SYS_TOYMATCH1:
3054f2c6587SXiaojuan Yang         toymatch_write(s, val, 1);
306c117f68aSXiaojuan Yang         break;
307c117f68aSXiaojuan Yang     case SYS_TOYMATCH2:
3084f2c6587SXiaojuan Yang         toymatch_write(s, val, 2);
309c117f68aSXiaojuan Yang         break;
310c117f68aSXiaojuan Yang     case SYS_RTCCTRL:
311c117f68aSXiaojuan Yang         /* get old ctrl */
312c117f68aSXiaojuan Yang         old_toyen = toy_enabled(s);
313c117f68aSXiaojuan Yang         old_rtcen = rtc_enabled(s);
314c117f68aSXiaojuan Yang 
315c117f68aSXiaojuan Yang         s->cntrctl = val;
316c117f68aSXiaojuan Yang         /* get new ctrl */
317c117f68aSXiaojuan Yang         new_toyen = toy_enabled(s);
318c117f68aSXiaojuan Yang         new_rtcen = rtc_enabled(s);
319c117f68aSXiaojuan Yang 
320c117f68aSXiaojuan Yang         /*
321c117f68aSXiaojuan Yang          * we do not consider if EO changed, as it always set at most time.
322c117f68aSXiaojuan Yang          * toy or rtc enabled should start timer. otherwise, stop timer
323c117f68aSXiaojuan Yang          */
324c117f68aSXiaojuan Yang         if (old_toyen != new_toyen) {
325c117f68aSXiaojuan Yang             if (new_toyen) {
326c117f68aSXiaojuan Yang                 ls7a_toy_start(s);
327c117f68aSXiaojuan Yang             } else {
328c117f68aSXiaojuan Yang                 ls7a_toy_stop(s);
329c117f68aSXiaojuan Yang             }
330c117f68aSXiaojuan Yang         }
331c117f68aSXiaojuan Yang         if (old_rtcen != new_rtcen) {
332c117f68aSXiaojuan Yang             if (new_rtcen) {
333c117f68aSXiaojuan Yang                 ls7a_rtc_start(s);
334c117f68aSXiaojuan Yang             } else {
335c117f68aSXiaojuan Yang                 ls7a_rtc_stop(s);
336c117f68aSXiaojuan Yang             }
337c117f68aSXiaojuan Yang         }
338c117f68aSXiaojuan Yang         break;
339c117f68aSXiaojuan Yang     case SYS_RTCWRTIE0:
340c117f68aSXiaojuan Yang         if (rtc_enabled(s)) {
341c117f68aSXiaojuan Yang             s->offset_rtc = val - ls7a_rtc_ticks();
342c117f68aSXiaojuan Yang         }
343c117f68aSXiaojuan Yang         break;
344c117f68aSXiaojuan Yang     case SYS_RTCMATCH0:
345c117f68aSXiaojuan Yang         rtcmatch_write(s, val, 0);
346c117f68aSXiaojuan Yang         break;
347c117f68aSXiaojuan Yang     case SYS_RTCMATCH1:
348c117f68aSXiaojuan Yang         rtcmatch_write(s, val, 1);
349c117f68aSXiaojuan Yang         break;
350c117f68aSXiaojuan Yang     case SYS_RTCMATCH2:
351c117f68aSXiaojuan Yang         rtcmatch_write(s, val, 2);
352c117f68aSXiaojuan Yang         break;
353c117f68aSXiaojuan Yang     default:
354c117f68aSXiaojuan Yang         break;
355c117f68aSXiaojuan Yang     }
356c117f68aSXiaojuan Yang }
357c117f68aSXiaojuan Yang 
358c117f68aSXiaojuan Yang static const MemoryRegionOps ls7a_rtc_ops = {
359c117f68aSXiaojuan Yang     .read = ls7a_rtc_read,
360c117f68aSXiaojuan Yang     .write = ls7a_rtc_write,
361c117f68aSXiaojuan Yang     .endianness = DEVICE_LITTLE_ENDIAN,
362c117f68aSXiaojuan Yang     .valid = {
363c117f68aSXiaojuan Yang         .min_access_size = 4,
364c117f68aSXiaojuan Yang         .max_access_size = 4,
365c117f68aSXiaojuan Yang     },
366c117f68aSXiaojuan Yang };
367c117f68aSXiaojuan Yang 
toy_timer_cb(void * opaque)368c117f68aSXiaojuan Yang static void toy_timer_cb(void *opaque)
369c117f68aSXiaojuan Yang {
370c117f68aSXiaojuan Yang     LS7ARtcState *s = opaque;
371c117f68aSXiaojuan Yang 
372c117f68aSXiaojuan Yang     if (toy_enabled(s)) {
373df11f3eaSXiaojuan Yang         qemu_irq_raise(s->irq);
374c117f68aSXiaojuan Yang     }
375c117f68aSXiaojuan Yang }
376c117f68aSXiaojuan Yang 
rtc_timer_cb(void * opaque)377c117f68aSXiaojuan Yang static void rtc_timer_cb(void *opaque)
378c117f68aSXiaojuan Yang {
379c117f68aSXiaojuan Yang     LS7ARtcState *s = opaque;
380c117f68aSXiaojuan Yang 
381c117f68aSXiaojuan Yang     if (rtc_enabled(s)) {
382df11f3eaSXiaojuan Yang         qemu_irq_raise(s->irq);
383c117f68aSXiaojuan Yang     }
384c117f68aSXiaojuan Yang }
385c117f68aSXiaojuan Yang 
ls7a_rtc_realize(DeviceState * dev,Error ** errp)386c117f68aSXiaojuan Yang static void ls7a_rtc_realize(DeviceState *dev, Error **errp)
387c117f68aSXiaojuan Yang {
388c117f68aSXiaojuan Yang     int i;
389c117f68aSXiaojuan Yang     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
390c117f68aSXiaojuan Yang     LS7ARtcState *d = LS7A_RTC(sbd);
391c117f68aSXiaojuan Yang     memory_region_init_io(&d->iomem, NULL, &ls7a_rtc_ops,
392c117f68aSXiaojuan Yang                          (void *)d, "ls7a_rtc", 0x100);
393c117f68aSXiaojuan Yang 
394c117f68aSXiaojuan Yang     sysbus_init_irq(sbd, &d->irq);
395c117f68aSXiaojuan Yang 
396c117f68aSXiaojuan Yang     sysbus_init_mmio(sbd, &d->iomem);
397c117f68aSXiaojuan Yang     for (i = 0; i < TIMER_NUMS; i++) {
398c117f68aSXiaojuan Yang         d->toymatch[i] = 0;
399c117f68aSXiaojuan Yang         d->rtcmatch[i] = 0;
400c117f68aSXiaojuan Yang         d->toy_timer[i] = timer_new_ms(rtc_clock, toy_timer_cb, d);
401c117f68aSXiaojuan Yang         d->rtc_timer[i] = timer_new_ms(rtc_clock, rtc_timer_cb, d);
402c117f68aSXiaojuan Yang     }
403c117f68aSXiaojuan Yang     d->offset_toy = 0;
404c117f68aSXiaojuan Yang     d->offset_rtc = 0;
405c117f68aSXiaojuan Yang 
406c117f68aSXiaojuan Yang }
407c117f68aSXiaojuan Yang 
408e5c0367eSXiaojuan Yang /* delete timer and clear reg when reset */
ls7a_rtc_reset(DeviceState * dev)409e5c0367eSXiaojuan Yang static void ls7a_rtc_reset(DeviceState *dev)
410e5c0367eSXiaojuan Yang {
411e5c0367eSXiaojuan Yang     int i;
412e5c0367eSXiaojuan Yang     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
413e5c0367eSXiaojuan Yang     LS7ARtcState *d = LS7A_RTC(sbd);
414e5c0367eSXiaojuan Yang     for (i = 0; i < TIMER_NUMS; i++) {
415e5c0367eSXiaojuan Yang         if (toy_enabled(d)) {
416e5c0367eSXiaojuan Yang             timer_del(d->toy_timer[i]);
417e5c0367eSXiaojuan Yang         }
418e5c0367eSXiaojuan Yang         if (rtc_enabled(d)) {
419e5c0367eSXiaojuan Yang             timer_del(d->rtc_timer[i]);
420e5c0367eSXiaojuan Yang         }
421e5c0367eSXiaojuan Yang         d->toymatch[i] = 0;
422e5c0367eSXiaojuan Yang         d->rtcmatch[i] = 0;
423e5c0367eSXiaojuan Yang     }
424e5c0367eSXiaojuan Yang     d->cntrctl = 0;
425e5c0367eSXiaojuan Yang }
426e5c0367eSXiaojuan Yang 
ls7a_rtc_pre_save(void * opaque)427c117f68aSXiaojuan Yang static int ls7a_rtc_pre_save(void *opaque)
428c117f68aSXiaojuan Yang {
429c117f68aSXiaojuan Yang     LS7ARtcState *s = LS7A_RTC(opaque);
430c117f68aSXiaojuan Yang 
431c117f68aSXiaojuan Yang     ls7a_toy_stop(s);
432c117f68aSXiaojuan Yang     ls7a_rtc_stop(s);
433c117f68aSXiaojuan Yang 
434c117f68aSXiaojuan Yang     return 0;
435c117f68aSXiaojuan Yang }
436c117f68aSXiaojuan Yang 
ls7a_rtc_post_load(void * opaque,int version_id)437c117f68aSXiaojuan Yang static int ls7a_rtc_post_load(void *opaque, int version_id)
438c117f68aSXiaojuan Yang {
439c117f68aSXiaojuan Yang     LS7ARtcState *s = LS7A_RTC(opaque);
440c117f68aSXiaojuan Yang     if (toy_enabled(s)) {
441c117f68aSXiaojuan Yang         ls7a_toy_start(s);
442c117f68aSXiaojuan Yang     }
443c117f68aSXiaojuan Yang 
444c117f68aSXiaojuan Yang     if (rtc_enabled(s)) {
445c117f68aSXiaojuan Yang         ls7a_rtc_start(s);
446c117f68aSXiaojuan Yang     }
447c117f68aSXiaojuan Yang 
448c117f68aSXiaojuan Yang     return 0;
449c117f68aSXiaojuan Yang }
450c117f68aSXiaojuan Yang 
451c117f68aSXiaojuan Yang static const VMStateDescription vmstate_ls7a_rtc = {
452c117f68aSXiaojuan Yang     .name = "ls7a_rtc",
453c117f68aSXiaojuan Yang     .version_id = 1,
454c117f68aSXiaojuan Yang     .minimum_version_id = 1,
455c117f68aSXiaojuan Yang     .pre_save = ls7a_rtc_pre_save,
456c117f68aSXiaojuan Yang     .post_load = ls7a_rtc_post_load,
457a80cc662SRichard Henderson     .fields = (const VMStateField[]) {
458c117f68aSXiaojuan Yang         VMSTATE_INT64(offset_toy, LS7ARtcState),
459c117f68aSXiaojuan Yang         VMSTATE_INT64(offset_rtc, LS7ARtcState),
460c117f68aSXiaojuan Yang         VMSTATE_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS),
461c117f68aSXiaojuan Yang         VMSTATE_UINT32_ARRAY(rtcmatch, LS7ARtcState, TIMER_NUMS),
462c117f68aSXiaojuan Yang         VMSTATE_UINT32(cntrctl, LS7ARtcState),
463c117f68aSXiaojuan Yang         VMSTATE_END_OF_LIST()
464c117f68aSXiaojuan Yang     }
465c117f68aSXiaojuan Yang };
466c117f68aSXiaojuan Yang 
ls7a_rtc_class_init(ObjectClass * klass,void * data)467c117f68aSXiaojuan Yang static void ls7a_rtc_class_init(ObjectClass *klass, void *data)
468c117f68aSXiaojuan Yang {
469c117f68aSXiaojuan Yang     DeviceClass *dc = DEVICE_CLASS(klass);
470c117f68aSXiaojuan Yang     dc->vmsd = &vmstate_ls7a_rtc;
471c117f68aSXiaojuan Yang     dc->realize = ls7a_rtc_realize;
472e5c0367eSXiaojuan Yang     dc->reset = ls7a_rtc_reset;
473c117f68aSXiaojuan Yang     dc->desc = "ls7a rtc";
474c117f68aSXiaojuan Yang }
475c117f68aSXiaojuan Yang 
476c117f68aSXiaojuan Yang static const TypeInfo ls7a_rtc_info = {
477c117f68aSXiaojuan Yang     .name          = TYPE_LS7A_RTC,
478c117f68aSXiaojuan Yang     .parent        = TYPE_SYS_BUS_DEVICE,
479c117f68aSXiaojuan Yang     .instance_size = sizeof(LS7ARtcState),
480c117f68aSXiaojuan Yang     .class_init    = ls7a_rtc_class_init,
481c117f68aSXiaojuan Yang };
482c117f68aSXiaojuan Yang 
ls7a_rtc_register_types(void)483c117f68aSXiaojuan Yang static void ls7a_rtc_register_types(void)
484c117f68aSXiaojuan Yang {
485c117f68aSXiaojuan Yang     type_register_static(&ls7a_rtc_info);
486c117f68aSXiaojuan Yang }
487c117f68aSXiaojuan Yang 
488c117f68aSXiaojuan Yang type_init(ls7a_rtc_register_types)
489