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