summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2019-01-21 06:09:30 -0500
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2019-01-22 12:34:42 -0500
commit5a5b614ba61cc2a89ad0dffc63d913a1a6ba1f9f (patch)
treef8be6feae309e045dcda0bb8c8be81ed82a1c07e
parentf724c6bee162be3a9cd3b9168414ca106e0cdfe2 (diff)
rtc: s3c: Rewrite clock handling
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>
-rw-r--r--drivers/rtc/rtc-s3c.c74
1 files changed, 31 insertions, 43 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 04c68178c42d..ea82efba6f94 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -39,7 +39,7 @@ struct s3c_rtc {
39 void __iomem *base; 39 void __iomem *base;
40 struct clk *rtc_clk; 40 struct clk *rtc_clk;
41 struct clk *rtc_src_clk; 41 struct clk *rtc_src_clk;
42 bool clk_disabled; 42 bool alarm_enabled;
43 43
44 const struct s3c_rtc_data *data; 44 const struct s3c_rtc_data *data;
45 45
@@ -47,7 +47,7 @@ struct s3c_rtc {
47 int irq_tick; 47 int irq_tick;
48 48
49 spinlock_t pie_lock; 49 spinlock_t pie_lock;
50 spinlock_t alarm_clk_lock; 50 spinlock_t alarm_lock;
51 51
52 int ticnt_save; 52 int ticnt_save;
53 int ticnt_en_save; 53 int ticnt_en_save;
@@ -70,44 +70,27 @@ struct s3c_rtc_data {
70 70
71static int s3c_rtc_enable_clk(struct s3c_rtc *info) 71static int s3c_rtc_enable_clk(struct s3c_rtc *info)
72{ 72{
73 unsigned long irq_flags; 73 int ret;
74 int ret = 0;
75 74
76 spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); 75 ret = clk_enable(info->rtc_clk);
76 if (ret)
77 return ret;
77 78
78 if (info->clk_disabled) { 79 if (info->data->needs_src_clk) {
79 ret = clk_enable(info->rtc_clk); 80 ret = clk_enable(info->rtc_src_clk);
80 if (ret) 81 if (ret) {
81 goto out; 82 clk_disable(info->rtc_clk);
82 83 return ret;
83 if (info->data->needs_src_clk) {
84 ret = clk_enable(info->rtc_src_clk);
85 if (ret) {
86 clk_disable(info->rtc_clk);
87 goto out;
88 }
89 } 84 }
90 info->clk_disabled = false;
91 } 85 }
92 86 return 0;
93out:
94 spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
95
96 return ret;
97} 87}
98 88
99static void s3c_rtc_disable_clk(struct s3c_rtc *info) 89static void s3c_rtc_disable_clk(struct s3c_rtc *info)
100{ 90{
101 unsigned long irq_flags; 91 if (info->data->needs_src_clk)
102 92 clk_disable(info->rtc_src_clk);
103 spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); 93 clk_disable(info->rtc_clk);
104 if (!info->clk_disabled) {
105 if (info->data->needs_src_clk)
106 clk_disable(info->rtc_src_clk);
107 clk_disable(info->rtc_clk);
108 info->clk_disabled = true;
109 }
110 spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
111} 94}
112 95
113/* IRQ Handlers */ 96/* IRQ Handlers */
@@ -135,6 +118,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
135static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) 118static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
136{ 119{
137 struct s3c_rtc *info = dev_get_drvdata(dev); 120 struct s3c_rtc *info = dev_get_drvdata(dev);
121 unsigned long flags;
138 unsigned int tmp; 122 unsigned int tmp;
139 int ret; 123 int ret;
140 124
@@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
151 135
152 writeb(tmp, info->base + S3C2410_RTCALM); 136 writeb(tmp, info->base + S3C2410_RTCALM);
153 137
154 s3c_rtc_disable_clk(info); 138 spin_lock_irqsave(&info->alarm_lock, flags);
155 139
156 if (enabled) { 140 if (info->alarm_enabled && !enabled)
157 ret = s3c_rtc_enable_clk(info);
158 if (ret)
159 return ret;
160 } else {
161 s3c_rtc_disable_clk(info); 141 s3c_rtc_disable_clk(info);
162 } 142 else if (!info->alarm_enabled && enabled)
143 ret = s3c_rtc_enable_clk(info);
163 144
164 return 0; 145 info->alarm_enabled = enabled;
146 spin_unlock_irqrestore(&info->alarm_lock, flags);
147
148 s3c_rtc_disable_clk(info);
149
150 return ret;
165} 151}
166 152
167/* Set RTC frequency */ 153/* Set RTC frequency */
@@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
357 343
358 writeb(alrm_en, info->base + S3C2410_RTCALM); 344 writeb(alrm_en, info->base + S3C2410_RTCALM);
359 345
360 s3c_rtc_disable_clk(info);
361
362 s3c_rtc_setaie(dev, alrm->enabled); 346 s3c_rtc_setaie(dev, alrm->enabled);
363 347
348 s3c_rtc_disable_clk(info);
349
364 return 0; 350 return 0;
365} 351}
366 352
@@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
491 return -EINVAL; 477 return -EINVAL;
492 } 478 }
493 spin_lock_init(&info->pie_lock); 479 spin_lock_init(&info->pie_lock);
494 spin_lock_init(&info->alarm_clk_lock); 480 spin_lock_init(&info->alarm_lock);
495 481
496 platform_set_drvdata(pdev, info); 482 platform_set_drvdata(pdev, info);
497 483
@@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
591 577
592 s3c_rtc_setfreq(info, 1); 578 s3c_rtc_setfreq(info, 1);
593 579
580 s3c_rtc_disable_clk(info);
581
594 return 0; 582 return 0;
595 583
596err_nortc: 584err_nortc: