1 /* 2 * Samsung exynos4210 Real Time Clock 3 * 4 * Copyright (c) 2012 Samsung Electronics Co., Ltd. 5 * Ogurtsov Oleg <o.ogurtsov@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * for more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, see <http://www.gnu.org/licenses/>. 19 * 20 */ 21 22 /* Description: 23 * Register RTCCON: 24 * CLKSEL Bit[1] not used 25 * CLKOUTEN Bit[9] not used 26 */ 27 28 #include "qemu/osdep.h" 29 #include "qemu-common.h" 30 #include "qemu/log.h" 31 #include "qemu/module.h" 32 #include "hw/sysbus.h" 33 #include "migration/vmstate.h" 34 #include "qemu/timer.h" 35 #include "qemu/bcd.h" 36 #include "hw/ptimer.h" 37 38 #include "hw/irq.h" 39 40 #include "hw/arm/exynos4210.h" 41 #include "qom/object.h" 42 43 #define DEBUG_RTC 0 44 45 #if DEBUG_RTC 46 #define DPRINTF(fmt, ...) \ 47 do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \ 48 ## __VA_ARGS__); } while (0) 49 #else 50 #define DPRINTF(fmt, ...) do {} while (0) 51 #endif 52 53 #define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100 54 55 #define INTP 0x0030 56 #define RTCCON 0x0040 57 #define TICCNT 0x0044 58 #define RTCALM 0x0050 59 #define ALMSEC 0x0054 60 #define ALMMIN 0x0058 61 #define ALMHOUR 0x005C 62 #define ALMDAY 0x0060 63 #define ALMMON 0x0064 64 #define ALMYEAR 0x0068 65 #define BCDSEC 0x0070 66 #define BCDMIN 0x0074 67 #define BCDHOUR 0x0078 68 #define BCDDAY 0x007C 69 #define BCDDAYWEEK 0x0080 70 #define BCDMON 0x0084 71 #define BCDYEAR 0x0088 72 #define CURTICNT 0x0090 73 74 #define TICK_TIMER_ENABLE 0x0100 75 #define TICNT_THRESHOLD 2 76 77 78 #define RTC_ENABLE 0x0001 79 80 #define INTP_TICK_ENABLE 0x0001 81 #define INTP_ALM_ENABLE 0x0002 82 83 #define ALARM_INT_ENABLE 0x0040 84 85 #define RTC_BASE_FREQ 32768 86 87 #define TYPE_EXYNOS4210_RTC "exynos4210.rtc" 88 OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210RTCState, EXYNOS4210_RTC) 89 90 struct Exynos4210RTCState { 91 SysBusDevice parent_obj; 92 93 MemoryRegion iomem; 94 95 /* registers */ 96 uint32_t reg_intp; 97 uint32_t reg_rtccon; 98 uint32_t reg_ticcnt; 99 uint32_t reg_rtcalm; 100 uint32_t reg_almsec; 101 uint32_t reg_almmin; 102 uint32_t reg_almhour; 103 uint32_t reg_almday; 104 uint32_t reg_almmon; 105 uint32_t reg_almyear; 106 uint32_t reg_curticcnt; 107 108 ptimer_state *ptimer; /* tick timer */ 109 ptimer_state *ptimer_1Hz; /* clock timer */ 110 uint32_t freq; 111 112 qemu_irq tick_irq; /* Time Tick Generator irq */ 113 qemu_irq alm_irq; /* alarm irq */ 114 115 struct tm current_tm; /* current time */ 116 }; 117 118 #define TICCKSEL(value) ((value & (0x0F << 4)) >> 4) 119 120 /*** VMState ***/ 121 static const VMStateDescription vmstate_exynos4210_rtc_state = { 122 .name = "exynos4210.rtc", 123 .version_id = 1, 124 .minimum_version_id = 1, 125 .fields = (VMStateField[]) { 126 VMSTATE_UINT32(reg_intp, Exynos4210RTCState), 127 VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState), 128 VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState), 129 VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState), 130 VMSTATE_UINT32(reg_almsec, Exynos4210RTCState), 131 VMSTATE_UINT32(reg_almmin, Exynos4210RTCState), 132 VMSTATE_UINT32(reg_almhour, Exynos4210RTCState), 133 VMSTATE_UINT32(reg_almday, Exynos4210RTCState), 134 VMSTATE_UINT32(reg_almmon, Exynos4210RTCState), 135 VMSTATE_UINT32(reg_almyear, Exynos4210RTCState), 136 VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState), 137 VMSTATE_PTIMER(ptimer, Exynos4210RTCState), 138 VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState), 139 VMSTATE_UINT32(freq, Exynos4210RTCState), 140 VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState), 141 VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState), 142 VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState), 143 VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState), 144 VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState), 145 VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState), 146 VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState), 147 VMSTATE_END_OF_LIST() 148 } 149 }; 150 151 #define BCD3DIGITS(x) \ 152 ((uint32_t)to_bcd((uint8_t)(x % 100)) + \ 153 ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8)) 154 155 static void check_alarm_raise(Exynos4210RTCState *s) 156 { 157 unsigned int alarm_raise = 0; 158 struct tm stm = s->current_tm; 159 160 if ((s->reg_rtcalm & 0x01) && 161 (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) { 162 alarm_raise = 1; 163 } 164 if ((s->reg_rtcalm & 0x02) && 165 (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) { 166 alarm_raise = 1; 167 } 168 if ((s->reg_rtcalm & 0x04) && 169 (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) { 170 alarm_raise = 1; 171 } 172 if ((s->reg_rtcalm & 0x08) && 173 (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) { 174 alarm_raise = 1; 175 } 176 if ((s->reg_rtcalm & 0x10) && 177 (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) { 178 alarm_raise = 1; 179 } 180 if ((s->reg_rtcalm & 0x20) && 181 (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) { 182 alarm_raise = 1; 183 } 184 185 if (alarm_raise) { 186 DPRINTF("ALARM IRQ\n"); 187 /* set irq status */ 188 s->reg_intp |= INTP_ALM_ENABLE; 189 qemu_irq_raise(s->alm_irq); 190 } 191 } 192 193 /* 194 * RTC update frequency 195 * Parameters: 196 * reg_value - current RTCCON register or his new value 197 * Must be called within a ptimer_transaction_begin/commit block for s->ptimer. 198 */ 199 static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, 200 uint32_t reg_value) 201 { 202 uint32_t freq; 203 204 freq = s->freq; 205 /* set frequncy for time generator */ 206 s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value)); 207 208 if (freq != s->freq) { 209 ptimer_set_freq(s->ptimer, s->freq); 210 DPRINTF("freq=%dHz\n", s->freq); 211 } 212 } 213 214 /* month is between 0 and 11. */ 215 static int get_days_in_month(int month, int year) 216 { 217 static const int days_tab[12] = { 218 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 219 }; 220 int d; 221 if ((unsigned)month >= 12) { 222 return 31; 223 } 224 d = days_tab[month]; 225 if (month == 1) { 226 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) { 227 d++; 228 } 229 } 230 return d; 231 } 232 233 /* update 'tm' to the next second */ 234 static void rtc_next_second(struct tm *tm) 235 { 236 int days_in_month; 237 238 tm->tm_sec++; 239 if ((unsigned)tm->tm_sec >= 60) { 240 tm->tm_sec = 0; 241 tm->tm_min++; 242 if ((unsigned)tm->tm_min >= 60) { 243 tm->tm_min = 0; 244 tm->tm_hour++; 245 if ((unsigned)tm->tm_hour >= 24) { 246 tm->tm_hour = 0; 247 /* next day */ 248 tm->tm_wday++; 249 if ((unsigned)tm->tm_wday >= 7) { 250 tm->tm_wday = 0; 251 } 252 days_in_month = get_days_in_month(tm->tm_mon, 253 tm->tm_year + 1900); 254 tm->tm_mday++; 255 if (tm->tm_mday < 1) { 256 tm->tm_mday = 1; 257 } else if (tm->tm_mday > days_in_month) { 258 tm->tm_mday = 1; 259 tm->tm_mon++; 260 if (tm->tm_mon >= 12) { 261 tm->tm_mon = 0; 262 tm->tm_year++; 263 } 264 } 265 } 266 } 267 } 268 } 269 270 /* 271 * tick handler 272 */ 273 static void exynos4210_rtc_tick(void *opaque) 274 { 275 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 276 277 DPRINTF("TICK IRQ\n"); 278 /* set irq status */ 279 s->reg_intp |= INTP_TICK_ENABLE; 280 /* raise IRQ */ 281 qemu_irq_raise(s->tick_irq); 282 283 /* restart timer */ 284 ptimer_set_count(s->ptimer, s->reg_ticcnt); 285 ptimer_run(s->ptimer, 1); 286 } 287 288 /* 289 * 1Hz clock handler 290 */ 291 static void exynos4210_rtc_1Hz_tick(void *opaque) 292 { 293 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 294 295 rtc_next_second(&s->current_tm); 296 /* DPRINTF("1Hz tick\n"); */ 297 298 /* raise IRQ */ 299 if (s->reg_rtcalm & ALARM_INT_ENABLE) { 300 check_alarm_raise(s); 301 } 302 303 ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); 304 ptimer_run(s->ptimer_1Hz, 1); 305 } 306 307 /* 308 * RTC Read 309 */ 310 static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, 311 unsigned size) 312 { 313 uint32_t value = 0; 314 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 315 316 switch (offset) { 317 case INTP: 318 value = s->reg_intp; 319 break; 320 case RTCCON: 321 value = s->reg_rtccon; 322 break; 323 case TICCNT: 324 value = s->reg_ticcnt; 325 break; 326 case RTCALM: 327 value = s->reg_rtcalm; 328 break; 329 case ALMSEC: 330 value = s->reg_almsec; 331 break; 332 case ALMMIN: 333 value = s->reg_almmin; 334 break; 335 case ALMHOUR: 336 value = s->reg_almhour; 337 break; 338 case ALMDAY: 339 value = s->reg_almday; 340 break; 341 case ALMMON: 342 value = s->reg_almmon; 343 break; 344 case ALMYEAR: 345 value = s->reg_almyear; 346 break; 347 348 case BCDSEC: 349 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec); 350 break; 351 case BCDMIN: 352 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min); 353 break; 354 case BCDHOUR: 355 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour); 356 break; 357 case BCDDAYWEEK: 358 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday); 359 break; 360 case BCDDAY: 361 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday); 362 break; 363 case BCDMON: 364 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1); 365 break; 366 case BCDYEAR: 367 value = BCD3DIGITS(s->current_tm.tm_year); 368 break; 369 370 case CURTICNT: 371 s->reg_curticcnt = ptimer_get_count(s->ptimer); 372 value = s->reg_curticcnt; 373 break; 374 375 default: 376 qemu_log_mask(LOG_GUEST_ERROR, 377 "exynos4210.rtc: bad read offset " TARGET_FMT_plx, 378 offset); 379 break; 380 } 381 return value; 382 } 383 384 /* 385 * RTC Write 386 */ 387 static void exynos4210_rtc_write(void *opaque, hwaddr offset, 388 uint64_t value, unsigned size) 389 { 390 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 391 392 switch (offset) { 393 case INTP: 394 if (value & INTP_ALM_ENABLE) { 395 qemu_irq_lower(s->alm_irq); 396 s->reg_intp &= (~INTP_ALM_ENABLE); 397 } 398 if (value & INTP_TICK_ENABLE) { 399 qemu_irq_lower(s->tick_irq); 400 s->reg_intp &= (~INTP_TICK_ENABLE); 401 } 402 break; 403 case RTCCON: 404 ptimer_transaction_begin(s->ptimer_1Hz); 405 ptimer_transaction_begin(s->ptimer); 406 if (value & RTC_ENABLE) { 407 exynos4210_rtc_update_freq(s, value); 408 } 409 if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) { 410 /* clock timer */ 411 ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); 412 ptimer_run(s->ptimer_1Hz, 1); 413 DPRINTF("run clock timer\n"); 414 } 415 if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) { 416 /* tick timer */ 417 ptimer_stop(s->ptimer); 418 /* clock timer */ 419 ptimer_stop(s->ptimer_1Hz); 420 DPRINTF("stop all timers\n"); 421 } 422 if (value & RTC_ENABLE) { 423 if ((value & TICK_TIMER_ENABLE) > 424 (s->reg_rtccon & TICK_TIMER_ENABLE) && 425 (s->reg_ticcnt)) { 426 ptimer_set_count(s->ptimer, s->reg_ticcnt); 427 ptimer_run(s->ptimer, 1); 428 DPRINTF("run tick timer\n"); 429 } 430 if ((value & TICK_TIMER_ENABLE) < 431 (s->reg_rtccon & TICK_TIMER_ENABLE)) { 432 ptimer_stop(s->ptimer); 433 } 434 } 435 ptimer_transaction_commit(s->ptimer_1Hz); 436 ptimer_transaction_commit(s->ptimer); 437 s->reg_rtccon = value; 438 break; 439 case TICCNT: 440 if (value > TICNT_THRESHOLD) { 441 s->reg_ticcnt = value; 442 } else { 443 qemu_log_mask(LOG_GUEST_ERROR, 444 "exynos4210.rtc: bad TICNT value %u", 445 (uint32_t)value); 446 } 447 break; 448 449 case RTCALM: 450 s->reg_rtcalm = value; 451 break; 452 case ALMSEC: 453 s->reg_almsec = (value & 0x7f); 454 break; 455 case ALMMIN: 456 s->reg_almmin = (value & 0x7f); 457 break; 458 case ALMHOUR: 459 s->reg_almhour = (value & 0x3f); 460 break; 461 case ALMDAY: 462 s->reg_almday = (value & 0x3f); 463 break; 464 case ALMMON: 465 s->reg_almmon = (value & 0x1f); 466 break; 467 case ALMYEAR: 468 s->reg_almyear = (value & 0x0fff); 469 break; 470 471 case BCDSEC: 472 if (s->reg_rtccon & RTC_ENABLE) { 473 s->current_tm.tm_sec = (int)from_bcd((uint8_t)value); 474 } 475 break; 476 case BCDMIN: 477 if (s->reg_rtccon & RTC_ENABLE) { 478 s->current_tm.tm_min = (int)from_bcd((uint8_t)value); 479 } 480 break; 481 case BCDHOUR: 482 if (s->reg_rtccon & RTC_ENABLE) { 483 s->current_tm.tm_hour = (int)from_bcd((uint8_t)value); 484 } 485 break; 486 case BCDDAYWEEK: 487 if (s->reg_rtccon & RTC_ENABLE) { 488 s->current_tm.tm_wday = (int)from_bcd((uint8_t)value); 489 } 490 break; 491 case BCDDAY: 492 if (s->reg_rtccon & RTC_ENABLE) { 493 s->current_tm.tm_mday = (int)from_bcd((uint8_t)value); 494 } 495 break; 496 case BCDMON: 497 if (s->reg_rtccon & RTC_ENABLE) { 498 s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1; 499 } 500 break; 501 case BCDYEAR: 502 if (s->reg_rtccon & RTC_ENABLE) { 503 /* 3 digits */ 504 s->current_tm.tm_year = (int)from_bcd((uint8_t)value) + 505 (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100; 506 } 507 break; 508 509 default: 510 qemu_log_mask(LOG_GUEST_ERROR, 511 "exynos4210.rtc: bad write offset " TARGET_FMT_plx, 512 offset); 513 break; 514 515 } 516 } 517 518 /* 519 * Set default values to timer fields and registers 520 */ 521 static void exynos4210_rtc_reset(DeviceState *d) 522 { 523 Exynos4210RTCState *s = EXYNOS4210_RTC(d); 524 525 qemu_get_timedate(&s->current_tm, 0); 526 527 DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n", 528 s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday, 529 s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec); 530 531 s->reg_intp = 0; 532 s->reg_rtccon = 0; 533 s->reg_ticcnt = 0; 534 s->reg_rtcalm = 0; 535 s->reg_almsec = 0; 536 s->reg_almmin = 0; 537 s->reg_almhour = 0; 538 s->reg_almday = 0; 539 s->reg_almmon = 0; 540 s->reg_almyear = 0; 541 542 s->reg_curticcnt = 0; 543 544 ptimer_transaction_begin(s->ptimer); 545 exynos4210_rtc_update_freq(s, s->reg_rtccon); 546 ptimer_stop(s->ptimer); 547 ptimer_transaction_commit(s->ptimer); 548 ptimer_transaction_begin(s->ptimer_1Hz); 549 ptimer_stop(s->ptimer_1Hz); 550 ptimer_transaction_commit(s->ptimer_1Hz); 551 } 552 553 static const MemoryRegionOps exynos4210_rtc_ops = { 554 .read = exynos4210_rtc_read, 555 .write = exynos4210_rtc_write, 556 .endianness = DEVICE_NATIVE_ENDIAN, 557 }; 558 559 /* 560 * RTC timer initialization 561 */ 562 static void exynos4210_rtc_init(Object *obj) 563 { 564 Exynos4210RTCState *s = EXYNOS4210_RTC(obj); 565 SysBusDevice *dev = SYS_BUS_DEVICE(obj); 566 567 s->ptimer = ptimer_init(exynos4210_rtc_tick, s, PTIMER_POLICY_DEFAULT); 568 ptimer_transaction_begin(s->ptimer); 569 ptimer_set_freq(s->ptimer, RTC_BASE_FREQ); 570 exynos4210_rtc_update_freq(s, 0); 571 ptimer_transaction_commit(s->ptimer); 572 573 s->ptimer_1Hz = ptimer_init(exynos4210_rtc_1Hz_tick, 574 s, PTIMER_POLICY_DEFAULT); 575 ptimer_transaction_begin(s->ptimer_1Hz); 576 ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ); 577 ptimer_transaction_commit(s->ptimer_1Hz); 578 579 sysbus_init_irq(dev, &s->alm_irq); 580 sysbus_init_irq(dev, &s->tick_irq); 581 582 memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s, 583 "exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE); 584 sysbus_init_mmio(dev, &s->iomem); 585 } 586 587 static void exynos4210_rtc_finalize(Object *obj) 588 { 589 Exynos4210RTCState *s = EXYNOS4210_RTC(obj); 590 591 ptimer_free(s->ptimer); 592 ptimer_free(s->ptimer_1Hz); 593 } 594 595 static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) 596 { 597 DeviceClass *dc = DEVICE_CLASS(klass); 598 599 dc->reset = exynos4210_rtc_reset; 600 dc->vmsd = &vmstate_exynos4210_rtc_state; 601 } 602 603 static const TypeInfo exynos4210_rtc_info = { 604 .name = TYPE_EXYNOS4210_RTC, 605 .parent = TYPE_SYS_BUS_DEVICE, 606 .instance_size = sizeof(Exynos4210RTCState), 607 .instance_init = exynos4210_rtc_init, 608 .instance_finalize = exynos4210_rtc_finalize, 609 .class_init = exynos4210_rtc_class_init, 610 }; 611 612 static void exynos4210_rtc_register_types(void) 613 { 614 type_register_static(&exynos4210_rtc_info); 615 } 616 617 type_init(exynos4210_rtc_register_types) 618