aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/samsung/clk-cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/samsung/clk-cpu.c')
-rw-r--r--drivers/clk/samsung/clk-cpu.c131
1 files changed, 130 insertions, 1 deletions
diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c
index 813003d6ce09..8bf7e805fd34 100644
--- a/drivers/clk/samsung/clk-cpu.c
+++ b/drivers/clk/samsung/clk-cpu.c
@@ -45,6 +45,13 @@
45#define E4210_DIV_STAT_CPU0 0x400 45#define E4210_DIV_STAT_CPU0 0x400
46#define E4210_DIV_STAT_CPU1 0x404 46#define E4210_DIV_STAT_CPU1 0x404
47 47
48#define E5433_MUX_SEL2 0x008
49#define E5433_MUX_STAT2 0x208
50#define E5433_DIV_CPU0 0x400
51#define E5433_DIV_CPU1 0x404
52#define E5433_DIV_STAT_CPU0 0x500
53#define E5433_DIV_STAT_CPU1 0x504
54
48#define E4210_DIV0_RATIO0_MASK 0x7 55#define E4210_DIV0_RATIO0_MASK 0x7
49#define E4210_DIV1_HPM_MASK (0x7 << 4) 56#define E4210_DIV1_HPM_MASK (0x7 << 4)
50#define E4210_DIV1_COPY_MASK (0x7 << 0) 57#define E4210_DIV1_COPY_MASK (0x7 << 0)
@@ -253,6 +260,102 @@ static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
253} 260}
254 261
255/* 262/*
263 * Helper function to set the 'safe' dividers for the CPU clock. The parameters
264 * div and mask contain the divider value and the register bit mask of the
265 * dividers to be programmed.
266 */
267static void exynos5433_set_safe_div(void __iomem *base, unsigned long div,
268 unsigned long mask)
269{
270 unsigned long div0;
271
272 div0 = readl(base + E5433_DIV_CPU0);
273 div0 = (div0 & ~mask) | (div & mask);
274 writel(div0, base + E5433_DIV_CPU0);
275 wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, mask);
276}
277
278/* handler for pre-rate change notification from parent clock */
279static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
280 struct exynos_cpuclk *cpuclk, void __iomem *base)
281{
282 const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
283 unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent);
284 unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
285 unsigned long div0, div1 = 0, mux_reg;
286 unsigned long flags;
287
288 /* find out the divider values to use for clock data */
289 while ((cfg_data->prate * 1000) != ndata->new_rate) {
290 if (cfg_data->prate == 0)
291 return -EINVAL;
292 cfg_data++;
293 }
294
295 spin_lock_irqsave(cpuclk->lock, flags);
296
297 /*
298 * For the selected PLL clock frequency, get the pre-defined divider
299 * values.
300 */
301 div0 = cfg_data->div0;
302 div1 = cfg_data->div1;
303
304 /*
305 * If the old parent clock speed is less than the clock speed of
306 * the alternate parent, then it should be ensured that at no point
307 * the armclk speed is more than the old_prate until the dividers are
308 * set. Also workaround the issue of the dividers being set to lower
309 * values before the parent clock speed is set to new lower speed
310 * (this can result in too high speed of armclk output clocks).
311 */
312 if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
313 unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
314
315 alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
316 WARN_ON(alt_div >= MAX_DIV);
317
318 exynos5433_set_safe_div(base, alt_div, alt_div_mask);
319 div0 |= alt_div;
320 }
321
322 /* select the alternate parent */
323 mux_reg = readl(base + E5433_MUX_SEL2);
324 writel(mux_reg | 1, base + E5433_MUX_SEL2);
325 wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 2);
326
327 /* alternate parent is active now. set the dividers */
328 writel(div0, base + E5433_DIV_CPU0);
329 wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, DIV_MASK_ALL);
330
331 writel(div1, base + E5433_DIV_CPU1);
332 wait_until_divider_stable(base + E5433_DIV_STAT_CPU1, DIV_MASK_ALL);
333
334 spin_unlock_irqrestore(cpuclk->lock, flags);
335 return 0;
336}
337
338/* handler for post-rate change notification from parent clock */
339static int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
340 struct exynos_cpuclk *cpuclk, void __iomem *base)
341{
342 unsigned long div = 0, div_mask = DIV_MASK;
343 unsigned long mux_reg;
344 unsigned long flags;
345
346 spin_lock_irqsave(cpuclk->lock, flags);
347
348 /* select apll as the alternate parent */
349 mux_reg = readl(base + E5433_MUX_SEL2);
350 writel(mux_reg & ~1, base + E5433_MUX_SEL2);
351 wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 1);
352
353 exynos5433_set_safe_div(base, div, div_mask);
354 spin_unlock_irqrestore(cpuclk->lock, flags);
355 return 0;
356}
357
358/*
256 * This notifier function is called for the pre-rate and post-rate change 359 * This notifier function is called for the pre-rate and post-rate change
257 * notifications of the parent clock of cpuclk. 360 * notifications of the parent clock of cpuclk.
258 */ 361 */
@@ -275,6 +378,29 @@ static int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
275 return notifier_from_errno(err); 378 return notifier_from_errno(err);
276} 379}
277 380
381/*
382 * This notifier function is called for the pre-rate and post-rate change
383 * notifications of the parent clock of cpuclk.
384 */
385static int exynos5433_cpuclk_notifier_cb(struct notifier_block *nb,
386 unsigned long event, void *data)
387{
388 struct clk_notifier_data *ndata = data;
389 struct exynos_cpuclk *cpuclk;
390 void __iomem *base;
391 int err = 0;
392
393 cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
394 base = cpuclk->ctrl_base;
395
396 if (event == PRE_RATE_CHANGE)
397 err = exynos5433_cpuclk_pre_rate_change(ndata, cpuclk, base);
398 else if (event == POST_RATE_CHANGE)
399 err = exynos5433_cpuclk_post_rate_change(ndata, cpuclk, base);
400
401 return notifier_from_errno(err);
402}
403
278/* helper function to register a CPU clock */ 404/* helper function to register a CPU clock */
279int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, 405int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
280 unsigned int lookup_id, const char *name, const char *parent, 406 unsigned int lookup_id, const char *name, const char *parent,
@@ -301,7 +427,10 @@ int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
301 cpuclk->ctrl_base = ctx->reg_base + offset; 427 cpuclk->ctrl_base = ctx->reg_base + offset;
302 cpuclk->lock = &ctx->lock; 428 cpuclk->lock = &ctx->lock;
303 cpuclk->flags = flags; 429 cpuclk->flags = flags;
304 cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb; 430 if (flags & CLK_CPU_HAS_E5433_REGS_LAYOUT)
431 cpuclk->clk_nb.notifier_call = exynos5433_cpuclk_notifier_cb;
432 else
433 cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
305 434
306 cpuclk->alt_parent = __clk_lookup(alt_parent); 435 cpuclk->alt_parent = __clk_lookup(alt_parent);
307 if (!cpuclk->alt_parent) { 436 if (!cpuclk->alt_parent) {