File Index
Files and directories
Sign in
FILE

clk-cpu.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/samsung/clk-cpu.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
  4 * Author: Thomas Abraham <thomas.ab@samsung.com>
  5 *
  6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
  7 * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
  8 *
  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.
 17 *
 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
 23 * clocks.
 24 *
 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.
 30 */
 31
 32#include <linux/delay.h>
 33#include <linux/errno.h>
 34#include <linux/io.h>
 35#include <linux/slab.h>
 36#include <linux/clk.h>
 37#include <linux/clk-provider.h>
 38
 39#include "clk.h"
 40#include "clk-cpu.h"
 41
 42struct exynos_cpuclk;
 43
 44typedef int (*exynos_rate_change_fn_t)(struct clk_notifier_data *ndata,
 45				       struct exynos_cpuclk *cpuclk);
 46
 47/**
 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)
 57 */
 58struct exynos_cpuclk_regs {
 59	u32 mux_sel;
 60	u32 mux_stat;
 61	u32 div_cpu0;
 62	u32 div_cpu1;
 63	u32 div_stat_cpu0;
 64	u32 div_stat_cpu1;
 65
 66	u32 mux;
 67	u32 divs[4];
 68};
 69
 70/**
 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
 75 */
 76struct 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;
 80};
 81
 82/**
 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
 95 *
 96 * This structure holds information required for programming the CPU clock for
 97 * various clock speeds.
 98 */
 99struct exynos_cpuclk {
100	struct clk_hw				hw;
101	const struct clk_hw			*alt_parent;
102	void __iomem				*base;
103	spinlock_t				*lock;
104	const struct exynos_cpuclk_cfg_data	*cfg;
105	const unsigned long			num_cfgs;
106	struct notifier_block			clk_nb;
107	unsigned long				flags;
108	const struct exynos_cpuclk_chip		*chip;
109};
110
111/* ---- Common code --------------------------------------------------------- */
112
113/* Divider stabilization time, msec */
114#define MAX_STAB_TIME		10
115#define MAX_DIV			8
116#define DIV_MASK		GENMASK(2, 0)
117#define DIV_MASK_ALL		GENMASK(31, 0)
118#define MUX_MASK		GENMASK(2, 0)
119
120/*
121 * Helper function to wait until divider(s) have stabilized after the divider
122 * value has changed.
123 */
124static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask)
125{
126	unsigned long timeout = jiffies + msecs_to_jiffies(MAX_STAB_TIME);
127
128	do {
129		if (!(readl(div_reg) & mask))
130			return;
131	} while (time_before(jiffies, timeout));
132
133	if (!(readl(div_reg) & mask))
134		return;
135
136	pr_err("%s: timeout in divider stabilization\n", __func__);
137}
138
139/*
140 * Helper function to wait until mux has stabilized after the mux selection
141 * value was changed.
142 */
143static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos,
144				  unsigned long mask, unsigned long mux_value)
145{
146	unsigned long timeout = jiffies + msecs_to_jiffies(MAX_STAB_TIME);
147
148	do {
149		if (((readl(mux_reg) >> mux_pos) & mask) == mux_value)
150			return;
151	} while (time_before(jiffies, timeout));
152
153	if (((readl(mux_reg) >> mux_pos) & mask) == mux_value)
154		return;
155
156	pr_err("%s: re-parenting mux timed-out\n", __func__);
157}
158
159/*
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.
163 */
164static void exynos_set_safe_div(struct exynos_cpuclk *cpuclk, unsigned long div,
165				unsigned long mask)
166{
167	const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
168	void __iomem *base = cpuclk->base;
169	unsigned long div0;
170
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);
175}
176
177/* ---- Exynos 3/4/5 -------------------------------------------------------- */
178
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)
185
186static const struct exynos_cpuclk_regs e4210_cpuclk_regs = {
187	.mux_sel	= 0x200,
188	.mux_stat	= 0x400,
189	.div_cpu0	= 0x500,
190	.div_cpu1	= 0x504,
191	.div_stat_cpu0	= 0x600,
192	.div_stat_cpu1	= 0x604,
193};
194
195/* handler for pre-rate change notification from parent clock */
196static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
197					 struct exynos_cpuclk *cpuclk)
198{
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;
204	unsigned long flags;
205
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)
209			return -EINVAL;
210		cfg_data++;
211	}
212
213	spin_lock_irqsave(cpuclk->lock, flags);
214
215	/*
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.
219	 */
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);
226	}
227
228	/*
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).
235	 */
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;
239
240		alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
241		WARN_ON(alt_div >= MAX_DIV);
242
243		if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
244			/*
245			 * In Exynos4210, ATB clock parent is also mout_core. So
246			 * ATB clock also needs to be maintained at safe speed.
247			 */
248			alt_div |= E4210_DIV0_ATB_MASK;
249			alt_div_mask |= E4210_DIV0_ATB_MASK;
250		}
251		exynos_set_safe_div(cpuclk, alt_div, alt_div_mask);
252		div0 |= alt_div;
253	}
254
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);
259
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);
263
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,
267					  DIV_MASK_ALL);
268	}
269
270	spin_unlock_irqrestore(cpuclk->lock, flags);
271	return 0;
272}
273
274/* handler for post-rate change notification from parent clock */
275static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
276					  struct exynos_cpuclk *cpuclk)
277{
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;
283	unsigned long flags;
284
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)
289				return -EINVAL;
290			cfg_data++;
291		}
292	}
293
294	spin_lock_irqsave(cpuclk->lock, flags);
295
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);
300
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;
304	}
305
306	exynos_set_safe_div(cpuclk, div, div_mask);
307	spin_unlock_irqrestore(cpuclk->lock, flags);
308	return 0;
309}
310
311/* ---- Exynos5433 ---------------------------------------------------------- */
312
313static const struct exynos_cpuclk_regs e5433_cpuclk_regs = {
314	.mux_sel	= 0x208,
315	.mux_stat	= 0x408,
316	.div_cpu0	= 0x600,
317	.div_cpu1	= 0x604,
318	.div_stat_cpu0	= 0x700,
319	.div_stat_cpu1	= 0x704,
320};
321
322/* handler for pre-rate change notification from parent clock */
323static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
324					     struct exynos_cpuclk *cpuclk)
325{
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;
331	unsigned long flags;
332
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)
336			return -EINVAL;
337		cfg_data++;
338	}
339
340	spin_lock_irqsave(cpuclk->lock, flags);
341
342	/*
343	 * For the selected PLL clock frequency, get the pre-defined divider
344	 * values.
345	 */
346	div0 = cfg_data->div0;
347	div1 = cfg_data->div1;
348
349	/*
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).
356	 */
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;
360
361		alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
362		WARN_ON(alt_div >= MAX_DIV);
363
364		exynos_set_safe_div(cpuclk, alt_div, alt_div_mask);
365		div0 |= alt_div;
366	}
367
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);
372
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);
376
377	writel(div1, base + regs->div_cpu1);
378	wait_until_divider_stable(base + regs->div_stat_cpu1, DIV_MASK_ALL);
379
380	spin_unlock_irqrestore(cpuclk->lock, flags);
381	return 0;
382}
383
384/* handler for post-rate change notification from parent clock */
385static int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
386					      struct exynos_cpuclk *cpuclk)
387{
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;
392	unsigned long flags;
393
394	spin_lock_irqsave(cpuclk->lock, flags);
395
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);
400
401	exynos_set_safe_div(cpuclk, div, div_mask);
402	spin_unlock_irqrestore(cpuclk->lock, flags);
403	return 0;
404}
405
406/* ---- Exynos850 ----------------------------------------------------------- */
407
408#define E850_DIV_RATIO_MASK	GENMASK(3, 0)
409#define E850_BUSY_MASK		BIT(16)
410
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)
415
416static const struct exynos_cpuclk_regs e850cl0_cpuclk_regs = {
417	.mux	= 0x100c,
418	.divs	= { 0x1800, 0x1808, 0x180c, 0x1810 },
419};
420
421static const struct exynos_cpuclk_regs e850cl1_cpuclk_regs = {
422	.mux	= 0x1000,
423	.divs	= { 0x1800, 0x1808, 0x180c, 0x1810 },
424};
425
426/*
427 * Set alternate parent rate to "rate" value or less.
428 *
429 * rate: Desired alt_parent rate, or 0 for max alt_parent rate
430 *
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.
434 *
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.
437 */
438static int exynos850_alt_parent_set_max_rate(const struct clk_hw *alt_parent,
439					     unsigned long rate)
440{
441	struct clk_hw *clk_div, *clk_divp;
442	unsigned long divp_rate, div_rate, div;
443	int ret;
444
445	/* Divider from CMU_TOP */
446	clk_div = clk_hw_get_parent(alt_parent);
447	if (!clk_div)
448		return -ENOENT;
449	/* Divider's parent from CMU_TOP */
450	clk_divp = clk_hw_get_parent(clk_div);
451	if (!clk_divp)
452		return -ENOENT;
453	/* Divider input rate */
454	divp_rate = clk_hw_get_rate(clk_divp);
455	if (!divp_rate)
456		return -EINVAL;
457
458	/* Calculate new alt_parent rate for integer divider value */
459	if (rate == 0)
460		div = 1;
461	else
462		div = DIV_ROUND_UP(divp_rate, rate);
463	div_rate = DIV_ROUND_UP(divp_rate, div);
464	WARN_ON(div >= MAX_DIV);
465
466	/* alt_parent will propagate this change up to the divider */
467	ret = clk_set_rate(alt_parent->clk, div_rate);
468	if (ret)
469		return ret;
470	udelay(E850_DIV_MUX_STAB_TIME);
471
472	return 0;
473}
474
475/* Handler for pre-rate change notification from parent clock */
476static int exynos850_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
477					    struct exynos_cpuclk *cpuclk)
478{
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);
485	unsigned long flags;
486	u32 mux_reg;
487	size_t i;
488	int ret;
489
490	/* No actions are needed when switching to or from OSCCLK parent */
491	if (ndata->new_rate == E850_OSCCLK || ndata->old_rate == E850_OSCCLK)
492		return 0;
493
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)
497			return -EINVAL;
498		cfg_data++;
499	}
500
501	/*
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).
508	 */
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);
511
512		ret = exynos850_alt_parent_set_max_rate(alt_parent, tmp_rate);
513		if (ret)
514			return ret;
515	}
516
517	spin_lock_irqsave(cpuclk->lock, flags);
518
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);
523
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;
527		u32 val;
528
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);
533	}
534
535	spin_unlock_irqrestore(cpuclk->lock, flags);
536
537	return 0;
538}
539
540/* Handler for post-rate change notification from parent clock */
541static int exynos850_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
542					     struct exynos_cpuclk *cpuclk)
543{
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;
547	unsigned long flags;
548	u32 mux_reg;
549
550	/* No actions are needed when switching to or from OSCCLK parent */
551	if (ndata->new_rate == E850_OSCCLK || ndata->old_rate == E850_OSCCLK)
552		return 0;
553
554	spin_lock_irqsave(cpuclk->lock, flags);
555
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);
560
561	spin_unlock_irqrestore(cpuclk->lock, flags);
562
563	/* Set alt_parent rate back to max */
564	return exynos850_alt_parent_set_max_rate(alt_parent, 0);
565}
566
567/* -------------------------------------------------------------------------- */
568
569/* Common round rate callback usable for all types of CPU clocks */
570static int exynos_cpuclk_determine_rate(struct clk_hw *hw,
571					struct clk_rate_request *req)
572{
573	struct clk_hw *parent = clk_hw_get_parent(hw);
574	req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
575	req->rate = req->best_parent_rate;
576
577	return 0;
578}
579
580/* Common recalc rate callback usable for all types of CPU clocks */
581static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw,
582					       unsigned long parent_rate)
583{
584	/*
585	 * The CPU clock output (armclk) rate is the same as its parent
586	 * rate. Although there exist certain dividers inside the CPU
587	 * clock block that could be used to divide the parent clock,
588	 * the driver does not make use of them currently, except during
589	 * frequency transitions.
590	 */
591	return parent_rate;
592}
593
594static const struct clk_ops exynos_cpuclk_clk_ops = {
595	.recalc_rate = exynos_cpuclk_recalc_rate,
596	.determine_rate = exynos_cpuclk_determine_rate,
597};
598
599/*
600 * This notifier function is called for the pre-rate and post-rate change
601 * notifications of the parent clock of cpuclk.
602 */
603static int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
604				     unsigned long event, void *data)
605{
606	struct clk_notifier_data *ndata = data;
607	struct exynos_cpuclk *cpuclk;
608	int err = 0;
609
610	cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
611
612	if (event == PRE_RATE_CHANGE)
613		err = cpuclk->chip->pre_rate_cb(ndata, cpuclk);
614	else if (event == POST_RATE_CHANGE)
615		err = cpuclk->chip->post_rate_cb(ndata, cpuclk);
616
617	return notifier_from_errno(err);
618}
619
620static const struct exynos_cpuclk_chip exynos_clkcpu_chips[] = {
621	[CPUCLK_LAYOUT_E4210] = {
622		.regs		= &e4210_cpuclk_regs,
623		.pre_rate_cb	= exynos_cpuclk_pre_rate_change,
624		.post_rate_cb	= exynos_cpuclk_post_rate_change,
625	},
626	[CPUCLK_LAYOUT_E5433] = {
627		.regs		= &e5433_cpuclk_regs,
628		.pre_rate_cb	= exynos5433_cpuclk_pre_rate_change,
629		.post_rate_cb	= exynos5433_cpuclk_post_rate_change,
630	},
631	[CPUCLK_LAYOUT_E850_CL0] = {
632		.regs		= &e850cl0_cpuclk_regs,
633		.pre_rate_cb	= exynos850_cpuclk_pre_rate_change,
634		.post_rate_cb	= exynos850_cpuclk_post_rate_change,
635	},
636	[CPUCLK_LAYOUT_E850_CL1] = {
637		.regs		= &e850cl1_cpuclk_regs,
638		.pre_rate_cb	= exynos850_cpuclk_pre_rate_change,
639		.post_rate_cb	= exynos850_cpuclk_post_rate_change,
640	},
641};
642
643/* helper function to register a CPU clock */
644static int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
645				const struct samsung_cpu_clock *clk_data)
646{
647	const struct clk_hw *parent, *alt_parent;
648	struct clk_hw **hws;
649	struct exynos_cpuclk *cpuclk;
650	struct clk_init_data init;
651	const char *parent_name;
652	unsigned int num_cfgs;
653	int ret = 0;
654
655	hws = ctx->clk_data.hws;
656	parent = hws[clk_data->parent_id];
657	alt_parent = hws[clk_data->alt_parent_id];
658	if (IS_ERR(parent) || IS_ERR(alt_parent)) {
659		pr_err("%s: invalid parent clock(s)\n", __func__);
660		return -EINVAL;
661	}
662
663	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
664	if (!cpuclk)
665		return -ENOMEM;
666
667	parent_name = clk_hw_get_name(parent);
668
669	init.name = clk_data->name;
670	init.flags = CLK_SET_RATE_PARENT;
671	init.parent_names = &parent_name;
672	init.num_parents = 1;
673	init.ops = &exynos_cpuclk_clk_ops;
674
675	cpuclk->alt_parent = alt_parent;
676	cpuclk->hw.init = &init;
677	cpuclk->base = ctx->reg_base + clk_data->offset;
678	cpuclk->lock = &ctx->lock;
679	cpuclk->flags = clk_data->flags;
680	cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
681	cpuclk->chip = &exynos_clkcpu_chips[clk_data->reg_layout];
682
683	ret = clk_notifier_register(parent->clk, &cpuclk->clk_nb);
684	if (ret) {
685		pr_err("%s: failed to register clock notifier for %s\n",
686		       __func__, clk_data->name);
687		goto free_cpuclk;
688	}
689
690	/* Find count of configuration rates in cfg */
691	for (num_cfgs = 0; clk_data->cfg[num_cfgs].prate != 0; )
692		num_cfgs++;
693
694	cpuclk->cfg = kmemdup_array(clk_data->cfg, num_cfgs, sizeof(*cpuclk->cfg),
695				    GFP_KERNEL);
696	if (!cpuclk->cfg) {
697		ret = -ENOMEM;
698		goto unregister_clk_nb;
699	}
700
701	ret = clk_hw_register(NULL, &cpuclk->hw);
702	if (ret) {
703		pr_err("%s: could not register cpuclk %s\n", __func__,
704		       clk_data->name);
705		goto free_cpuclk_data;
706	}
707
708	samsung_clk_add_lookup(ctx, &cpuclk->hw, clk_data->id);
709	return 0;
710
711free_cpuclk_data:
712	kfree(cpuclk->cfg);
713unregister_clk_nb:
714	clk_notifier_unregister(parent->clk, &cpuclk->clk_nb);
715free_cpuclk:
716	kfree(cpuclk);
717	return ret;
718}
719
720void __init samsung_clk_register_cpu(struct samsung_clk_provider *ctx,
721		const struct samsung_cpu_clock *list, unsigned int nr_clk)
722{
723	unsigned int idx;
724
725	for (idx = 0; idx < nr_clk; idx++)
726		exynos_register_cpu_clock(ctx, &list[idx]);
727}
~/ Read-only file preview.