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