18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Maxime Ripard 48c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "ccu_gate.h" 118c2ecf20Sopenharmony_ci#include "ccu_mp.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic void ccu_mp_find_best(unsigned long parent, unsigned long rate, 148c2ecf20Sopenharmony_ci unsigned int max_m, unsigned int max_p, 158c2ecf20Sopenharmony_ci unsigned int *m, unsigned int *p) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci unsigned long best_rate = 0; 188c2ecf20Sopenharmony_ci unsigned int best_m = 0, best_p = 0; 198c2ecf20Sopenharmony_ci unsigned int _m, _p; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci for (_p = 1; _p <= max_p; _p <<= 1) { 228c2ecf20Sopenharmony_ci for (_m = 1; _m <= max_m; _m++) { 238c2ecf20Sopenharmony_ci unsigned long tmp_rate = parent / _p / _m; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (tmp_rate > rate) 268c2ecf20Sopenharmony_ci continue; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if ((rate - tmp_rate) < (rate - best_rate)) { 298c2ecf20Sopenharmony_ci best_rate = tmp_rate; 308c2ecf20Sopenharmony_ci best_m = _m; 318c2ecf20Sopenharmony_ci best_p = _p; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci *m = best_m; 378c2ecf20Sopenharmony_ci *p = best_p; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw, 418c2ecf20Sopenharmony_ci unsigned long *parent, 428c2ecf20Sopenharmony_ci unsigned long rate, 438c2ecf20Sopenharmony_ci unsigned int max_m, 448c2ecf20Sopenharmony_ci unsigned int max_p) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci unsigned long parent_rate_saved; 478c2ecf20Sopenharmony_ci unsigned long parent_rate, now; 488c2ecf20Sopenharmony_ci unsigned long best_rate = 0; 498c2ecf20Sopenharmony_ci unsigned int _m, _p, div; 508c2ecf20Sopenharmony_ci unsigned long maxdiv; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci parent_rate_saved = *parent; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * The maximum divider we can use without overflowing 568c2ecf20Sopenharmony_ci * unsigned long in rate * m * p below 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci maxdiv = max_m * max_p; 598c2ecf20Sopenharmony_ci maxdiv = min(ULONG_MAX / rate, maxdiv); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci for (_p = 1; _p <= max_p; _p <<= 1) { 628c2ecf20Sopenharmony_ci for (_m = 1; _m <= max_m; _m++) { 638c2ecf20Sopenharmony_ci div = _m * _p; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (div > maxdiv) 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (rate * div == parent_rate_saved) { 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * It's the most ideal case if the requested 718c2ecf20Sopenharmony_ci * rate can be divided from parent clock without 728c2ecf20Sopenharmony_ci * needing to change parent rate, so return the 738c2ecf20Sopenharmony_ci * divider immediately. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci *parent = parent_rate_saved; 768c2ecf20Sopenharmony_ci return rate; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci parent_rate = clk_hw_round_rate(hw, rate * div); 808c2ecf20Sopenharmony_ci now = parent_rate / div; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (now <= rate && now > best_rate) { 838c2ecf20Sopenharmony_ci best_rate = now; 848c2ecf20Sopenharmony_ci *parent = parent_rate; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (now == rate) 878c2ecf20Sopenharmony_ci return rate; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return best_rate; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, 968c2ecf20Sopenharmony_ci struct clk_hw *hw, 978c2ecf20Sopenharmony_ci unsigned long *parent_rate, 988c2ecf20Sopenharmony_ci unsigned long rate, 998c2ecf20Sopenharmony_ci void *data) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct ccu_mp *cmp = data; 1028c2ecf20Sopenharmony_ci unsigned int max_m, max_p; 1038c2ecf20Sopenharmony_ci unsigned int m, p; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 1068c2ecf20Sopenharmony_ci rate *= cmp->fixed_post_div; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci max_m = cmp->m.max ?: 1 << cmp->m.width; 1098c2ecf20Sopenharmony_ci max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!clk_hw_can_set_rate_parent(&cmp->common.hw)) { 1128c2ecf20Sopenharmony_ci ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); 1138c2ecf20Sopenharmony_ci rate = *parent_rate / p / m; 1148c2ecf20Sopenharmony_ci } else { 1158c2ecf20Sopenharmony_ci rate = ccu_mp_find_best_with_parent_adj(hw, parent_rate, rate, 1168c2ecf20Sopenharmony_ci max_m, max_p); 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 1208c2ecf20Sopenharmony_ci rate /= cmp->fixed_post_div; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return rate; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void ccu_mp_disable(struct clk_hw *hw) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return ccu_gate_helper_disable(&cmp->common, cmp->enable); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int ccu_mp_enable(struct clk_hw *hw) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return ccu_gate_helper_enable(&cmp->common, cmp->enable); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int ccu_mp_is_enabled(struct clk_hw *hw) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return ccu_gate_helper_is_enabled(&cmp->common, cmp->enable); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic unsigned long ccu_mp_recalc_rate(struct clk_hw *hw, 1478c2ecf20Sopenharmony_ci unsigned long parent_rate) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 1508c2ecf20Sopenharmony_ci unsigned long rate; 1518c2ecf20Sopenharmony_ci unsigned int m, p; 1528c2ecf20Sopenharmony_ci u32 reg; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Adjust parent_rate according to pre-dividers */ 1558c2ecf20Sopenharmony_ci parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1, 1568c2ecf20Sopenharmony_ci parent_rate); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci reg = readl(cmp->common.base + cmp->common.reg); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci m = reg >> cmp->m.shift; 1618c2ecf20Sopenharmony_ci m &= (1 << cmp->m.width) - 1; 1628c2ecf20Sopenharmony_ci m += cmp->m.offset; 1638c2ecf20Sopenharmony_ci if (!m) 1648c2ecf20Sopenharmony_ci m++; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci p = reg >> cmp->p.shift; 1678c2ecf20Sopenharmony_ci p &= (1 << cmp->p.width) - 1; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rate = (parent_rate >> p) / m; 1708c2ecf20Sopenharmony_ci if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 1718c2ecf20Sopenharmony_ci rate /= cmp->fixed_post_div; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return rate; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int ccu_mp_determine_rate(struct clk_hw *hw, 1778c2ecf20Sopenharmony_ci struct clk_rate_request *req) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ccu_mux_helper_determine_rate(&cmp->common, &cmp->mux, 1828c2ecf20Sopenharmony_ci req, ccu_mp_round_rate, cmp); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, 1868c2ecf20Sopenharmony_ci unsigned long parent_rate) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 1898c2ecf20Sopenharmony_ci unsigned long flags; 1908c2ecf20Sopenharmony_ci unsigned int max_m, max_p; 1918c2ecf20Sopenharmony_ci unsigned int m, p; 1928c2ecf20Sopenharmony_ci u32 reg; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Adjust parent_rate according to pre-dividers */ 1958c2ecf20Sopenharmony_ci parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1, 1968c2ecf20Sopenharmony_ci parent_rate); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci max_m = cmp->m.max ?: 1 << cmp->m.width; 1998c2ecf20Sopenharmony_ci max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Adjust target rate according to post-dividers */ 2028c2ecf20Sopenharmony_ci if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 2038c2ecf20Sopenharmony_ci rate = rate * cmp->fixed_post_div; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci spin_lock_irqsave(cmp->common.lock, flags); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci reg = readl(cmp->common.base + cmp->common.reg); 2108c2ecf20Sopenharmony_ci reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift); 2118c2ecf20Sopenharmony_ci reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift); 2128c2ecf20Sopenharmony_ci reg |= (m - cmp->m.offset) << cmp->m.shift; 2138c2ecf20Sopenharmony_ci reg |= ilog2(p) << cmp->p.shift; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci writel(reg, cmp->common.base + cmp->common.reg); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cmp->common.lock, flags); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic u8 ccu_mp_get_parent(struct clk_hw *hw) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return ccu_mux_helper_get_parent(&cmp->common, &cmp->mux); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int ccu_mp_set_parent(struct clk_hw *hw, u8 index) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct ccu_mp *cmp = hw_to_ccu_mp(hw); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return ccu_mux_helper_set_parent(&cmp->common, &cmp->mux, index); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciconst struct clk_ops ccu_mp_ops = { 2378c2ecf20Sopenharmony_ci .disable = ccu_mp_disable, 2388c2ecf20Sopenharmony_ci .enable = ccu_mp_enable, 2398c2ecf20Sopenharmony_ci .is_enabled = ccu_mp_is_enabled, 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci .get_parent = ccu_mp_get_parent, 2428c2ecf20Sopenharmony_ci .set_parent = ccu_mp_set_parent, 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci .determine_rate = ccu_mp_determine_rate, 2458c2ecf20Sopenharmony_ci .recalc_rate = ccu_mp_recalc_rate, 2468c2ecf20Sopenharmony_ci .set_rate = ccu_mp_set_rate, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * Support for MMC timing mode switching 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * The MMC clocks on some SoCs support switching between old and 2538c2ecf20Sopenharmony_ci * new timing modes. A platform specific API is provided to query 2548c2ecf20Sopenharmony_ci * and set the timing mode on supported SoCs. 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * In addition, a special class of ccu_mp_ops is provided, which 2578c2ecf20Sopenharmony_ci * takes in to account the timing mode switch. When the new timing 2588c2ecf20Sopenharmony_ci * mode is active, the clock output rate is halved. This new class 2598c2ecf20Sopenharmony_ci * is a wrapper around the generic ccu_mp_ops. When clock rates 2608c2ecf20Sopenharmony_ci * are passed through to ccu_mp_ops callbacks, they are doubled 2618c2ecf20Sopenharmony_ci * if the new timing mode bit is set, to account for the post 2628c2ecf20Sopenharmony_ci * divider. Conversely, when clock rates are passed back, they 2638c2ecf20Sopenharmony_ci * are halved if the mode bit is set. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic unsigned long ccu_mp_mmc_recalc_rate(struct clk_hw *hw, 2678c2ecf20Sopenharmony_ci unsigned long parent_rate) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci unsigned long rate = ccu_mp_recalc_rate(hw, parent_rate); 2708c2ecf20Sopenharmony_ci struct ccu_common *cm = hw_to_ccu_common(hw); 2718c2ecf20Sopenharmony_ci u32 val = readl(cm->base + cm->reg); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (val & CCU_MMC_NEW_TIMING_MODE) 2748c2ecf20Sopenharmony_ci return rate / 2; 2758c2ecf20Sopenharmony_ci return rate; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int ccu_mp_mmc_determine_rate(struct clk_hw *hw, 2798c2ecf20Sopenharmony_ci struct clk_rate_request *req) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct ccu_common *cm = hw_to_ccu_common(hw); 2828c2ecf20Sopenharmony_ci u32 val = readl(cm->base + cm->reg); 2838c2ecf20Sopenharmony_ci int ret; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* adjust the requested clock rate */ 2868c2ecf20Sopenharmony_ci if (val & CCU_MMC_NEW_TIMING_MODE) { 2878c2ecf20Sopenharmony_ci req->rate *= 2; 2888c2ecf20Sopenharmony_ci req->min_rate *= 2; 2898c2ecf20Sopenharmony_ci req->max_rate *= 2; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = ccu_mp_determine_rate(hw, req); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* re-adjust the requested clock rate back */ 2958c2ecf20Sopenharmony_ci if (val & CCU_MMC_NEW_TIMING_MODE) { 2968c2ecf20Sopenharmony_ci req->rate /= 2; 2978c2ecf20Sopenharmony_ci req->min_rate /= 2; 2988c2ecf20Sopenharmony_ci req->max_rate /= 2; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return ret; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int ccu_mp_mmc_set_rate(struct clk_hw *hw, unsigned long rate, 3058c2ecf20Sopenharmony_ci unsigned long parent_rate) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct ccu_common *cm = hw_to_ccu_common(hw); 3088c2ecf20Sopenharmony_ci u32 val = readl(cm->base + cm->reg); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (val & CCU_MMC_NEW_TIMING_MODE) 3118c2ecf20Sopenharmony_ci rate *= 2; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return ccu_mp_set_rate(hw, rate, parent_rate); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ciconst struct clk_ops ccu_mp_mmc_ops = { 3178c2ecf20Sopenharmony_ci .disable = ccu_mp_disable, 3188c2ecf20Sopenharmony_ci .enable = ccu_mp_enable, 3198c2ecf20Sopenharmony_ci .is_enabled = ccu_mp_is_enabled, 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci .get_parent = ccu_mp_get_parent, 3228c2ecf20Sopenharmony_ci .set_parent = ccu_mp_set_parent, 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci .determine_rate = ccu_mp_mmc_determine_rate, 3258c2ecf20Sopenharmony_ci .recalc_rate = ccu_mp_mmc_recalc_rate, 3268c2ecf20Sopenharmony_ci .set_rate = ccu_mp_mmc_set_rate, 3278c2ecf20Sopenharmony_ci}; 328