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(LS7ARtcState
*s
, uint64_t val
, struct tm
*tm
)
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));
162 static void toymatch_write(LS7ARtcState
*s
, uint64_t val
, int num
)
164 int64_t now
, expire_time
;
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
);
178 static void rtcmatch_write(LS7ARtcState
*s
, uint64_t val
, int num
)
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
);
191 static void ls7a_toy_stop(LS7ARtcState
*s
)
196 * save time when disabled toy,
197 * because toy time not add counters.
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
);
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
]);
209 static void ls7a_rtc_stop(LS7ARtcState
*s
)
215 time
= ls7a_rtc_ticks() + s
->offset_rtc
;
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
]);
224 static void ls7a_toy_start(LS7ARtcState
*s
)
227 uint64_t expire_time
, now
;
230 * need to recaculate toy offset
231 * and expire time when enable it.
233 toy_val_to_time_mon(s
->save_toy_mon
, &tm
);
234 toy_val_to_time_year(s
->save_toy_year
, &tm
);
236 s
->offset_toy
= qemu_timedate_diff(&tm
);
237 now
= qemu_clock_get_ms(rtc_clock
);
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
);
247 static void ls7a_rtc_start(LS7ARtcState
*s
)
250 uint64_t expire_time
, now
;
253 * need to recaculate rtc offset
254 * and expire time when enable it.
256 now
= ls7a_rtc_ticks();
257 s
->offset_rtc
= s
->save_rtc
- now
;
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
);
266 static uint64_t ls7a_rtc_read(void *opaque
, hwaddr addr
, unsigned size
)
268 LS7ARtcState
*s
= LS7A_RTC(opaque
);
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
);
279 /* read save mon val */
280 val
= s
->save_toy_mon
;
284 /* if toy disabled, read save toy time */
285 if (toy_enabled(s
)) {
286 qemu_get_timedate(&tm
, s
->offset_toy
);
289 /* read save year val */
290 val
= s
->save_toy_year
;
294 val
= s
->toymatch
[0];
297 val
= s
->toymatch
[1];
300 val
= s
->toymatch
[2];
306 /* if rtc disabled, read save rtc time */
307 if (rtc_enabled(s
)) {
308 val
= ls7a_rtc_ticks() + s
->offset_rtc
;
314 val
= s
->rtcmatch
[0];
317 val
= s
->rtcmatch
[1];
320 val
= s
->rtcmatch
[2];
329 static void ls7a_rtc_write(void *opaque
, hwaddr addr
,
330 uint64_t val
, unsigned size
)
332 int old_toyen
, old_rtcen
, new_toyen
, new_rtcen
;
333 LS7ARtcState
*s
= LS7A_RTC(opaque
);
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
);
350 if (toy_enabled(s
)) {
351 qemu_get_timedate(&tm
, s
->offset_toy
);
353 s
->offset_toy
= qemu_timedate_diff(&tm
);
357 toymatch_write(s
, val
, 0);
360 toymatch_write(s
, val
, 1);
363 toymatch_write(s
, val
, 2);
367 old_toyen
= toy_enabled(s
);
368 old_rtcen
= rtc_enabled(s
);
372 new_toyen
= toy_enabled(s
);
373 new_rtcen
= rtc_enabled(s
);
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
379 if (old_toyen
!= new_toyen
) {
386 if (old_rtcen
!= new_rtcen
) {
395 if (rtc_enabled(s
)) {
396 s
->offset_rtc
= val
- ls7a_rtc_ticks();
400 rtcmatch_write(s
, val
, 0);
403 rtcmatch_write(s
, val
, 1);
406 rtcmatch_write(s
, val
, 2);
413 static const MemoryRegionOps ls7a_rtc_ops
= {
414 .read
= ls7a_rtc_read
,
415 .write
= ls7a_rtc_write
,
416 .endianness
= DEVICE_LITTLE_ENDIAN
,
418 .min_access_size
= 4,
419 .max_access_size
= 4,
423 static void toy_timer_cb(void *opaque
)
425 LS7ARtcState
*s
= opaque
;
427 if (toy_enabled(s
)) {
428 qemu_irq_pulse(s
->irq
);
432 static void rtc_timer_cb(void *opaque
)
434 LS7ARtcState
*s
= opaque
;
436 if (rtc_enabled(s
)) {
437 qemu_irq_pulse(s
->irq
);
441 static void ls7a_rtc_realize(DeviceState
*dev
, Error
**errp
)
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);
449 sysbus_init_irq(sbd
, &d
->irq
);
451 sysbus_init_mmio(sbd
, &d
->iomem
);
452 for (i
= 0; i
< TIMER_NUMS
; i
++) {
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
);
461 d
->save_toy_year
= 0;
464 create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4);
467 static int ls7a_rtc_pre_save(void *opaque
)
469 LS7ARtcState
*s
= LS7A_RTC(opaque
);
477 static int ls7a_rtc_post_load(void *opaque
, int version_id
)
479 LS7ARtcState
*s
= LS7A_RTC(opaque
);
480 if (toy_enabled(s
)) {
484 if (rtc_enabled(s
)) {
491 static const VMStateDescription vmstate_ls7a_rtc
= {
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()
510 static void ls7a_rtc_class_init(ObjectClass
*klass
, void *data
)
512 DeviceClass
*dc
= DEVICE_CLASS(klass
);
513 dc
->vmsd
= &vmstate_ls7a_rtc
;
514 dc
->realize
= ls7a_rtc_realize
;
515 dc
->desc
= "ls7a rtc";
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
,
525 static void ls7a_rtc_register_types(void)
527 type_register_static(&ls7a_rtc_info
);
530 type_init(ls7a_rtc_register_types
)