aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSoren Brinkmann <soren.brinkmann@xilinx.com>2014-02-19 18:14:42 -0500
committerDaniel Lezcano <daniel.lezcano@linaro.org>2014-03-11 18:10:03 -0400
commitb3e90722f6f53fa457a88146a877e34ea71d25ea (patch)
tree9a9bdf62adcb2384b17600b1aefa1e202e2f4b46 /drivers
parent5f0ba3b462b2d36b3c28748863747fb1050f40d0 (diff)
clocksource/cadence_ttc: Overhaul clocksource frequency adjustment
The currently used method adjusting the clocksource to a changing input frequency does not work on kernels from 3.11 on. The new approach is to keep the timer frequency as constant as possible. I.e. - due to the TTC's prescaler limitations, allow frequency changes only if the frequency scales by a power of 2 - adjust the counter's divider on the fly when a frequency change occurs This limits cpufreq to scale by certain factors only. But we may keep the time base somewhat constant, so that sleep() & co keep working as expected, while supporting cpufreq. Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Michal Simek <michal.simek@xilinx.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clocksource/cadence_ttc_timer.c104
1 files changed, 84 insertions, 20 deletions
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index f05b9fdc7425..49fbe2847c84 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -16,6 +16,7 @@
16 */ 16 */
17 17
18#include <linux/clk.h> 18#include <linux/clk.h>
19#include <linux/clk-provider.h>
19#include <linux/interrupt.h> 20#include <linux/interrupt.h>
20#include <linux/clockchips.h> 21#include <linux/clockchips.h>
21#include <linux/of_address.h> 22#include <linux/of_address.h>
@@ -52,6 +53,8 @@
52#define TTC_CNT_CNTRL_DISABLE_MASK 0x1 53#define TTC_CNT_CNTRL_DISABLE_MASK 0x1
53 54
54#define TTC_CLK_CNTRL_CSRC_MASK (1 << 5) /* clock source */ 55#define TTC_CLK_CNTRL_CSRC_MASK (1 << 5) /* clock source */
56#define TTC_CLK_CNTRL_PSV_MASK 0x1e
57#define TTC_CLK_CNTRL_PSV_SHIFT 1
55 58
56/* 59/*
57 * Setup the timers to use pre-scaling, using a fixed value for now that will 60 * Setup the timers to use pre-scaling, using a fixed value for now that will
@@ -63,6 +66,8 @@
63#define CLK_CNTRL_PRESCALE_EN 1 66#define CLK_CNTRL_PRESCALE_EN 1
64#define CNT_CNTRL_RESET (1 << 4) 67#define CNT_CNTRL_RESET (1 << 4)
65 68
69#define MAX_F_ERR 50
70
66/** 71/**
67 * struct ttc_timer - This definition defines local timer structure 72 * struct ttc_timer - This definition defines local timer structure
68 * 73 *
@@ -82,6 +87,8 @@ struct ttc_timer {
82 container_of(x, struct ttc_timer, clk_rate_change_nb) 87 container_of(x, struct ttc_timer, clk_rate_change_nb)
83 88
84struct ttc_timer_clocksource { 89struct ttc_timer_clocksource {
90 u32 scale_clk_ctrl_reg_old;
91 u32 scale_clk_ctrl_reg_new;
85 struct ttc_timer ttc; 92 struct ttc_timer ttc;
86 struct clocksource cs; 93 struct clocksource cs;
87}; 94};
@@ -229,32 +236,89 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
229 struct ttc_timer_clocksource, ttc); 236 struct ttc_timer_clocksource, ttc);
230 237
231 switch (event) { 238 switch (event) {
232 case POST_RATE_CHANGE: 239 case PRE_RATE_CHANGE:
240 {
241 u32 psv;
242 unsigned long factor, rate_low, rate_high;
243
244 if (ndata->new_rate > ndata->old_rate) {
245 factor = DIV_ROUND_CLOSEST(ndata->new_rate,
246 ndata->old_rate);
247 rate_low = ndata->old_rate;
248 rate_high = ndata->new_rate;
249 } else {
250 factor = DIV_ROUND_CLOSEST(ndata->old_rate,
251 ndata->new_rate);
252 rate_low = ndata->new_rate;
253 rate_high = ndata->old_rate;
254 }
255
256 if (!is_power_of_2(factor))
257 return NOTIFY_BAD;
258
259 if (abs(rate_high - (factor * rate_low)) > MAX_F_ERR)
260 return NOTIFY_BAD;
261
262 factor = __ilog2_u32(factor);
263
233 /* 264 /*
234 * Do whatever is necessary to maintain a proper time base 265 * store timer clock ctrl register so we can restore it in case
235 * 266 * of an abort.
236 * I cannot find a way to adjust the currently used clocksource
237 * to the new frequency. __clocksource_updatefreq_hz() sounds
238 * good, but does not work. Not sure what's that missing.
239 *
240 * This approach works, but triggers two clocksource switches.
241 * The first after unregister to clocksource jiffies. And
242 * another one after the register to the newly registered timer.
243 *
244 * Alternatively we could 'waste' another HW timer to ping pong
245 * between clock sources. That would also use one register and
246 * one unregister call, but only trigger one clocksource switch
247 * for the cost of another HW timer used by the OS.
248 */ 267 */
249 clocksource_unregister(&ttccs->cs); 268 ttccs->scale_clk_ctrl_reg_old =
250 clocksource_register_hz(&ttccs->cs, 269 __raw_readl(ttccs->ttc.base_addr +
251 ndata->new_rate / PRESCALE); 270 TTC_CLK_CNTRL_OFFSET);
252 /* fall through */ 271
253 case PRE_RATE_CHANGE: 272 psv = (ttccs->scale_clk_ctrl_reg_old &
273 TTC_CLK_CNTRL_PSV_MASK) >>
274 TTC_CLK_CNTRL_PSV_SHIFT;
275 if (ndata->new_rate < ndata->old_rate)
276 psv -= factor;
277 else
278 psv += factor;
279
280 /* prescaler within legal range? */
281 if (psv & ~(TTC_CLK_CNTRL_PSV_MASK >> TTC_CLK_CNTRL_PSV_SHIFT))
282 return NOTIFY_BAD;
283
284 ttccs->scale_clk_ctrl_reg_new = ttccs->scale_clk_ctrl_reg_old &
285 ~TTC_CLK_CNTRL_PSV_MASK;
286 ttccs->scale_clk_ctrl_reg_new |= psv << TTC_CLK_CNTRL_PSV_SHIFT;
287
288
289 /* scale down: adjust divider in post-change notification */
290 if (ndata->new_rate < ndata->old_rate)
291 return NOTIFY_DONE;
292
293 /* scale up: adjust divider now - before frequency change */
294 __raw_writel(ttccs->scale_clk_ctrl_reg_new,
295 ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
296 break;
297 }
298 case POST_RATE_CHANGE:
299 /* scale up: pre-change notification did the adjustment */
300 if (ndata->new_rate > ndata->old_rate)
301 return NOTIFY_OK;
302
303 /* scale down: adjust divider now - after frequency change */
304 __raw_writel(ttccs->scale_clk_ctrl_reg_new,
305 ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
306 break;
307
254 case ABORT_RATE_CHANGE: 308 case ABORT_RATE_CHANGE:
309 /* we have to undo the adjustment in case we scale up */
310 if (ndata->new_rate < ndata->old_rate)
311 return NOTIFY_OK;
312
313 /* restore original register value */
314 __raw_writel(ttccs->scale_clk_ctrl_reg_old,
315 ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
316 /* fall through */
255 default: 317 default:
256 return NOTIFY_DONE; 318 return NOTIFY_DONE;
257 } 319 }
320
321 return NOTIFY_DONE;
258} 322}
259 323
260static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) 324static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)