linux/drivers/rtc/rtc-snvs.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
   3 *
   4 * The code contained herein is licensed under the GNU General Public
   5 * License. You may obtain a copy of the GNU General Public License
   6 * Version 2 or later at the following locations:
   7 *
   8 * http://www.opensource.org/licenses/gpl-license.html
   9 * http://www.gnu.org/copyleft/gpl.html
  10 */
  11
  12#include <linux/init.h>
  13#include <linux/io.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/rtc.h>
  20
  21/* These register offsets are relative to LP (Low Power) range */
  22#define SNVS_LPCR               0x04
  23#define SNVS_LPSR               0x18
  24#define SNVS_LPSRTCMR           0x1c
  25#define SNVS_LPSRTCLR           0x20
  26#define SNVS_LPTAR              0x24
  27#define SNVS_LPPGDR             0x30
  28
  29#define SNVS_LPCR_SRTC_ENV      (1 << 0)
  30#define SNVS_LPCR_LPTA_EN       (1 << 1)
  31#define SNVS_LPCR_LPWUI_EN      (1 << 3)
  32#define SNVS_LPSR_LPTA          (1 << 0)
  33
  34#define SNVS_LPPGDR_INIT        0x41736166
  35#define CNTR_TO_SECS_SH         15
  36
  37struct snvs_rtc_data {
  38        struct rtc_device *rtc;
  39        void __iomem *ioaddr;
  40        int irq;
  41        spinlock_t lock;
  42};
  43
  44static u32 rtc_read_lp_counter(void __iomem *ioaddr)
  45{
  46        u64 read1, read2;
  47
  48        do {
  49                read1 = readl(ioaddr + SNVS_LPSRTCMR);
  50                read1 <<= 32;
  51                read1 |= readl(ioaddr + SNVS_LPSRTCLR);
  52
  53                read2 = readl(ioaddr + SNVS_LPSRTCMR);
  54                read2 <<= 32;
  55                read2 |= readl(ioaddr + SNVS_LPSRTCLR);
  56        } while (read1 != read2);
  57
  58        /* Convert 47-bit counter to 32-bit raw second count */
  59        return (u32) (read1 >> CNTR_TO_SECS_SH);
  60}
  61
  62static void rtc_write_sync_lp(void __iomem *ioaddr)
  63{
  64        u32 count1, count2, count3;
  65        int i;
  66
  67        /* Wait for 3 CKIL cycles */
  68        for (i = 0; i < 3; i++) {
  69                do {
  70                        count1 = readl(ioaddr + SNVS_LPSRTCLR);
  71                        count2 = readl(ioaddr + SNVS_LPSRTCLR);
  72                } while (count1 != count2);
  73
  74                /* Now wait until counter value changes */
  75                do {
  76                        do {
  77                                count2 = readl(ioaddr + SNVS_LPSRTCLR);
  78                                count3 = readl(ioaddr + SNVS_LPSRTCLR);
  79                        } while (count2 != count3);
  80                } while (count3 == count1);
  81        }
  82}
  83
  84static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
  85{
  86        unsigned long flags;
  87        int timeout = 1000;
  88        u32 lpcr;
  89
  90        spin_lock_irqsave(&data->lock, flags);
  91
  92        lpcr = readl(data->ioaddr + SNVS_LPCR);
  93        if (enable)
  94                lpcr |= SNVS_LPCR_SRTC_ENV;
  95        else
  96                lpcr &= ~SNVS_LPCR_SRTC_ENV;
  97        writel(lpcr, data->ioaddr + SNVS_LPCR);
  98
  99        spin_unlock_irqrestore(&data->lock, flags);
 100
 101        while (--timeout) {
 102                lpcr = readl(data->ioaddr + SNVS_LPCR);
 103
 104                if (enable) {
 105                        if (lpcr & SNVS_LPCR_SRTC_ENV)
 106                                break;
 107                } else {
 108                        if (!(lpcr & SNVS_LPCR_SRTC_ENV))
 109                                break;
 110                }
 111        }
 112
 113        if (!timeout)
 114                return -ETIMEDOUT;
 115
 116        return 0;
 117}
 118
 119static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
 120{
 121        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 122        unsigned long time = rtc_read_lp_counter(data->ioaddr);
 123
 124        rtc_time_to_tm(time, tm);
 125
 126        return 0;
 127}
 128
 129static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
 130{
 131        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 132        unsigned long time;
 133
 134        rtc_tm_to_time(tm, &time);
 135
 136        /* Disable RTC first */
 137        snvs_rtc_enable(data, false);
 138
 139        /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
 140        writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
 141        writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
 142
 143        /* Enable RTC again */
 144        snvs_rtc_enable(data, true);
 145
 146        return 0;
 147}
 148
 149static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 150{
 151        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 152        u32 lptar, lpsr;
 153
 154        lptar = readl(data->ioaddr + SNVS_LPTAR);
 155        rtc_time_to_tm(lptar, &alrm->time);
 156
 157        lpsr = readl(data->ioaddr + SNVS_LPSR);
 158        alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
 159
 160        return 0;
 161}
 162
 163static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
 164{
 165        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 166        u32 lpcr;
 167        unsigned long flags;
 168
 169        spin_lock_irqsave(&data->lock, flags);
 170
 171        lpcr = readl(data->ioaddr + SNVS_LPCR);
 172        if (enable)
 173                lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
 174        else
 175                lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
 176        writel(lpcr, data->ioaddr + SNVS_LPCR);
 177
 178        spin_unlock_irqrestore(&data->lock, flags);
 179
 180        rtc_write_sync_lp(data->ioaddr);
 181
 182        return 0;
 183}
 184
 185static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 186{
 187        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 188        struct rtc_time *alrm_tm = &alrm->time;
 189        unsigned long time;
 190        unsigned long flags;
 191        u32 lpcr;
 192
 193        rtc_tm_to_time(alrm_tm, &time);
 194
 195        spin_lock_irqsave(&data->lock, flags);
 196
 197        /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
 198        lpcr = readl(data->ioaddr + SNVS_LPCR);
 199        lpcr &= ~SNVS_LPCR_LPTA_EN;
 200        writel(lpcr, data->ioaddr + SNVS_LPCR);
 201
 202        spin_unlock_irqrestore(&data->lock, flags);
 203
 204        writel(time, data->ioaddr + SNVS_LPTAR);
 205
 206        /* Clear alarm interrupt status bit */
 207        writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
 208
 209        return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
 210}
 211
 212static const struct rtc_class_ops snvs_rtc_ops = {
 213        .read_time = snvs_rtc_read_time,
 214        .set_time = snvs_rtc_set_time,
 215        .read_alarm = snvs_rtc_read_alarm,
 216        .set_alarm = snvs_rtc_set_alarm,
 217        .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
 218};
 219
 220static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
 221{
 222        struct device *dev = dev_id;
 223        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 224        u32 lpsr;
 225        u32 events = 0;
 226
 227        lpsr = readl(data->ioaddr + SNVS_LPSR);
 228
 229        if (lpsr & SNVS_LPSR_LPTA) {
 230                events |= (RTC_AF | RTC_IRQF);
 231
 232                /* RTC alarm should be one-shot */
 233                snvs_rtc_alarm_irq_enable(dev, 0);
 234
 235                rtc_update_irq(data->rtc, 1, events);
 236        }
 237
 238        /* clear interrupt status */
 239        writel(lpsr, data->ioaddr + SNVS_LPSR);
 240
 241        return events ? IRQ_HANDLED : IRQ_NONE;
 242}
 243
 244static int snvs_rtc_probe(struct platform_device *pdev)
 245{
 246        struct snvs_rtc_data *data;
 247        struct resource *res;
 248        int ret;
 249
 250        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 251        if (!data)
 252                return -ENOMEM;
 253
 254        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 255        data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
 256        if (IS_ERR(data->ioaddr))
 257                return PTR_ERR(data->ioaddr);
 258
 259        data->irq = platform_get_irq(pdev, 0);
 260        if (data->irq < 0)
 261                return data->irq;
 262
 263        platform_set_drvdata(pdev, data);
 264
 265        spin_lock_init(&data->lock);
 266
 267        /* Initialize glitch detect */
 268        writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
 269
 270        /* Clear interrupt status */
 271        writel(0xffffffff, data->ioaddr + SNVS_LPSR);
 272
 273        /* Enable RTC */
 274        snvs_rtc_enable(data, true);
 275
 276        device_init_wakeup(&pdev->dev, true);
 277
 278        ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
 279                               IRQF_SHARED, "rtc alarm", &pdev->dev);
 280        if (ret) {
 281                dev_err(&pdev->dev, "failed to request irq %d: %d\n",
 282                        data->irq, ret);
 283                return ret;
 284        }
 285
 286        data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
 287                                        &snvs_rtc_ops, THIS_MODULE);
 288        if (IS_ERR(data->rtc)) {
 289                ret = PTR_ERR(data->rtc);
 290                dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
 291                return ret;
 292        }
 293
 294        return 0;
 295}
 296
 297static int snvs_rtc_remove(struct platform_device *pdev)
 298{
 299        return 0;
 300}
 301
 302#ifdef CONFIG_PM_SLEEP
 303static int snvs_rtc_suspend(struct device *dev)
 304{
 305        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 306
 307        if (device_may_wakeup(dev))
 308                enable_irq_wake(data->irq);
 309
 310        return 0;
 311}
 312
 313static int snvs_rtc_resume(struct device *dev)
 314{
 315        struct snvs_rtc_data *data = dev_get_drvdata(dev);
 316
 317        if (device_may_wakeup(dev))
 318                disable_irq_wake(data->irq);
 319
 320        return 0;
 321}
 322#endif
 323
 324static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume);
 325
 326static const struct of_device_id snvs_dt_ids[] = {
 327        { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
 328        { /* sentinel */ }
 329};
 330MODULE_DEVICE_TABLE(of, snvs_dt_ids);
 331
 332static struct platform_driver snvs_rtc_driver = {
 333        .driver = {
 334                .name   = "snvs_rtc",
 335                .owner  = THIS_MODULE,
 336                .pm     = &snvs_rtc_pm_ops,
 337                .of_match_table = of_match_ptr(snvs_dt_ids),
 338        },
 339        .probe          = snvs_rtc_probe,
 340        .remove         = snvs_rtc_remove,
 341};
 342module_platform_driver(snvs_rtc_driver);
 343
 344MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 345MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
 346MODULE_LICENSE("GPL");
 347
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.