18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci * Author: Thomas Abraham <thomas.ab@samsung.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2015 Samsung Electronics Co., Ltd.
78c2ecf20Sopenharmony_ci * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file contains the utility function to register CPU clock for Samsung
108c2ecf20Sopenharmony_ci * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a
118c2ecf20Sopenharmony_ci * group of CPUs. The CPU clock is typically derived from a hierarchy of clock
128c2ecf20Sopenharmony_ci * blocks which includes mux and divider blocks. There are a number of other
138c2ecf20Sopenharmony_ci * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
148c2ecf20Sopenharmony_ci * clock for CPU domain. The rates of these auxiliary clocks are related to the
158c2ecf20Sopenharmony_ci * CPU clock rate and this relation is usually specified in the hardware manual
168c2ecf20Sopenharmony_ci * of the SoC or supplied after the SoC characterization.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * The below implementation of the CPU clock allows the rate changes of the CPU
198c2ecf20Sopenharmony_ci * clock and the corresponding rate changes of the auxillary clocks of the CPU
208c2ecf20Sopenharmony_ci * domain. The platform clock driver provides a clock register configuration
218c2ecf20Sopenharmony_ci * for each configurable rate which is then used to program the clock hardware
228c2ecf20Sopenharmony_ci * registers to acheive a fast co-oridinated rate change for all the CPU domain
238c2ecf20Sopenharmony_ci * clocks.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * On a rate change request for the CPU clock, the rate change is propagated
268c2ecf20Sopenharmony_ci * upto the PLL supplying the clock to the CPU domain clock blocks. While the
278c2ecf20Sopenharmony_ci * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
288c2ecf20Sopenharmony_ci * alternate clock source. If required, the alternate clock source is divided
298c2ecf20Sopenharmony_ci * down in order to keep the output clock rate within the previous OPP limits.
308c2ecf20Sopenharmony_ci*/
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/errno.h>
338c2ecf20Sopenharmony_ci#include <linux/io.h>
348c2ecf20Sopenharmony_ci#include <linux/slab.h>
358c2ecf20Sopenharmony_ci#include <linux/clk.h>
368c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
378c2ecf20Sopenharmony_ci#include "clk-cpu.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define E4210_SRC_CPU		0x0
408c2ecf20Sopenharmony_ci#define E4210_STAT_CPU		0x200
418c2ecf20Sopenharmony_ci#define E4210_DIV_CPU0		0x300
428c2ecf20Sopenharmony_ci#define E4210_DIV_CPU1		0x304
438c2ecf20Sopenharmony_ci#define E4210_DIV_STAT_CPU0	0x400
448c2ecf20Sopenharmony_ci#define E4210_DIV_STAT_CPU1	0x404
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define E5433_MUX_SEL2		0x008
478c2ecf20Sopenharmony_ci#define E5433_MUX_STAT2		0x208
488c2ecf20Sopenharmony_ci#define E5433_DIV_CPU0		0x400
498c2ecf20Sopenharmony_ci#define E5433_DIV_CPU1		0x404
508c2ecf20Sopenharmony_ci#define E5433_DIV_STAT_CPU0	0x500
518c2ecf20Sopenharmony_ci#define E5433_DIV_STAT_CPU1	0x504
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define E4210_DIV0_RATIO0_MASK	0x7
548c2ecf20Sopenharmony_ci#define E4210_DIV1_HPM_MASK	(0x7 << 4)
558c2ecf20Sopenharmony_ci#define E4210_DIV1_COPY_MASK	(0x7 << 0)
568c2ecf20Sopenharmony_ci#define E4210_MUX_HPM_MASK	(1 << 20)
578c2ecf20Sopenharmony_ci#define E4210_DIV0_ATB_SHIFT	16
588c2ecf20Sopenharmony_ci#define E4210_DIV0_ATB_MASK	(DIV_MASK << E4210_DIV0_ATB_SHIFT)
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define MAX_DIV			8
618c2ecf20Sopenharmony_ci#define DIV_MASK		7
628c2ecf20Sopenharmony_ci#define DIV_MASK_ALL		0xffffffff
638c2ecf20Sopenharmony_ci#define MUX_MASK		7
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * Helper function to wait until divider(s) have stabilized after the divider
678c2ecf20Sopenharmony_ci * value has changed.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(10);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	do {
748c2ecf20Sopenharmony_ci		if (!(readl(div_reg) & mask))
758c2ecf20Sopenharmony_ci			return;
768c2ecf20Sopenharmony_ci	} while (time_before(jiffies, timeout));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (!(readl(div_reg) & mask))
798c2ecf20Sopenharmony_ci		return;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	pr_err("%s: timeout in divider stablization\n", __func__);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * Helper function to wait until mux has stabilized after the mux selection
868c2ecf20Sopenharmony_ci * value was changed.
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_cistatic void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos,
898c2ecf20Sopenharmony_ci					unsigned long mux_value)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(10);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	do {
948c2ecf20Sopenharmony_ci		if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
958c2ecf20Sopenharmony_ci			return;
968c2ecf20Sopenharmony_ci	} while (time_before(jiffies, timeout));
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
998c2ecf20Sopenharmony_ci		return;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	pr_err("%s: re-parenting mux timed-out\n", __func__);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* common round rate callback useable for all types of CPU clocks */
1058c2ecf20Sopenharmony_cistatic long exynos_cpuclk_round_rate(struct clk_hw *hw,
1068c2ecf20Sopenharmony_ci			unsigned long drate, unsigned long *prate)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct clk_hw *parent = clk_hw_get_parent(hw);
1098c2ecf20Sopenharmony_ci	*prate = clk_hw_round_rate(parent, drate);
1108c2ecf20Sopenharmony_ci	return *prate;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* common recalc rate callback useable for all types of CPU clocks */
1148c2ecf20Sopenharmony_cistatic unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw,
1158c2ecf20Sopenharmony_ci			unsigned long parent_rate)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * The CPU clock output (armclk) rate is the same as its parent
1198c2ecf20Sopenharmony_ci	 * rate. Although there exist certain dividers inside the CPU
1208c2ecf20Sopenharmony_ci	 * clock block that could be used to divide the parent clock,
1218c2ecf20Sopenharmony_ci	 * the driver does not make use of them currently, except during
1228c2ecf20Sopenharmony_ci	 * frequency transitions.
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	return parent_rate;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic const struct clk_ops exynos_cpuclk_clk_ops = {
1288c2ecf20Sopenharmony_ci	.recalc_rate = exynos_cpuclk_recalc_rate,
1298c2ecf20Sopenharmony_ci	.round_rate = exynos_cpuclk_round_rate,
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/*
1338c2ecf20Sopenharmony_ci * Helper function to set the 'safe' dividers for the CPU clock. The parameters
1348c2ecf20Sopenharmony_ci * div and mask contain the divider value and the register bit mask of the
1358c2ecf20Sopenharmony_ci * dividers to be programmed.
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_cistatic void exynos_set_safe_div(void __iomem *base, unsigned long div,
1388c2ecf20Sopenharmony_ci					unsigned long mask)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	unsigned long div0;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	div0 = readl(base + E4210_DIV_CPU0);
1438c2ecf20Sopenharmony_ci	div0 = (div0 & ~mask) | (div & mask);
1448c2ecf20Sopenharmony_ci	writel(div0, base + E4210_DIV_CPU0);
1458c2ecf20Sopenharmony_ci	wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* handler for pre-rate change notification from parent clock */
1498c2ecf20Sopenharmony_cistatic int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
1508c2ecf20Sopenharmony_ci			struct exynos_cpuclk *cpuclk, void __iomem *base)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
1538c2ecf20Sopenharmony_ci	unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
1548c2ecf20Sopenharmony_ci	unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
1558c2ecf20Sopenharmony_ci	unsigned long div0, div1 = 0, mux_reg;
1568c2ecf20Sopenharmony_ci	unsigned long flags;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* find out the divider values to use for clock data */
1598c2ecf20Sopenharmony_ci	while ((cfg_data->prate * 1000) != ndata->new_rate) {
1608c2ecf20Sopenharmony_ci		if (cfg_data->prate == 0)
1618c2ecf20Sopenharmony_ci			return -EINVAL;
1628c2ecf20Sopenharmony_ci		cfg_data++;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	spin_lock_irqsave(cpuclk->lock, flags);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/*
1688c2ecf20Sopenharmony_ci	 * For the selected PLL clock frequency, get the pre-defined divider
1698c2ecf20Sopenharmony_ci	 * values. If the clock for sclk_hpm is not sourced from apll, then
1708c2ecf20Sopenharmony_ci	 * the values for DIV_COPY and DIV_HPM dividers need not be set.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	div0 = cfg_data->div0;
1738c2ecf20Sopenharmony_ci	if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
1748c2ecf20Sopenharmony_ci		div1 = cfg_data->div1;
1758c2ecf20Sopenharmony_ci		if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK)
1768c2ecf20Sopenharmony_ci			div1 = readl(base + E4210_DIV_CPU1) &
1778c2ecf20Sopenharmony_ci				(E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK);
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/*
1818c2ecf20Sopenharmony_ci	 * If the old parent clock speed is less than the clock speed of
1828c2ecf20Sopenharmony_ci	 * the alternate parent, then it should be ensured that at no point
1838c2ecf20Sopenharmony_ci	 * the armclk speed is more than the old_prate until the dividers are
1848c2ecf20Sopenharmony_ci	 * set.  Also workaround the issue of the dividers being set to lower
1858c2ecf20Sopenharmony_ci	 * values before the parent clock speed is set to new lower speed
1868c2ecf20Sopenharmony_ci	 * (this can result in too high speed of armclk output clocks).
1878c2ecf20Sopenharmony_ci	 */
1888c2ecf20Sopenharmony_ci	if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
1898c2ecf20Sopenharmony_ci		unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
1928c2ecf20Sopenharmony_ci		WARN_ON(alt_div >= MAX_DIV);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
1958c2ecf20Sopenharmony_ci			/*
1968c2ecf20Sopenharmony_ci			 * In Exynos4210, ATB clock parent is also mout_core. So
1978c2ecf20Sopenharmony_ci			 * ATB clock also needs to be mantained at safe speed.
1988c2ecf20Sopenharmony_ci			 */
1998c2ecf20Sopenharmony_ci			alt_div |= E4210_DIV0_ATB_MASK;
2008c2ecf20Sopenharmony_ci			alt_div_mask |= E4210_DIV0_ATB_MASK;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci		exynos_set_safe_div(base, alt_div, alt_div_mask);
2038c2ecf20Sopenharmony_ci		div0 |= alt_div;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* select sclk_mpll as the alternate parent */
2078c2ecf20Sopenharmony_ci	mux_reg = readl(base + E4210_SRC_CPU);
2088c2ecf20Sopenharmony_ci	writel(mux_reg | (1 << 16), base + E4210_SRC_CPU);
2098c2ecf20Sopenharmony_ci	wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* alternate parent is active now. set the dividers */
2128c2ecf20Sopenharmony_ci	writel(div0, base + E4210_DIV_CPU0);
2138c2ecf20Sopenharmony_ci	wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
2168c2ecf20Sopenharmony_ci		writel(div1, base + E4210_DIV_CPU1);
2178c2ecf20Sopenharmony_ci		wait_until_divider_stable(base + E4210_DIV_STAT_CPU1,
2188c2ecf20Sopenharmony_ci				DIV_MASK_ALL);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(cpuclk->lock, flags);
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/* handler for post-rate change notification from parent clock */
2268c2ecf20Sopenharmony_cistatic int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
2278c2ecf20Sopenharmony_ci			struct exynos_cpuclk *cpuclk, void __iomem *base)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
2308c2ecf20Sopenharmony_ci	unsigned long div = 0, div_mask = DIV_MASK;
2318c2ecf20Sopenharmony_ci	unsigned long mux_reg;
2328c2ecf20Sopenharmony_ci	unsigned long flags;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* find out the divider values to use for clock data */
2358c2ecf20Sopenharmony_ci	if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
2368c2ecf20Sopenharmony_ci		while ((cfg_data->prate * 1000) != ndata->new_rate) {
2378c2ecf20Sopenharmony_ci			if (cfg_data->prate == 0)
2388c2ecf20Sopenharmony_ci				return -EINVAL;
2398c2ecf20Sopenharmony_ci			cfg_data++;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	spin_lock_irqsave(cpuclk->lock, flags);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* select mout_apll as the alternate parent */
2468c2ecf20Sopenharmony_ci	mux_reg = readl(base + E4210_SRC_CPU);
2478c2ecf20Sopenharmony_ci	writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU);
2488c2ecf20Sopenharmony_ci	wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
2518c2ecf20Sopenharmony_ci		div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK);
2528c2ecf20Sopenharmony_ci		div_mask |= E4210_DIV0_ATB_MASK;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	exynos_set_safe_div(base, div, div_mask);
2568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(cpuclk->lock, flags);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/*
2618c2ecf20Sopenharmony_ci * Helper function to set the 'safe' dividers for the CPU clock. The parameters
2628c2ecf20Sopenharmony_ci * div and mask contain the divider value and the register bit mask of the
2638c2ecf20Sopenharmony_ci * dividers to be programmed.
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_cistatic void exynos5433_set_safe_div(void __iomem *base, unsigned long div,
2668c2ecf20Sopenharmony_ci					unsigned long mask)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	unsigned long div0;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	div0 = readl(base + E5433_DIV_CPU0);
2718c2ecf20Sopenharmony_ci	div0 = (div0 & ~mask) | (div & mask);
2728c2ecf20Sopenharmony_ci	writel(div0, base + E5433_DIV_CPU0);
2738c2ecf20Sopenharmony_ci	wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, mask);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/* handler for pre-rate change notification from parent clock */
2778c2ecf20Sopenharmony_cistatic int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
2788c2ecf20Sopenharmony_ci			struct exynos_cpuclk *cpuclk, void __iomem *base)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
2818c2ecf20Sopenharmony_ci	unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
2828c2ecf20Sopenharmony_ci	unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
2838c2ecf20Sopenharmony_ci	unsigned long div0, div1 = 0, mux_reg;
2848c2ecf20Sopenharmony_ci	unsigned long flags;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* find out the divider values to use for clock data */
2878c2ecf20Sopenharmony_ci	while ((cfg_data->prate * 1000) != ndata->new_rate) {
2888c2ecf20Sopenharmony_ci		if (cfg_data->prate == 0)
2898c2ecf20Sopenharmony_ci			return -EINVAL;
2908c2ecf20Sopenharmony_ci		cfg_data++;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	spin_lock_irqsave(cpuclk->lock, flags);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/*
2968c2ecf20Sopenharmony_ci	 * For the selected PLL clock frequency, get the pre-defined divider
2978c2ecf20Sopenharmony_ci	 * values.
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	div0 = cfg_data->div0;
3008c2ecf20Sopenharmony_ci	div1 = cfg_data->div1;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * If the old parent clock speed is less than the clock speed of
3048c2ecf20Sopenharmony_ci	 * the alternate parent, then it should be ensured that at no point
3058c2ecf20Sopenharmony_ci	 * the armclk speed is more than the old_prate until the dividers are
3068c2ecf20Sopenharmony_ci	 * set.  Also workaround the issue of the dividers being set to lower
3078c2ecf20Sopenharmony_ci	 * values before the parent clock speed is set to new lower speed
3088c2ecf20Sopenharmony_ci	 * (this can result in too high speed of armclk output clocks).
3098c2ecf20Sopenharmony_ci	 */
3108c2ecf20Sopenharmony_ci	if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
3118c2ecf20Sopenharmony_ci		unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
3148c2ecf20Sopenharmony_ci		WARN_ON(alt_div >= MAX_DIV);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		exynos5433_set_safe_div(base, alt_div, alt_div_mask);
3178c2ecf20Sopenharmony_ci		div0 |= alt_div;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* select the alternate parent */
3218c2ecf20Sopenharmony_ci	mux_reg = readl(base + E5433_MUX_SEL2);
3228c2ecf20Sopenharmony_ci	writel(mux_reg | 1, base + E5433_MUX_SEL2);
3238c2ecf20Sopenharmony_ci	wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 2);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* alternate parent is active now. set the dividers */
3268c2ecf20Sopenharmony_ci	writel(div0, base + E5433_DIV_CPU0);
3278c2ecf20Sopenharmony_ci	wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, DIV_MASK_ALL);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	writel(div1, base + E5433_DIV_CPU1);
3308c2ecf20Sopenharmony_ci	wait_until_divider_stable(base + E5433_DIV_STAT_CPU1, DIV_MASK_ALL);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(cpuclk->lock, flags);
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/* handler for post-rate change notification from parent clock */
3378c2ecf20Sopenharmony_cistatic int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
3388c2ecf20Sopenharmony_ci			struct exynos_cpuclk *cpuclk, void __iomem *base)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	unsigned long div = 0, div_mask = DIV_MASK;
3418c2ecf20Sopenharmony_ci	unsigned long mux_reg;
3428c2ecf20Sopenharmony_ci	unsigned long flags;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	spin_lock_irqsave(cpuclk->lock, flags);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* select apll as the alternate parent */
3478c2ecf20Sopenharmony_ci	mux_reg = readl(base + E5433_MUX_SEL2);
3488c2ecf20Sopenharmony_ci	writel(mux_reg & ~1, base + E5433_MUX_SEL2);
3498c2ecf20Sopenharmony_ci	wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 1);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	exynos5433_set_safe_div(base, div, div_mask);
3528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(cpuclk->lock, flags);
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/*
3578c2ecf20Sopenharmony_ci * This notifier function is called for the pre-rate and post-rate change
3588c2ecf20Sopenharmony_ci * notifications of the parent clock of cpuclk.
3598c2ecf20Sopenharmony_ci */
3608c2ecf20Sopenharmony_cistatic int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
3618c2ecf20Sopenharmony_ci				unsigned long event, void *data)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct clk_notifier_data *ndata = data;
3648c2ecf20Sopenharmony_ci	struct exynos_cpuclk *cpuclk;
3658c2ecf20Sopenharmony_ci	void __iomem *base;
3668c2ecf20Sopenharmony_ci	int err = 0;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
3698c2ecf20Sopenharmony_ci	base = cpuclk->ctrl_base;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (event == PRE_RATE_CHANGE)
3728c2ecf20Sopenharmony_ci		err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base);
3738c2ecf20Sopenharmony_ci	else if (event == POST_RATE_CHANGE)
3748c2ecf20Sopenharmony_ci		err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/*
3808c2ecf20Sopenharmony_ci * This notifier function is called for the pre-rate and post-rate change
3818c2ecf20Sopenharmony_ci * notifications of the parent clock of cpuclk.
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_cistatic int exynos5433_cpuclk_notifier_cb(struct notifier_block *nb,
3848c2ecf20Sopenharmony_ci				unsigned long event, void *data)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct clk_notifier_data *ndata = data;
3878c2ecf20Sopenharmony_ci	struct exynos_cpuclk *cpuclk;
3888c2ecf20Sopenharmony_ci	void __iomem *base;
3898c2ecf20Sopenharmony_ci	int err = 0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
3928c2ecf20Sopenharmony_ci	base = cpuclk->ctrl_base;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (event == PRE_RATE_CHANGE)
3958c2ecf20Sopenharmony_ci		err = exynos5433_cpuclk_pre_rate_change(ndata, cpuclk, base);
3968c2ecf20Sopenharmony_ci	else if (event == POST_RATE_CHANGE)
3978c2ecf20Sopenharmony_ci		err = exynos5433_cpuclk_post_rate_change(ndata, cpuclk, base);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/* helper function to register a CPU clock */
4038c2ecf20Sopenharmony_ciint __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
4048c2ecf20Sopenharmony_ci		unsigned int lookup_id, const char *name,
4058c2ecf20Sopenharmony_ci		const struct clk_hw *parent, const struct clk_hw *alt_parent,
4068c2ecf20Sopenharmony_ci		unsigned long offset, const struct exynos_cpuclk_cfg_data *cfg,
4078c2ecf20Sopenharmony_ci		unsigned long num_cfgs, unsigned long flags)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct exynos_cpuclk *cpuclk;
4108c2ecf20Sopenharmony_ci	struct clk_init_data init;
4118c2ecf20Sopenharmony_ci	const char *parent_name;
4128c2ecf20Sopenharmony_ci	int ret = 0;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (IS_ERR(parent) || IS_ERR(alt_parent)) {
4158c2ecf20Sopenharmony_ci		pr_err("%s: invalid parent clock(s)\n", __func__);
4168c2ecf20Sopenharmony_ci		return -EINVAL;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
4208c2ecf20Sopenharmony_ci	if (!cpuclk)
4218c2ecf20Sopenharmony_ci		return -ENOMEM;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	parent_name = clk_hw_get_name(parent);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	init.name = name;
4268c2ecf20Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT;
4278c2ecf20Sopenharmony_ci	init.parent_names = &parent_name;
4288c2ecf20Sopenharmony_ci	init.num_parents = 1;
4298c2ecf20Sopenharmony_ci	init.ops = &exynos_cpuclk_clk_ops;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	cpuclk->alt_parent = alt_parent;
4328c2ecf20Sopenharmony_ci	cpuclk->hw.init = &init;
4338c2ecf20Sopenharmony_ci	cpuclk->ctrl_base = ctx->reg_base + offset;
4348c2ecf20Sopenharmony_ci	cpuclk->lock = &ctx->lock;
4358c2ecf20Sopenharmony_ci	cpuclk->flags = flags;
4368c2ecf20Sopenharmony_ci	if (flags & CLK_CPU_HAS_E5433_REGS_LAYOUT)
4378c2ecf20Sopenharmony_ci		cpuclk->clk_nb.notifier_call = exynos5433_cpuclk_notifier_cb;
4388c2ecf20Sopenharmony_ci	else
4398c2ecf20Sopenharmony_ci		cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	ret = clk_notifier_register(parent->clk, &cpuclk->clk_nb);
4438c2ecf20Sopenharmony_ci	if (ret) {
4448c2ecf20Sopenharmony_ci		pr_err("%s: failed to register clock notifier for %s\n",
4458c2ecf20Sopenharmony_ci				__func__, name);
4468c2ecf20Sopenharmony_ci		goto free_cpuclk;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL);
4508c2ecf20Sopenharmony_ci	if (!cpuclk->cfg) {
4518c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4528c2ecf20Sopenharmony_ci		goto unregister_clk_nb;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	ret = clk_hw_register(NULL, &cpuclk->hw);
4568c2ecf20Sopenharmony_ci	if (ret) {
4578c2ecf20Sopenharmony_ci		pr_err("%s: could not register cpuclk %s\n", __func__,	name);
4588c2ecf20Sopenharmony_ci		goto free_cpuclk_data;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	samsung_clk_add_lookup(ctx, &cpuclk->hw, lookup_id);
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cifree_cpuclk_data:
4658c2ecf20Sopenharmony_ci	kfree(cpuclk->cfg);
4668c2ecf20Sopenharmony_ciunregister_clk_nb:
4678c2ecf20Sopenharmony_ci	clk_notifier_unregister(parent->clk, &cpuclk->clk_nb);
4688c2ecf20Sopenharmony_cifree_cpuclk:
4698c2ecf20Sopenharmony_ci	kfree(cpuclk);
4708c2ecf20Sopenharmony_ci	return ret;
4718c2ecf20Sopenharmony_ci}
472