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 "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 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 uint64_t ticks_to_ns(uint64_t ticks) 96 { 97 return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ; 98 } 99 100 static 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 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 struct tm to toy value */ 113 static uint64_t toy_time_to_val_mon(const struct tm *tm) 114 { 115 uint64_t val = 0; 116 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; 123 } 124 125 static void toymatch_val_to_time(LS7ARtcState *s, uint64_t val, struct tm *tm) 126 { 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)); 134 } 135 136 static void toymatch_write(LS7ARtcState *s, uint64_t val, int num) 137 { 138 int64_t now, expire_time; 139 struct tm tm = {}; 140 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); 149 } 150 } 151 152 static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) 153 { 154 uint64_t expire_ns; 155 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); 162 } 163 } 164 165 static void ls7a_toy_stop(LS7ARtcState *s) 166 { 167 int i; 168 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]); 172 } 173 } 174 175 static void ls7a_rtc_stop(LS7ARtcState *s) 176 { 177 int i; 178 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]); 182 } 183 } 184 185 static void ls7a_toy_start(LS7ARtcState *s) 186 { 187 int i; 188 uint64_t expire_time, now; 189 struct tm tm = {}; 190 191 now = qemu_clock_get_ms(rtc_clock); 192 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); 198 } 199 } 200 201 static void ls7a_rtc_start(LS7ARtcState *s) 202 { 203 int i; 204 uint64_t expire_time; 205 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); 210 } 211 } 212 213 static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size) 214 { 215 LS7ARtcState *s = LS7A_RTC(opaque); 216 struct tm tm; 217 int val = 0; 218 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; 227 } 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; 236 } 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; 256 } 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; 270 } 271 return val; 272 } 273 274 static void ls7a_rtc_write(void *opaque, hwaddr addr, 275 uint64_t val, unsigned size) 276 { 277 int old_toyen, old_rtcen, new_toyen, new_rtcen; 278 LS7ARtcState *s = LS7A_RTC(opaque); 279 struct tm tm; 280 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); 292 } 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); 299 } 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); 314 315 s->cntrctl = val; 316 /* get new ctrl */ 317 new_toyen = toy_enabled(s); 318 new_rtcen = rtc_enabled(s); 319 320 /* 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 323 */ 324 if (old_toyen != new_toyen) { 325 if (new_toyen) { 326 ls7a_toy_start(s); 327 } else { 328 ls7a_toy_stop(s); 329 } 330 } 331 if (old_rtcen != new_rtcen) { 332 if (new_rtcen) { 333 ls7a_rtc_start(s); 334 } else { 335 ls7a_rtc_stop(s); 336 } 337 } 338 break; 339 case SYS_RTCWRTIE0: 340 if (rtc_enabled(s)) { 341 s->offset_rtc = val - ls7a_rtc_ticks(); 342 } 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; 355 } 356 } 357 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, 365 }, 366 }; 367 368 static void toy_timer_cb(void *opaque) 369 { 370 LS7ARtcState *s = opaque; 371 372 if (toy_enabled(s)) { 373 qemu_irq_raise(s->irq); 374 } 375 } 376 377 static void rtc_timer_cb(void *opaque) 378 { 379 LS7ARtcState *s = opaque; 380 381 if (rtc_enabled(s)) { 382 qemu_irq_raise(s->irq); 383 } 384 } 385 386 static void ls7a_rtc_realize(DeviceState *dev, Error **errp) 387 { 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); 393 394 sysbus_init_irq(sbd, &d->irq); 395 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); 402 } 403 d->offset_toy = 0; 404 d->offset_rtc = 0; 405 406 } 407 408 /* delete timer and clear reg when reset */ 409 static void ls7a_rtc_reset(DeviceState *dev) 410 { 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]); 417 } 418 if (rtc_enabled(d)) { 419 timer_del(d->rtc_timer[i]); 420 } 421 d->toymatch[i] = 0; 422 d->rtcmatch[i] = 0; 423 } 424 d->cntrctl = 0; 425 } 426 427 static int ls7a_rtc_pre_save(void *opaque) 428 { 429 LS7ARtcState *s = LS7A_RTC(opaque); 430 431 ls7a_toy_stop(s); 432 ls7a_rtc_stop(s); 433 434 return 0; 435 } 436 437 static int ls7a_rtc_post_load(void *opaque, int version_id) 438 { 439 LS7ARtcState *s = LS7A_RTC(opaque); 440 if (toy_enabled(s)) { 441 ls7a_toy_start(s); 442 } 443 444 if (rtc_enabled(s)) { 445 ls7a_rtc_start(s); 446 } 447 448 return 0; 449 } 450 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() 464 } 465 }; 466 467 static void ls7a_rtc_class_init(ObjectClass *klass, void *data) 468 { 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"; 474 } 475 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, 481 }; 482 483 static void ls7a_rtc_register_types(void) 484 { 485 type_register_static(&ls7a_rtc_info); 486 } 487 488 type_init(ls7a_rtc_register_types) 489