xref: /openbmc/qemu/hw/rtc/ls7a_rtc.c (revision df11f3ea6957cfdbc1690767e90cf04585a6c601)
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     uint64_t save_toy_mon;
76     uint64_t save_toy_year;
77     uint64_t save_rtc;
78     int64_t data;
79     int tidx;
80     uint32_t toymatch[3];
81     uint32_t toytrim;
82     uint32_t cntrctl;
83     uint32_t rtctrim;
84     uint32_t rtccount;
85     uint32_t rtcmatch[3];
86     QEMUTimer *toy_timer[TIMER_NUMS];
87     QEMUTimer *rtc_timer[TIMER_NUMS];
88     qemu_irq irq;
89 };
90 
91 /* switch nanoseconds time to rtc ticks */
92 static inline uint64_t ls7a_rtc_ticks(void)
93 {
94     return qemu_clock_get_ns(rtc_clock) * LS7A_RTC_FREQ / NANOSECONDS_PER_SECOND;
95 }
96 
97 /* switch rtc ticks to nanoseconds */
98 static inline uint64_t ticks_to_ns(uint64_t ticks)
99 {
100     return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ;
101 }
102 
103 static inline bool toy_enabled(LS7ARtcState *s)
104 {
105     return FIELD_EX32(s->cntrctl, RTC_CTRL, TOYEN) &&
106            FIELD_EX32(s->cntrctl, RTC_CTRL, EO);
107 }
108 
109 static inline bool rtc_enabled(LS7ARtcState *s)
110 {
111     return FIELD_EX32(s->cntrctl, RTC_CTRL, RTCEN) &&
112            FIELD_EX32(s->cntrctl, RTC_CTRL, EO);
113 }
114 
115 /* parse toy value to struct tm */
116 static inline void toy_val_to_time_mon(uint64_t toy_val, struct tm *tm)
117 {
118     tm->tm_sec = FIELD_EX32(toy_val, TOY, SEC);
119     tm->tm_min = FIELD_EX32(toy_val, TOY, MIN);
120     tm->tm_hour = FIELD_EX32(toy_val, TOY, HOUR);
121     tm->tm_mday = FIELD_EX32(toy_val, TOY, DAY);
122     tm->tm_mon = FIELD_EX32(toy_val, TOY, MON) - 1;
123 }
124 
125 static inline void toy_val_to_time_year(uint64_t toy_year, struct tm *tm)
126 {
127     tm->tm_year = toy_year;
128 }
129 
130 /* parse struct tm to toy value */
131 static inline uint64_t toy_time_to_val_mon(struct tm tm)
132 {
133     uint64_t val = 0;
134 
135     val = FIELD_DP32(val, TOY, MON, tm.tm_mon + 1);
136     val = FIELD_DP32(val, TOY, DAY, tm.tm_mday);
137     val = FIELD_DP32(val, TOY, HOUR, tm.tm_hour);
138     val = FIELD_DP32(val, TOY, MIN, tm.tm_min);
139     val = FIELD_DP32(val, TOY, SEC, tm.tm_sec);
140     return val;
141 }
142 
143 static inline uint64_t toy_time_to_val_year(struct tm tm)
144 {
145     uint64_t year;
146 
147     year = tm.tm_year;
148     return year;
149 }
150 
151 static inline void toymatch_val_to_time(LS7ARtcState *s, uint64_t val, struct tm *tm)
152 {
153     qemu_get_timedate(tm, s->offset_toy);
154     tm->tm_sec = FIELD_EX32(val, TOY_MATCH, SEC);
155     tm->tm_min = FIELD_EX32(val, TOY_MATCH, MIN);
156     tm->tm_hour = FIELD_EX32(val, TOY_MATCH, HOUR);
157     tm->tm_mday = FIELD_EX32(val, TOY_MATCH, DAY);
158     tm->tm_mon = FIELD_EX32(val, TOY_MATCH, MON) - 1;
159     tm->tm_year += (FIELD_EX32(val, TOY_MATCH, YEAR) - (tm->tm_year & 0x3f));
160 }
161 
162 static void toymatch_write(LS7ARtcState *s, uint64_t val, int num)
163 {
164     int64_t now, expire_time;
165     struct tm tm = {};
166 
167     /* it do not support write when toy disabled */
168     if (toy_enabled(s)) {
169         s->toymatch[num] = val;
170         /* caculate expire time */
171         now = qemu_clock_get_ms(rtc_clock);
172         toymatch_val_to_time(s, val, &tm);
173         expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
174         timer_mod(s->toy_timer[num], expire_time);
175     }
176 }
177 
178 static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)
179 {
180     uint64_t expire_ns;
181 
182     /* it do not support write when toy disabled */
183     if (rtc_enabled(s)) {
184         s->rtcmatch[num] = val;
185         /* caculate expire time */
186         expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
187         timer_mod_ns(s->rtc_timer[num], expire_ns);
188     }
189 }
190 
191 static void ls7a_toy_stop(LS7ARtcState *s)
192 {
193     int i;
194     struct tm tm;
195     /*
196      * save time when disabled toy,
197      * because toy time not add counters.
198      */
199     qemu_get_timedate(&tm, s->offset_toy);
200     s->save_toy_mon = toy_time_to_val_mon(tm);
201     s->save_toy_year = toy_time_to_val_year(tm);
202 
203     /* delete timers, and when re-enabled, recaculate expire time */
204     for (i = 0; i < TIMER_NUMS; i++) {
205         timer_del(s->toy_timer[i]);
206     }
207 }
208 
209 static void ls7a_rtc_stop(LS7ARtcState *s)
210 {
211     int i;
212     uint64_t time;
213 
214     /* save rtc time */
215     time = ls7a_rtc_ticks() + s->offset_rtc;
216     s->save_rtc = time;
217 
218     /* delete timers, and when re-enabled, recaculate expire time */
219     for (i = 0; i < TIMER_NUMS; i++) {
220         timer_del(s->rtc_timer[i]);
221     }
222 }
223 
224 static void ls7a_toy_start(LS7ARtcState *s)
225 {
226     int i;
227     uint64_t expire_time, now;
228     struct tm tm = {};
229     /*
230      * need to recaculate toy offset
231      * and expire time when enable it.
232      */
233     toy_val_to_time_mon(s->save_toy_mon, &tm);
234     toy_val_to_time_year(s->save_toy_year, &tm);
235 
236     s->offset_toy = qemu_timedate_diff(&tm);
237     now = qemu_clock_get_ms(rtc_clock);
238 
239     /* recaculate expire time and enable timer */
240     for (i = 0; i < TIMER_NUMS; i++) {
241         toymatch_val_to_time(s, s->toymatch[i], &tm);
242         expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
243         timer_mod(s->toy_timer[i], expire_time);
244     }
245 }
246 
247 static void ls7a_rtc_start(LS7ARtcState *s)
248 {
249     int i;
250     uint64_t expire_time, now;
251 
252     /*
253      * need to recaculate rtc offset
254      * and expire time when enable it.
255      */
256     now = ls7a_rtc_ticks();
257     s->offset_rtc = s->save_rtc - now;
258 
259     /* recaculate expire time and enable timer */
260     for (i = 0; i < TIMER_NUMS; i++) {
261         expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc);
262         timer_mod_ns(s->rtc_timer[i], expire_time);
263     }
264 }
265 
266 static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size)
267 {
268     LS7ARtcState *s = LS7A_RTC(opaque);
269     struct tm tm;
270     int val = 0;
271 
272     switch (addr) {
273     case SYS_TOYREAD0:
274         /* if toy disabled, read save toy time */
275         if (toy_enabled(s)) {
276             qemu_get_timedate(&tm, s->offset_toy);
277             val = toy_time_to_val_mon(tm);
278         } else {
279             /* read save mon val */
280             val = s->save_toy_mon;
281         }
282         break;
283     case SYS_TOYREAD1:
284         /* if toy disabled, read save toy time */
285         if (toy_enabled(s)) {
286             qemu_get_timedate(&tm, s->offset_toy);
287             val = tm.tm_year;
288         } else {
289             /* read save year val */
290             val = s->save_toy_year;
291         }
292         break;
293     case SYS_TOYMATCH0:
294         val = s->toymatch[0];
295         break;
296     case SYS_TOYMATCH1:
297         val = s->toymatch[1];
298         break;
299     case SYS_TOYMATCH2:
300         val = s->toymatch[2];
301         break;
302     case SYS_RTCCTRL:
303         val = s->cntrctl;
304         break;
305     case SYS_RTCREAD0:
306         /* if rtc disabled, read save rtc time */
307         if (rtc_enabled(s)) {
308             val = ls7a_rtc_ticks() + s->offset_rtc;
309         } else {
310             val = s->save_rtc;
311         }
312         break;
313     case SYS_RTCMATCH0:
314         val = s->rtcmatch[0];
315         break;
316     case SYS_RTCMATCH1:
317         val = s->rtcmatch[1];
318         break;
319     case SYS_RTCMATCH2:
320         val = s->rtcmatch[2];
321         break;
322     default:
323         val = 0;
324         break;
325     }
326     return val;
327 }
328 
329 static void ls7a_rtc_write(void *opaque, hwaddr addr,
330                            uint64_t val, unsigned size)
331 {
332     int old_toyen, old_rtcen, new_toyen, new_rtcen;
333     LS7ARtcState *s = LS7A_RTC(opaque);
334     struct tm tm;
335 
336     switch (addr) {
337     case SYS_TOYWRITE0:
338         /* it do not support write when toy disabled */
339         if (toy_enabled(s)) {
340             qemu_get_timedate(&tm, s->offset_toy);
341             tm.tm_sec = FIELD_EX32(val, TOY, SEC);
342             tm.tm_min = FIELD_EX32(val, TOY, MIN);
343             tm.tm_hour = FIELD_EX32(val, TOY, HOUR);
344             tm.tm_mday = FIELD_EX32(val, TOY, DAY);
345             tm.tm_mon = FIELD_EX32(val, TOY, MON) - 1;
346             s->offset_toy = qemu_timedate_diff(&tm);
347         }
348     break;
349     case SYS_TOYWRITE1:
350         if (toy_enabled(s)) {
351             qemu_get_timedate(&tm, s->offset_toy);
352             tm.tm_year = val;
353             s->offset_toy = qemu_timedate_diff(&tm);
354         }
355         break;
356     case SYS_TOYMATCH0:
357         toymatch_write(s, val, 0);
358         break;
359     case SYS_TOYMATCH1:
360         toymatch_write(s, val, 1);
361         break;
362     case SYS_TOYMATCH2:
363         toymatch_write(s, val, 2);
364         break;
365     case SYS_RTCCTRL:
366         /* get old ctrl */
367         old_toyen = toy_enabled(s);
368         old_rtcen = rtc_enabled(s);
369 
370         s->cntrctl = val;
371         /* get new ctrl */
372         new_toyen = toy_enabled(s);
373         new_rtcen = rtc_enabled(s);
374 
375         /*
376          * we do not consider if EO changed, as it always set at most time.
377          * toy or rtc enabled should start timer. otherwise, stop timer
378          */
379         if (old_toyen != new_toyen) {
380             if (new_toyen) {
381                 ls7a_toy_start(s);
382             } else {
383                 ls7a_toy_stop(s);
384             }
385         }
386         if (old_rtcen != new_rtcen) {
387             if (new_rtcen) {
388                 ls7a_rtc_start(s);
389             } else {
390                 ls7a_rtc_stop(s);
391             }
392         }
393         break;
394     case SYS_RTCWRTIE0:
395         if (rtc_enabled(s)) {
396             s->offset_rtc = val - ls7a_rtc_ticks();
397         }
398         break;
399     case SYS_RTCMATCH0:
400         rtcmatch_write(s, val, 0);
401         break;
402     case SYS_RTCMATCH1:
403         rtcmatch_write(s, val, 1);
404         break;
405     case SYS_RTCMATCH2:
406         rtcmatch_write(s, val, 2);
407         break;
408     default:
409         break;
410     }
411 }
412 
413 static const MemoryRegionOps ls7a_rtc_ops = {
414     .read = ls7a_rtc_read,
415     .write = ls7a_rtc_write,
416     .endianness = DEVICE_LITTLE_ENDIAN,
417     .valid = {
418         .min_access_size = 4,
419         .max_access_size = 4,
420     },
421 };
422 
423 static void toy_timer_cb(void *opaque)
424 {
425     LS7ARtcState *s = opaque;
426 
427     if (toy_enabled(s)) {
428         qemu_irq_raise(s->irq);
429     }
430 }
431 
432 static void rtc_timer_cb(void *opaque)
433 {
434     LS7ARtcState *s = opaque;
435 
436     if (rtc_enabled(s)) {
437         qemu_irq_raise(s->irq);
438     }
439 }
440 
441 static void ls7a_rtc_realize(DeviceState *dev, Error **errp)
442 {
443     int i;
444     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
445     LS7ARtcState *d = LS7A_RTC(sbd);
446     memory_region_init_io(&d->iomem, NULL, &ls7a_rtc_ops,
447                          (void *)d, "ls7a_rtc", 0x100);
448 
449     sysbus_init_irq(sbd, &d->irq);
450 
451     sysbus_init_mmio(sbd, &d->iomem);
452     for (i = 0; i < TIMER_NUMS; i++) {
453         d->toymatch[i] = 0;
454         d->rtcmatch[i] = 0;
455         d->toy_timer[i] = timer_new_ms(rtc_clock, toy_timer_cb, d);
456         d->rtc_timer[i] = timer_new_ms(rtc_clock, rtc_timer_cb, d);
457     }
458     d->offset_toy = 0;
459     d->offset_rtc = 0;
460     d->save_toy_mon = 0;
461     d->save_toy_year = 0;
462     d->save_rtc = 0;
463 
464     create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4);
465 }
466 
467 static int ls7a_rtc_pre_save(void *opaque)
468 {
469     LS7ARtcState *s = LS7A_RTC(opaque);
470 
471     ls7a_toy_stop(s);
472     ls7a_rtc_stop(s);
473 
474     return 0;
475 }
476 
477 static int ls7a_rtc_post_load(void *opaque, int version_id)
478 {
479     LS7ARtcState *s = LS7A_RTC(opaque);
480     if (toy_enabled(s)) {
481         ls7a_toy_start(s);
482     }
483 
484     if (rtc_enabled(s)) {
485         ls7a_rtc_start(s);
486     }
487 
488     return 0;
489 }
490 
491 static const VMStateDescription vmstate_ls7a_rtc = {
492     .name = "ls7a_rtc",
493     .version_id = 1,
494     .minimum_version_id = 1,
495     .pre_save = ls7a_rtc_pre_save,
496     .post_load = ls7a_rtc_post_load,
497     .fields = (VMStateField[]) {
498         VMSTATE_INT64(offset_toy, LS7ARtcState),
499         VMSTATE_INT64(offset_rtc, LS7ARtcState),
500         VMSTATE_UINT64(save_toy_mon, LS7ARtcState),
501         VMSTATE_UINT64(save_toy_year, LS7ARtcState),
502         VMSTATE_UINT64(save_rtc, LS7ARtcState),
503         VMSTATE_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS),
504         VMSTATE_UINT32_ARRAY(rtcmatch, LS7ARtcState, TIMER_NUMS),
505         VMSTATE_UINT32(cntrctl, LS7ARtcState),
506         VMSTATE_END_OF_LIST()
507     }
508 };
509 
510 static void ls7a_rtc_class_init(ObjectClass *klass, void *data)
511 {
512     DeviceClass *dc = DEVICE_CLASS(klass);
513     dc->vmsd = &vmstate_ls7a_rtc;
514     dc->realize = ls7a_rtc_realize;
515     dc->desc = "ls7a rtc";
516 }
517 
518 static const TypeInfo ls7a_rtc_info = {
519     .name          = TYPE_LS7A_RTC,
520     .parent        = TYPE_SYS_BUS_DEVICE,
521     .instance_size = sizeof(LS7ARtcState),
522     .class_init    = ls7a_rtc_class_init,
523 };
524 
525 static void ls7a_rtc_register_types(void)
526 {
527     type_register_static(&ls7a_rtc_info);
528 }
529 
530 type_init(ls7a_rtc_register_types)
531