| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* drivers/rtc/rtc-s3c.c |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. |
|---|
| .. | .. |
|---|
| 6 | 7 | * Copyright (c) 2004,2006 Simtec Electronics |
|---|
| 7 | 8 | * Ben Dooks, <ben@simtec.co.uk> |
|---|
| 8 | 9 | * http://armlinux.simtec.co.uk/ |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 11 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 12 | | - * published by the Free Software Foundation. |
|---|
| 13 | 10 | * |
|---|
| 14 | 11 | * S3C2410/S3C2440/S3C24XX Internal RTC Driver |
|---|
| 15 | 12 | */ |
|---|
| .. | .. |
|---|
| 26 | 23 | #include <linux/log2.h> |
|---|
| 27 | 24 | #include <linux/slab.h> |
|---|
| 28 | 25 | #include <linux/of.h> |
|---|
| 26 | +#include <linux/of_device.h> |
|---|
| 29 | 27 | #include <linux/uaccess.h> |
|---|
| 30 | 28 | #include <linux/io.h> |
|---|
| 31 | 29 | |
|---|
| .. | .. |
|---|
| 39 | 37 | void __iomem *base; |
|---|
| 40 | 38 | struct clk *rtc_clk; |
|---|
| 41 | 39 | struct clk *rtc_src_clk; |
|---|
| 42 | | - bool clk_disabled; |
|---|
| 40 | + bool alarm_enabled; |
|---|
| 43 | 41 | |
|---|
| 44 | 42 | const struct s3c_rtc_data *data; |
|---|
| 45 | 43 | |
|---|
| .. | .. |
|---|
| 47 | 45 | int irq_tick; |
|---|
| 48 | 46 | |
|---|
| 49 | 47 | spinlock_t pie_lock; |
|---|
| 50 | | - spinlock_t alarm_clk_lock; |
|---|
| 48 | + spinlock_t alarm_lock; |
|---|
| 51 | 49 | |
|---|
| 52 | 50 | int ticnt_save; |
|---|
| 53 | 51 | int ticnt_en_save; |
|---|
| .. | .. |
|---|
| 70 | 68 | |
|---|
| 71 | 69 | static int s3c_rtc_enable_clk(struct s3c_rtc *info) |
|---|
| 72 | 70 | { |
|---|
| 73 | | - unsigned long irq_flags; |
|---|
| 74 | | - int ret = 0; |
|---|
| 71 | + int ret; |
|---|
| 75 | 72 | |
|---|
| 76 | | - spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); |
|---|
| 73 | + ret = clk_enable(info->rtc_clk); |
|---|
| 74 | + if (ret) |
|---|
| 75 | + return ret; |
|---|
| 77 | 76 | |
|---|
| 78 | | - if (info->clk_disabled) { |
|---|
| 79 | | - ret = clk_enable(info->rtc_clk); |
|---|
| 80 | | - if (ret) |
|---|
| 81 | | - goto out; |
|---|
| 82 | | - |
|---|
| 83 | | - if (info->data->needs_src_clk) { |
|---|
| 84 | | - ret = clk_enable(info->rtc_src_clk); |
|---|
| 85 | | - if (ret) { |
|---|
| 86 | | - clk_disable(info->rtc_clk); |
|---|
| 87 | | - goto out; |
|---|
| 88 | | - } |
|---|
| 77 | + if (info->data->needs_src_clk) { |
|---|
| 78 | + ret = clk_enable(info->rtc_src_clk); |
|---|
| 79 | + if (ret) { |
|---|
| 80 | + clk_disable(info->rtc_clk); |
|---|
| 81 | + return ret; |
|---|
| 89 | 82 | } |
|---|
| 90 | | - info->clk_disabled = false; |
|---|
| 91 | 83 | } |
|---|
| 92 | | - |
|---|
| 93 | | -out: |
|---|
| 94 | | - spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); |
|---|
| 95 | | - |
|---|
| 96 | | - return ret; |
|---|
| 84 | + return 0; |
|---|
| 97 | 85 | } |
|---|
| 98 | 86 | |
|---|
| 99 | 87 | static void s3c_rtc_disable_clk(struct s3c_rtc *info) |
|---|
| 100 | 88 | { |
|---|
| 101 | | - unsigned long irq_flags; |
|---|
| 102 | | - |
|---|
| 103 | | - spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); |
|---|
| 104 | | - if (!info->clk_disabled) { |
|---|
| 105 | | - if (info->data->needs_src_clk) |
|---|
| 106 | | - clk_disable(info->rtc_src_clk); |
|---|
| 107 | | - clk_disable(info->rtc_clk); |
|---|
| 108 | | - info->clk_disabled = true; |
|---|
| 109 | | - } |
|---|
| 110 | | - spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); |
|---|
| 89 | + if (info->data->needs_src_clk) |
|---|
| 90 | + clk_disable(info->rtc_src_clk); |
|---|
| 91 | + clk_disable(info->rtc_clk); |
|---|
| 111 | 92 | } |
|---|
| 112 | 93 | |
|---|
| 113 | 94 | /* IRQ Handlers */ |
|---|
| .. | .. |
|---|
| 135 | 116 | static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) |
|---|
| 136 | 117 | { |
|---|
| 137 | 118 | struct s3c_rtc *info = dev_get_drvdata(dev); |
|---|
| 119 | + unsigned long flags; |
|---|
| 138 | 120 | unsigned int tmp; |
|---|
| 139 | 121 | int ret; |
|---|
| 140 | 122 | |
|---|
| .. | .. |
|---|
| 151 | 133 | |
|---|
| 152 | 134 | writeb(tmp, info->base + S3C2410_RTCALM); |
|---|
| 153 | 135 | |
|---|
| 136 | + spin_lock_irqsave(&info->alarm_lock, flags); |
|---|
| 137 | + |
|---|
| 138 | + if (info->alarm_enabled && !enabled) |
|---|
| 139 | + s3c_rtc_disable_clk(info); |
|---|
| 140 | + else if (!info->alarm_enabled && enabled) |
|---|
| 141 | + ret = s3c_rtc_enable_clk(info); |
|---|
| 142 | + |
|---|
| 143 | + info->alarm_enabled = enabled; |
|---|
| 144 | + spin_unlock_irqrestore(&info->alarm_lock, flags); |
|---|
| 145 | + |
|---|
| 154 | 146 | s3c_rtc_disable_clk(info); |
|---|
| 155 | 147 | |
|---|
| 156 | | - if (enabled) { |
|---|
| 157 | | - ret = s3c_rtc_enable_clk(info); |
|---|
| 158 | | - if (ret) |
|---|
| 159 | | - return ret; |
|---|
| 160 | | - } else { |
|---|
| 161 | | - s3c_rtc_disable_clk(info); |
|---|
| 162 | | - } |
|---|
| 163 | | - |
|---|
| 164 | | - return 0; |
|---|
| 148 | + return ret; |
|---|
| 165 | 149 | } |
|---|
| 166 | 150 | |
|---|
| 167 | 151 | /* Set RTC frequency */ |
|---|
| .. | .. |
|---|
| 225 | 209 | s3c_rtc_disable_clk(info); |
|---|
| 226 | 210 | |
|---|
| 227 | 211 | rtc_tm->tm_year += 100; |
|---|
| 228 | | - |
|---|
| 229 | | - dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n", |
|---|
| 230 | | - 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, |
|---|
| 231 | | - rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); |
|---|
| 232 | | - |
|---|
| 233 | 212 | rtc_tm->tm_mon -= 1; |
|---|
| 234 | 213 | |
|---|
| 214 | + dev_dbg(dev, "read time %ptR\n", rtc_tm); |
|---|
| 235 | 215 | return 0; |
|---|
| 236 | 216 | } |
|---|
| 237 | 217 | |
|---|
| .. | .. |
|---|
| 241 | 221 | int year = tm->tm_year - 100; |
|---|
| 242 | 222 | int ret; |
|---|
| 243 | 223 | |
|---|
| 244 | | - dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", |
|---|
| 245 | | - 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, |
|---|
| 246 | | - tm->tm_hour, tm->tm_min, tm->tm_sec); |
|---|
| 224 | + dev_dbg(dev, "set time %ptR\n", tm); |
|---|
| 247 | 225 | |
|---|
| 248 | 226 | /* we get around y2k by simply not supporting it */ |
|---|
| 249 | 227 | |
|---|
| .. | .. |
|---|
| 292 | 270 | |
|---|
| 293 | 271 | alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; |
|---|
| 294 | 272 | |
|---|
| 295 | | - dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", |
|---|
| 296 | | - alm_en, |
|---|
| 297 | | - 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, |
|---|
| 298 | | - alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); |
|---|
| 273 | + dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm); |
|---|
| 299 | 274 | |
|---|
| 300 | 275 | /* decode the alarm enable field */ |
|---|
| 301 | 276 | if (alm_en & S3C2410_RTCALM_SECEN) |
|---|
| .. | .. |
|---|
| 328 | 303 | unsigned int alrm_en; |
|---|
| 329 | 304 | int ret; |
|---|
| 330 | 305 | |
|---|
| 331 | | - dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", |
|---|
| 332 | | - alrm->enabled, |
|---|
| 333 | | - 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, |
|---|
| 334 | | - tm->tm_hour, tm->tm_min, tm->tm_sec); |
|---|
| 306 | + dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm); |
|---|
| 335 | 307 | |
|---|
| 336 | 308 | ret = s3c_rtc_enable_clk(info); |
|---|
| 337 | 309 | if (ret) |
|---|
| .. | .. |
|---|
| 369 | 341 | |
|---|
| 370 | 342 | writeb(alrm_en, info->base + S3C2410_RTCALM); |
|---|
| 371 | 343 | |
|---|
| 372 | | - s3c_rtc_disable_clk(info); |
|---|
| 373 | | - |
|---|
| 374 | 344 | s3c_rtc_setaie(dev, alrm->enabled); |
|---|
| 345 | + |
|---|
| 346 | + s3c_rtc_disable_clk(info); |
|---|
| 375 | 347 | |
|---|
| 376 | 348 | return 0; |
|---|
| 377 | 349 | } |
|---|
| .. | .. |
|---|
| 468 | 440 | return 0; |
|---|
| 469 | 441 | } |
|---|
| 470 | 442 | |
|---|
| 471 | | -static const struct of_device_id s3c_rtc_dt_match[]; |
|---|
| 472 | | - |
|---|
| 473 | | -static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) |
|---|
| 474 | | -{ |
|---|
| 475 | | - const struct of_device_id *match; |
|---|
| 476 | | - |
|---|
| 477 | | - match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); |
|---|
| 478 | | - return match->data; |
|---|
| 479 | | -} |
|---|
| 480 | | - |
|---|
| 481 | 443 | static int s3c_rtc_probe(struct platform_device *pdev) |
|---|
| 482 | 444 | { |
|---|
| 483 | 445 | struct s3c_rtc *info = NULL; |
|---|
| 484 | 446 | struct rtc_time rtc_tm; |
|---|
| 485 | | - struct resource *res; |
|---|
| 486 | 447 | int ret; |
|---|
| 487 | 448 | |
|---|
| 488 | 449 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 491 | 452 | |
|---|
| 492 | 453 | /* find the IRQs */ |
|---|
| 493 | 454 | info->irq_tick = platform_get_irq(pdev, 1); |
|---|
| 494 | | - if (info->irq_tick < 0) { |
|---|
| 495 | | - dev_err(&pdev->dev, "no irq for rtc tick\n"); |
|---|
| 455 | + if (info->irq_tick < 0) |
|---|
| 496 | 456 | return info->irq_tick; |
|---|
| 497 | | - } |
|---|
| 498 | 457 | |
|---|
| 499 | 458 | info->dev = &pdev->dev; |
|---|
| 500 | | - info->data = s3c_rtc_get_data(pdev); |
|---|
| 459 | + info->data = of_device_get_match_data(&pdev->dev); |
|---|
| 501 | 460 | if (!info->data) { |
|---|
| 502 | 461 | dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); |
|---|
| 503 | 462 | return -EINVAL; |
|---|
| 504 | 463 | } |
|---|
| 505 | 464 | spin_lock_init(&info->pie_lock); |
|---|
| 506 | | - spin_lock_init(&info->alarm_clk_lock); |
|---|
| 465 | + spin_lock_init(&info->alarm_lock); |
|---|
| 507 | 466 | |
|---|
| 508 | 467 | platform_set_drvdata(pdev, info); |
|---|
| 509 | 468 | |
|---|
| 510 | 469 | info->irq_alarm = platform_get_irq(pdev, 0); |
|---|
| 511 | | - if (info->irq_alarm < 0) { |
|---|
| 512 | | - dev_err(&pdev->dev, "no irq for alarm\n"); |
|---|
| 470 | + if (info->irq_alarm < 0) |
|---|
| 513 | 471 | return info->irq_alarm; |
|---|
| 514 | | - } |
|---|
| 515 | 472 | |
|---|
| 516 | 473 | dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", |
|---|
| 517 | 474 | info->irq_tick, info->irq_alarm); |
|---|
| 518 | 475 | |
|---|
| 519 | 476 | /* get the memory region */ |
|---|
| 520 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 521 | | - info->base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 477 | + info->base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 522 | 478 | if (IS_ERR(info->base)) |
|---|
| 523 | 479 | return PTR_ERR(info->base); |
|---|
| 524 | 480 | |
|---|
| .. | .. |
|---|
| 538 | 494 | if (info->data->needs_src_clk) { |
|---|
| 539 | 495 | info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); |
|---|
| 540 | 496 | if (IS_ERR(info->rtc_src_clk)) { |
|---|
| 541 | | - ret = PTR_ERR(info->rtc_src_clk); |
|---|
| 542 | | - if (ret != -EPROBE_DEFER) |
|---|
| 543 | | - dev_err(&pdev->dev, |
|---|
| 544 | | - "failed to find rtc source clock\n"); |
|---|
| 545 | | - else |
|---|
| 546 | | - dev_dbg(&pdev->dev, |
|---|
| 547 | | - "probe deferred due to missing rtc src clk\n"); |
|---|
| 497 | + ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk), |
|---|
| 498 | + "failed to find rtc source clock\n"); |
|---|
| 548 | 499 | goto err_src_clk; |
|---|
| 549 | 500 | } |
|---|
| 550 | 501 | ret = clk_prepare_enable(info->rtc_src_clk); |
|---|
| .. | .. |
|---|
| 603 | 554 | |
|---|
| 604 | 555 | s3c_rtc_setfreq(info, 1); |
|---|
| 605 | 556 | |
|---|
| 557 | + s3c_rtc_disable_clk(info); |
|---|
| 558 | + |
|---|
| 606 | 559 | return 0; |
|---|
| 607 | 560 | |
|---|
| 608 | 561 | err_nortc: |
|---|