diff options
author | Marek Szyprowski <m.szyprowski@samsung.com> | 2019-01-21 06:09:30 -0500 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@bootlin.com> | 2019-01-22 12:34:42 -0500 |
commit | 5a5b614ba61cc2a89ad0dffc63d913a1a6ba1f9f (patch) | |
tree | f8be6feae309e045dcda0bb8c8be81ed82a1c07e | |
parent | f724c6bee162be3a9cd3b9168414ca106e0cdfe2 (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.c | 74 |
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 | ||
71 | static int s3c_rtc_enable_clk(struct s3c_rtc *info) | 71 | static 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; | |
93 | out: | ||
94 | spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); | ||
95 | |||
96 | return ret; | ||
97 | } | 87 | } |
98 | 88 | ||
99 | static void s3c_rtc_disable_clk(struct s3c_rtc *info) | 89 | static 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) | |||
135 | static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) | 118 | static 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 | ||
596 | err_nortc: | 584 | err_nortc: |