1 // SPDX-License-Identifier: GPL-2.0-only 2 /* drivers/rtc/rtc-s3c.c 3 * 4 * Copyright (c) 2010 Samsung Electronics Co., Ltd. 5 * http://www.samsung.com/ 6 * 7 * Copyright (c) 2004,2006 Simtec Electronics 8 * Ben Dooks, <ben@simtec.co.uk> 9 * http://armlinux.simtec.co.uk/ 10 * 11 * S3C2410/S3C2440/S3C24XX Internal RTC Driver 12 */ 13 14 #include <linux/module.h> 15 #include <linux/fs.h> 16 #include <linux/string.h> 17 #include <linux/init.h> 18 #include <linux/platform_device.h> 19 #include <linux/interrupt.h> 20 #include <linux/rtc.h> 21 #include <linux/bcd.h> 22 #include <linux/clk.h> 23 #include <linux/log2.h> 24 #include <linux/slab.h> 25 #include <linux/of.h> 26 #include <linux/of_device.h> 27 #include <linux/uaccess.h> 28 #include <linux/io.h> 29 30 #include <asm/irq.h> 31 #include "rtc-s3c.h" 32 33 struct s3c_rtc { 34 struct device *dev; 35 struct rtc_device *rtc; 36 37 void __iomem *base; 38 struct clk *rtc_clk; 39 struct clk *rtc_src_clk; 40 bool alarm_enabled; 41 42 const struct s3c_rtc_data *data; 43 44 int irq_alarm; 45 spinlock_t alarm_lock; 46 47 bool wake_en; 48 }; 49 50 struct s3c_rtc_data { 51 bool needs_src_clk; 52 53 void (*irq_handler) (struct s3c_rtc *info, int mask); 54 void (*enable) (struct s3c_rtc *info); 55 void (*disable) (struct s3c_rtc *info); 56 }; 57 58 static int s3c_rtc_enable_clk(struct s3c_rtc *info) 59 { 60 int ret; 61 62 ret = clk_enable(info->rtc_clk); 63 if (ret) 64 return ret; 65 66 if (info->data->needs_src_clk) { 67 ret = clk_enable(info->rtc_src_clk); 68 if (ret) { 69 clk_disable(info->rtc_clk); 70 return ret; 71 } 72 } 73 return 0; 74 } 75 76 static void s3c_rtc_disable_clk(struct s3c_rtc *info) 77 { 78 if (info->data->needs_src_clk) 79 clk_disable(info->rtc_src_clk); 80 clk_disable(info->rtc_clk); 81 } 82 83 /* IRQ Handler */ 84 static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) 85 { 86 struct s3c_rtc *info = (struct s3c_rtc *)id; 87 88 if (info->data->irq_handler) 89 info->data->irq_handler(info, S3C2410_INTP_ALM); 90 91 return IRQ_HANDLED; 92 } 93 94 /* Update control registers */ 95 static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) 96 { 97 struct s3c_rtc *info = dev_get_drvdata(dev); 98 unsigned long flags; 99 unsigned int tmp; 100 int ret; 101 102 dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); 103 104 ret = s3c_rtc_enable_clk(info); 105 if (ret) 106 return ret; 107 108 tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; 109 110 if (enabled) 111 tmp |= S3C2410_RTCALM_ALMEN; 112 113 writeb(tmp, info->base + S3C2410_RTCALM); 114 115 spin_lock_irqsave(&info->alarm_lock, flags); 116 117 if (info->alarm_enabled && !enabled) 118 s3c_rtc_disable_clk(info); 119 else if (!info->alarm_enabled && enabled) 120 ret = s3c_rtc_enable_clk(info); 121 122 info->alarm_enabled = enabled; 123 spin_unlock_irqrestore(&info->alarm_lock, flags); 124 125 s3c_rtc_disable_clk(info); 126 127 return ret; 128 } 129 130 /* Read time from RTC and convert it from BCD */ 131 static int s3c_rtc_read_time(struct s3c_rtc *info, struct rtc_time *tm) 132 { 133 unsigned int have_retried = 0; 134 int ret; 135 136 ret = s3c_rtc_enable_clk(info); 137 if (ret) 138 return ret; 139 140 retry_get_time: 141 tm->tm_min = readb(info->base + S3C2410_RTCMIN); 142 tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); 143 tm->tm_mday = readb(info->base + S3C2410_RTCDATE); 144 tm->tm_mon = readb(info->base + S3C2410_RTCMON); 145 tm->tm_year = readb(info->base + S3C2410_RTCYEAR); 146 tm->tm_sec = readb(info->base + S3C2410_RTCSEC); 147 148 /* 149 * The only way to work out whether the system was mid-update 150 * when we read it is to check the second counter, and if it 151 * is zero, then we re-try the entire read 152 */ 153 if (tm->tm_sec == 0 && !have_retried) { 154 have_retried = 1; 155 goto retry_get_time; 156 } 157 158 s3c_rtc_disable_clk(info); 159 160 tm->tm_sec = bcd2bin(tm->tm_sec); 161 tm->tm_min = bcd2bin(tm->tm_min); 162 tm->tm_hour = bcd2bin(tm->tm_hour); 163 tm->tm_mday = bcd2bin(tm->tm_mday); 164 tm->tm_mon = bcd2bin(tm->tm_mon); 165 tm->tm_year = bcd2bin(tm->tm_year); 166 167 return 0; 168 } 169 170 /* Convert time to BCD and write it to RTC */ 171 static int s3c_rtc_write_time(struct s3c_rtc *info, const struct rtc_time *tm) 172 { 173 int ret; 174 175 ret = s3c_rtc_enable_clk(info); 176 if (ret) 177 return ret; 178 179 writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); 180 writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); 181 writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); 182 writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); 183 writeb(bin2bcd(tm->tm_mon), info->base + S3C2410_RTCMON); 184 writeb(bin2bcd(tm->tm_year), info->base + S3C2410_RTCYEAR); 185 186 s3c_rtc_disable_clk(info); 187 188 return 0; 189 } 190 191 static int s3c_rtc_gettime(struct device *dev, struct rtc_time *tm) 192 { 193 struct s3c_rtc *info = dev_get_drvdata(dev); 194 int ret; 195 196 ret = s3c_rtc_read_time(info, tm); 197 if (ret) 198 return ret; 199 200 /* Convert internal representation to actual date/time */ 201 tm->tm_year += 100; 202 tm->tm_mon -= 1; 203 204 dev_dbg(dev, "read time %ptR\n", tm); 205 return 0; 206 } 207 208 static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) 209 { 210 struct s3c_rtc *info = dev_get_drvdata(dev); 211 struct rtc_time rtc_tm = *tm; 212 213 dev_dbg(dev, "set time %ptR\n", tm); 214 215 /* 216 * Convert actual date/time to internal representation. 217 * We get around Y2K by simply not supporting it. 218 */ 219 rtc_tm.tm_year -= 100; 220 rtc_tm.tm_mon += 1; 221 222 return s3c_rtc_write_time(info, &rtc_tm); 223 } 224 225 static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) 226 { 227 struct s3c_rtc *info = dev_get_drvdata(dev); 228 struct rtc_time *alm_tm = &alrm->time; 229 unsigned int alm_en; 230 int ret; 231 232 ret = s3c_rtc_enable_clk(info); 233 if (ret) 234 return ret; 235 236 alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); 237 alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); 238 alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); 239 alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON); 240 alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE); 241 alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR); 242 243 alm_en = readb(info->base + S3C2410_RTCALM); 244 245 s3c_rtc_disable_clk(info); 246 247 alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; 248 249 dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm); 250 251 /* decode the alarm enable field */ 252 if (alm_en & S3C2410_RTCALM_SECEN) 253 alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); 254 255 if (alm_en & S3C2410_RTCALM_MINEN) 256 alm_tm->tm_min = bcd2bin(alm_tm->tm_min); 257 258 if (alm_en & S3C2410_RTCALM_HOUREN) 259 alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); 260 261 if (alm_en & S3C2410_RTCALM_DAYEN) 262 alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); 263 264 if (alm_en & S3C2410_RTCALM_MONEN) { 265 alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); 266 alm_tm->tm_mon -= 1; 267 } 268 269 if (alm_en & S3C2410_RTCALM_YEAREN) 270 alm_tm->tm_year = bcd2bin(alm_tm->tm_year); 271 272 return 0; 273 } 274 275 static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 276 { 277 struct s3c_rtc *info = dev_get_drvdata(dev); 278 struct rtc_time *tm = &alrm->time; 279 unsigned int alrm_en; 280 int ret; 281 282 dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm); 283 284 ret = s3c_rtc_enable_clk(info); 285 if (ret) 286 return ret; 287 288 alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; 289 writeb(0x00, info->base + S3C2410_RTCALM); 290 291 if (tm->tm_sec < 60 && tm->tm_sec >= 0) { 292 alrm_en |= S3C2410_RTCALM_SECEN; 293 writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC); 294 } 295 296 if (tm->tm_min < 60 && tm->tm_min >= 0) { 297 alrm_en |= S3C2410_RTCALM_MINEN; 298 writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN); 299 } 300 301 if (tm->tm_hour < 24 && tm->tm_hour >= 0) { 302 alrm_en |= S3C2410_RTCALM_HOUREN; 303 writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); 304 } 305 306 if (tm->tm_mon < 12 && tm->tm_mon >= 0) { 307 alrm_en |= S3C2410_RTCALM_MONEN; 308 writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON); 309 } 310 311 if (tm->tm_mday <= 31 && tm->tm_mday >= 1) { 312 alrm_en |= S3C2410_RTCALM_DAYEN; 313 writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE); 314 } 315 316 dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); 317 318 writeb(alrm_en, info->base + S3C2410_RTCALM); 319 320 s3c_rtc_setaie(dev, alrm->enabled); 321 322 s3c_rtc_disable_clk(info); 323 324 return 0; 325 } 326 327 static const struct rtc_class_ops s3c_rtcops = { 328 .read_time = s3c_rtc_gettime, 329 .set_time = s3c_rtc_settime, 330 .read_alarm = s3c_rtc_getalarm, 331 .set_alarm = s3c_rtc_setalarm, 332 .alarm_irq_enable = s3c_rtc_setaie, 333 }; 334 335 static void s3c24xx_rtc_enable(struct s3c_rtc *info) 336 { 337 unsigned int con, tmp; 338 339 con = readw(info->base + S3C2410_RTCCON); 340 /* re-enable the device, and check it is ok */ 341 if ((con & S3C2410_RTCCON_RTCEN) == 0) { 342 dev_info(info->dev, "rtc disabled, re-enabling\n"); 343 344 tmp = readw(info->base + S3C2410_RTCCON); 345 writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON); 346 } 347 348 if (con & S3C2410_RTCCON_CNTSEL) { 349 dev_info(info->dev, "removing RTCCON_CNTSEL\n"); 350 351 tmp = readw(info->base + S3C2410_RTCCON); 352 writew(tmp & ~S3C2410_RTCCON_CNTSEL, 353 info->base + S3C2410_RTCCON); 354 } 355 356 if (con & S3C2410_RTCCON_CLKRST) { 357 dev_info(info->dev, "removing RTCCON_CLKRST\n"); 358 359 tmp = readw(info->base + S3C2410_RTCCON); 360 writew(tmp & ~S3C2410_RTCCON_CLKRST, 361 info->base + S3C2410_RTCCON); 362 } 363 } 364 365 static void s3c24xx_rtc_disable(struct s3c_rtc *info) 366 { 367 unsigned int con; 368 369 con = readw(info->base + S3C2410_RTCCON); 370 con &= ~S3C2410_RTCCON_RTCEN; 371 writew(con, info->base + S3C2410_RTCCON); 372 373 con = readb(info->base + S3C2410_TICNT); 374 con &= ~S3C2410_TICNT_ENABLE; 375 writeb(con, info->base + S3C2410_TICNT); 376 } 377 378 static void s3c6410_rtc_disable(struct s3c_rtc *info) 379 { 380 unsigned int con; 381 382 con = readw(info->base + S3C2410_RTCCON); 383 con &= ~S3C64XX_RTCCON_TICEN; 384 con &= ~S3C2410_RTCCON_RTCEN; 385 writew(con, info->base + S3C2410_RTCCON); 386 } 387 388 static int s3c_rtc_remove(struct platform_device *pdev) 389 { 390 struct s3c_rtc *info = platform_get_drvdata(pdev); 391 392 s3c_rtc_setaie(info->dev, 0); 393 394 if (info->data->needs_src_clk) 395 clk_unprepare(info->rtc_src_clk); 396 clk_unprepare(info->rtc_clk); 397 398 return 0; 399 } 400 401 static int s3c_rtc_probe(struct platform_device *pdev) 402 { 403 struct s3c_rtc *info = NULL; 404 int ret; 405 406 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 407 if (!info) 408 return -ENOMEM; 409 410 info->dev = &pdev->dev; 411 info->data = of_device_get_match_data(&pdev->dev); 412 if (!info->data) { 413 dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); 414 return -EINVAL; 415 } 416 spin_lock_init(&info->alarm_lock); 417 418 platform_set_drvdata(pdev, info); 419 420 info->irq_alarm = platform_get_irq(pdev, 0); 421 if (info->irq_alarm < 0) 422 return info->irq_alarm; 423 424 dev_dbg(&pdev->dev, "s3c2410_rtc: alarm irq %d\n", info->irq_alarm); 425 426 /* get the memory region */ 427 info->base = devm_platform_ioremap_resource(pdev, 0); 428 if (IS_ERR(info->base)) 429 return PTR_ERR(info->base); 430 431 info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); 432 if (IS_ERR(info->rtc_clk)) 433 return dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_clk), 434 "failed to find rtc clock\n"); 435 ret = clk_prepare_enable(info->rtc_clk); 436 if (ret) 437 return ret; 438 439 if (info->data->needs_src_clk) { 440 info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); 441 if (IS_ERR(info->rtc_src_clk)) { 442 ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk), 443 "failed to find rtc source clock\n"); 444 goto err_src_clk; 445 } 446 ret = clk_prepare_enable(info->rtc_src_clk); 447 if (ret) 448 goto err_src_clk; 449 } 450 451 /* disable RTC enable bits potentially set by the bootloader */ 452 if (info->data->disable) 453 info->data->disable(info); 454 455 /* check to see if everything is setup correctly */ 456 if (info->data->enable) 457 info->data->enable(info); 458 459 dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", 460 readw(info->base + S3C2410_RTCCON)); 461 462 device_init_wakeup(&pdev->dev, 1); 463 464 info->rtc = devm_rtc_allocate_device(&pdev->dev); 465 if (IS_ERR(info->rtc)) { 466 ret = PTR_ERR(info->rtc); 467 goto err_nortc; 468 } 469 470 info->rtc->ops = &s3c_rtcops; 471 info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 472 info->rtc->range_max = RTC_TIMESTAMP_END_2099; 473 474 ret = devm_rtc_register_device(info->rtc); 475 if (ret) 476 goto err_nortc; 477 478 ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, 479 0, "s3c2410-rtc alarm", info); 480 if (ret) { 481 dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); 482 goto err_nortc; 483 } 484 485 s3c_rtc_disable_clk(info); 486 487 return 0; 488 489 err_nortc: 490 if (info->data->disable) 491 info->data->disable(info); 492 493 if (info->data->needs_src_clk) 494 clk_disable_unprepare(info->rtc_src_clk); 495 err_src_clk: 496 clk_disable_unprepare(info->rtc_clk); 497 498 return ret; 499 } 500 501 #ifdef CONFIG_PM_SLEEP 502 503 static int s3c_rtc_suspend(struct device *dev) 504 { 505 struct s3c_rtc *info = dev_get_drvdata(dev); 506 int ret; 507 508 ret = s3c_rtc_enable_clk(info); 509 if (ret) 510 return ret; 511 512 if (info->data->disable) 513 info->data->disable(info); 514 515 if (device_may_wakeup(dev) && !info->wake_en) { 516 if (enable_irq_wake(info->irq_alarm) == 0) 517 info->wake_en = true; 518 else 519 dev_err(dev, "enable_irq_wake failed\n"); 520 } 521 522 return 0; 523 } 524 525 static int s3c_rtc_resume(struct device *dev) 526 { 527 struct s3c_rtc *info = dev_get_drvdata(dev); 528 529 if (info->data->enable) 530 info->data->enable(info); 531 532 s3c_rtc_disable_clk(info); 533 534 if (device_may_wakeup(dev) && info->wake_en) { 535 disable_irq_wake(info->irq_alarm); 536 info->wake_en = false; 537 } 538 539 return 0; 540 } 541 #endif 542 static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); 543 544 static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) 545 { 546 rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); 547 } 548 549 static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) 550 { 551 rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); 552 writeb(mask, info->base + S3C2410_INTP); 553 } 554 555 static struct s3c_rtc_data const s3c2410_rtc_data = { 556 .irq_handler = s3c24xx_rtc_irq, 557 .enable = s3c24xx_rtc_enable, 558 .disable = s3c24xx_rtc_disable, 559 }; 560 561 static struct s3c_rtc_data const s3c2416_rtc_data = { 562 .irq_handler = s3c24xx_rtc_irq, 563 .enable = s3c24xx_rtc_enable, 564 .disable = s3c24xx_rtc_disable, 565 }; 566 567 static struct s3c_rtc_data const s3c2443_rtc_data = { 568 .irq_handler = s3c24xx_rtc_irq, 569 .enable = s3c24xx_rtc_enable, 570 .disable = s3c24xx_rtc_disable, 571 }; 572 573 static struct s3c_rtc_data const s3c6410_rtc_data = { 574 .needs_src_clk = true, 575 .irq_handler = s3c6410_rtc_irq, 576 .enable = s3c24xx_rtc_enable, 577 .disable = s3c6410_rtc_disable, 578 }; 579 580 static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = { 581 { 582 .compatible = "samsung,s3c2410-rtc", 583 .data = &s3c2410_rtc_data, 584 }, { 585 .compatible = "samsung,s3c2416-rtc", 586 .data = &s3c2416_rtc_data, 587 }, { 588 .compatible = "samsung,s3c2443-rtc", 589 .data = &s3c2443_rtc_data, 590 }, { 591 .compatible = "samsung,s3c6410-rtc", 592 .data = &s3c6410_rtc_data, 593 }, { 594 .compatible = "samsung,exynos3250-rtc", 595 .data = &s3c6410_rtc_data, 596 }, 597 { /* sentinel */ }, 598 }; 599 MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); 600 601 static struct platform_driver s3c_rtc_driver = { 602 .probe = s3c_rtc_probe, 603 .remove = s3c_rtc_remove, 604 .driver = { 605 .name = "s3c-rtc", 606 .pm = &s3c_rtc_pm_ops, 607 .of_match_table = of_match_ptr(s3c_rtc_dt_match), 608 }, 609 }; 610 module_platform_driver(s3c_rtc_driver); 611 612 MODULE_DESCRIPTION("Samsung S3C RTC Driver"); 613 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 614 MODULE_LICENSE("GPL"); 615 MODULE_ALIAS("platform:s3c2410-rtc"); 616