linux/drivers/rtc/rtc-s3c.c
<<
>>
Prefs
   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
  33struct 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
  50struct 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
  58static 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
  76static 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 */
  84static 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 */
  95static 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 */
 131static 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
 140retry_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 */
 171static 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
 191static 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
 208static 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
 225static 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
 275static 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
 327static 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
 335static 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
 365static 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
 378static 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
 388static 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
 401static 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                ret = PTR_ERR(info->rtc_clk);
 434                if (ret != -EPROBE_DEFER)
 435                        dev_err(&pdev->dev, "failed to find rtc clock\n");
 436                else
 437                        dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
 438                return ret;
 439        }
 440        ret = clk_prepare_enable(info->rtc_clk);
 441        if (ret)
 442                return ret;
 443
 444        if (info->data->needs_src_clk) {
 445                info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
 446                if (IS_ERR(info->rtc_src_clk)) {
 447                        ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk),
 448                                            "failed to find rtc source clock\n");
 449                        goto err_src_clk;
 450                }
 451                ret = clk_prepare_enable(info->rtc_src_clk);
 452                if (ret)
 453                        goto err_src_clk;
 454        }
 455
 456        /* disable RTC enable bits potentially set by the bootloader */
 457        if (info->data->disable)
 458                info->data->disable(info);
 459
 460        /* check to see if everything is setup correctly */
 461        if (info->data->enable)
 462                info->data->enable(info);
 463
 464        dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
 465                readw(info->base + S3C2410_RTCCON));
 466
 467        device_init_wakeup(&pdev->dev, 1);
 468
 469        info->rtc = devm_rtc_allocate_device(&pdev->dev);
 470        if (IS_ERR(info->rtc)) {
 471                ret = PTR_ERR(info->rtc);
 472                goto err_nortc;
 473        }
 474
 475        info->rtc->ops = &s3c_rtcops;
 476        info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
 477        info->rtc->range_max = RTC_TIMESTAMP_END_2099;
 478
 479        ret = devm_rtc_register_device(info->rtc);
 480        if (ret)
 481                goto err_nortc;
 482
 483        ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
 484                               0, "s3c2410-rtc alarm", info);
 485        if (ret) {
 486                dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
 487                goto err_nortc;
 488        }
 489
 490        s3c_rtc_disable_clk(info);
 491
 492        return 0;
 493
 494err_nortc:
 495        if (info->data->disable)
 496                info->data->disable(info);
 497
 498        if (info->data->needs_src_clk)
 499                clk_disable_unprepare(info->rtc_src_clk);
 500err_src_clk:
 501        clk_disable_unprepare(info->rtc_clk);
 502
 503        return ret;
 504}
 505
 506#ifdef CONFIG_PM_SLEEP
 507
 508static int s3c_rtc_suspend(struct device *dev)
 509{
 510        struct s3c_rtc *info = dev_get_drvdata(dev);
 511        int ret;
 512
 513        ret = s3c_rtc_enable_clk(info);
 514        if (ret)
 515                return ret;
 516
 517        if (info->data->disable)
 518                info->data->disable(info);
 519
 520        if (device_may_wakeup(dev) && !info->wake_en) {
 521                if (enable_irq_wake(info->irq_alarm) == 0)
 522                        info->wake_en = true;
 523                else
 524                        dev_err(dev, "enable_irq_wake failed\n");
 525        }
 526
 527        return 0;
 528}
 529
 530static int s3c_rtc_resume(struct device *dev)
 531{
 532        struct s3c_rtc *info = dev_get_drvdata(dev);
 533
 534        if (info->data->enable)
 535                info->data->enable(info);
 536
 537        s3c_rtc_disable_clk(info);
 538
 539        if (device_may_wakeup(dev) && info->wake_en) {
 540                disable_irq_wake(info->irq_alarm);
 541                info->wake_en = false;
 542        }
 543
 544        return 0;
 545}
 546#endif
 547static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
 548
 549static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
 550{
 551        rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
 552}
 553
 554static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
 555{
 556        rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
 557        writeb(mask, info->base + S3C2410_INTP);
 558}
 559
 560static struct s3c_rtc_data const s3c2410_rtc_data = {
 561        .irq_handler            = s3c24xx_rtc_irq,
 562        .enable                 = s3c24xx_rtc_enable,
 563        .disable                = s3c24xx_rtc_disable,
 564};
 565
 566static struct s3c_rtc_data const s3c2416_rtc_data = {
 567        .irq_handler            = s3c24xx_rtc_irq,
 568        .enable                 = s3c24xx_rtc_enable,
 569        .disable                = s3c24xx_rtc_disable,
 570};
 571
 572static struct s3c_rtc_data const s3c2443_rtc_data = {
 573        .irq_handler            = s3c24xx_rtc_irq,
 574        .enable                 = s3c24xx_rtc_enable,
 575        .disable                = s3c24xx_rtc_disable,
 576};
 577
 578static struct s3c_rtc_data const s3c6410_rtc_data = {
 579        .needs_src_clk          = true,
 580        .irq_handler            = s3c6410_rtc_irq,
 581        .enable                 = s3c24xx_rtc_enable,
 582        .disable                = s3c6410_rtc_disable,
 583};
 584
 585static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = {
 586        {
 587                .compatible = "samsung,s3c2410-rtc",
 588                .data = &s3c2410_rtc_data,
 589        }, {
 590                .compatible = "samsung,s3c2416-rtc",
 591                .data = &s3c2416_rtc_data,
 592        }, {
 593                .compatible = "samsung,s3c2443-rtc",
 594                .data = &s3c2443_rtc_data,
 595        }, {
 596                .compatible = "samsung,s3c6410-rtc",
 597                .data = &s3c6410_rtc_data,
 598        }, {
 599                .compatible = "samsung,exynos3250-rtc",
 600                .data = &s3c6410_rtc_data,
 601        },
 602        { /* sentinel */ },
 603};
 604MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
 605
 606static struct platform_driver s3c_rtc_driver = {
 607        .probe          = s3c_rtc_probe,
 608        .remove         = s3c_rtc_remove,
 609        .driver         = {
 610                .name   = "s3c-rtc",
 611                .pm     = &s3c_rtc_pm_ops,
 612                .of_match_table = of_match_ptr(s3c_rtc_dt_match),
 613        },
 614};
 615module_platform_driver(s3c_rtc_driver);
 616
 617MODULE_DESCRIPTION("Samsung S3C RTC Driver");
 618MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 619MODULE_LICENSE("GPL");
 620MODULE_ALIAS("platform:s3c2410-rtc");
 621