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