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 typedef struct Exynos4210RTCState Exynos4210RTCState; 89 DECLARE_INSTANCE_CHECKER(Exynos4210RTCState, EXYNOS4210_RTC, 90 TYPE_EXYNOS4210_RTC) 91 92 struct Exynos4210RTCState { 93 SysBusDevice parent_obj; 94 95 MemoryRegion iomem; 96 97 /* registers */ 98 uint32_t reg_intp; 99 uint32_t reg_rtccon; 100 uint32_t reg_ticcnt; 101 uint32_t reg_rtcalm; 102 uint32_t reg_almsec; 103 uint32_t reg_almmin; 104 uint32_t reg_almhour; 105 uint32_t reg_almday; 106 uint32_t reg_almmon; 107 uint32_t reg_almyear; 108 uint32_t reg_curticcnt; 109 110 ptimer_state *ptimer; /* tick timer */ 111 ptimer_state *ptimer_1Hz; /* clock timer */ 112 uint32_t freq; 113 114 qemu_irq tick_irq; /* Time Tick Generator irq */ 115 qemu_irq alm_irq; /* alarm irq */ 116 117 struct tm current_tm; /* current time */ 118 }; 119 120 #define TICCKSEL(value) ((value & (0x0F << 4)) >> 4) 121 122 /*** VMState ***/ 123 static const VMStateDescription vmstate_exynos4210_rtc_state = { 124 .name = "exynos4210.rtc", 125 .version_id = 1, 126 .minimum_version_id = 1, 127 .fields = (VMStateField[]) { 128 VMSTATE_UINT32(reg_intp, Exynos4210RTCState), 129 VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState), 130 VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState), 131 VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState), 132 VMSTATE_UINT32(reg_almsec, Exynos4210RTCState), 133 VMSTATE_UINT32(reg_almmin, Exynos4210RTCState), 134 VMSTATE_UINT32(reg_almhour, Exynos4210RTCState), 135 VMSTATE_UINT32(reg_almday, Exynos4210RTCState), 136 VMSTATE_UINT32(reg_almmon, Exynos4210RTCState), 137 VMSTATE_UINT32(reg_almyear, Exynos4210RTCState), 138 VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState), 139 VMSTATE_PTIMER(ptimer, Exynos4210RTCState), 140 VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState), 141 VMSTATE_UINT32(freq, Exynos4210RTCState), 142 VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState), 143 VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState), 144 VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState), 145 VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState), 146 VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState), 147 VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState), 148 VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState), 149 VMSTATE_END_OF_LIST() 150 } 151 }; 152 153 #define BCD3DIGITS(x) \ 154 ((uint32_t)to_bcd((uint8_t)(x % 100)) + \ 155 ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8)) 156 157 static void check_alarm_raise(Exynos4210RTCState *s) 158 { 159 unsigned int alarm_raise = 0; 160 struct tm stm = s->current_tm; 161 162 if ((s->reg_rtcalm & 0x01) && 163 (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) { 164 alarm_raise = 1; 165 } 166 if ((s->reg_rtcalm & 0x02) && 167 (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) { 168 alarm_raise = 1; 169 } 170 if ((s->reg_rtcalm & 0x04) && 171 (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) { 172 alarm_raise = 1; 173 } 174 if ((s->reg_rtcalm & 0x08) && 175 (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) { 176 alarm_raise = 1; 177 } 178 if ((s->reg_rtcalm & 0x10) && 179 (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) { 180 alarm_raise = 1; 181 } 182 if ((s->reg_rtcalm & 0x20) && 183 (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) { 184 alarm_raise = 1; 185 } 186 187 if (alarm_raise) { 188 DPRINTF("ALARM IRQ\n"); 189 /* set irq status */ 190 s->reg_intp |= INTP_ALM_ENABLE; 191 qemu_irq_raise(s->alm_irq); 192 } 193 } 194 195 /* 196 * RTC update frequency 197 * Parameters: 198 * reg_value - current RTCCON register or his new value 199 * Must be called within a ptimer_transaction_begin/commit block for s->ptimer. 200 */ 201 static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, 202 uint32_t reg_value) 203 { 204 uint32_t freq; 205 206 freq = s->freq; 207 /* set frequncy for time generator */ 208 s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value)); 209 210 if (freq != s->freq) { 211 ptimer_set_freq(s->ptimer, s->freq); 212 DPRINTF("freq=%dHz\n", s->freq); 213 } 214 } 215 216 /* month is between 0 and 11. */ 217 static int get_days_in_month(int month, int year) 218 { 219 static const int days_tab[12] = { 220 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 221 }; 222 int d; 223 if ((unsigned)month >= 12) { 224 return 31; 225 } 226 d = days_tab[month]; 227 if (month == 1) { 228 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) { 229 d++; 230 } 231 } 232 return d; 233 } 234 235 /* update 'tm' to the next second */ 236 static void rtc_next_second(struct tm *tm) 237 { 238 int days_in_month; 239 240 tm->tm_sec++; 241 if ((unsigned)tm->tm_sec >= 60) { 242 tm->tm_sec = 0; 243 tm->tm_min++; 244 if ((unsigned)tm->tm_min >= 60) { 245 tm->tm_min = 0; 246 tm->tm_hour++; 247 if ((unsigned)tm->tm_hour >= 24) { 248 tm->tm_hour = 0; 249 /* next day */ 250 tm->tm_wday++; 251 if ((unsigned)tm->tm_wday >= 7) { 252 tm->tm_wday = 0; 253 } 254 days_in_month = get_days_in_month(tm->tm_mon, 255 tm->tm_year + 1900); 256 tm->tm_mday++; 257 if (tm->tm_mday < 1) { 258 tm->tm_mday = 1; 259 } else if (tm->tm_mday > days_in_month) { 260 tm->tm_mday = 1; 261 tm->tm_mon++; 262 if (tm->tm_mon >= 12) { 263 tm->tm_mon = 0; 264 tm->tm_year++; 265 } 266 } 267 } 268 } 269 } 270 } 271 272 /* 273 * tick handler 274 */ 275 static void exynos4210_rtc_tick(void *opaque) 276 { 277 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 278 279 DPRINTF("TICK IRQ\n"); 280 /* set irq status */ 281 s->reg_intp |= INTP_TICK_ENABLE; 282 /* raise IRQ */ 283 qemu_irq_raise(s->tick_irq); 284 285 /* restart timer */ 286 ptimer_set_count(s->ptimer, s->reg_ticcnt); 287 ptimer_run(s->ptimer, 1); 288 } 289 290 /* 291 * 1Hz clock handler 292 */ 293 static void exynos4210_rtc_1Hz_tick(void *opaque) 294 { 295 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 296 297 rtc_next_second(&s->current_tm); 298 /* DPRINTF("1Hz tick\n"); */ 299 300 /* raise IRQ */ 301 if (s->reg_rtcalm & ALARM_INT_ENABLE) { 302 check_alarm_raise(s); 303 } 304 305 ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); 306 ptimer_run(s->ptimer_1Hz, 1); 307 } 308 309 /* 310 * RTC Read 311 */ 312 static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, 313 unsigned size) 314 { 315 uint32_t value = 0; 316 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 317 318 switch (offset) { 319 case INTP: 320 value = s->reg_intp; 321 break; 322 case RTCCON: 323 value = s->reg_rtccon; 324 break; 325 case TICCNT: 326 value = s->reg_ticcnt; 327 break; 328 case RTCALM: 329 value = s->reg_rtcalm; 330 break; 331 case ALMSEC: 332 value = s->reg_almsec; 333 break; 334 case ALMMIN: 335 value = s->reg_almmin; 336 break; 337 case ALMHOUR: 338 value = s->reg_almhour; 339 break; 340 case ALMDAY: 341 value = s->reg_almday; 342 break; 343 case ALMMON: 344 value = s->reg_almmon; 345 break; 346 case ALMYEAR: 347 value = s->reg_almyear; 348 break; 349 350 case BCDSEC: 351 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec); 352 break; 353 case BCDMIN: 354 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min); 355 break; 356 case BCDHOUR: 357 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour); 358 break; 359 case BCDDAYWEEK: 360 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday); 361 break; 362 case BCDDAY: 363 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday); 364 break; 365 case BCDMON: 366 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1); 367 break; 368 case BCDYEAR: 369 value = BCD3DIGITS(s->current_tm.tm_year); 370 break; 371 372 case CURTICNT: 373 s->reg_curticcnt = ptimer_get_count(s->ptimer); 374 value = s->reg_curticcnt; 375 break; 376 377 default: 378 qemu_log_mask(LOG_GUEST_ERROR, 379 "exynos4210.rtc: bad read offset " TARGET_FMT_plx, 380 offset); 381 break; 382 } 383 return value; 384 } 385 386 /* 387 * RTC Write 388 */ 389 static void exynos4210_rtc_write(void *opaque, hwaddr offset, 390 uint64_t value, unsigned size) 391 { 392 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; 393 394 switch (offset) { 395 case INTP: 396 if (value & INTP_ALM_ENABLE) { 397 qemu_irq_lower(s->alm_irq); 398 s->reg_intp &= (~INTP_ALM_ENABLE); 399 } 400 if (value & INTP_TICK_ENABLE) { 401 qemu_irq_lower(s->tick_irq); 402 s->reg_intp &= (~INTP_TICK_ENABLE); 403 } 404 break; 405 case RTCCON: 406 ptimer_transaction_begin(s->ptimer_1Hz); 407 ptimer_transaction_begin(s->ptimer); 408 if (value & RTC_ENABLE) { 409 exynos4210_rtc_update_freq(s, value); 410 } 411 if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) { 412 /* clock timer */ 413 ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); 414 ptimer_run(s->ptimer_1Hz, 1); 415 DPRINTF("run clock timer\n"); 416 } 417 if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) { 418 /* tick timer */ 419 ptimer_stop(s->ptimer); 420 /* clock timer */ 421 ptimer_stop(s->ptimer_1Hz); 422 DPRINTF("stop all timers\n"); 423 } 424 if (value & RTC_ENABLE) { 425 if ((value & TICK_TIMER_ENABLE) > 426 (s->reg_rtccon & TICK_TIMER_ENABLE) && 427 (s->reg_ticcnt)) { 428 ptimer_set_count(s->ptimer, s->reg_ticcnt); 429 ptimer_run(s->ptimer, 1); 430 DPRINTF("run tick timer\n"); 431 } 432 if ((value & TICK_TIMER_ENABLE) < 433 (s->reg_rtccon & TICK_TIMER_ENABLE)) { 434 ptimer_stop(s->ptimer); 435 } 436 } 437 ptimer_transaction_commit(s->ptimer_1Hz); 438 ptimer_transaction_commit(s->ptimer); 439 s->reg_rtccon = value; 440 break; 441 case TICCNT: 442 if (value > TICNT_THRESHOLD) { 443 s->reg_ticcnt = value; 444 } else { 445 qemu_log_mask(LOG_GUEST_ERROR, 446 "exynos4210.rtc: bad TICNT value %u", 447 (uint32_t)value); 448 } 449 break; 450 451 case RTCALM: 452 s->reg_rtcalm = value; 453 break; 454 case ALMSEC: 455 s->reg_almsec = (value & 0x7f); 456 break; 457 case ALMMIN: 458 s->reg_almmin = (value & 0x7f); 459 break; 460 case ALMHOUR: 461 s->reg_almhour = (value & 0x3f); 462 break; 463 case ALMDAY: 464 s->reg_almday = (value & 0x3f); 465 break; 466 case ALMMON: 467 s->reg_almmon = (value & 0x1f); 468 break; 469 case ALMYEAR: 470 s->reg_almyear = (value & 0x0fff); 471 break; 472 473 case BCDSEC: 474 if (s->reg_rtccon & RTC_ENABLE) { 475 s->current_tm.tm_sec = (int)from_bcd((uint8_t)value); 476 } 477 break; 478 case BCDMIN: 479 if (s->reg_rtccon & RTC_ENABLE) { 480 s->current_tm.tm_min = (int)from_bcd((uint8_t)value); 481 } 482 break; 483 case BCDHOUR: 484 if (s->reg_rtccon & RTC_ENABLE) { 485 s->current_tm.tm_hour = (int)from_bcd((uint8_t)value); 486 } 487 break; 488 case BCDDAYWEEK: 489 if (s->reg_rtccon & RTC_ENABLE) { 490 s->current_tm.tm_wday = (int)from_bcd((uint8_t)value); 491 } 492 break; 493 case BCDDAY: 494 if (s->reg_rtccon & RTC_ENABLE) { 495 s->current_tm.tm_mday = (int)from_bcd((uint8_t)value); 496 } 497 break; 498 case BCDMON: 499 if (s->reg_rtccon & RTC_ENABLE) { 500 s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1; 501 } 502 break; 503 case BCDYEAR: 504 if (s->reg_rtccon & RTC_ENABLE) { 505 /* 3 digits */ 506 s->current_tm.tm_year = (int)from_bcd((uint8_t)value) + 507 (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100; 508 } 509 break; 510 511 default: 512 qemu_log_mask(LOG_GUEST_ERROR, 513 "exynos4210.rtc: bad write offset " TARGET_FMT_plx, 514 offset); 515 break; 516 517 } 518 } 519 520 /* 521 * Set default values to timer fields and registers 522 */ 523 static void exynos4210_rtc_reset(DeviceState *d) 524 { 525 Exynos4210RTCState *s = EXYNOS4210_RTC(d); 526 527 qemu_get_timedate(&s->current_tm, 0); 528 529 DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n", 530 s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday, 531 s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec); 532 533 s->reg_intp = 0; 534 s->reg_rtccon = 0; 535 s->reg_ticcnt = 0; 536 s->reg_rtcalm = 0; 537 s->reg_almsec = 0; 538 s->reg_almmin = 0; 539 s->reg_almhour = 0; 540 s->reg_almday = 0; 541 s->reg_almmon = 0; 542 s->reg_almyear = 0; 543 544 s->reg_curticcnt = 0; 545 546 ptimer_transaction_begin(s->ptimer); 547 exynos4210_rtc_update_freq(s, s->reg_rtccon); 548 ptimer_stop(s->ptimer); 549 ptimer_transaction_commit(s->ptimer); 550 ptimer_transaction_begin(s->ptimer_1Hz); 551 ptimer_stop(s->ptimer_1Hz); 552 ptimer_transaction_commit(s->ptimer_1Hz); 553 } 554 555 static const MemoryRegionOps exynos4210_rtc_ops = { 556 .read = exynos4210_rtc_read, 557 .write = exynos4210_rtc_write, 558 .endianness = DEVICE_NATIVE_ENDIAN, 559 }; 560 561 /* 562 * RTC timer initialization 563 */ 564 static void exynos4210_rtc_init(Object *obj) 565 { 566 Exynos4210RTCState *s = EXYNOS4210_RTC(obj); 567 SysBusDevice *dev = SYS_BUS_DEVICE(obj); 568 569 s->ptimer = ptimer_init(exynos4210_rtc_tick, s, PTIMER_POLICY_DEFAULT); 570 ptimer_transaction_begin(s->ptimer); 571 ptimer_set_freq(s->ptimer, RTC_BASE_FREQ); 572 exynos4210_rtc_update_freq(s, 0); 573 ptimer_transaction_commit(s->ptimer); 574 575 s->ptimer_1Hz = ptimer_init(exynos4210_rtc_1Hz_tick, 576 s, PTIMER_POLICY_DEFAULT); 577 ptimer_transaction_begin(s->ptimer_1Hz); 578 ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ); 579 ptimer_transaction_commit(s->ptimer_1Hz); 580 581 sysbus_init_irq(dev, &s->alm_irq); 582 sysbus_init_irq(dev, &s->tick_irq); 583 584 memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s, 585 "exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE); 586 sysbus_init_mmio(dev, &s->iomem); 587 } 588 589 static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) 590 { 591 DeviceClass *dc = DEVICE_CLASS(klass); 592 593 dc->reset = exynos4210_rtc_reset; 594 dc->vmsd = &vmstate_exynos4210_rtc_state; 595 } 596 597 static const TypeInfo exynos4210_rtc_info = { 598 .name = TYPE_EXYNOS4210_RTC, 599 .parent = TYPE_SYS_BUS_DEVICE, 600 .instance_size = sizeof(Exynos4210RTCState), 601 .instance_init = exynos4210_rtc_init, 602 .class_init = exynos4210_rtc_class_init, 603 }; 604 605 static void exynos4210_rtc_register_types(void) 606 { 607 type_register_static(&exynos4210_rtc_info); 608 } 609 610 type_init(exynos4210_rtc_register_types) 611