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
83 QEMUTimer
*toy_timer
[TIMER_NUMS
];
84 QEMUTimer
*rtc_timer
[TIMER_NUMS
];
88 /* switch nanoseconds time to rtc ticks */
89 static uint64_t ls7a_rtc_ticks(void)
91 return qemu_clock_get_ns(rtc_clock
) * LS7A_RTC_FREQ
/ NANOSECONDS_PER_SECOND
;
94 /* switch rtc ticks to nanoseconds */
95 static uint64_t ticks_to_ns(uint64_t ticks
)
97 return ticks
* NANOSECONDS_PER_SECOND
/ LS7A_RTC_FREQ
;
100 static bool toy_enabled(LS7ARtcState
*s
)
102 return FIELD_EX32(s
->cntrctl
, RTC_CTRL
, TOYEN
) &&
103 FIELD_EX32(s
->cntrctl
, RTC_CTRL
, EO
);
106 static bool rtc_enabled(LS7ARtcState
*s
)
108 return FIELD_EX32(s
->cntrctl
, RTC_CTRL
, RTCEN
) &&
109 FIELD_EX32(s
->cntrctl
, RTC_CTRL
, EO
);
112 /* parse struct tm to toy value */
113 static uint64_t toy_time_to_val_mon(const struct tm
*tm
)
117 val
= FIELD_DP32(val
, TOY
, MON
, tm
->tm_mon
+ 1);
118 val
= FIELD_DP32(val
, TOY
, DAY
, tm
->tm_mday
);
119 val
= FIELD_DP32(val
, TOY
, HOUR
, tm
->tm_hour
);
120 val
= FIELD_DP32(val
, TOY
, MIN
, tm
->tm_min
);
121 val
= FIELD_DP32(val
, TOY
, SEC
, tm
->tm_sec
);
125 static void toymatch_val_to_time(LS7ARtcState
*s
, uint64_t val
, struct tm
*tm
)
127 qemu_get_timedate(tm
, s
->offset_toy
);
128 tm
->tm_sec
= FIELD_EX32(val
, TOY_MATCH
, SEC
);
129 tm
->tm_min
= FIELD_EX32(val
, TOY_MATCH
, MIN
);
130 tm
->tm_hour
= FIELD_EX32(val
, TOY_MATCH
, HOUR
);
131 tm
->tm_mday
= FIELD_EX32(val
, TOY_MATCH
, DAY
);
132 tm
->tm_mon
= FIELD_EX32(val
, TOY_MATCH
, MON
) - 1;
133 tm
->tm_year
+= (FIELD_EX32(val
, TOY_MATCH
, YEAR
) - (tm
->tm_year
& 0x3f));
136 static void toymatch_write(LS7ARtcState
*s
, uint64_t val
, int num
)
138 int64_t now
, expire_time
;
141 /* it do not support write when toy disabled */
142 if (toy_enabled(s
)) {
143 s
->toymatch
[num
] = val
;
144 /* calculate expire time */
145 now
= qemu_clock_get_ms(rtc_clock
);
146 toymatch_val_to_time(s
, val
, &tm
);
147 expire_time
= now
+ (qemu_timedate_diff(&tm
) - s
->offset_toy
) * 1000;
148 timer_mod(s
->toy_timer
[num
], expire_time
);
152 static void rtcmatch_write(LS7ARtcState
*s
, uint64_t val
, int num
)
156 /* it do not support write when toy disabled */
157 if (rtc_enabled(s
)) {
158 s
->rtcmatch
[num
] = val
;
159 /* calculate expire time */
160 expire_ns
= ticks_to_ns(val
) - ticks_to_ns(s
->offset_rtc
);
161 timer_mod_ns(s
->rtc_timer
[num
], expire_ns
);
165 static void ls7a_toy_stop(LS7ARtcState
*s
)
169 /* delete timers, and when re-enabled, recalculate expire time */
170 for (i
= 0; i
< TIMER_NUMS
; i
++) {
171 timer_del(s
->toy_timer
[i
]);
175 static void ls7a_rtc_stop(LS7ARtcState
*s
)
179 /* delete timers, and when re-enabled, recalculate expire time */
180 for (i
= 0; i
< TIMER_NUMS
; i
++) {
181 timer_del(s
->rtc_timer
[i
]);
185 static void ls7a_toy_start(LS7ARtcState
*s
)
188 uint64_t expire_time
, now
;
191 now
= qemu_clock_get_ms(rtc_clock
);
193 /* recalculate expire time and enable timer */
194 for (i
= 0; i
< TIMER_NUMS
; i
++) {
195 toymatch_val_to_time(s
, s
->toymatch
[i
], &tm
);
196 expire_time
= now
+ (qemu_timedate_diff(&tm
) - s
->offset_toy
) * 1000;
197 timer_mod(s
->toy_timer
[i
], expire_time
);
201 static void ls7a_rtc_start(LS7ARtcState
*s
)
204 uint64_t expire_time
;
206 /* recalculate expire time and enable timer */
207 for (i
= 0; i
< TIMER_NUMS
; i
++) {
208 expire_time
= ticks_to_ns(s
->rtcmatch
[i
]) - ticks_to_ns(s
->offset_rtc
);
209 timer_mod_ns(s
->rtc_timer
[i
], expire_time
);
213 static uint64_t ls7a_rtc_read(void *opaque
, hwaddr addr
, unsigned size
)
215 LS7ARtcState
*s
= LS7A_RTC(opaque
);
221 if (toy_enabled(s
)) {
222 qemu_get_timedate(&tm
, s
->offset_toy
);
223 val
= toy_time_to_val_mon(&tm
);
225 /* return 0 when toy disabled */
230 if (toy_enabled(s
)) {
231 qemu_get_timedate(&tm
, s
->offset_toy
);
234 /* return 0 when toy disabled */
239 val
= s
->toymatch
[0];
242 val
= s
->toymatch
[1];
245 val
= s
->toymatch
[2];
251 if (rtc_enabled(s
)) {
252 val
= ls7a_rtc_ticks() + s
->offset_rtc
;
254 /* return 0 when rtc disabled */
259 val
= s
->rtcmatch
[0];
262 val
= s
->rtcmatch
[1];
265 val
= s
->rtcmatch
[2];
274 static void ls7a_rtc_write(void *opaque
, hwaddr addr
,
275 uint64_t val
, unsigned size
)
277 int old_toyen
, old_rtcen
, new_toyen
, new_rtcen
;
278 LS7ARtcState
*s
= LS7A_RTC(opaque
);
283 /* it do not support write when toy disabled */
284 if (toy_enabled(s
)) {
285 qemu_get_timedate(&tm
, s
->offset_toy
);
286 tm
.tm_sec
= FIELD_EX32(val
, TOY
, SEC
);
287 tm
.tm_min
= FIELD_EX32(val
, TOY
, MIN
);
288 tm
.tm_hour
= FIELD_EX32(val
, TOY
, HOUR
);
289 tm
.tm_mday
= FIELD_EX32(val
, TOY
, DAY
);
290 tm
.tm_mon
= FIELD_EX32(val
, TOY
, MON
) - 1;
291 s
->offset_toy
= qemu_timedate_diff(&tm
);
295 if (toy_enabled(s
)) {
296 qemu_get_timedate(&tm
, s
->offset_toy
);
298 s
->offset_toy
= qemu_timedate_diff(&tm
);
302 toymatch_write(s
, val
, 0);
305 toymatch_write(s
, val
, 1);
308 toymatch_write(s
, val
, 2);
312 old_toyen
= toy_enabled(s
);
313 old_rtcen
= rtc_enabled(s
);
317 new_toyen
= toy_enabled(s
);
318 new_rtcen
= rtc_enabled(s
);
321 * we do not consider if EO changed, as it always set at most time.
322 * toy or rtc enabled should start timer. otherwise, stop timer
324 if (old_toyen
!= new_toyen
) {
331 if (old_rtcen
!= new_rtcen
) {
340 if (rtc_enabled(s
)) {
341 s
->offset_rtc
= val
- ls7a_rtc_ticks();
345 rtcmatch_write(s
, val
, 0);
348 rtcmatch_write(s
, val
, 1);
351 rtcmatch_write(s
, val
, 2);
358 static const MemoryRegionOps ls7a_rtc_ops
= {
359 .read
= ls7a_rtc_read
,
360 .write
= ls7a_rtc_write
,
361 .endianness
= DEVICE_LITTLE_ENDIAN
,
363 .min_access_size
= 4,
364 .max_access_size
= 4,
368 static void toy_timer_cb(void *opaque
)
370 LS7ARtcState
*s
= opaque
;
372 if (toy_enabled(s
)) {
373 qemu_irq_raise(s
->irq
);
377 static void rtc_timer_cb(void *opaque
)
379 LS7ARtcState
*s
= opaque
;
381 if (rtc_enabled(s
)) {
382 qemu_irq_raise(s
->irq
);
386 static void ls7a_rtc_realize(DeviceState
*dev
, Error
**errp
)
389 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
390 LS7ARtcState
*d
= LS7A_RTC(sbd
);
391 memory_region_init_io(&d
->iomem
, NULL
, &ls7a_rtc_ops
,
392 (void *)d
, "ls7a_rtc", 0x100);
394 sysbus_init_irq(sbd
, &d
->irq
);
396 sysbus_init_mmio(sbd
, &d
->iomem
);
397 for (i
= 0; i
< TIMER_NUMS
; i
++) {
400 d
->toy_timer
[i
] = timer_new_ms(rtc_clock
, toy_timer_cb
, d
);
401 d
->rtc_timer
[i
] = timer_new_ms(rtc_clock
, rtc_timer_cb
, d
);
408 /* delete timer and clear reg when reset */
409 static void ls7a_rtc_reset(DeviceState
*dev
)
412 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
413 LS7ARtcState
*d
= LS7A_RTC(sbd
);
414 for (i
= 0; i
< TIMER_NUMS
; i
++) {
415 if (toy_enabled(d
)) {
416 timer_del(d
->toy_timer
[i
]);
418 if (rtc_enabled(d
)) {
419 timer_del(d
->rtc_timer
[i
]);
427 static int ls7a_rtc_pre_save(void *opaque
)
429 LS7ARtcState
*s
= LS7A_RTC(opaque
);
437 static int ls7a_rtc_post_load(void *opaque
, int version_id
)
439 LS7ARtcState
*s
= LS7A_RTC(opaque
);
440 if (toy_enabled(s
)) {
444 if (rtc_enabled(s
)) {
451 static const VMStateDescription vmstate_ls7a_rtc
= {
454 .minimum_version_id
= 1,
455 .pre_save
= ls7a_rtc_pre_save
,
456 .post_load
= ls7a_rtc_post_load
,
457 .fields
= (VMStateField
[]) {
458 VMSTATE_INT64(offset_toy
, LS7ARtcState
),
459 VMSTATE_INT64(offset_rtc
, LS7ARtcState
),
460 VMSTATE_UINT32_ARRAY(toymatch
, LS7ARtcState
, TIMER_NUMS
),
461 VMSTATE_UINT32_ARRAY(rtcmatch
, LS7ARtcState
, TIMER_NUMS
),
462 VMSTATE_UINT32(cntrctl
, LS7ARtcState
),
463 VMSTATE_END_OF_LIST()
467 static void ls7a_rtc_class_init(ObjectClass
*klass
, void *data
)
469 DeviceClass
*dc
= DEVICE_CLASS(klass
);
470 dc
->vmsd
= &vmstate_ls7a_rtc
;
471 dc
->realize
= ls7a_rtc_realize
;
472 dc
->reset
= ls7a_rtc_reset
;
473 dc
->desc
= "ls7a rtc";
476 static const TypeInfo ls7a_rtc_info
= {
477 .name
= TYPE_LS7A_RTC
,
478 .parent
= TYPE_SYS_BUS_DEVICE
,
479 .instance_size
= sizeof(LS7ARtcState
),
480 .class_init
= ls7a_rtc_class_init
,
483 static void ls7a_rtc_register_types(void)
485 type_register_static(&ls7a_rtc_info
);
488 type_init(ls7a_rtc_register_types
)