linux/drivers/rtc/rtc-lpc32xx.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2010 NXP Semiconductors
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 *  You should have received a copy of the GNU General Public License along
  10 *  with this program; if not, write to the Free Software Foundation, Inc.,
  11 *  675 Mass Ave, Cambridge, MA 02139, USA.
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/platform_device.h>
  18#include <linux/spinlock.h>
  19#include <linux/rtc.h>
  20#include <linux/slab.h>
  21#include <linux/io.h>
  22
  23/*
  24 * Clock and Power control register offsets
  25 */
  26#define LPC32XX_RTC_UCOUNT              0x00
  27#define LPC32XX_RTC_DCOUNT              0x04
  28#define LPC32XX_RTC_MATCH0              0x08
  29#define LPC32XX_RTC_MATCH1              0x0C
  30#define LPC32XX_RTC_CTRL                0x10
  31#define LPC32XX_RTC_INTSTAT             0x14
  32#define LPC32XX_RTC_KEY                 0x18
  33#define LPC32XX_RTC_SRAM                0x80
  34
  35#define LPC32XX_RTC_CTRL_MATCH0         (1 << 0)
  36#define LPC32XX_RTC_CTRL_MATCH1         (1 << 1)
  37#define LPC32XX_RTC_CTRL_ONSW_MATCH0    (1 << 2)
  38#define LPC32XX_RTC_CTRL_ONSW_MATCH1    (1 << 3)
  39#define LPC32XX_RTC_CTRL_SW_RESET       (1 << 4)
  40#define LPC32XX_RTC_CTRL_CNTR_DIS       (1 << 6)
  41#define LPC32XX_RTC_CTRL_ONSW_FORCE_HI  (1 << 7)
  42
  43#define LPC32XX_RTC_INTSTAT_MATCH0      (1 << 0)
  44#define LPC32XX_RTC_INTSTAT_MATCH1      (1 << 1)
  45#define LPC32XX_RTC_INTSTAT_ONSW        (1 << 2)
  46
  47#define LPC32XX_RTC_KEY_ONSW_LOADVAL    0xB5C13F27
  48
  49#define RTC_NAME "rtc-lpc32xx"
  50
  51#define rtc_readl(dev, reg) \
  52        __raw_readl((dev)->rtc_base + (reg))
  53#define rtc_writel(dev, reg, val) \
  54        __raw_writel((val), (dev)->rtc_base + (reg))
  55
  56struct lpc32xx_rtc {
  57        void __iomem *rtc_base;
  58        int irq;
  59        unsigned char alarm_enabled;
  60        struct rtc_device *rtc;
  61        spinlock_t lock;
  62};
  63
  64static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
  65{
  66        unsigned long elapsed_sec;
  67        struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
  68
  69        elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
  70        rtc_time_to_tm(elapsed_sec, time);
  71
  72        return rtc_valid_tm(time);
  73}
  74
  75static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
  76{
  77        struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
  78        u32 tmp;
  79
  80        spin_lock_irq(&rtc->lock);
  81
  82        /* RTC must be disabled during count update */
  83        tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
  84        rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS);
  85        rtc_writel(rtc, LPC32XX_RTC_UCOUNT, secs);
  86        rtc_writel(rtc, LPC32XX_RTC_DCOUNT, 0xFFFFFFFF - secs);
  87        rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS);
  88
  89        spin_unlock_irq(&rtc->lock);
  90
  91        return 0;
  92}
  93
  94static int lpc32xx_rtc_read_alarm(struct device *dev,
  95        struct rtc_wkalrm *wkalrm)
  96{
  97        struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
  98
  99        rtc_time_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time);
 100        wkalrm->enabled = rtc->alarm_enabled;
 101        wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) &
 102                LPC32XX_RTC_INTSTAT_MATCH0);
 103
 104        return rtc_valid_tm(&wkalrm->time);
 105}
 106
 107static int lpc32xx_rtc_set_alarm(struct device *dev,
 108        struct rtc_wkalrm *wkalrm)
 109{
 110        struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
 111        unsigned long alarmsecs;
 112        u32 tmp;
 113        int ret;
 114
 115        ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs);
 116        if (ret < 0) {
 117                dev_warn(dev, "Failed to convert time: %d\n", ret);
 118                return ret;
 119        }
 120
 121        spin_lock_irq(&rtc->lock);
 122
 123        /* Disable alarm during update */
 124        tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
 125        rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp & ~LPC32XX_RTC_CTRL_MATCH0);
 126
 127        rtc_writel(rtc, LPC32XX_RTC_MATCH0, alarmsecs);
 128
 129        rtc->alarm_enabled = wkalrm->enabled;
 130        if (wkalrm->enabled) {
 131                rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
 132                           LPC32XX_RTC_INTSTAT_MATCH0);
 133                rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp |
 134                           LPC32XX_RTC_CTRL_MATCH0);
 135        }
 136
 137        spin_unlock_irq(&rtc->lock);
 138
 139        return 0;
 140}
 141
 142static int lpc32xx_rtc_alarm_irq_enable(struct device *dev,
 143        unsigned int enabled)
 144{
 145        struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
 146        u32 tmp;
 147
 148        spin_lock_irq(&rtc->lock);
 149        tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
 150
 151        if (enabled) {
 152                rtc->alarm_enabled = 1;
 153                tmp |= LPC32XX_RTC_CTRL_MATCH0;
 154        } else {
 155                rtc->alarm_enabled = 0;
 156                tmp &= ~LPC32XX_RTC_CTRL_MATCH0;
 157        }
 158
 159        rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
 160        spin_unlock_irq(&rtc->lock);
 161
 162        return 0;
 163}
 164
 165static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev)
 166{
 167        struct lpc32xx_rtc *rtc = dev;
 168
 169        spin_lock(&rtc->lock);
 170
 171        /* Disable alarm interrupt */
 172        rtc_writel(rtc, LPC32XX_RTC_CTRL,
 173                rtc_readl(rtc, LPC32XX_RTC_CTRL) &
 174                          ~LPC32XX_RTC_CTRL_MATCH0);
 175        rtc->alarm_enabled = 0;
 176
 177        /*
 178         * Write a large value to the match value so the RTC won't
 179         * keep firing the match status
 180         */
 181        rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
 182        rtc_writel(rtc, LPC32XX_RTC_INTSTAT, LPC32XX_RTC_INTSTAT_MATCH0);
 183
 184        spin_unlock(&rtc->lock);
 185
 186        rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
 187
 188        return IRQ_HANDLED;
 189}
 190
 191static const struct rtc_class_ops lpc32xx_rtc_ops = {
 192        .read_time              = lpc32xx_rtc_read_time,
 193        .set_mmss               = lpc32xx_rtc_set_mmss,
 194        .read_alarm             = lpc32xx_rtc_read_alarm,
 195        .set_alarm              = lpc32xx_rtc_set_alarm,
 196        .alarm_irq_enable       = lpc32xx_rtc_alarm_irq_enable,
 197};
 198
 199static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
 200{
 201        struct resource *res;
 202        struct lpc32xx_rtc *rtc;
 203        resource_size_t size;
 204        int rtcirq;
 205        u32 tmp;
 206
 207        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 208        if (!res) {
 209                dev_err(&pdev->dev, "Can't get memory resource\n");
 210                return -ENOENT;
 211        }
 212
 213        rtcirq = platform_get_irq(pdev, 0);
 214        if (rtcirq < 0 || rtcirq >= NR_IRQS) {
 215                dev_warn(&pdev->dev, "Can't get interrupt resource\n");
 216                rtcirq = -1;
 217        }
 218
 219        rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
 220        if (unlikely(!rtc)) {
 221                dev_err(&pdev->dev, "Can't allocate memory\n");
 222                return -ENOMEM;
 223        }
 224        rtc->irq = rtcirq;
 225
 226        size = resource_size(res);
 227
 228        if (!devm_request_mem_region(&pdev->dev, res->start, size,
 229                                     pdev->name)) {
 230                dev_err(&pdev->dev, "RTC registers are not free\n");
 231                return -EBUSY;
 232        }
 233
 234        rtc->rtc_base = devm_ioremap(&pdev->dev, res->start, size);
 235        if (!rtc->rtc_base) {
 236                dev_err(&pdev->dev, "Can't map memory\n");
 237                return -ENOMEM;
 238        }
 239
 240        spin_lock_init(&rtc->lock);
 241
 242        /*
 243         * The RTC is on a separate power domain and can keep it's state
 244         * across a chip power cycle. If the RTC has never been previously
 245         * setup, then set it up now for the first time.
 246         */
 247        tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
 248        if (rtc_readl(rtc, LPC32XX_RTC_KEY) != LPC32XX_RTC_KEY_ONSW_LOADVAL) {
 249                tmp &= ~(LPC32XX_RTC_CTRL_SW_RESET |
 250                        LPC32XX_RTC_CTRL_CNTR_DIS |
 251                        LPC32XX_RTC_CTRL_MATCH0 |
 252                        LPC32XX_RTC_CTRL_MATCH1 |
 253                        LPC32XX_RTC_CTRL_ONSW_MATCH0 |
 254                        LPC32XX_RTC_CTRL_ONSW_MATCH1 |
 255                        LPC32XX_RTC_CTRL_ONSW_FORCE_HI);
 256                rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
 257
 258                /* Clear latched interrupt states */
 259                rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
 260                rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
 261                           LPC32XX_RTC_INTSTAT_MATCH0 |
 262                           LPC32XX_RTC_INTSTAT_MATCH1 |
 263                           LPC32XX_RTC_INTSTAT_ONSW);
 264
 265                /* Write key value to RTC so it won't reload on reset */
 266                rtc_writel(rtc, LPC32XX_RTC_KEY,
 267                           LPC32XX_RTC_KEY_ONSW_LOADVAL);
 268        } else {
 269                rtc_writel(rtc, LPC32XX_RTC_CTRL,
 270                           tmp & ~LPC32XX_RTC_CTRL_MATCH0);
 271        }
 272
 273        platform_set_drvdata(pdev, rtc);
 274
 275        rtc->rtc = rtc_device_register(RTC_NAME, &pdev->dev, &lpc32xx_rtc_ops,
 276                THIS_MODULE);
 277        if (IS_ERR(rtc->rtc)) {
 278                dev_err(&pdev->dev, "Can't get RTC\n");
 279                platform_set_drvdata(pdev, NULL);
 280                return PTR_ERR(rtc->rtc);
 281        }
 282
 283        /*
 284         * IRQ is enabled after device registration in case alarm IRQ
 285         * is pending upon suspend exit.
 286         */
 287        if (rtc->irq >= 0) {
 288                if (devm_request_irq(&pdev->dev, rtc->irq,
 289                                     lpc32xx_rtc_alarm_interrupt,
 290                                     IRQF_DISABLED, pdev->name, rtc) < 0) {
 291                        dev_warn(&pdev->dev, "Can't request interrupt.\n");
 292                        rtc->irq = -1;
 293                } else {
 294                        device_init_wakeup(&pdev->dev, 1);
 295                }
 296        }
 297
 298        return 0;
 299}
 300
 301static int __devexit lpc32xx_rtc_remove(struct platform_device *pdev)
 302{
 303        struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
 304
 305        if (rtc->irq >= 0)
 306                device_init_wakeup(&pdev->dev, 0);
 307
 308        platform_set_drvdata(pdev, NULL);
 309        rtc_device_unregister(rtc->rtc);
 310
 311        return 0;
 312}
 313
 314#ifdef CONFIG_PM
 315static int lpc32xx_rtc_suspend(struct device *dev)
 316{
 317        struct platform_device *pdev = to_platform_device(dev);
 318        struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
 319
 320        if (rtc->irq >= 0) {
 321                if (device_may_wakeup(&pdev->dev))
 322                        enable_irq_wake(rtc->irq);
 323                else
 324                        disable_irq_wake(rtc->irq);
 325        }
 326
 327        return 0;
 328}
 329
 330static int lpc32xx_rtc_resume(struct device *dev)
 331{
 332        struct platform_device *pdev = to_platform_device(dev);
 333        struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
 334
 335        if (rtc->irq >= 0 && device_may_wakeup(&pdev->dev))
 336                disable_irq_wake(rtc->irq);
 337
 338        return 0;
 339}
 340
 341/* Unconditionally disable the alarm */
 342static int lpc32xx_rtc_freeze(struct device *dev)
 343{
 344        struct platform_device *pdev = to_platform_device(dev);
 345        struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
 346
 347        spin_lock_irq(&rtc->lock);
 348
 349        rtc_writel(rtc, LPC32XX_RTC_CTRL,
 350                rtc_readl(rtc, LPC32XX_RTC_CTRL) &
 351                          ~LPC32XX_RTC_CTRL_MATCH0);
 352
 353        spin_unlock_irq(&rtc->lock);
 354
 355        return 0;
 356}
 357
 358static int lpc32xx_rtc_thaw(struct device *dev)
 359{
 360        struct platform_device *pdev = to_platform_device(dev);
 361        struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
 362
 363        if (rtc->alarm_enabled) {
 364                spin_lock_irq(&rtc->lock);
 365
 366                rtc_writel(rtc, LPC32XX_RTC_CTRL,
 367                           rtc_readl(rtc, LPC32XX_RTC_CTRL) |
 368                           LPC32XX_RTC_CTRL_MATCH0);
 369
 370                spin_unlock_irq(&rtc->lock);
 371        }
 372
 373        return 0;
 374}
 375
 376static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
 377        .suspend = lpc32xx_rtc_suspend,
 378        .resume = lpc32xx_rtc_resume,
 379        .freeze = lpc32xx_rtc_freeze,
 380        .thaw = lpc32xx_rtc_thaw,
 381        .restore = lpc32xx_rtc_resume
 382};
 383
 384#define LPC32XX_RTC_PM_OPS (&lpc32xx_rtc_pm_ops)
 385#else
 386#define LPC32XX_RTC_PM_OPS NULL
 387#endif
 388
 389static struct platform_driver lpc32xx_rtc_driver = {
 390        .probe          = lpc32xx_rtc_probe,
 391        .remove         = __devexit_p(lpc32xx_rtc_remove),
 392        .driver = {
 393                .name   = RTC_NAME,
 394                .owner  = THIS_MODULE,
 395                .pm     = LPC32XX_RTC_PM_OPS
 396        },
 397};
 398
 399module_platform_driver(lpc32xx_rtc_driver);
 400
 401MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com");
 402MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
 403MODULE_LICENSE("GPL");
 404MODULE_ALIAS("platform:rtc-lpc32xx");
 405