1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Author: Thomas Abraham <thomas.ab@samsung.com>
6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
9 * This file contains the utility function to register CPU clock for Samsung
10 * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a
11 * group of CPUs. The CPU clock is typically derived from a hierarchy of clock
12 * blocks which includes mux and divider blocks. There are a number of other
13 * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
14 * clock for CPU domain. The rates of these auxiliary clocks are related to the
15 * CPU clock rate and this relation is usually specified in the hardware manual
16 * of the SoC or supplied after the SoC characterization.
18 * The below implementation of the CPU clock allows the rate changes of the CPU
19 * clock and the corresponding rate changes of the auxiliary clocks of the CPU
20 * domain. The platform clock driver provides a clock register configuration
21 * for each configurable rate which is then used to program the clock hardware
22 * registers to achieve a fast coordinated rate change for all the CPU domain
25 * On a rate change request for the CPU clock, the rate change is propagated
26 * up to the PLL supplying the clock to the CPU domain clock blocks. While the
27 * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
28 * alternate clock source. If required, the alternate clock source is divided
29 * down in order to keep the output clock rate within the previous OPP limits.
32 #include <linux/delay.h>
33 #include <linux/errno.h>
35 #include <linux/slab.h>
36 #include <linux/clk.h>
37 #include <linux/clk-provider.h>
44 typedef int (*exynos_rate_change_fn_t
)(struct clk_notifier_data
*ndata
,
45 struct exynos_cpuclk
*cpuclk
);
48 * struct exynos_cpuclk_regs - Register offsets for CPU related clocks
49 * @mux_sel: offset of CPU MUX_SEL register (for selecting MUX clock parent)
50 * @mux_stat: offset of CPU MUX_STAT register (for checking MUX clock status)
51 * @div_cpu0: offset of CPU DIV0 register (for modifying divider values)
52 * @div_cpu1: offset of CPU DIV1 register (for modifying divider values)
53 * @div_stat_cpu0: offset of CPU DIV0_STAT register (for checking DIV status)
54 * @div_stat_cpu1: offset of CPU DIV1_STAT register (for checking DIV status)
55 * @mux: offset of MUX register for choosing CPU clock source
56 * @divs: offsets of DIV registers (ACLK, ATCLK, PCLKDBG and PERIPHCLK)
58 struct exynos_cpuclk_regs
{
71 * struct exynos_cpuclk_chip - Chip specific data for CPU clock
72 * @regs: register offsets for CPU related clocks
73 * @pre_rate_cb: callback to run before CPU clock rate change
74 * @post_rate_cb: callback to run after CPU clock rate change
76 struct exynos_cpuclk_chip
{
77 const struct exynos_cpuclk_regs
*regs
;
78 exynos_rate_change_fn_t pre_rate_cb
;
79 exynos_rate_change_fn_t post_rate_cb
;
83 * struct exynos_cpuclk - information about clock supplied to a CPU core
84 * @hw: handle between CCF and CPU clock
85 * @alt_parent: alternate parent clock to use when switching the speed
86 * of the primary parent clock
87 * @base: start address of the CPU clock registers block
88 * @lock: cpu clock domain register access lock
89 * @cfg: cpu clock rate configuration data
90 * @num_cfgs: number of array elements in @cfg array
91 * @clk_nb: clock notifier registered for changes in clock speed of the
92 * primary parent clock
93 * @flags: configuration flags for the CPU clock
94 * @chip: chip-specific data for the CPU clock
96 * This structure holds information required for programming the CPU clock for
97 * various clock speeds.
99 struct exynos_cpuclk
{
101 const struct clk_hw
*alt_parent
;
104 const struct exynos_cpuclk_cfg_data
*cfg
;
105 const unsigned long num_cfgs
;
106 struct notifier_block clk_nb
;
108 const struct exynos_cpuclk_chip
*chip
;
111 /* ---- Common code --------------------------------------------------------- */
113 /* Divider stabilization time, msec */
114 #define MAX_STAB_TIME 10
116 #define DIV_MASK GENMASK(2, 0)
117 #define DIV_MASK_ALL GENMASK(31, 0)
118 #define MUX_MASK GENMASK(2, 0)
121 * Helper function to wait until divider(s) have stabilized after the divider
124 static void wait_until_divider_stable(void __iomem
*div_reg
, unsigned long mask
)
126 unsigned long timeout
= jiffies
+ msecs_to_jiffies(MAX_STAB_TIME
);
129 if (!(readl(div_reg
) & mask
))
131 } while (time_before(jiffies
, timeout
));
133 if (!(readl(div_reg
) & mask
))
136 pr_err("%s: timeout in divider stablization\n", __func__
);
140 * Helper function to wait until mux has stabilized after the mux selection
143 static void wait_until_mux_stable(void __iomem
*mux_reg
, u32 mux_pos
,
144 unsigned long mask
, unsigned long mux_value
)
146 unsigned long timeout
= jiffies
+ msecs_to_jiffies(MAX_STAB_TIME
);
149 if (((readl(mux_reg
) >> mux_pos
) & mask
) == mux_value
)
151 } while (time_before(jiffies
, timeout
));
153 if (((readl(mux_reg
) >> mux_pos
) & mask
) == mux_value
)
156 pr_err("%s: re-parenting mux timed-out\n", __func__
);
160 * Helper function to set the 'safe' dividers for the CPU clock. The parameters
161 * div and mask contain the divider value and the register bit mask of the
162 * dividers to be programmed.
164 static void exynos_set_safe_div(struct exynos_cpuclk
*cpuclk
, unsigned long div
,
167 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
168 void __iomem
*base
= cpuclk
->base
;
171 div0
= readl(base
+ regs
->div_cpu0
);
172 div0
= (div0
& ~mask
) | (div
& mask
);
173 writel(div0
, base
+ regs
->div_cpu0
);
174 wait_until_divider_stable(base
+ regs
->div_stat_cpu0
, mask
);
177 /* ---- Exynos 3/4/5 -------------------------------------------------------- */
179 #define E4210_DIV0_RATIO0_MASK GENMASK(2, 0)
180 #define E4210_DIV1_HPM_MASK GENMASK(6, 4)
181 #define E4210_DIV1_COPY_MASK GENMASK(2, 0)
182 #define E4210_MUX_HPM_MASK BIT(20)
183 #define E4210_DIV0_ATB_SHIFT 16
184 #define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT)
186 static const struct exynos_cpuclk_regs e4210_cpuclk_regs
= {
191 .div_stat_cpu0
= 0x600,
192 .div_stat_cpu1
= 0x604,
195 /* handler for pre-rate change notification from parent clock */
196 static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data
*ndata
,
197 struct exynos_cpuclk
*cpuclk
)
199 const struct exynos_cpuclk_cfg_data
*cfg_data
= cpuclk
->cfg
;
200 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
201 void __iomem
*base
= cpuclk
->base
;
202 unsigned long alt_prate
= clk_hw_get_rate(cpuclk
->alt_parent
);
203 unsigned long div0
, div1
= 0, mux_reg
;
206 /* find out the divider values to use for clock data */
207 while ((cfg_data
->prate
* 1000) != ndata
->new_rate
) {
208 if (cfg_data
->prate
== 0)
213 spin_lock_irqsave(cpuclk
->lock
, flags
);
216 * For the selected PLL clock frequency, get the pre-defined divider
217 * values. If the clock for sclk_hpm is not sourced from apll, then
218 * the values for DIV_COPY and DIV_HPM dividers need not be set.
220 div0
= cfg_data
->div0
;
221 if (cpuclk
->flags
& CLK_CPU_HAS_DIV1
) {
222 div1
= cfg_data
->div1
;
223 if (readl(base
+ regs
->mux_sel
) & E4210_MUX_HPM_MASK
)
224 div1
= readl(base
+ regs
->div_cpu1
) &
225 (E4210_DIV1_HPM_MASK
| E4210_DIV1_COPY_MASK
);
229 * If the old parent clock speed is less than the clock speed of
230 * the alternate parent, then it should be ensured that at no point
231 * the armclk speed is more than the old_prate until the dividers are
232 * set. Also workaround the issue of the dividers being set to lower
233 * values before the parent clock speed is set to new lower speed
234 * (this can result in too high speed of armclk output clocks).
236 if (alt_prate
> ndata
->old_rate
|| ndata
->old_rate
> ndata
->new_rate
) {
237 unsigned long tmp_rate
= min(ndata
->old_rate
, ndata
->new_rate
);
238 unsigned long alt_div
, alt_div_mask
= DIV_MASK
;
240 alt_div
= DIV_ROUND_UP(alt_prate
, tmp_rate
) - 1;
241 WARN_ON(alt_div
>= MAX_DIV
);
243 if (cpuclk
->flags
& CLK_CPU_NEEDS_DEBUG_ALT_DIV
) {
245 * In Exynos4210, ATB clock parent is also mout_core. So
246 * ATB clock also needs to be mantained at safe speed.
248 alt_div
|= E4210_DIV0_ATB_MASK
;
249 alt_div_mask
|= E4210_DIV0_ATB_MASK
;
251 exynos_set_safe_div(cpuclk
, alt_div
, alt_div_mask
);
255 /* select sclk_mpll as the alternate parent */
256 mux_reg
= readl(base
+ regs
->mux_sel
);
257 writel(mux_reg
| (1 << 16), base
+ regs
->mux_sel
);
258 wait_until_mux_stable(base
+ regs
->mux_stat
, 16, MUX_MASK
, 2);
260 /* alternate parent is active now. set the dividers */
261 writel(div0
, base
+ regs
->div_cpu0
);
262 wait_until_divider_stable(base
+ regs
->div_stat_cpu0
, DIV_MASK_ALL
);
264 if (cpuclk
->flags
& CLK_CPU_HAS_DIV1
) {
265 writel(div1
, base
+ regs
->div_cpu1
);
266 wait_until_divider_stable(base
+ regs
->div_stat_cpu1
,
270 spin_unlock_irqrestore(cpuclk
->lock
, flags
);
274 /* handler for post-rate change notification from parent clock */
275 static int exynos_cpuclk_post_rate_change(struct clk_notifier_data
*ndata
,
276 struct exynos_cpuclk
*cpuclk
)
278 const struct exynos_cpuclk_cfg_data
*cfg_data
= cpuclk
->cfg
;
279 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
280 void __iomem
*base
= cpuclk
->base
;
281 unsigned long div
= 0, div_mask
= DIV_MASK
;
282 unsigned long mux_reg
;
285 /* find out the divider values to use for clock data */
286 if (cpuclk
->flags
& CLK_CPU_NEEDS_DEBUG_ALT_DIV
) {
287 while ((cfg_data
->prate
* 1000) != ndata
->new_rate
) {
288 if (cfg_data
->prate
== 0)
294 spin_lock_irqsave(cpuclk
->lock
, flags
);
296 /* select mout_apll as the alternate parent */
297 mux_reg
= readl(base
+ regs
->mux_sel
);
298 writel(mux_reg
& ~(1 << 16), base
+ regs
->mux_sel
);
299 wait_until_mux_stable(base
+ regs
->mux_stat
, 16, MUX_MASK
, 1);
301 if (cpuclk
->flags
& CLK_CPU_NEEDS_DEBUG_ALT_DIV
) {
302 div
|= (cfg_data
->div0
& E4210_DIV0_ATB_MASK
);
303 div_mask
|= E4210_DIV0_ATB_MASK
;
306 exynos_set_safe_div(cpuclk
, div
, div_mask
);
307 spin_unlock_irqrestore(cpuclk
->lock
, flags
);
311 /* ---- Exynos5433 ---------------------------------------------------------- */
313 static const struct exynos_cpuclk_regs e5433_cpuclk_regs
= {
318 .div_stat_cpu0
= 0x700,
319 .div_stat_cpu1
= 0x704,
322 /* handler for pre-rate change notification from parent clock */
323 static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data
*ndata
,
324 struct exynos_cpuclk
*cpuclk
)
326 const struct exynos_cpuclk_cfg_data
*cfg_data
= cpuclk
->cfg
;
327 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
328 void __iomem
*base
= cpuclk
->base
;
329 unsigned long alt_prate
= clk_hw_get_rate(cpuclk
->alt_parent
);
330 unsigned long div0
, div1
= 0, mux_reg
;
333 /* find out the divider values to use for clock data */
334 while ((cfg_data
->prate
* 1000) != ndata
->new_rate
) {
335 if (cfg_data
->prate
== 0)
340 spin_lock_irqsave(cpuclk
->lock
, flags
);
343 * For the selected PLL clock frequency, get the pre-defined divider
346 div0
= cfg_data
->div0
;
347 div1
= cfg_data
->div1
;
350 * If the old parent clock speed is less than the clock speed of
351 * the alternate parent, then it should be ensured that at no point
352 * the armclk speed is more than the old_prate until the dividers are
353 * set. Also workaround the issue of the dividers being set to lower
354 * values before the parent clock speed is set to new lower speed
355 * (this can result in too high speed of armclk output clocks).
357 if (alt_prate
> ndata
->old_rate
|| ndata
->old_rate
> ndata
->new_rate
) {
358 unsigned long tmp_rate
= min(ndata
->old_rate
, ndata
->new_rate
);
359 unsigned long alt_div
, alt_div_mask
= DIV_MASK
;
361 alt_div
= DIV_ROUND_UP(alt_prate
, tmp_rate
) - 1;
362 WARN_ON(alt_div
>= MAX_DIV
);
364 exynos_set_safe_div(cpuclk
, alt_div
, alt_div_mask
);
368 /* select the alternate parent */
369 mux_reg
= readl(base
+ regs
->mux_sel
);
370 writel(mux_reg
| 1, base
+ regs
->mux_sel
);
371 wait_until_mux_stable(base
+ regs
->mux_stat
, 0, MUX_MASK
, 2);
373 /* alternate parent is active now. set the dividers */
374 writel(div0
, base
+ regs
->div_cpu0
);
375 wait_until_divider_stable(base
+ regs
->div_stat_cpu0
, DIV_MASK_ALL
);
377 writel(div1
, base
+ regs
->div_cpu1
);
378 wait_until_divider_stable(base
+ regs
->div_stat_cpu1
, DIV_MASK_ALL
);
380 spin_unlock_irqrestore(cpuclk
->lock
, flags
);
384 /* handler for post-rate change notification from parent clock */
385 static int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data
*ndata
,
386 struct exynos_cpuclk
*cpuclk
)
388 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
389 void __iomem
*base
= cpuclk
->base
;
390 unsigned long div
= 0, div_mask
= DIV_MASK
;
391 unsigned long mux_reg
;
394 spin_lock_irqsave(cpuclk
->lock
, flags
);
396 /* select apll as the alternate parent */
397 mux_reg
= readl(base
+ regs
->mux_sel
);
398 writel(mux_reg
& ~1, base
+ regs
->mux_sel
);
399 wait_until_mux_stable(base
+ regs
->mux_stat
, 0, MUX_MASK
, 1);
401 exynos_set_safe_div(cpuclk
, div
, div_mask
);
402 spin_unlock_irqrestore(cpuclk
->lock
, flags
);
406 /* ---- Exynos850 ----------------------------------------------------------- */
408 #define E850_DIV_RATIO_MASK GENMASK(3, 0)
409 #define E850_BUSY_MASK BIT(16)
411 /* Max time for divider or mux to stabilize, usec */
412 #define E850_DIV_MUX_STAB_TIME 100
413 /* OSCCLK clock rate, Hz */
414 #define E850_OSCCLK (26 * MHZ)
416 static const struct exynos_cpuclk_regs e850cl0_cpuclk_regs
= {
418 .divs
= { 0x1800, 0x1808, 0x180c, 0x1810 },
421 static const struct exynos_cpuclk_regs e850cl1_cpuclk_regs
= {
423 .divs
= { 0x1800, 0x1808, 0x180c, 0x1810 },
427 * Set alternate parent rate to "rate" value or less.
429 * rate: Desired alt_parent rate, or 0 for max alt_parent rate
431 * Exynos850 doesn't have CPU clock divider in CMU_CPUCLx block (CMUREF divider
432 * doesn't affect CPU speed). So CPUCLx_SWITCH divider from CMU_TOP is used
433 * instead to adjust alternate parent speed.
435 * It's possible to use clk_set_max_rate() instead of this function, but it
436 * would set overly pessimistic rate values to alternate parent.
438 static int exynos850_alt_parent_set_max_rate(const struct clk_hw
*alt_parent
,
441 struct clk_hw
*clk_div
, *clk_divp
;
442 unsigned long divp_rate
, div_rate
, div
;
445 /* Divider from CMU_TOP */
446 clk_div
= clk_hw_get_parent(alt_parent
);
449 /* Divider's parent from CMU_TOP */
450 clk_divp
= clk_hw_get_parent(clk_div
);
453 /* Divider input rate */
454 divp_rate
= clk_hw_get_rate(clk_divp
);
458 /* Calculate new alt_parent rate for integer divider value */
462 div
= DIV_ROUND_UP(divp_rate
, rate
);
463 div_rate
= DIV_ROUND_UP(divp_rate
, div
);
464 WARN_ON(div
>= MAX_DIV
);
466 /* alt_parent will propagate this change up to the divider */
467 ret
= clk_set_rate(alt_parent
->clk
, div_rate
);
470 udelay(E850_DIV_MUX_STAB_TIME
);
475 /* Handler for pre-rate change notification from parent clock */
476 static int exynos850_cpuclk_pre_rate_change(struct clk_notifier_data
*ndata
,
477 struct exynos_cpuclk
*cpuclk
)
479 const unsigned int shifts
[4] = { 16, 12, 8, 4 }; /* E850_CPU_DIV0() */
480 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
481 const struct exynos_cpuclk_cfg_data
*cfg_data
= cpuclk
->cfg
;
482 const struct clk_hw
*alt_parent
= cpuclk
->alt_parent
;
483 void __iomem
*base
= cpuclk
->base
;
484 unsigned long alt_prate
= clk_hw_get_rate(alt_parent
);
490 /* No actions are needed when switching to or from OSCCLK parent */
491 if (ndata
->new_rate
== E850_OSCCLK
|| ndata
->old_rate
== E850_OSCCLK
)
494 /* Find out the divider values to use for clock data */
495 while ((cfg_data
->prate
* 1000) != ndata
->new_rate
) {
496 if (cfg_data
->prate
== 0)
502 * If the old parent clock speed is less than the clock speed of
503 * the alternate parent, then it should be ensured that at no point
504 * the armclk speed is more than the old_prate until the dividers are
505 * set. Also workaround the issue of the dividers being set to lower
506 * values before the parent clock speed is set to new lower speed
507 * (this can result in too high speed of armclk output clocks).
509 if (alt_prate
> ndata
->old_rate
|| ndata
->old_rate
> ndata
->new_rate
) {
510 unsigned long tmp_rate
= min(ndata
->old_rate
, ndata
->new_rate
);
512 ret
= exynos850_alt_parent_set_max_rate(alt_parent
, tmp_rate
);
517 spin_lock_irqsave(cpuclk
->lock
, flags
);
519 /* Select the alternate parent */
520 mux_reg
= readl(base
+ regs
->mux
);
521 writel(mux_reg
| 1, base
+ regs
->mux
);
522 wait_until_mux_stable(base
+ regs
->mux
, 16, 1, 0);
524 /* Alternate parent is active now. Set the dividers */
525 for (i
= 0; i
< ARRAY_SIZE(shifts
); ++i
) {
526 unsigned long div
= (cfg_data
->div0
>> shifts
[i
]) & 0xf;
529 val
= readl(base
+ regs
->divs
[i
]);
530 val
= (val
& ~E850_DIV_RATIO_MASK
) | div
;
531 writel(val
, base
+ regs
->divs
[i
]);
532 wait_until_divider_stable(base
+ regs
->divs
[i
], E850_BUSY_MASK
);
535 spin_unlock_irqrestore(cpuclk
->lock
, flags
);
540 /* Handler for post-rate change notification from parent clock */
541 static int exynos850_cpuclk_post_rate_change(struct clk_notifier_data
*ndata
,
542 struct exynos_cpuclk
*cpuclk
)
544 const struct exynos_cpuclk_regs
* const regs
= cpuclk
->chip
->regs
;
545 const struct clk_hw
*alt_parent
= cpuclk
->alt_parent
;
546 void __iomem
*base
= cpuclk
->base
;
550 /* No actions are needed when switching to or from OSCCLK parent */
551 if (ndata
->new_rate
== E850_OSCCLK
|| ndata
->old_rate
== E850_OSCCLK
)
554 spin_lock_irqsave(cpuclk
->lock
, flags
);
556 /* Select main parent (PLL) for mux */
557 mux_reg
= readl(base
+ regs
->mux
);
558 writel(mux_reg
& ~1, base
+ regs
->mux
);
559 wait_until_mux_stable(base
+ regs
->mux
, 16, 1, 0);
561 spin_unlock_irqrestore(cpuclk
->lock
, flags
);
563 /* Set alt_parent rate back to max */
564 return exynos850_alt_parent_set_max_rate(alt_parent
, 0);
567 /* -------------------------------------------------------------------------- */
569 /* Common round rate callback usable for all types of CPU clocks */
570 static long exynos_cpuclk_round_rate(struct clk_hw
*hw
, unsigned long drate
,
571 unsigned long *prate
)
573 struct clk_hw
*parent
= clk_hw_get_parent(hw
);
574 *prate
= clk_hw_round_rate(parent
, drate
);
578 /* Common recalc rate callback usable for all types of CPU clocks */
579 static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw
*hw
,
580 unsigned long parent_rate
)
583 * The CPU clock output (armclk) rate is the same as its parent
584 * rate. Although there exist certain dividers inside the CPU
585 * clock block that could be used to divide the parent clock,
586 * the driver does not make use of them currently, except during
587 * frequency transitions.
592 static const struct clk_ops exynos_cpuclk_clk_ops
= {
593 .recalc_rate
= exynos_cpuclk_recalc_rate
,
594 .round_rate
= exynos_cpuclk_round_rate
,
598 * This notifier function is called for the pre-rate and post-rate change
599 * notifications of the parent clock of cpuclk.
601 static int exynos_cpuclk_notifier_cb(struct notifier_block
*nb
,
602 unsigned long event
, void *data
)
604 struct clk_notifier_data
*ndata
= data
;
605 struct exynos_cpuclk
*cpuclk
;
608 cpuclk
= container_of(nb
, struct exynos_cpuclk
, clk_nb
);
610 if (event
== PRE_RATE_CHANGE
)
611 err
= cpuclk
->chip
->pre_rate_cb(ndata
, cpuclk
);
612 else if (event
== POST_RATE_CHANGE
)
613 err
= cpuclk
->chip
->post_rate_cb(ndata
, cpuclk
);
615 return notifier_from_errno(err
);
618 static const struct exynos_cpuclk_chip exynos_clkcpu_chips
[] = {
619 [CPUCLK_LAYOUT_E4210
] = {
620 .regs
= &e4210_cpuclk_regs
,
621 .pre_rate_cb
= exynos_cpuclk_pre_rate_change
,
622 .post_rate_cb
= exynos_cpuclk_post_rate_change
,
624 [CPUCLK_LAYOUT_E5433
] = {
625 .regs
= &e5433_cpuclk_regs
,
626 .pre_rate_cb
= exynos5433_cpuclk_pre_rate_change
,
627 .post_rate_cb
= exynos5433_cpuclk_post_rate_change
,
629 [CPUCLK_LAYOUT_E850_CL0
] = {
630 .regs
= &e850cl0_cpuclk_regs
,
631 .pre_rate_cb
= exynos850_cpuclk_pre_rate_change
,
632 .post_rate_cb
= exynos850_cpuclk_post_rate_change
,
634 [CPUCLK_LAYOUT_E850_CL1
] = {
635 .regs
= &e850cl1_cpuclk_regs
,
636 .pre_rate_cb
= exynos850_cpuclk_pre_rate_change
,
637 .post_rate_cb
= exynos850_cpuclk_post_rate_change
,
641 /* helper function to register a CPU clock */
642 static int __init
exynos_register_cpu_clock(struct samsung_clk_provider
*ctx
,
643 const struct samsung_cpu_clock
*clk_data
)
645 const struct clk_hw
*parent
, *alt_parent
;
647 struct exynos_cpuclk
*cpuclk
;
648 struct clk_init_data init
;
649 const char *parent_name
;
650 unsigned int num_cfgs
;
653 hws
= ctx
->clk_data
.hws
;
654 parent
= hws
[clk_data
->parent_id
];
655 alt_parent
= hws
[clk_data
->alt_parent_id
];
656 if (IS_ERR(parent
) || IS_ERR(alt_parent
)) {
657 pr_err("%s: invalid parent clock(s)\n", __func__
);
661 cpuclk
= kzalloc(sizeof(*cpuclk
), GFP_KERNEL
);
665 parent_name
= clk_hw_get_name(parent
);
667 init
.name
= clk_data
->name
;
668 init
.flags
= CLK_SET_RATE_PARENT
;
669 init
.parent_names
= &parent_name
;
670 init
.num_parents
= 1;
671 init
.ops
= &exynos_cpuclk_clk_ops
;
673 cpuclk
->alt_parent
= alt_parent
;
674 cpuclk
->hw
.init
= &init
;
675 cpuclk
->base
= ctx
->reg_base
+ clk_data
->offset
;
676 cpuclk
->lock
= &ctx
->lock
;
677 cpuclk
->flags
= clk_data
->flags
;
678 cpuclk
->clk_nb
.notifier_call
= exynos_cpuclk_notifier_cb
;
679 cpuclk
->chip
= &exynos_clkcpu_chips
[clk_data
->reg_layout
];
681 ret
= clk_notifier_register(parent
->clk
, &cpuclk
->clk_nb
);
683 pr_err("%s: failed to register clock notifier for %s\n",
684 __func__
, clk_data
->name
);
688 /* Find count of configuration rates in cfg */
689 for (num_cfgs
= 0; clk_data
->cfg
[num_cfgs
].prate
!= 0; )
692 cpuclk
->cfg
= kmemdup_array(clk_data
->cfg
, num_cfgs
, sizeof(*cpuclk
->cfg
),
696 goto unregister_clk_nb
;
699 ret
= clk_hw_register(NULL
, &cpuclk
->hw
);
701 pr_err("%s: could not register cpuclk %s\n", __func__
,
703 goto free_cpuclk_data
;
706 samsung_clk_add_lookup(ctx
, &cpuclk
->hw
, clk_data
->id
);
712 clk_notifier_unregister(parent
->clk
, &cpuclk
->clk_nb
);
718 void __init
samsung_clk_register_cpu(struct samsung_clk_provider
*ctx
,
719 const struct samsung_cpu_clock
*list
, unsigned int nr_clk
)
723 for (idx
= 0; idx
< nr_clk
; idx
++)
724 exynos_register_cpu_clock(ctx
, &list
[idx
]);