Merge tag 'pull-loongarch-20241016' of https://gitlab.com/gaosong/qemu into staging
[qemu/armbru.git] / hw / rtc / ls7a_rtc.c
blobc9c3cd84da75db0c7ac66e76f9658cdfab6a089d
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * LoongArch LS7A Real Time Clock emulation
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
8 #include "qemu/osdep.h"
9 #include "hw/sysbus.h"
10 #include "hw/irq.h"
11 #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"
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
38 #define TIMER_NUMS 3
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)
47 FIELD(TOY, SEC, 4, 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)
64 struct LS7ARtcState {
65 SysBusDevice parent_obj;
67 MemoryRegion iomem;
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.
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;
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)
115 uint64_t val = 0;
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);
122 return val;
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;
139 struct tm tm = {};
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)
154 uint64_t expire_ns;
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)
167 int i;
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)
177 int i;
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)
187 int i;
188 uint64_t expire_time, now;
189 struct tm tm = {};
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)
203 int i;
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);
216 struct tm tm;
217 int val = 0;
219 switch (addr) {
220 case SYS_TOYREAD0:
221 if (toy_enabled(s)) {
222 qemu_get_timedate(&tm, s->offset_toy);
223 val = toy_time_to_val_mon(&tm);
224 } else {
225 /* return 0 when toy disabled */
226 val = 0;
228 break;
229 case SYS_TOYREAD1:
230 if (toy_enabled(s)) {
231 qemu_get_timedate(&tm, s->offset_toy);
232 val = tm.tm_year;
233 } else {
234 /* return 0 when toy disabled */
235 val = 0;
237 break;
238 case SYS_TOYMATCH0:
239 val = s->toymatch[0];
240 break;
241 case SYS_TOYMATCH1:
242 val = s->toymatch[1];
243 break;
244 case SYS_TOYMATCH2:
245 val = s->toymatch[2];
246 break;
247 case SYS_RTCCTRL:
248 val = s->cntrctl;
249 break;
250 case SYS_RTCREAD0:
251 if (rtc_enabled(s)) {
252 val = ls7a_rtc_ticks() + s->offset_rtc;
253 } else {
254 /* return 0 when rtc disabled */
255 val = 0;
257 break;
258 case SYS_RTCMATCH0:
259 val = s->rtcmatch[0];
260 break;
261 case SYS_RTCMATCH1:
262 val = s->rtcmatch[1];
263 break;
264 case SYS_RTCMATCH2:
265 val = s->rtcmatch[2];
266 break;
267 default:
268 val = 0;
269 break;
271 return val;
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);
279 struct tm tm;
281 switch (addr) {
282 case SYS_TOYWRITE0:
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);
293 break;
294 case SYS_TOYWRITE1:
295 if (toy_enabled(s)) {
296 qemu_get_timedate(&tm, s->offset_toy);
297 tm.tm_year = val;
298 s->offset_toy = qemu_timedate_diff(&tm);
300 break;
301 case SYS_TOYMATCH0:
302 toymatch_write(s, val, 0);
303 break;
304 case SYS_TOYMATCH1:
305 toymatch_write(s, val, 1);
306 break;
307 case SYS_TOYMATCH2:
308 toymatch_write(s, val, 2);
309 break;
310 case SYS_RTCCTRL:
311 /* get old ctrl */
312 old_toyen = toy_enabled(s);
313 old_rtcen = rtc_enabled(s);
315 s->cntrctl = val;
316 /* get new ctrl */
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) {
325 if (new_toyen) {
326 ls7a_toy_start(s);
327 } else {
328 ls7a_toy_stop(s);
331 if (old_rtcen != new_rtcen) {
332 if (new_rtcen) {
333 ls7a_rtc_start(s);
334 } else {
335 ls7a_rtc_stop(s);
338 break;
339 case SYS_RTCWRTIE0:
340 if (rtc_enabled(s)) {
341 s->offset_rtc = val - ls7a_rtc_ticks();
343 break;
344 case SYS_RTCMATCH0:
345 rtcmatch_write(s, val, 0);
346 break;
347 case SYS_RTCMATCH1:
348 rtcmatch_write(s, val, 1);
349 break;
350 case SYS_RTCMATCH2:
351 rtcmatch_write(s, val, 2);
352 break;
353 default:
354 break;
358 static const MemoryRegionOps ls7a_rtc_ops = {
359 .read = ls7a_rtc_read,
360 .write = ls7a_rtc_write,
361 .endianness = DEVICE_LITTLE_ENDIAN,
362 .valid = {
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)
388 int i;
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++) {
398 d->toymatch[i] = 0;
399 d->rtcmatch[i] = 0;
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);
403 d->offset_toy = 0;
404 d->offset_rtc = 0;
408 /* delete timer and clear reg when reset */
409 static void ls7a_rtc_reset(DeviceState *dev)
411 int i;
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]);
421 d->toymatch[i] = 0;
422 d->rtcmatch[i] = 0;
424 d->cntrctl = 0;
427 static int ls7a_rtc_pre_save(void *opaque)
429 LS7ARtcState *s = LS7A_RTC(opaque);
431 ls7a_toy_stop(s);
432 ls7a_rtc_stop(s);
434 return 0;
437 static int ls7a_rtc_post_load(void *opaque, int version_id)
439 LS7ARtcState *s = LS7A_RTC(opaque);
440 if (toy_enabled(s)) {
441 ls7a_toy_start(s);
444 if (rtc_enabled(s)) {
445 ls7a_rtc_start(s);
448 return 0;
451 static const VMStateDescription vmstate_ls7a_rtc = {
452 .name = "ls7a_rtc",
453 .version_id = 1,
454 .minimum_version_id = 1,
455 .pre_save = ls7a_rtc_pre_save,
456 .post_load = ls7a_rtc_post_load,
457 .fields = (const 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 device_class_set_legacy_reset(dc, 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)