UPSTREAM: clk: rockchip: rk3036: fix uarts clock error
[firefly-linux-kernel-4.4.55.git] / drivers / rtc / rtc-at91rm9200.c
index f296f3f7db9bb72574396182f3a8dfd115e16786..b60fd477778f7fed1e8c1a510d80646f47d8373f 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/time.h>
-#include <linux/rtc.h>
 #include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
 #include <linux/interrupt.h>
-#include <linux/spinlock.h>
 #include <linux/ioctl.h>
-#include <linux/completion.h>
 #include <linux/io.h>
-#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of_device.h>
-
-#include <asm/uaccess.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+#include <linux/suspend.h>
+#include <linux/time.h>
+#include <linux/uaccess.h>
 
 #include "rtc-at91rm9200.h"
 
 #define at91_rtc_read(field) \
-       __raw_readl(at91_rtc_regs + field)
+       readl_relaxed(at91_rtc_regs + field)
 #define at91_rtc_write(field, val) \
-       __raw_writel((val), at91_rtc_regs + field)
+       writel_relaxed((val), at91_rtc_regs + field)
 
 #define AT91_RTC_EPOCH         1900UL  /* just like arch/arm/common/rtctime.c */
 
@@ -49,11 +50,17 @@ struct at91_rtc_config {
 
 static const struct at91_rtc_config *at91_rtc_config;
 static DECLARE_COMPLETION(at91_rtc_updated);
+static DECLARE_COMPLETION(at91_rtc_upd_rdy);
 static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
 static void __iomem *at91_rtc_regs;
 static int irq;
 static DEFINE_SPINLOCK(at91_rtc_lock);
 static u32 at91_rtc_shadow_imr;
+static bool suspended;
+static DEFINE_SPINLOCK(suspended_lock);
+static unsigned long cached_events;
+static u32 at91_rtc_imr;
+static struct clk *sclk;
 
 static void at91_rtc_write_ier(u32 mask)
 {
@@ -162,6 +169,8 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
                1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
                tm->tm_hour, tm->tm_min, tm->tm_sec);
 
+       wait_for_completion(&at91_rtc_upd_rdy);
+
        /* Stop Time/Calendar from counting */
        cr = at91_rtc_read(AT91_RTC_CR);
        at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM);
@@ -184,7 +193,9 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
 
        /* Restart Time/Calendar */
        cr = at91_rtc_read(AT91_RTC_CR);
+       at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_SECEV);
        at91_rtc_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM));
+       at91_rtc_write_ier(AT91_RTC_SECEV);
 
        return 0;
 }
@@ -221,6 +232,8 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        at91_alarm_year = tm.tm_year;
 
+       tm.tm_mon = alrm->time.tm_mon;
+       tm.tm_mday = alrm->time.tm_mday;
        tm.tm_hour = alrm->time.tm_hour;
        tm.tm_min = alrm->time.tm_min;
        tm.tm_sec = alrm->time.tm_sec;
@@ -284,26 +297,38 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
        struct rtc_device *rtc = platform_get_drvdata(pdev);
        unsigned int rtsr;
        unsigned long events = 0;
+       int ret = IRQ_NONE;
 
+       spin_lock(&suspended_lock);
        rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr();
        if (rtsr) {             /* this interrupt is shared!  Is it ours? */
                if (rtsr & AT91_RTC_ALARM)
                        events |= (RTC_AF | RTC_IRQF);
-               if (rtsr & AT91_RTC_SECEV)
-                       events |= (RTC_UF | RTC_IRQF);
+               if (rtsr & AT91_RTC_SECEV) {
+                       complete(&at91_rtc_upd_rdy);
+                       at91_rtc_write_idr(AT91_RTC_SECEV);
+               }
                if (rtsr & AT91_RTC_ACKUPD)
                        complete(&at91_rtc_updated);
 
                at91_rtc_write(AT91_RTC_SCCR, rtsr);    /* clear status reg */
 
-               rtc_update_irq(rtc, 1, events);
+               if (!suspended) {
+                       rtc_update_irq(rtc, 1, events);
 
-               dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", __func__,
-                       events >> 8, events & 0x000000FF);
+                       dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n",
+                               __func__, events >> 8, events & 0x000000FF);
+               } else {
+                       cached_events |= events;
+                       at91_rtc_write_idr(at91_rtc_imr);
+                       pm_system_wakeup();
+               }
 
-               return IRQ_HANDLED;
+               ret = IRQ_HANDLED;
        }
-       return IRQ_NONE;                /* not handled */
+       spin_unlock(&suspended_lock);
+
+       return ret;
 }
 
 static const struct at91_rtc_config at91rm9200_config = {
@@ -377,12 +402,23 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       at91_rtc_regs = ioremap(regs->start, resource_size(regs));
+       at91_rtc_regs = devm_ioremap(&pdev->dev, regs->start,
+                                    resource_size(regs));
        if (!at91_rtc_regs) {
                dev_err(&pdev->dev, "failed to map registers, aborting.\n");
                return -ENOMEM;
        }
 
+       sclk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(sclk))
+               return PTR_ERR(sclk);
+
+       ret = clk_prepare_enable(sclk);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not enable slow clock\n");
+               return ret;
+       }
+
        at91_rtc_write(AT91_RTC_CR, 0);
        at91_rtc_write(AT91_RTC_MR, 0);         /* 24 hour mode */
 
@@ -391,12 +427,12 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
                                        AT91_RTC_SECEV | AT91_RTC_TIMEV |
                                        AT91_RTC_CALEV);
 
-       ret = request_irq(irq, at91_rtc_interrupt,
-                               IRQF_SHARED,
-                               "at91_rtc", pdev);
+       ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt,
+                              IRQF_SHARED | IRQF_COND_SUSPEND,
+                              "at91_rtc", pdev);
        if (ret) {
                dev_err(&pdev->dev, "IRQ %d already in use.\n", irq);
-               goto err_unmap;
+               goto err_clk;
        }
 
        /* cpu init code should really have flagged this device as
@@ -405,21 +441,24 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
        if (!device_can_wakeup(&pdev->dev))
                device_init_wakeup(&pdev->dev, 1);
 
-       rtc = rtc_device_register(pdev->name, &pdev->dev,
+       rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
                                &at91_rtc_ops, THIS_MODULE);
        if (IS_ERR(rtc)) {
                ret = PTR_ERR(rtc);
-               goto err_free_irq;
+               goto err_clk;
        }
        platform_set_drvdata(pdev, rtc);
 
+       /* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy
+        * completion.
+        */
+       at91_rtc_write_ier(AT91_RTC_SECEV);
+
        dev_info(&pdev->dev, "AT91 Real Time Clock driver.\n");
        return 0;
 
-err_free_irq:
-       free_irq(irq, pdev);
-err_unmap:
-       iounmap(at91_rtc_regs);
+err_clk:
+       clk_disable_unprepare(sclk);
 
        return ret;
 }
@@ -429,50 +468,74 @@ err_unmap:
  */
 static int __exit at91_rtc_remove(struct platform_device *pdev)
 {
-       struct rtc_device *rtc = platform_get_drvdata(pdev);
-
        /* Disable all interrupts */
        at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM |
                                        AT91_RTC_SECEV | AT91_RTC_TIMEV |
                                        AT91_RTC_CALEV);
-       free_irq(irq, pdev);
 
-       rtc_device_unregister(rtc);
-       iounmap(at91_rtc_regs);
-       platform_set_drvdata(pdev, NULL);
+       clk_disable_unprepare(sclk);
 
        return 0;
 }
 
+static void at91_rtc_shutdown(struct platform_device *pdev)
+{
+       /* Disable all interrupts */
+       at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+                                       AT91_RTC_SECEV | AT91_RTC_TIMEV |
+                                       AT91_RTC_CALEV);
+}
+
 #ifdef CONFIG_PM_SLEEP
 
 /* AT91RM9200 RTC Power management control */
 
-static u32 at91_rtc_imr;
-
 static int at91_rtc_suspend(struct device *dev)
 {
        /* this IRQ is shared with DBGU and other hardware which isn't
         * necessarily doing PM like we are...
         */
+       at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
+
        at91_rtc_imr = at91_rtc_read_imr()
                        & (AT91_RTC_ALARM|AT91_RTC_SECEV);
        if (at91_rtc_imr) {
-               if (device_may_wakeup(dev))
+               if (device_may_wakeup(dev)) {
+                       unsigned long flags;
+
                        enable_irq_wake(irq);
-               else
+
+                       spin_lock_irqsave(&suspended_lock, flags);
+                       suspended = true;
+                       spin_unlock_irqrestore(&suspended_lock, flags);
+               } else {
                        at91_rtc_write_idr(at91_rtc_imr);
+               }
        }
        return 0;
 }
 
 static int at91_rtc_resume(struct device *dev)
 {
+       struct rtc_device *rtc = dev_get_drvdata(dev);
+
        if (at91_rtc_imr) {
-               if (device_may_wakeup(dev))
+               if (device_may_wakeup(dev)) {
+                       unsigned long flags;
+
+                       spin_lock_irqsave(&suspended_lock, flags);
+
+                       if (cached_events) {
+                               rtc_update_irq(rtc, 1, cached_events);
+                               cached_events = 0;
+                       }
+
+                       suspended = false;
+                       spin_unlock_irqrestore(&suspended_lock, flags);
+
                        disable_irq_wake(irq);
-               else
-                       at91_rtc_write_ier(at91_rtc_imr);
+               }
+               at91_rtc_write_ier(at91_rtc_imr);
        }
        return 0;
 }
@@ -482,9 +545,9 @@ static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
 
 static struct platform_driver at91_rtc_driver = {
        .remove         = __exit_p(at91_rtc_remove),
+       .shutdown       = at91_rtc_shutdown,
        .driver         = {
                .name   = "at91_rtc",
-               .owner  = THIS_MODULE,
                .pm     = &at91_rtc_pm_ops,
                .of_match_table = of_match_ptr(at91_rtc_dt_ids),
        },