linux/drivers/rtc/rtc-xgene.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * APM X-Gene SoC Real Time Clock Driver
   4 *
   5 * Copyright (c) 2014, Applied Micro Circuits Corporation
   6 * Author: Rameshwar Prasad Sahu <rsahu@apm.com>
   7 *         Loc Ho <lho@apm.com>
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/delay.h>
  12#include <linux/init.h>
  13#include <linux/io.h>
  14#include <linux/module.h>
  15#include <linux/of.h>
  16#include <linux/platform_device.h>
  17#include <linux/rtc.h>
  18#include <linux/slab.h>
  19
  20/* RTC CSR Registers */
  21#define RTC_CCVR                0x00
  22#define RTC_CMR                 0x04
  23#define RTC_CLR                 0x08
  24#define RTC_CCR                 0x0C
  25#define  RTC_CCR_IE             BIT(0)
  26#define  RTC_CCR_MASK           BIT(1)
  27#define  RTC_CCR_EN             BIT(2)
  28#define  RTC_CCR_WEN            BIT(3)
  29#define RTC_STAT                0x10
  30#define  RTC_STAT_BIT           BIT(0)
  31#define RTC_RSTAT               0x14
  32#define RTC_EOI                 0x18
  33#define RTC_VER                 0x1C
  34
  35struct xgene_rtc_dev {
  36        struct rtc_device *rtc;
  37        struct device *dev;
  38        void __iomem *csr_base;
  39        struct clk *clk;
  40        unsigned int irq_wake;
  41        unsigned int irq_enabled;
  42};
  43
  44static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm)
  45{
  46        struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
  47
  48        rtc_time64_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
  49        return 0;
  50}
  51
  52static int xgene_rtc_set_time(struct device *dev, struct rtc_time *tm)
  53{
  54        struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
  55
  56        /*
  57         * NOTE: After the following write, the RTC_CCVR is only reflected
  58         *       after the update cycle of 1 seconds.
  59         */
  60        writel((u32)rtc_tm_to_time64(tm), pdata->csr_base + RTC_CLR);
  61        readl(pdata->csr_base + RTC_CLR); /* Force a barrier */
  62
  63        return 0;
  64}
  65
  66static int xgene_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  67{
  68        struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
  69
  70        /* If possible, CMR should be read here */
  71        rtc_time64_to_tm(0, &alrm->time);
  72        alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE;
  73
  74        return 0;
  75}
  76
  77static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
  78{
  79        struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
  80        u32 ccr;
  81
  82        ccr = readl(pdata->csr_base + RTC_CCR);
  83        if (enabled) {
  84                ccr &= ~RTC_CCR_MASK;
  85                ccr |= RTC_CCR_IE;
  86        } else {
  87                ccr &= ~RTC_CCR_IE;
  88                ccr |= RTC_CCR_MASK;
  89        }
  90        writel(ccr, pdata->csr_base + RTC_CCR);
  91
  92        return 0;
  93}
  94
  95static int xgene_rtc_alarm_irq_enabled(struct device *dev)
  96{
  97        struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
  98
  99        return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 0;
 100}
 101
 102static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 103{
 104        struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
 105
 106        writel((u32)rtc_tm_to_time64(&alrm->time), pdata->csr_base + RTC_CMR);
 107
 108        xgene_rtc_alarm_irq_enable(dev, alrm->enabled);
 109
 110        return 0;
 111}
 112
 113static const struct rtc_class_ops xgene_rtc_ops = {
 114        .read_time      = xgene_rtc_read_time,
 115        .set_time       = xgene_rtc_set_time,
 116        .read_alarm     = xgene_rtc_read_alarm,
 117        .set_alarm      = xgene_rtc_set_alarm,
 118        .alarm_irq_enable = xgene_rtc_alarm_irq_enable,
 119};
 120
 121static irqreturn_t xgene_rtc_interrupt(int irq, void *id)
 122{
 123        struct xgene_rtc_dev *pdata = id;
 124
 125        /* Check if interrupt asserted */
 126        if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT))
 127                return IRQ_NONE;
 128
 129        /* Clear interrupt */
 130        readl(pdata->csr_base + RTC_EOI);
 131
 132        rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
 133
 134        return IRQ_HANDLED;
 135}
 136
 137static int xgene_rtc_probe(struct platform_device *pdev)
 138{
 139        struct xgene_rtc_dev *pdata;
 140        struct resource *res;
 141        int ret;
 142        int irq;
 143
 144        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
 145        if (!pdata)
 146                return -ENOMEM;
 147        platform_set_drvdata(pdev, pdata);
 148        pdata->dev = &pdev->dev;
 149
 150        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 151        pdata->csr_base = devm_ioremap_resource(&pdev->dev, res);
 152        if (IS_ERR(pdata->csr_base))
 153                return PTR_ERR(pdata->csr_base);
 154
 155        pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
 156        if (IS_ERR(pdata->rtc))
 157                return PTR_ERR(pdata->rtc);
 158
 159        irq = platform_get_irq(pdev, 0);
 160        if (irq < 0) {
 161                dev_err(&pdev->dev, "No IRQ resource\n");
 162                return irq;
 163        }
 164        ret = devm_request_irq(&pdev->dev, irq, xgene_rtc_interrupt, 0,
 165                               dev_name(&pdev->dev), pdata);
 166        if (ret) {
 167                dev_err(&pdev->dev, "Could not request IRQ\n");
 168                return ret;
 169        }
 170
 171        pdata->clk = devm_clk_get(&pdev->dev, NULL);
 172        if (IS_ERR(pdata->clk)) {
 173                dev_err(&pdev->dev, "Couldn't get the clock for RTC\n");
 174                return -ENODEV;
 175        }
 176        ret = clk_prepare_enable(pdata->clk);
 177        if (ret)
 178                return ret;
 179
 180        /* Turn on the clock and the crystal */
 181        writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR);
 182
 183        ret = device_init_wakeup(&pdev->dev, 1);
 184        if (ret) {
 185                clk_disable_unprepare(pdata->clk);
 186                return ret;
 187        }
 188
 189        /* HW does not support update faster than 1 seconds */
 190        pdata->rtc->uie_unsupported = 1;
 191        pdata->rtc->ops = &xgene_rtc_ops;
 192        pdata->rtc->range_max = U32_MAX;
 193
 194        ret = rtc_register_device(pdata->rtc);
 195        if (ret) {
 196                clk_disable_unprepare(pdata->clk);
 197                return ret;
 198        }
 199
 200        return 0;
 201}
 202
 203static int xgene_rtc_remove(struct platform_device *pdev)
 204{
 205        struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
 206
 207        xgene_rtc_alarm_irq_enable(&pdev->dev, 0);
 208        device_init_wakeup(&pdev->dev, 0);
 209        clk_disable_unprepare(pdata->clk);
 210        return 0;
 211}
 212
 213static int __maybe_unused xgene_rtc_suspend(struct device *dev)
 214{
 215        struct platform_device *pdev = to_platform_device(dev);
 216        struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
 217        int irq;
 218
 219        irq = platform_get_irq(pdev, 0);
 220
 221        /*
 222         * If this RTC alarm will be used for waking the system up,
 223         * don't disable it of course. Else we just disable the alarm
 224         * and await suspension.
 225         */
 226        if (device_may_wakeup(&pdev->dev)) {
 227                if (!enable_irq_wake(irq))
 228                        pdata->irq_wake = 1;
 229        } else {
 230                pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev);
 231                xgene_rtc_alarm_irq_enable(dev, 0);
 232                clk_disable_unprepare(pdata->clk);
 233        }
 234        return 0;
 235}
 236
 237static int __maybe_unused xgene_rtc_resume(struct device *dev)
 238{
 239        struct platform_device *pdev = to_platform_device(dev);
 240        struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
 241        int irq;
 242        int rc;
 243
 244        irq = platform_get_irq(pdev, 0);
 245
 246        if (device_may_wakeup(&pdev->dev)) {
 247                if (pdata->irq_wake) {
 248                        disable_irq_wake(irq);
 249                        pdata->irq_wake = 0;
 250                }
 251        } else {
 252                rc = clk_prepare_enable(pdata->clk);
 253                if (rc) {
 254                        dev_err(dev, "Unable to enable clock error %d\n", rc);
 255                        return rc;
 256                }
 257                xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled);
 258        }
 259
 260        return 0;
 261}
 262
 263static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume);
 264
 265#ifdef CONFIG_OF
 266static const struct of_device_id xgene_rtc_of_match[] = {
 267        {.compatible = "apm,xgene-rtc" },
 268        { }
 269};
 270MODULE_DEVICE_TABLE(of, xgene_rtc_of_match);
 271#endif
 272
 273static struct platform_driver xgene_rtc_driver = {
 274        .probe          = xgene_rtc_probe,
 275        .remove         = xgene_rtc_remove,
 276        .driver         = {
 277                .name   = "xgene-rtc",
 278                .pm = &xgene_rtc_pm_ops,
 279                .of_match_table = of_match_ptr(xgene_rtc_of_match),
 280        },
 281};
 282
 283module_platform_driver(xgene_rtc_driver);
 284
 285MODULE_DESCRIPTION("APM X-Gene SoC RTC driver");
 286MODULE_AUTHOR("Rameshwar Sahu <rsahu@apm.com>");
 287MODULE_LICENSE("GPL");
 288