Lines Matching +full:lock +full:- +full:less
1 // SPDX-License-Identifier: GPL-2.0-only
37 #include <linux/clk-provider.h>
40 #include "clk-cpu.h"
48 * struct exynos_cpuclk_regs - Register offsets for CPU related clocks
71 * struct exynos_cpuclk_chip - Chip specific data for CPU clock
83 * struct exynos_cpuclk - information about clock supplied to a CPU core
88 * @lock: cpu clock domain register access lock
94 * @chip: chip-specific data for the CPU clock
103 spinlock_t *lock; member
111 /* ---- Common code --------------------------------------------------------- */
156 pr_err("%s: re-parenting mux timed-out\n", __func__); in wait_until_mux_stable()
167 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos_set_safe_div()
168 void __iomem *base = cpuclk->base; in exynos_set_safe_div()
171 div0 = readl(base + regs->div_cpu0); in exynos_set_safe_div()
173 writel(div0, base + regs->div_cpu0); in exynos_set_safe_div()
174 wait_until_divider_stable(base + regs->div_stat_cpu0, mask); in exynos_set_safe_div()
177 /* ---- Exynos 3/4/5 -------------------------------------------------------- */
195 /* handler for pre-rate change notification from parent clock */
199 const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; in exynos_cpuclk_pre_rate_change()
200 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos_cpuclk_pre_rate_change()
201 void __iomem *base = cpuclk->base; in exynos_cpuclk_pre_rate_change()
202 unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent); in exynos_cpuclk_pre_rate_change()
207 while ((cfg_data->prate * 1000) != ndata->new_rate) { in exynos_cpuclk_pre_rate_change()
208 if (cfg_data->prate == 0) in exynos_cpuclk_pre_rate_change()
209 return -EINVAL; in exynos_cpuclk_pre_rate_change()
213 spin_lock_irqsave(cpuclk->lock, flags); in exynos_cpuclk_pre_rate_change()
216 * For the selected PLL clock frequency, get the pre-defined divider in exynos_cpuclk_pre_rate_change()
220 div0 = cfg_data->div0; in exynos_cpuclk_pre_rate_change()
221 if (cpuclk->flags & CLK_CPU_HAS_DIV1) { in exynos_cpuclk_pre_rate_change()
222 div1 = cfg_data->div1; in exynos_cpuclk_pre_rate_change()
223 if (readl(base + regs->mux_sel) & E4210_MUX_HPM_MASK) in exynos_cpuclk_pre_rate_change()
224 div1 = readl(base + regs->div_cpu1) & in exynos_cpuclk_pre_rate_change()
229 * If the old parent clock speed is less than the clock speed of in exynos_cpuclk_pre_rate_change()
236 if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { in exynos_cpuclk_pre_rate_change()
237 unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); in exynos_cpuclk_pre_rate_change()
240 alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; in exynos_cpuclk_pre_rate_change()
243 if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { in exynos_cpuclk_pre_rate_change()
256 mux_reg = readl(base + regs->mux_sel); in exynos_cpuclk_pre_rate_change()
257 writel(mux_reg | (1 << 16), base + regs->mux_sel); in exynos_cpuclk_pre_rate_change()
258 wait_until_mux_stable(base + regs->mux_stat, 16, MUX_MASK, 2); in exynos_cpuclk_pre_rate_change()
261 writel(div0, base + regs->div_cpu0); in exynos_cpuclk_pre_rate_change()
262 wait_until_divider_stable(base + regs->div_stat_cpu0, DIV_MASK_ALL); in exynos_cpuclk_pre_rate_change()
264 if (cpuclk->flags & CLK_CPU_HAS_DIV1) { in exynos_cpuclk_pre_rate_change()
265 writel(div1, base + regs->div_cpu1); in exynos_cpuclk_pre_rate_change()
266 wait_until_divider_stable(base + regs->div_stat_cpu1, in exynos_cpuclk_pre_rate_change()
270 spin_unlock_irqrestore(cpuclk->lock, flags); in exynos_cpuclk_pre_rate_change()
274 /* handler for post-rate change notification from parent clock */
278 const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; in exynos_cpuclk_post_rate_change()
279 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos_cpuclk_post_rate_change()
280 void __iomem *base = cpuclk->base; in exynos_cpuclk_post_rate_change()
286 if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { in exynos_cpuclk_post_rate_change()
287 while ((cfg_data->prate * 1000) != ndata->new_rate) { in exynos_cpuclk_post_rate_change()
288 if (cfg_data->prate == 0) in exynos_cpuclk_post_rate_change()
289 return -EINVAL; in exynos_cpuclk_post_rate_change()
294 spin_lock_irqsave(cpuclk->lock, flags); in exynos_cpuclk_post_rate_change()
297 mux_reg = readl(base + regs->mux_sel); in exynos_cpuclk_post_rate_change()
298 writel(mux_reg & ~(1 << 16), base + regs->mux_sel); in exynos_cpuclk_post_rate_change()
299 wait_until_mux_stable(base + regs->mux_stat, 16, MUX_MASK, 1); in exynos_cpuclk_post_rate_change()
301 if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { in exynos_cpuclk_post_rate_change()
302 div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); in exynos_cpuclk_post_rate_change()
307 spin_unlock_irqrestore(cpuclk->lock, flags); in exynos_cpuclk_post_rate_change()
311 /* ---- Exynos5433 ---------------------------------------------------------- */
322 /* handler for pre-rate change notification from parent clock */
326 const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; in exynos5433_cpuclk_pre_rate_change()
327 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos5433_cpuclk_pre_rate_change()
328 void __iomem *base = cpuclk->base; in exynos5433_cpuclk_pre_rate_change()
329 unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent); in exynos5433_cpuclk_pre_rate_change()
334 while ((cfg_data->prate * 1000) != ndata->new_rate) { in exynos5433_cpuclk_pre_rate_change()
335 if (cfg_data->prate == 0) in exynos5433_cpuclk_pre_rate_change()
336 return -EINVAL; in exynos5433_cpuclk_pre_rate_change()
340 spin_lock_irqsave(cpuclk->lock, flags); in exynos5433_cpuclk_pre_rate_change()
343 * For the selected PLL clock frequency, get the pre-defined divider in exynos5433_cpuclk_pre_rate_change()
346 div0 = cfg_data->div0; in exynos5433_cpuclk_pre_rate_change()
347 div1 = cfg_data->div1; in exynos5433_cpuclk_pre_rate_change()
350 * If the old parent clock speed is less than the clock speed of in exynos5433_cpuclk_pre_rate_change()
357 if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { in exynos5433_cpuclk_pre_rate_change()
358 unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); in exynos5433_cpuclk_pre_rate_change()
361 alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; in exynos5433_cpuclk_pre_rate_change()
369 mux_reg = readl(base + regs->mux_sel); in exynos5433_cpuclk_pre_rate_change()
370 writel(mux_reg | 1, base + regs->mux_sel); in exynos5433_cpuclk_pre_rate_change()
371 wait_until_mux_stable(base + regs->mux_stat, 0, MUX_MASK, 2); in exynos5433_cpuclk_pre_rate_change()
374 writel(div0, base + regs->div_cpu0); in exynos5433_cpuclk_pre_rate_change()
375 wait_until_divider_stable(base + regs->div_stat_cpu0, DIV_MASK_ALL); in exynos5433_cpuclk_pre_rate_change()
377 writel(div1, base + regs->div_cpu1); in exynos5433_cpuclk_pre_rate_change()
378 wait_until_divider_stable(base + regs->div_stat_cpu1, DIV_MASK_ALL); in exynos5433_cpuclk_pre_rate_change()
380 spin_unlock_irqrestore(cpuclk->lock, flags); in exynos5433_cpuclk_pre_rate_change()
384 /* handler for post-rate change notification from parent clock */
388 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos5433_cpuclk_post_rate_change()
389 void __iomem *base = cpuclk->base; in exynos5433_cpuclk_post_rate_change()
394 spin_lock_irqsave(cpuclk->lock, flags); in exynos5433_cpuclk_post_rate_change()
397 mux_reg = readl(base + regs->mux_sel); in exynos5433_cpuclk_post_rate_change()
398 writel(mux_reg & ~1, base + regs->mux_sel); in exynos5433_cpuclk_post_rate_change()
399 wait_until_mux_stable(base + regs->mux_stat, 0, MUX_MASK, 1); in exynos5433_cpuclk_post_rate_change()
402 spin_unlock_irqrestore(cpuclk->lock, flags); in exynos5433_cpuclk_post_rate_change()
406 /* ---- Exynos850 ----------------------------------------------------------- */
427 * Set alternate parent rate to "rate" value or less.
448 return -ENOENT; in exynos850_alt_parent_set_max_rate()
452 return -ENOENT; in exynos850_alt_parent_set_max_rate()
456 return -EINVAL; in exynos850_alt_parent_set_max_rate()
467 ret = clk_set_rate(alt_parent->clk, div_rate); in exynos850_alt_parent_set_max_rate()
475 /* Handler for pre-rate change notification from parent clock */
480 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos850_cpuclk_pre_rate_change()
481 const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; in exynos850_cpuclk_pre_rate_change()
482 const struct clk_hw *alt_parent = cpuclk->alt_parent; in exynos850_cpuclk_pre_rate_change()
483 void __iomem *base = cpuclk->base; in exynos850_cpuclk_pre_rate_change()
491 if (ndata->new_rate == E850_OSCCLK || ndata->old_rate == E850_OSCCLK) in exynos850_cpuclk_pre_rate_change()
495 while ((cfg_data->prate * 1000) != ndata->new_rate) { in exynos850_cpuclk_pre_rate_change()
496 if (cfg_data->prate == 0) in exynos850_cpuclk_pre_rate_change()
497 return -EINVAL; in exynos850_cpuclk_pre_rate_change()
502 * If the old parent clock speed is less than the clock speed of in exynos850_cpuclk_pre_rate_change()
509 if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { in exynos850_cpuclk_pre_rate_change()
510 unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); in exynos850_cpuclk_pre_rate_change()
517 spin_lock_irqsave(cpuclk->lock, flags); in exynos850_cpuclk_pre_rate_change()
520 mux_reg = readl(base + regs->mux); in exynos850_cpuclk_pre_rate_change()
521 writel(mux_reg | 1, base + regs->mux); in exynos850_cpuclk_pre_rate_change()
522 wait_until_mux_stable(base + regs->mux, 16, 1, 0); in exynos850_cpuclk_pre_rate_change()
526 unsigned long div = (cfg_data->div0 >> shifts[i]) & 0xf; in exynos850_cpuclk_pre_rate_change()
529 val = readl(base + regs->divs[i]); in exynos850_cpuclk_pre_rate_change()
531 writel(val, base + regs->divs[i]); in exynos850_cpuclk_pre_rate_change()
532 wait_until_divider_stable(base + regs->divs[i], E850_BUSY_MASK); in exynos850_cpuclk_pre_rate_change()
535 spin_unlock_irqrestore(cpuclk->lock, flags); in exynos850_cpuclk_pre_rate_change()
540 /* Handler for post-rate change notification from parent clock */
544 const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs; in exynos850_cpuclk_post_rate_change()
545 const struct clk_hw *alt_parent = cpuclk->alt_parent; in exynos850_cpuclk_post_rate_change()
546 void __iomem *base = cpuclk->base; in exynos850_cpuclk_post_rate_change()
551 if (ndata->new_rate == E850_OSCCLK || ndata->old_rate == E850_OSCCLK) in exynos850_cpuclk_post_rate_change()
554 spin_lock_irqsave(cpuclk->lock, flags); in exynos850_cpuclk_post_rate_change()
557 mux_reg = readl(base + regs->mux); in exynos850_cpuclk_post_rate_change()
558 writel(mux_reg & ~1, base + regs->mux); in exynos850_cpuclk_post_rate_change()
559 wait_until_mux_stable(base + regs->mux, 16, 1, 0); in exynos850_cpuclk_post_rate_change()
561 spin_unlock_irqrestore(cpuclk->lock, flags); in exynos850_cpuclk_post_rate_change()
567 /* -------------------------------------------------------------------------- */
598 * This notifier function is called for the pre-rate and post-rate change
611 err = cpuclk->chip->pre_rate_cb(ndata, cpuclk); in exynos_cpuclk_notifier_cb()
613 err = cpuclk->chip->post_rate_cb(ndata, cpuclk); in exynos_cpuclk_notifier_cb()
653 hws = ctx->clk_data.hws; in exynos_register_cpu_clock()
654 parent = hws[clk_data->parent_id]; in exynos_register_cpu_clock()
655 alt_parent = hws[clk_data->alt_parent_id]; in exynos_register_cpu_clock()
658 return -EINVAL; in exynos_register_cpu_clock()
663 return -ENOMEM; in exynos_register_cpu_clock()
667 init.name = clk_data->name; in exynos_register_cpu_clock()
673 cpuclk->alt_parent = alt_parent; in exynos_register_cpu_clock()
674 cpuclk->hw.init = &init; in exynos_register_cpu_clock()
675 cpuclk->base = ctx->reg_base + clk_data->offset; in exynos_register_cpu_clock()
676 cpuclk->lock = &ctx->lock; in exynos_register_cpu_clock()
677 cpuclk->flags = clk_data->flags; in exynos_register_cpu_clock()
678 cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb; in exynos_register_cpu_clock()
679 cpuclk->chip = &exynos_clkcpu_chips[clk_data->reg_layout]; in exynos_register_cpu_clock()
681 ret = clk_notifier_register(parent->clk, &cpuclk->clk_nb); in exynos_register_cpu_clock()
684 __func__, clk_data->name); in exynos_register_cpu_clock()
689 for (num_cfgs = 0; clk_data->cfg[num_cfgs].prate != 0; ) in exynos_register_cpu_clock()
692 cpuclk->cfg = kmemdup_array(clk_data->cfg, num_cfgs, sizeof(*cpuclk->cfg), in exynos_register_cpu_clock()
694 if (!cpuclk->cfg) { in exynos_register_cpu_clock()
695 ret = -ENOMEM; in exynos_register_cpu_clock()
699 ret = clk_hw_register(NULL, &cpuclk->hw); in exynos_register_cpu_clock()
702 clk_data->name); in exynos_register_cpu_clock()
706 samsung_clk_add_lookup(ctx, &cpuclk->hw, clk_data->id); in exynos_register_cpu_clock()
710 kfree(cpuclk->cfg); in exynos_register_cpu_clock()
712 clk_notifier_unregister(parent->clk, &cpuclk->clk_nb); in exynos_register_cpu_clock()