rtc: s3c: Rewrite clock handling
authorMarek Szyprowski <m.szyprowski@samsung.com>
Mon, 21 Jan 2019 11:09:30 +0000 (12:09 +0100)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Tue, 22 Jan 2019 17:34:42 +0000 (18:34 +0100)
s3c_rtc_enable/disable_clk() functions were designed to be called multiple
times without reference counting, because they were initially only used in
alarm setting/clearing functions, which can be called both when alarm is
already set or not. Later however, calls to those functions have been added to
other places in the driver - like time and /proc reading callbacks, what
results in broken alarm if any of such events happens after the alarm has
been set. Fix this by simplifying s3c_rtc_enable/disable_clk() functions
to rely on proper reference counting in clock core and move alarm enable
counter to s3c_rtc_setaie() function.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/rtc-s3c.c

index 04c68178c42def374c6a0a9aafd2a4dc746fbca5..ea82efba6f9447aef31c7eed6cf52f8d0576a6ab 100644 (file)
@@ -39,7 +39,7 @@ struct s3c_rtc {
        void __iomem *base;
        struct clk *rtc_clk;
        struct clk *rtc_src_clk;
-       bool clk_disabled;
+       bool alarm_enabled;
 
        const struct s3c_rtc_data *data;
 
@@ -47,7 +47,7 @@ struct s3c_rtc {
        int irq_tick;
 
        spinlock_t pie_lock;
-       spinlock_t alarm_clk_lock;
+       spinlock_t alarm_lock;
 
        int ticnt_save;
        int ticnt_en_save;
@@ -70,44 +70,27 @@ struct s3c_rtc_data {
 
 static int s3c_rtc_enable_clk(struct s3c_rtc *info)
 {
-       unsigned long irq_flags;
-       int ret = 0;
+       int ret;
 
-       spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
+       ret = clk_enable(info->rtc_clk);
+       if (ret)
+               return ret;
 
-       if (info->clk_disabled) {
-               ret = clk_enable(info->rtc_clk);
-               if (ret)
-                       goto out;
-
-               if (info->data->needs_src_clk) {
-                       ret = clk_enable(info->rtc_src_clk);
-                       if (ret) {
-                               clk_disable(info->rtc_clk);
-                               goto out;
-                       }
+       if (info->data->needs_src_clk) {
+               ret = clk_enable(info->rtc_src_clk);
+               if (ret) {
+                       clk_disable(info->rtc_clk);
+                       return ret;
                }
-               info->clk_disabled = false;
        }
-
-out:
-       spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
-
-       return ret;
+       return 0;
 }
 
 static void s3c_rtc_disable_clk(struct s3c_rtc *info)
 {
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
-       if (!info->clk_disabled) {
-               if (info->data->needs_src_clk)
-                       clk_disable(info->rtc_src_clk);
-               clk_disable(info->rtc_clk);
-               info->clk_disabled = true;
-       }
-       spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
+       if (info->data->needs_src_clk)
+               clk_disable(info->rtc_src_clk);
+       clk_disable(info->rtc_clk);
 }
 
 /* IRQ Handlers */
@@ -135,6 +118,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
 static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 {
        struct s3c_rtc *info = dev_get_drvdata(dev);
+       unsigned long flags;
        unsigned int tmp;
        int ret;
 
@@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 
        writeb(tmp, info->base + S3C2410_RTCALM);
 
-       s3c_rtc_disable_clk(info);
+       spin_lock_irqsave(&info->alarm_lock, flags);
 
-       if (enabled) {
-               ret = s3c_rtc_enable_clk(info);
-               if (ret)
-                       return ret;
-       } else {
+       if (info->alarm_enabled && !enabled)
                s3c_rtc_disable_clk(info);
-       }
+       else if (!info->alarm_enabled && enabled)
+               ret = s3c_rtc_enable_clk(info);
 
-       return 0;
+       info->alarm_enabled = enabled;
+       spin_unlock_irqrestore(&info->alarm_lock, flags);
+
+       s3c_rtc_disable_clk(info);
+
+       return ret;
 }
 
 /* Set RTC frequency */
@@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        writeb(alrm_en, info->base + S3C2410_RTCALM);
 
-       s3c_rtc_disable_clk(info);
-
        s3c_rtc_setaie(dev, alrm->enabled);
 
+       s3c_rtc_disable_clk(info);
+
        return 0;
 }
 
@@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
                return -EINVAL;
        }
        spin_lock_init(&info->pie_lock);
-       spin_lock_init(&info->alarm_clk_lock);
+       spin_lock_init(&info->alarm_lock);
 
        platform_set_drvdata(pdev, info);
 
@@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
 
        s3c_rtc_setfreq(info, 1);
 
+       s3c_rtc_disable_clk(info);
+
        return 0;
 
 err_nortc: