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