linux/drivers/rtc/rtc-at91sam9.c
<<
>>
Prefs
   1/*
   2 * "RTT as Real Time Clock" driver for AT91SAM9 SoC family
   3 *
   4 * (C) 2007 Michel Benoit
   5 *
   6 * Based on rtc-at91rm9200.c by Rick Bronson
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version
  11 * 2 of the License, or (at your option) any later version.
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/kernel.h>
  16#include <linux/platform_device.h>
  17#include <linux/time.h>
  18#include <linux/rtc.h>
  19#include <linux/interrupt.h>
  20#include <linux/ioctl.h>
  21#include <linux/slab.h>
  22#include <linux/platform_data/atmel.h>
  23#include <linux/io.h>
  24
  25#include <mach/at91_rtt.h>
  26#include <mach/cpu.h>
  27
  28
  29/*
  30 * This driver uses two configurable hardware resources that live in the
  31 * AT91SAM9 backup power domain (intended to be powered at all times)
  32 * to implement the Real Time Clock interfaces
  33 *
  34 *  - A "Real-time Timer" (RTT) counts up in seconds from a base time.
  35 *    We can't assign the counter value (CRTV) ... but we can reset it.
  36 *
  37 *  - One of the "General Purpose Backup Registers" (GPBRs) holds the
  38 *    base time, normally an offset from the beginning of the POSIX
  39 *    epoch (1970-Jan-1 00:00:00 UTC).  Some systems also include the
  40 *    local timezone's offset.
  41 *
  42 * The RTC's value is the RTT counter plus that offset.  The RTC's alarm
  43 * is likewise a base (ALMV) plus that offset.
  44 *
  45 * Not all RTTs will be used as RTCs; some systems have multiple RTTs to
  46 * choose from, or a "real" RTC module.  All systems have multiple GPBR
  47 * registers available, likewise usable for more than "RTC" support.
  48 */
  49
  50/*
  51 * We store ALARM_DISABLED in ALMV to record that no alarm is set.
  52 * It's also the reset value for that field.
  53 */
  54#define ALARM_DISABLED  ((u32)~0)
  55
  56
  57struct sam9_rtc {
  58        void __iomem            *rtt;
  59        struct rtc_device       *rtcdev;
  60        u32                     imr;
  61        void __iomem            *gpbr;
  62        int                     irq;
  63};
  64
  65#define rtt_readl(rtc, field) \
  66        __raw_readl((rtc)->rtt + AT91_RTT_ ## field)
  67#define rtt_writel(rtc, field, val) \
  68        __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field)
  69
  70#define gpbr_readl(rtc) \
  71        __raw_readl((rtc)->gpbr)
  72#define gpbr_writel(rtc, val) \
  73        __raw_writel((val), (rtc)->gpbr)
  74
  75/*
  76 * Read current time and date in RTC
  77 */
  78static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
  79{
  80        struct sam9_rtc *rtc = dev_get_drvdata(dev);
  81        u32 secs, secs2;
  82        u32 offset;
  83
  84        /* read current time offset */
  85        offset = gpbr_readl(rtc);
  86        if (offset == 0)
  87                return -EILSEQ;
  88
  89        /* reread the counter to help sync the two clock domains */
  90        secs = rtt_readl(rtc, VR);
  91        secs2 = rtt_readl(rtc, VR);
  92        if (secs != secs2)
  93                secs = rtt_readl(rtc, VR);
  94
  95        rtc_time_to_tm(offset + secs, tm);
  96
  97        dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readtime",
  98                1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
  99                tm->tm_hour, tm->tm_min, tm->tm_sec);
 100
 101        return 0;
 102}
 103
 104/*
 105 * Set current time and date in RTC
 106 */
 107static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
 108{
 109        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 110        int err;
 111        u32 offset, alarm, mr;
 112        unsigned long secs;
 113
 114        dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "settime",
 115                1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
 116                tm->tm_hour, tm->tm_min, tm->tm_sec);
 117
 118        err = rtc_tm_to_time(tm, &secs);
 119        if (err != 0)
 120                return err;
 121
 122        mr = rtt_readl(rtc, MR);
 123
 124        /* disable interrupts */
 125        rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
 126
 127        /* read current time offset */
 128        offset = gpbr_readl(rtc);
 129
 130        /* store the new base time in a battery backup register */
 131        secs += 1;
 132        gpbr_writel(rtc, secs);
 133
 134        /* adjust the alarm time for the new base */
 135        alarm = rtt_readl(rtc, AR);
 136        if (alarm != ALARM_DISABLED) {
 137                if (offset > secs) {
 138                        /* time jumped backwards, increase time until alarm */
 139                        alarm += (offset - secs);
 140                } else if ((alarm + offset) > secs) {
 141                        /* time jumped forwards, decrease time until alarm */
 142                        alarm -= (secs - offset);
 143                } else {
 144                        /* time jumped past the alarm, disable alarm */
 145                        alarm = ALARM_DISABLED;
 146                        mr &= ~AT91_RTT_ALMIEN;
 147                }
 148                rtt_writel(rtc, AR, alarm);
 149        }
 150
 151        /* reset the timer, and re-enable interrupts */
 152        rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST);
 153
 154        return 0;
 155}
 156
 157static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
 158{
 159        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 160        struct rtc_time *tm = &alrm->time;
 161        u32 alarm = rtt_readl(rtc, AR);
 162        u32 offset;
 163
 164        offset = gpbr_readl(rtc);
 165        if (offset == 0)
 166                return -EILSEQ;
 167
 168        memset(alrm, 0, sizeof(*alrm));
 169        if (alarm != ALARM_DISABLED && offset != 0) {
 170                rtc_time_to_tm(offset + alarm, tm);
 171
 172                dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readalarm",
 173                        1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
 174                        tm->tm_hour, tm->tm_min, tm->tm_sec);
 175
 176                if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN)
 177                        alrm->enabled = 1;
 178        }
 179
 180        return 0;
 181}
 182
 183static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 184{
 185        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 186        struct rtc_time *tm = &alrm->time;
 187        unsigned long secs;
 188        u32 offset;
 189        u32 mr;
 190        int err;
 191
 192        err = rtc_tm_to_time(tm, &secs);
 193        if (err != 0)
 194                return err;
 195
 196        offset = gpbr_readl(rtc);
 197        if (offset == 0) {
 198                /* time is not set */
 199                return -EILSEQ;
 200        }
 201        mr = rtt_readl(rtc, MR);
 202        rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN);
 203
 204        /* alarm in the past? finish and leave disabled */
 205        if (secs <= offset) {
 206                rtt_writel(rtc, AR, ALARM_DISABLED);
 207                return 0;
 208        }
 209
 210        /* else set alarm and maybe enable it */
 211        rtt_writel(rtc, AR, secs - offset);
 212        if (alrm->enabled)
 213                rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
 214
 215        dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "setalarm",
 216                tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour,
 217                tm->tm_min, tm->tm_sec);
 218
 219        return 0;
 220}
 221
 222static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
 223{
 224        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 225        u32 mr = rtt_readl(rtc, MR);
 226
 227        dev_dbg(dev, "alarm_irq_enable: enabled=%08x, mr %08x\n", enabled, mr);
 228        if (enabled)
 229                rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
 230        else
 231                rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN);
 232        return 0;
 233}
 234
 235/*
 236 * Provide additional RTC information in /proc/driver/rtc
 237 */
 238static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
 239{
 240        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 241        u32 mr = mr = rtt_readl(rtc, MR);
 242
 243        seq_printf(seq, "update_IRQ\t: %s\n",
 244                        (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no");
 245        return 0;
 246}
 247
 248/*
 249 * IRQ handler for the RTC
 250 */
 251static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
 252{
 253        struct sam9_rtc *rtc = _rtc;
 254        u32 sr, mr;
 255        unsigned long events = 0;
 256
 257        /* Shared interrupt may be for another device.  Note: reading
 258         * SR clears it, so we must only read it in this irq handler!
 259         */
 260        mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
 261        sr = rtt_readl(rtc, SR) & (mr >> 16);
 262        if (!sr)
 263                return IRQ_NONE;
 264
 265        /* alarm status */
 266        if (sr & AT91_RTT_ALMS)
 267                events |= (RTC_AF | RTC_IRQF);
 268
 269        /* timer update/increment */
 270        if (sr & AT91_RTT_RTTINC)
 271                events |= (RTC_UF | RTC_IRQF);
 272
 273        rtc_update_irq(rtc->rtcdev, 1, events);
 274
 275        pr_debug("%s: num=%ld, events=0x%02lx\n", __func__,
 276                events >> 8, events & 0x000000FF);
 277
 278        return IRQ_HANDLED;
 279}
 280
 281static const struct rtc_class_ops at91_rtc_ops = {
 282        .read_time      = at91_rtc_readtime,
 283        .set_time       = at91_rtc_settime,
 284        .read_alarm     = at91_rtc_readalarm,
 285        .set_alarm      = at91_rtc_setalarm,
 286        .proc           = at91_rtc_proc,
 287        .alarm_irq_enable = at91_rtc_alarm_irq_enable,
 288};
 289
 290/*
 291 * Initialize and install RTC driver
 292 */
 293static int at91_rtc_probe(struct platform_device *pdev)
 294{
 295        struct resource *r, *r_gpbr;
 296        struct sam9_rtc *rtc;
 297        int             ret, irq;
 298        u32             mr;
 299
 300        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 301        r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 302        if (!r || !r_gpbr) {
 303                dev_err(&pdev->dev, "need 2 ressources\n");
 304                return -ENODEV;
 305        }
 306
 307        irq = platform_get_irq(pdev, 0);
 308        if (irq < 0) {
 309                dev_err(&pdev->dev, "failed to get interrupt resource\n");
 310                return irq;
 311        }
 312
 313        rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
 314        if (!rtc)
 315                return -ENOMEM;
 316
 317        rtc->irq = irq;
 318
 319        /* platform setup code should have handled this; sigh */
 320        if (!device_can_wakeup(&pdev->dev))
 321                device_init_wakeup(&pdev->dev, 1);
 322
 323        platform_set_drvdata(pdev, rtc);
 324        rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r));
 325        if (!rtc->rtt) {
 326                dev_err(&pdev->dev, "failed to map registers, aborting.\n");
 327                ret = -ENOMEM;
 328                goto fail;
 329        }
 330
 331        rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start,
 332                                resource_size(r_gpbr));
 333        if (!rtc->gpbr) {
 334                dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n");
 335                ret = -ENOMEM;
 336                goto fail;
 337        }
 338
 339        mr = rtt_readl(rtc, MR);
 340
 341        /* unless RTT is counting at 1 Hz, re-initialize it */
 342        if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) {
 343                mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES);
 344                gpbr_writel(rtc, 0);
 345        }
 346
 347        /* disable all interrupts (same as on shutdown path) */
 348        mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
 349        rtt_writel(rtc, MR, mr);
 350
 351        rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
 352                                        &at91_rtc_ops, THIS_MODULE);
 353        if (IS_ERR(rtc->rtcdev)) {
 354                ret = PTR_ERR(rtc->rtcdev);
 355                goto fail;
 356        }
 357
 358        /* register irq handler after we know what name we'll use */
 359        ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt,
 360                                IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc);
 361        if (ret) {
 362                dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq);
 363                goto fail;
 364        }
 365
 366        /* NOTE:  sam9260 rev A silicon has a ROM bug which resets the
 367         * RTT on at least some reboots.  If you have that chip, you must
 368         * initialize the time from some external source like a GPS, wall
 369         * clock, discrete RTC, etc
 370         */
 371
 372        if (gpbr_readl(rtc) == 0)
 373                dev_warn(&pdev->dev, "%s: SET TIME!\n",
 374                                dev_name(&rtc->rtcdev->dev));
 375
 376        return 0;
 377
 378fail:
 379        platform_set_drvdata(pdev, NULL);
 380        return ret;
 381}
 382
 383/*
 384 * Disable and remove the RTC driver
 385 */
 386static int at91_rtc_remove(struct platform_device *pdev)
 387{
 388        struct sam9_rtc *rtc = platform_get_drvdata(pdev);
 389        u32             mr = rtt_readl(rtc, MR);
 390
 391        /* disable all interrupts */
 392        rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
 393
 394        platform_set_drvdata(pdev, NULL);
 395        return 0;
 396}
 397
 398static void at91_rtc_shutdown(struct platform_device *pdev)
 399{
 400        struct sam9_rtc *rtc = platform_get_drvdata(pdev);
 401        u32             mr = rtt_readl(rtc, MR);
 402
 403        rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
 404        rtt_writel(rtc, MR, mr & ~rtc->imr);
 405}
 406
 407#ifdef CONFIG_PM_SLEEP
 408
 409/* AT91SAM9 RTC Power management control */
 410
 411static int at91_rtc_suspend(struct device *dev)
 412{
 413        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 414        u32             mr = rtt_readl(rtc, MR);
 415
 416        /*
 417         * This IRQ is shared with DBGU and other hardware which isn't
 418         * necessarily a wakeup event source.
 419         */
 420        rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
 421        if (rtc->imr) {
 422                if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) {
 423                        enable_irq_wake(rtc->irq);
 424                        /* don't let RTTINC cause wakeups */
 425                        if (mr & AT91_RTT_RTTINCIEN)
 426                                rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
 427                } else
 428                        rtt_writel(rtc, MR, mr & ~rtc->imr);
 429        }
 430
 431        return 0;
 432}
 433
 434static int at91_rtc_resume(struct device *dev)
 435{
 436        struct sam9_rtc *rtc = dev_get_drvdata(dev);
 437        u32             mr;
 438
 439        if (rtc->imr) {
 440                if (device_may_wakeup(dev))
 441                        disable_irq_wake(rtc->irq);
 442                mr = rtt_readl(rtc, MR);
 443                rtt_writel(rtc, MR, mr | rtc->imr);
 444        }
 445
 446        return 0;
 447}
 448#endif
 449
 450static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
 451
 452static struct platform_driver at91_rtc_driver = {
 453        .probe          = at91_rtc_probe,
 454        .remove         = at91_rtc_remove,
 455        .shutdown       = at91_rtc_shutdown,
 456        .driver         = {
 457                .name   = "rtc-at91sam9",
 458                .owner  = THIS_MODULE,
 459                .pm     = &at91_rtc_pm_ops,
 460        },
 461};
 462
 463module_platform_driver(at91_rtc_driver);
 464
 465MODULE_AUTHOR("Michel Benoit");
 466MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x");
 467MODULE_LICENSE("GPL");
 468