1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * Loongarch LS7A Real Time Clock emulation
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
8 #include "qemu/osdep.h"
11 #include "include/hw/register.h"
12 #include "qemu/timer.h"
13 #include "sysemu/sysemu.h"
14 #include "qemu/cutils.h"
16 #include "migration/vmstate.h"
17 #include "hw/misc/unimp.h"
18 #include "sysemu/rtc.h"
19 #include "hw/registerfields.h"
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
37 #define LS7A_RTC_FREQ 32768
40 * Shift bits and filed mask
43 FIELD(TOY
, MON
, 26, 6)
44 FIELD(TOY
, DAY
, 21, 5)
45 FIELD(TOY
, HOUR
, 16, 5)
46 FIELD(TOY
, MIN
, 10, 6)
48 FIELD(TOY
, MSEC
, 0, 4)
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)
57 FIELD(RTC_CTRL
, RTCEN
, 13, 1)
58 FIELD(RTC_CTRL
, TOYEN
, 11, 1)
59 FIELD(RTC_CTRL
, EO
, 8, 1)
61 #define TYPE_LS7A_RTC "ls7a_rtc"
62 OBJECT_DECLARE_SIMPLE_TYPE(LS7ARtcState
, LS7A_RTC
)
65 SysBusDevice parent_obj
;
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
75 uint64_t save_toy_mon
;
76 uint64_t save_toy_year
;
86 QEMUTimer
*toy_timer
[TIMER_NUMS
];
87 QEMUTimer
*rtc_timer
[TIMER_NUMS
];
91 /* switch nanoseconds time to rtc ticks */
92 static inline uint64_t ls7a_rtc_ticks(void)
94 return qemu_clock_get_ns(rtc_clock
) * LS7A_RTC_FREQ
/ NANOSECONDS_PER_SECOND
;
97 /* switch rtc ticks to nanoseconds */
98 static inline uint64_t ticks_to_ns(uint64_t ticks
)
100 return ticks
* NANOSECONDS_PER_SECOND
/ LS7A_RTC_FREQ
;
103 static inline bool toy_enabled(LS7ARtcState
*s
)
105 return FIELD_EX32(s
->cntrctl
, RTC_CTRL
, TOYEN
) &&
106 FIELD_EX32(s
->cntrctl
, RTC_CTRL
, EO
);
109 static inline bool rtc_enabled(LS7ARtcState
*s
)
111 return FIELD_EX32(s
->cntrctl
, RTC_CTRL
, RTCEN
) &&
112 FIELD_EX32(s
->cntrctl
, RTC_CTRL
, EO
);
115 /* parse toy value to struct tm */
116 static inline void toy_val_to_time_mon(uint64_t toy_val
, struct tm
*tm
)
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;
125 static inline void toy_val_to_time_year(uint64_t toy_year
, struct tm
*tm
)
127 tm
->tm_year
= toy_year
;
130 /* parse struct tm to toy value */
131 static inline uint64_t toy_time_to_val_mon(struct tm tm
)
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
);
143 static inline uint64_t toy_time_to_val_year(struct tm tm
)
151 static inline void toymatch_val_to_time(uint64_t val
, struct tm
*tm
)
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));
161 static void toymatch_write(LS7ARtcState
*s
, struct tm
*tm
, uint64_t val
, int num
)
163 int64_t now
, expire_time
;
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
);
176 static void rtcmatch_write(LS7ARtcState
*s
, uint64_t val
, int num
)
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
);
189 static void ls7a_toy_stop(LS7ARtcState
*s
)
194 * save time when disabled toy,
195 * because toy time not add counters.
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
);
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
]);
207 static void ls7a_rtc_stop(LS7ARtcState
*s
)
213 time
= ls7a_rtc_ticks() + s
->offset_rtc
;
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
]);
222 static void ls7a_toy_start(LS7ARtcState
*s
)
225 uint64_t expire_time
, now
;
228 * need to recaculate toy offset
229 * and expire time when enable it.
231 toy_val_to_time_mon(s
->save_toy_mon
, &tm
);
232 toy_val_to_time_year(s
->save_toy_year
, &tm
);
234 s
->offset_toy
= qemu_timedate_diff(&tm
);
235 now
= qemu_clock_get_ms(rtc_clock
);
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
);
245 static void ls7a_rtc_start(LS7ARtcState
*s
)
248 uint64_t expire_time
, now
;
251 * need to recaculate rtc offset
252 * and expire time when enable it.
254 now
= ls7a_rtc_ticks();
255 s
->offset_rtc
= s
->save_rtc
- now
;
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
);
264 static uint64_t ls7a_rtc_read(void *opaque
, hwaddr addr
, unsigned size
)
266 LS7ARtcState
*s
= LS7A_RTC(opaque
);
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
);
277 /* read save mon val */
278 val
= s
->save_toy_mon
;
282 /* if toy disabled, read save toy time */
283 if (toy_enabled(s
)) {
284 qemu_get_timedate(&tm
, s
->offset_toy
);
287 /* read save year val */
288 val
= s
->save_toy_year
;
292 val
= s
->toymatch
[0];
295 val
= s
->toymatch
[1];
298 val
= s
->toymatch
[2];
304 /* if rtc disabled, read save rtc time */
305 if (rtc_enabled(s
)) {
306 val
= ls7a_rtc_ticks() + s
->offset_rtc
;
312 val
= s
->rtcmatch
[0];
315 val
= s
->rtcmatch
[1];
318 val
= s
->rtcmatch
[2];
327 static void ls7a_rtc_write(void *opaque
, hwaddr addr
,
328 uint64_t val
, unsigned size
)
330 int old_toyen
, old_rtcen
, new_toyen
, new_rtcen
;
331 LS7ARtcState
*s
= LS7A_RTC(opaque
);
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
);
348 if (toy_enabled(s
)) {
349 qemu_get_timedate(&tm
, s
->offset_toy
);
351 s
->offset_toy
= qemu_timedate_diff(&tm
);
355 toymatch_write(s
, &tm
, val
, 0);
358 toymatch_write(s
, &tm
, val
, 1);
361 toymatch_write(s
, &tm
, val
, 2);
365 old_toyen
= toy_enabled(s
);
366 old_rtcen
= rtc_enabled(s
);
370 new_toyen
= toy_enabled(s
);
371 new_rtcen
= rtc_enabled(s
);
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
377 if (old_toyen
!= new_toyen
) {
384 if (old_rtcen
!= new_rtcen
) {
393 if (rtc_enabled(s
)) {
394 s
->offset_rtc
= val
- ls7a_rtc_ticks();
398 rtcmatch_write(s
, val
, 0);
401 rtcmatch_write(s
, val
, 1);
404 rtcmatch_write(s
, val
, 2);
411 static const MemoryRegionOps ls7a_rtc_ops
= {
412 .read
= ls7a_rtc_read
,
413 .write
= ls7a_rtc_write
,
414 .endianness
= DEVICE_LITTLE_ENDIAN
,
416 .min_access_size
= 4,
417 .max_access_size
= 4,
421 static void toy_timer_cb(void *opaque
)
423 LS7ARtcState
*s
= opaque
;
425 if (toy_enabled(s
)) {
426 qemu_irq_pulse(s
->irq
);
430 static void rtc_timer_cb(void *opaque
)
432 LS7ARtcState
*s
= opaque
;
434 if (rtc_enabled(s
)) {
435 qemu_irq_pulse(s
->irq
);
439 static void ls7a_rtc_realize(DeviceState
*dev
, Error
**errp
)
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);
447 sysbus_init_irq(sbd
, &d
->irq
);
449 sysbus_init_mmio(sbd
, &d
->iomem
);
450 for (i
= 0; i
< TIMER_NUMS
; i
++) {
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
);
459 d
->save_toy_year
= 0;
462 create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4);
465 static int ls7a_rtc_pre_save(void *opaque
)
467 LS7ARtcState
*s
= LS7A_RTC(opaque
);
475 static int ls7a_rtc_post_load(void *opaque
, int version_id
)
477 LS7ARtcState
*s
= LS7A_RTC(opaque
);
478 if (toy_enabled(s
)) {
482 if (rtc_enabled(s
)) {
489 static const VMStateDescription vmstate_ls7a_rtc
= {
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()
508 static void ls7a_rtc_class_init(ObjectClass
*klass
, void *data
)
510 DeviceClass
*dc
= DEVICE_CLASS(klass
);
511 dc
->vmsd
= &vmstate_ls7a_rtc
;
512 dc
->realize
= ls7a_rtc_realize
;
513 dc
->desc
= "ls7a rtc";
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
,
523 static void ls7a_rtc_register_types(void)
525 type_register_static(&ls7a_rtc_info
);
528 type_init(ls7a_rtc_register_types
)