18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define DSS_SUBSYS_NAME "PLL" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "omapdss.h" 168c2ecf20Sopenharmony_ci#include "dss.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PLL_CONTROL 0x0000 198c2ecf20Sopenharmony_ci#define PLL_STATUS 0x0004 208c2ecf20Sopenharmony_ci#define PLL_GO 0x0008 218c2ecf20Sopenharmony_ci#define PLL_CONFIGURATION1 0x000C 228c2ecf20Sopenharmony_ci#define PLL_CONFIGURATION2 0x0010 238c2ecf20Sopenharmony_ci#define PLL_CONFIGURATION3 0x0014 248c2ecf20Sopenharmony_ci#define PLL_SSC_CONFIGURATION1 0x0018 258c2ecf20Sopenharmony_ci#define PLL_SSC_CONFIGURATION2 0x001C 268c2ecf20Sopenharmony_ci#define PLL_CONFIGURATION4 0x0020 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciint dss_pll_register(struct dss_device *dss, struct dss_pll *pll) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int i; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) { 338c2ecf20Sopenharmony_ci if (!dss->plls[i]) { 348c2ecf20Sopenharmony_ci dss->plls[i] = pll; 358c2ecf20Sopenharmony_ci pll->dss = dss; 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return -EBUSY; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_civoid dss_pll_unregister(struct dss_pll *pll) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct dss_device *dss = pll->dss; 468c2ecf20Sopenharmony_ci int i; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) { 498c2ecf20Sopenharmony_ci if (dss->plls[i] == pll) { 508c2ecf20Sopenharmony_ci dss->plls[i] = NULL; 518c2ecf20Sopenharmony_ci pll->dss = NULL; 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct dss_pll *dss_pll_find(struct dss_device *dss, const char *name) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int i; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) { 628c2ecf20Sopenharmony_ci if (dss->plls[i] && strcmp(dss->plls[i]->name, name) == 0) 638c2ecf20Sopenharmony_ci return dss->plls[i]; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return NULL; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct dss_pll *dss_pll_find_by_src(struct dss_device *dss, 708c2ecf20Sopenharmony_ci enum dss_clk_source src) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct dss_pll *pll; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (src) { 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci case DSS_CLK_SRC_FCK: 778c2ecf20Sopenharmony_ci return NULL; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci case DSS_CLK_SRC_HDMI_PLL: 808c2ecf20Sopenharmony_ci return dss_pll_find(dss, "hdmi"); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL1_1: 838c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL1_2: 848c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL1_3: 858c2ecf20Sopenharmony_ci pll = dss_pll_find(dss, "dsi0"); 868c2ecf20Sopenharmony_ci if (!pll) 878c2ecf20Sopenharmony_ci pll = dss_pll_find(dss, "video0"); 888c2ecf20Sopenharmony_ci return pll; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL2_1: 918c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL2_2: 928c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL2_3: 938c2ecf20Sopenharmony_ci pll = dss_pll_find(dss, "dsi1"); 948c2ecf20Sopenharmony_ci if (!pll) 958c2ecf20Sopenharmony_ci pll = dss_pll_find(dss, "video1"); 968c2ecf20Sopenharmony_ci return pll; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciunsigned int dss_pll_get_clkout_idx_for_src(enum dss_clk_source src) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci switch (src) { 1038c2ecf20Sopenharmony_ci case DSS_CLK_SRC_HDMI_PLL: 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL1_1: 1078c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL2_1: 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL1_2: 1118c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL2_2: 1128c2ecf20Sopenharmony_ci return 1; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL1_3: 1158c2ecf20Sopenharmony_ci case DSS_CLK_SRC_PLL2_3: 1168c2ecf20Sopenharmony_ci return 2; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci default: 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ciint dss_pll_enable(struct dss_pll *pll) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int r; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci r = clk_prepare_enable(pll->clkin); 1288c2ecf20Sopenharmony_ci if (r) 1298c2ecf20Sopenharmony_ci return r; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (pll->regulator) { 1328c2ecf20Sopenharmony_ci r = regulator_enable(pll->regulator); 1338c2ecf20Sopenharmony_ci if (r) 1348c2ecf20Sopenharmony_ci goto err_reg; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci r = pll->ops->enable(pll); 1388c2ecf20Sopenharmony_ci if (r) 1398c2ecf20Sopenharmony_ci goto err_enable; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cierr_enable: 1448c2ecf20Sopenharmony_ci if (pll->regulator) 1458c2ecf20Sopenharmony_ci regulator_disable(pll->regulator); 1468c2ecf20Sopenharmony_cierr_reg: 1478c2ecf20Sopenharmony_ci clk_disable_unprepare(pll->clkin); 1488c2ecf20Sopenharmony_ci return r; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_civoid dss_pll_disable(struct dss_pll *pll) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci pll->ops->disable(pll); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (pll->regulator) 1568c2ecf20Sopenharmony_ci regulator_disable(pll->regulator); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci clk_disable_unprepare(pll->clkin); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci memset(&pll->cinfo, 0, sizeof(pll->cinfo)); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciint dss_pll_set_config(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int r; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci r = pll->ops->set_config(pll, cinfo); 1688c2ecf20Sopenharmony_ci if (r) 1698c2ecf20Sopenharmony_ci return r; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci pll->cinfo = *cinfo; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cibool dss_pll_hsdiv_calc_a(const struct dss_pll *pll, unsigned long clkdco, 1778c2ecf20Sopenharmony_ci unsigned long out_min, unsigned long out_max, 1788c2ecf20Sopenharmony_ci dss_hsdiv_calc_func func, void *data) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 1818c2ecf20Sopenharmony_ci int m, m_start, m_stop; 1828c2ecf20Sopenharmony_ci unsigned long out; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci out_min = out_min ? out_min : 1; 1858c2ecf20Sopenharmony_ci out_max = out_max ? out_max : ULONG_MAX; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci m_start = max(DIV_ROUND_UP(clkdco, out_max), 1ul); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci m_stop = min((unsigned)(clkdco / out_min), hw->mX_max); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci for (m = m_start; m <= m_stop; ++m) { 1928c2ecf20Sopenharmony_ci out = clkdco / m; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (func(m, out, data)) 1958c2ecf20Sopenharmony_ci return true; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return false; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * clkdco = clkin / n * m * 2 2038c2ecf20Sopenharmony_ci * clkoutX = clkdco / mX 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cibool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin, 2068c2ecf20Sopenharmony_ci unsigned long pll_min, unsigned long pll_max, 2078c2ecf20Sopenharmony_ci dss_pll_calc_func func, void *data) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 2108c2ecf20Sopenharmony_ci int n, n_start, n_stop, n_inc; 2118c2ecf20Sopenharmony_ci int m, m_start, m_stop, m_inc; 2128c2ecf20Sopenharmony_ci unsigned long fint, clkdco; 2138c2ecf20Sopenharmony_ci unsigned long pll_hw_max; 2148c2ecf20Sopenharmony_ci unsigned long fint_hw_min, fint_hw_max; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci pll_hw_max = hw->clkdco_max; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci fint_hw_min = hw->fint_min; 2198c2ecf20Sopenharmony_ci fint_hw_max = hw->fint_max; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); 2228c2ecf20Sopenharmony_ci n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max); 2238c2ecf20Sopenharmony_ci n_inc = 1; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (hw->errata_i886) { 2268c2ecf20Sopenharmony_ci swap(n_start, n_stop); 2278c2ecf20Sopenharmony_ci n_inc = -1; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci pll_max = pll_max ? pll_max : ULONG_MAX; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci for (n = n_start; n != n_stop; n += n_inc) { 2338c2ecf20Sopenharmony_ci fint = clkin / n; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), 2368c2ecf20Sopenharmony_ci 1ul); 2378c2ecf20Sopenharmony_ci m_stop = min3((unsigned)(pll_max / fint / 2), 2388c2ecf20Sopenharmony_ci (unsigned)(pll_hw_max / fint / 2), 2398c2ecf20Sopenharmony_ci hw->m_max); 2408c2ecf20Sopenharmony_ci m_inc = 1; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (hw->errata_i886) { 2438c2ecf20Sopenharmony_ci swap(m_start, m_stop); 2448c2ecf20Sopenharmony_ci m_inc = -1; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci for (m = m_start; m != m_stop; m += m_inc) { 2488c2ecf20Sopenharmony_ci clkdco = 2 * m * fint; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (func(n, m, fint, clkdco, data)) 2518c2ecf20Sopenharmony_ci return true; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return false; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * This calculates a PLL config that will provide the target_clkout rate 2608c2ecf20Sopenharmony_ci * for clkout. Additionally clkdco rate will be the same as clkout rate 2618c2ecf20Sopenharmony_ci * when clkout rate is >= min_clkdco. 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * clkdco = clkin / n * m + clkin / n * mf / 262144 2648c2ecf20Sopenharmony_ci * clkout = clkdco / m2 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cibool dss_pll_calc_b(const struct dss_pll *pll, unsigned long clkin, 2678c2ecf20Sopenharmony_ci unsigned long target_clkout, struct dss_pll_clock_info *cinfo) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci unsigned long fint, clkdco, clkout; 2708c2ecf20Sopenharmony_ci unsigned long target_clkdco; 2718c2ecf20Sopenharmony_ci unsigned long min_dco; 2728c2ecf20Sopenharmony_ci unsigned int n, m, mf, m2, sd; 2738c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci DSSDBG("clkin %lu, target clkout %lu\n", clkin, target_clkout); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Fint */ 2788c2ecf20Sopenharmony_ci n = DIV_ROUND_UP(clkin, hw->fint_max); 2798c2ecf20Sopenharmony_ci fint = clkin / n; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* adjust m2 so that the clkdco will be high enough */ 2828c2ecf20Sopenharmony_ci min_dco = roundup(hw->clkdco_min, fint); 2838c2ecf20Sopenharmony_ci m2 = DIV_ROUND_UP(min_dco, target_clkout); 2848c2ecf20Sopenharmony_ci if (m2 == 0) 2858c2ecf20Sopenharmony_ci m2 = 1; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci target_clkdco = target_clkout * m2; 2888c2ecf20Sopenharmony_ci m = target_clkdco / fint; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci clkdco = fint * m; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* adjust clkdco with fractional mf */ 2938c2ecf20Sopenharmony_ci if (WARN_ON(target_clkdco - clkdco > fint)) 2948c2ecf20Sopenharmony_ci mf = 0; 2958c2ecf20Sopenharmony_ci else 2968c2ecf20Sopenharmony_ci mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (mf > 0) 2998c2ecf20Sopenharmony_ci clkdco += (u32)div_u64((u64)mf * fint, 262144); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci clkout = clkdco / m2; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* sigma-delta */ 3048c2ecf20Sopenharmony_ci sd = DIV_ROUND_UP(fint * m, 250000000); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n", 3078c2ecf20Sopenharmony_ci n, m, mf, m2, sd); 3088c2ecf20Sopenharmony_ci DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci cinfo->n = n; 3118c2ecf20Sopenharmony_ci cinfo->m = m; 3128c2ecf20Sopenharmony_ci cinfo->mf = mf; 3138c2ecf20Sopenharmony_ci cinfo->mX[0] = m2; 3148c2ecf20Sopenharmony_ci cinfo->sd = sd; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci cinfo->fint = fint; 3178c2ecf20Sopenharmony_ci cinfo->clkdco = clkdco; 3188c2ecf20Sopenharmony_ci cinfo->clkout[0] = clkout; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return true; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int wait_for_bit_change(void __iomem *reg, int bitnum, int value) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci unsigned long timeout; 3268c2ecf20Sopenharmony_ci ktime_t wait; 3278c2ecf20Sopenharmony_ci int t; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* first busyloop to see if the bit changes right away */ 3308c2ecf20Sopenharmony_ci t = 100; 3318c2ecf20Sopenharmony_ci while (t-- > 0) { 3328c2ecf20Sopenharmony_ci if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value) 3338c2ecf20Sopenharmony_ci return value; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* then loop for 500ms, sleeping for 1ms in between */ 3378c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 3388c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 3398c2ecf20Sopenharmony_ci if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value) 3408c2ecf20Sopenharmony_ci return value; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci wait = ns_to_ktime(1000 * 1000); 3438c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 3448c2ecf20Sopenharmony_ci schedule_hrtimeout(&wait, HRTIMER_MODE_REL); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return !value; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciint dss_pll_wait_reset_done(struct dss_pll *pll) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci void __iomem *base = pll->base; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_STATUS, 0, 1) != 1) 3558c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3568c2ecf20Sopenharmony_ci else 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci int t = 100; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci while (t-- > 0) { 3658c2ecf20Sopenharmony_ci u32 v = readl_relaxed(pll->base + PLL_STATUS); 3668c2ecf20Sopenharmony_ci v &= hsdiv_ack_mask; 3678c2ecf20Sopenharmony_ci if (v == hsdiv_ack_mask) 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic bool pll_is_locked(u32 stat) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Required value for each bitfield listed below 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * PLL_STATUS[6] = 0 PLL_BYPASS 3808c2ecf20Sopenharmony_ci * PLL_STATUS[5] = 0 PLL_HIGHJITTER 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * PLL_STATUS[3] = 0 PLL_LOSSREF 3838c2ecf20Sopenharmony_ci * PLL_STATUS[2] = 0 PLL_RECAL 3848c2ecf20Sopenharmony_ci * PLL_STATUS[1] = 1 PLL_LOCK 3858c2ecf20Sopenharmony_ci * PLL_STATUS[0] = 1 PLL_CTRL_RESET_DONE 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci return ((stat & 0x6f) == 0x3); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciint dss_pll_write_config_type_a(struct dss_pll *pll, 3918c2ecf20Sopenharmony_ci const struct dss_pll_clock_info *cinfo) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 3948c2ecf20Sopenharmony_ci void __iomem *base = pll->base; 3958c2ecf20Sopenharmony_ci int r = 0; 3968c2ecf20Sopenharmony_ci u32 l; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci l = 0; 3998c2ecf20Sopenharmony_ci if (hw->has_stopmode) 4008c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 0, 0); /* PLL_STOPMODE */ 4018c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->n - 1, hw->n_msb, hw->n_lsb); /* PLL_REGN */ 4028c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->m, hw->m_msb, hw->m_lsb); /* PLL_REGM */ 4038c2ecf20Sopenharmony_ci /* M4 */ 4048c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[0] ? cinfo->mX[0] - 1 : 0, 4058c2ecf20Sopenharmony_ci hw->mX_msb[0], hw->mX_lsb[0]); 4068c2ecf20Sopenharmony_ci /* M5 */ 4078c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[1] ? cinfo->mX[1] - 1 : 0, 4088c2ecf20Sopenharmony_ci hw->mX_msb[1], hw->mX_lsb[1]); 4098c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION1); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci l = 0; 4128c2ecf20Sopenharmony_ci /* M6 */ 4138c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[2] ? cinfo->mX[2] - 1 : 0, 4148c2ecf20Sopenharmony_ci hw->mX_msb[2], hw->mX_lsb[2]); 4158c2ecf20Sopenharmony_ci /* M7 */ 4168c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[3] ? cinfo->mX[3] - 1 : 0, 4178c2ecf20Sopenharmony_ci hw->mX_msb[3], hw->mX_lsb[3]); 4188c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION3); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION2); 4218c2ecf20Sopenharmony_ci if (hw->has_freqsel) { 4228c2ecf20Sopenharmony_ci u32 f = cinfo->fint < 1000000 ? 0x3 : 4238c2ecf20Sopenharmony_ci cinfo->fint < 1250000 ? 0x4 : 4248c2ecf20Sopenharmony_ci cinfo->fint < 1500000 ? 0x5 : 4258c2ecf20Sopenharmony_ci cinfo->fint < 1750000 ? 0x6 : 4268c2ecf20Sopenharmony_ci 0x7; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci l = FLD_MOD(l, f, 4, 1); /* PLL_FREQSEL */ 4298c2ecf20Sopenharmony_ci } else if (hw->has_selfreqdco) { 4308c2ecf20Sopenharmony_ci u32 f = cinfo->clkdco < hw->clkdco_low ? 0x2 : 0x4; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci l = FLD_MOD(l, f, 3, 1); /* PLL_SELFREQDCO */ 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 13, 13); /* PLL_REFEN */ 4358c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 14, 14); /* PHY_CLKINEN */ 4368c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 16, 16); /* M4_CLOCK_EN */ 4378c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 18, 18); /* M5_CLOCK_EN */ 4388c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 20, 20); /* HSDIVBYPASS */ 4398c2ecf20Sopenharmony_ci if (hw->has_refsel) 4408c2ecf20Sopenharmony_ci l = FLD_MOD(l, 3, 22, 21); /* REFSEL = sysclk */ 4418c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 23, 23); /* M6_CLOCK_EN */ 4428c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */ 4438c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION2); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (hw->errata_i932) { 4468c2ecf20Sopenharmony_ci int cnt = 0; 4478c2ecf20Sopenharmony_ci u32 sleep_time; 4488c2ecf20Sopenharmony_ci const u32 max_lock_retries = 20; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * Calculate wait time for PLL LOCK 4528c2ecf20Sopenharmony_ci * 1000 REFCLK cycles in us. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci sleep_time = DIV_ROUND_UP(1000*1000*1000, cinfo->fint); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci for (cnt = 0; cnt < max_lock_retries; cnt++) { 4578c2ecf20Sopenharmony_ci writel_relaxed(1, base + PLL_GO); /* PLL_GO */ 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /** 4608c2ecf20Sopenharmony_ci * read the register back to ensure the write is 4618c2ecf20Sopenharmony_ci * flushed 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci readl_relaxed(base + PLL_GO); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci usleep_range(sleep_time, sleep_time + 5); 4668c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_STATUS); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (pll_is_locked(l) && 4698c2ecf20Sopenharmony_ci !(readl_relaxed(base + PLL_GO) & 0x1)) 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (cnt == max_lock_retries) { 4758c2ecf20Sopenharmony_ci DSSERR("cannot lock PLL\n"); 4768c2ecf20Sopenharmony_ci r = -EIO; 4778c2ecf20Sopenharmony_ci goto err; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci } else { 4808c2ecf20Sopenharmony_ci writel_relaxed(1, base + PLL_GO); /* PLL_GO */ 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { 4838c2ecf20Sopenharmony_ci DSSERR("DSS DPLL GO bit not going down.\n"); 4848c2ecf20Sopenharmony_ci r = -EIO; 4858c2ecf20Sopenharmony_ci goto err; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { 4898c2ecf20Sopenharmony_ci DSSERR("cannot lock DSS DPLL\n"); 4908c2ecf20Sopenharmony_ci r = -EIO; 4918c2ecf20Sopenharmony_ci goto err; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION2); 4968c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 14, 14); /* PHY_CLKINEN */ 4978c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[0] ? 1 : 0, 16, 16); /* M4_CLOCK_EN */ 4988c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[1] ? 1 : 0, 18, 18); /* M5_CLOCK_EN */ 4998c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 20, 20); /* HSDIVBYPASS */ 5008c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[2] ? 1 : 0, 23, 23); /* M6_CLOCK_EN */ 5018c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[3] ? 1 : 0, 25, 25); /* M7_CLOCK_EN */ 5028c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION2); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci r = dss_wait_hsdiv_ack(pll, 5058c2ecf20Sopenharmony_ci (cinfo->mX[0] ? BIT(7) : 0) | 5068c2ecf20Sopenharmony_ci (cinfo->mX[1] ? BIT(8) : 0) | 5078c2ecf20Sopenharmony_ci (cinfo->mX[2] ? BIT(10) : 0) | 5088c2ecf20Sopenharmony_ci (cinfo->mX[3] ? BIT(11) : 0)); 5098c2ecf20Sopenharmony_ci if (r) { 5108c2ecf20Sopenharmony_ci DSSERR("failed to enable HSDIV clocks\n"); 5118c2ecf20Sopenharmony_ci goto err; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cierr: 5158c2ecf20Sopenharmony_ci return r; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ciint dss_pll_write_config_type_b(struct dss_pll *pll, 5198c2ecf20Sopenharmony_ci const struct dss_pll_clock_info *cinfo) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 5228c2ecf20Sopenharmony_ci void __iomem *base = pll->base; 5238c2ecf20Sopenharmony_ci u32 l; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci l = 0; 5268c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->m, 20, 9); /* PLL_REGM */ 5278c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->n - 1, 8, 1); /* PLL_REGN */ 5288c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION1); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION2); 5318c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ 5328c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x1, 13, 13); /* PLL_REFEN */ 5338c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x0, 14, 14); /* PHY_CLKINEN */ 5348c2ecf20Sopenharmony_ci if (hw->has_refsel) 5358c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x3, 22, 21); /* REFSEL = SYSCLK */ 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* PLL_SELFREQDCO */ 5388c2ecf20Sopenharmony_ci if (cinfo->clkdco > hw->clkdco_low) 5398c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x4, 3, 1); 5408c2ecf20Sopenharmony_ci else 5418c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x2, 3, 1); 5428c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION2); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION3); 5458c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->sd, 17, 10); /* PLL_REGSD */ 5468c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION3); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION4); 5498c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[0], 24, 18); /* PLL_REGM2 */ 5508c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mf, 17, 0); /* PLL_REGM_F */ 5518c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION4); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci writel_relaxed(1, base + PLL_GO); /* PLL_GO */ 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { 5568c2ecf20Sopenharmony_ci DSSERR("DSS DPLL GO bit not going down.\n"); 5578c2ecf20Sopenharmony_ci return -EIO; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { 5618c2ecf20Sopenharmony_ci DSSERR("cannot lock DSS DPLL\n"); 5628c2ecf20Sopenharmony_ci return -ETIMEDOUT; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 567