aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2015-03-31 06:12:26 -0400
committerIngo Molnar <mingo@kernel.org>2015-03-31 11:53:58 -0400
commit3071efa466b30636bf958f3d13bc808e03105cd8 (patch)
tree911c21789551a3aa19f18da0a0d2b5b1785fcd48
parent4a59058f0b09682200c04b1db236b4a3b92128d7 (diff)
clocksource/drivers/sun5i: Add clock notifiers
The parent clock of the sun5i timer is the AHB clock, which rate might change because of other devices requirements. This is for example the case on the Allwinner A31, where the DMA controller needs a minimum rate higher than the default, that is enforced after the timer driver has probed. Add clock notifiers to make sure we reflect the clock rate changes in the timer rates. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1427796746-373-5-git-send-email-daniel.lezcano@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--drivers/clocksource/timer-sun5i.c66
1 files changed, 64 insertions, 2 deletions
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index 23300901405b..28aa4b7bb602 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -40,9 +40,13 @@
40struct sun5i_timer { 40struct sun5i_timer {
41 void __iomem *base; 41 void __iomem *base;
42 struct clk *clk; 42 struct clk *clk;
43 struct notifier_block clk_rate_cb;
43 u32 ticks_per_jiffy; 44 u32 ticks_per_jiffy;
44}; 45};
45 46
47#define to_sun5i_timer(x) \
48 container_of(x, struct sun5i_timer, clk_rate_cb)
49
46struct sun5i_timer_clksrc { 50struct sun5i_timer_clksrc {
47 struct sun5i_timer timer; 51 struct sun5i_timer timer;
48 struct clocksource clksrc; 52 struct clocksource clksrc;
@@ -151,6 +155,29 @@ static cycle_t sun5i_clksrc_read(struct clocksource *clksrc)
151 return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1)); 155 return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1));
152} 156}
153 157
158static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
159 unsigned long event, void *data)
160{
161 struct clk_notifier_data *ndata = data;
162 struct sun5i_timer *timer = to_sun5i_timer(nb);
163 struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer);
164
165 switch (event) {
166 case PRE_RATE_CHANGE:
167 clocksource_unregister(&cs->clksrc);
168 break;
169
170 case POST_RATE_CHANGE:
171 clocksource_register_hz(&cs->clksrc, ndata->new_rate);
172 break;
173
174 default:
175 break;
176 }
177
178 return NOTIFY_DONE;
179}
180
154static int __init sun5i_setup_clocksource(struct device_node *node, 181static int __init sun5i_setup_clocksource(struct device_node *node,
155 void __iomem *base, 182 void __iomem *base,
156 struct clk *clk, int irq) 183 struct clk *clk, int irq)
@@ -173,6 +200,14 @@ static int __init sun5i_setup_clocksource(struct device_node *node,
173 200
174 cs->timer.base = base; 201 cs->timer.base = base;
175 cs->timer.clk = clk; 202 cs->timer.clk = clk;
203 cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc;
204 cs->timer.clk_rate_cb.next = NULL;
205
206 ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb);
207 if (ret) {
208 pr_err("Unable to register clock notifier.\n");
209 goto err_disable_clk;
210 }
176 211
177 writel(~0, base + TIMER_INTVAL_LO_REG(1)); 212 writel(~0, base + TIMER_INTVAL_LO_REG(1));
178 writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 213 writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
@@ -187,11 +222,13 @@ static int __init sun5i_setup_clocksource(struct device_node *node,
187 ret = clocksource_register_hz(&cs->clksrc, rate); 222 ret = clocksource_register_hz(&cs->clksrc, rate);
188 if (ret) { 223 if (ret) {
189 pr_err("Couldn't register clock source.\n"); 224 pr_err("Couldn't register clock source.\n");
190 goto err_disable_clk; 225 goto err_remove_notifier;
191 } 226 }
192 227
193 return 0; 228 return 0;
194 229
230err_remove_notifier:
231 clk_notifier_unregister(clk, &cs->timer.clk_rate_cb);
195err_disable_clk: 232err_disable_clk:
196 clk_disable_unprepare(clk); 233 clk_disable_unprepare(clk);
197err_free: 234err_free:
@@ -199,6 +236,21 @@ err_free:
199 return ret; 236 return ret;
200} 237}
201 238
239static int sun5i_rate_cb_clkevt(struct notifier_block *nb,
240 unsigned long event, void *data)
241{
242 struct clk_notifier_data *ndata = data;
243 struct sun5i_timer *timer = to_sun5i_timer(nb);
244 struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer);
245
246 if (event == POST_RATE_CHANGE) {
247 clockevents_update_freq(&ce->clkevt, ndata->new_rate);
248 ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ);
249 }
250
251 return NOTIFY_DONE;
252}
253
202static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base, 254static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base,
203 struct clk *clk, int irq) 255 struct clk *clk, int irq)
204{ 256{
@@ -222,6 +274,14 @@ static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem
222 ce->timer.base = base; 274 ce->timer.base = base;
223 ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); 275 ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
224 ce->timer.clk = clk; 276 ce->timer.clk = clk;
277 ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt;
278 ce->timer.clk_rate_cb.next = NULL;
279
280 ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb);
281 if (ret) {
282 pr_err("Unable to register clock notifier.\n");
283 goto err_disable_clk;
284 }
225 285
226 ce->clkevt.name = node->name; 286 ce->clkevt.name = node->name;
227 ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 287 ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
@@ -242,11 +302,13 @@ static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem
242 "sun5i_timer0", ce); 302 "sun5i_timer0", ce);
243 if (ret) { 303 if (ret) {
244 pr_err("Unable to register interrupt\n"); 304 pr_err("Unable to register interrupt\n");
245 goto err_disable_clk; 305 goto err_remove_notifier;
246 } 306 }
247 307
248 return 0; 308 return 0;
249 309
310err_remove_notifier:
311 clk_notifier_unregister(clk, &ce->timer.clk_rate_cb);
250err_disable_clk: 312err_disable_clk:
251 clk_disable_unprepare(clk); 313 clk_disable_unprepare(clk);
252err_free: 314err_free: