18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define DSS_SUBSYS_NAME "PLL" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <video/omapfb_dss.h> 158c2ecf20Sopenharmony_ci 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_cistatic struct dss_pll *dss_plls[4]; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint dss_pll_register(struct dss_pll *pll) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int i; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { 358c2ecf20Sopenharmony_ci if (!dss_plls[i]) { 368c2ecf20Sopenharmony_ci dss_plls[i] = pll; 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return -EBUSY; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_civoid dss_pll_unregister(struct dss_pll *pll) 458c2ecf20Sopenharmony_ci{ 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 return; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct dss_pll *dss_pll_find(const char *name) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { 618c2ecf20Sopenharmony_ci if (dss_plls[i] && strcmp(dss_plls[i]->name, name) == 0) 628c2ecf20Sopenharmony_ci return dss_plls[i]; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return NULL; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciint dss_pll_enable(struct dss_pll *pll) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int r; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci r = clk_prepare_enable(pll->clkin); 738c2ecf20Sopenharmony_ci if (r) 748c2ecf20Sopenharmony_ci return r; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (pll->regulator) { 778c2ecf20Sopenharmony_ci r = regulator_enable(pll->regulator); 788c2ecf20Sopenharmony_ci if (r) 798c2ecf20Sopenharmony_ci goto err_reg; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci r = pll->ops->enable(pll); 838c2ecf20Sopenharmony_ci if (r) 848c2ecf20Sopenharmony_ci goto err_enable; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cierr_enable: 898c2ecf20Sopenharmony_ci if (pll->regulator) 908c2ecf20Sopenharmony_ci regulator_disable(pll->regulator); 918c2ecf20Sopenharmony_cierr_reg: 928c2ecf20Sopenharmony_ci clk_disable_unprepare(pll->clkin); 938c2ecf20Sopenharmony_ci return r; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid dss_pll_disable(struct dss_pll *pll) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci pll->ops->disable(pll); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (pll->regulator) 1018c2ecf20Sopenharmony_ci regulator_disable(pll->regulator); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci clk_disable_unprepare(pll->clkin); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci memset(&pll->cinfo, 0, sizeof(pll->cinfo)); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ciint dss_pll_set_config(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci int r; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci r = pll->ops->set_config(pll, cinfo); 1138c2ecf20Sopenharmony_ci if (r) 1148c2ecf20Sopenharmony_ci return r; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci pll->cinfo = *cinfo; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cibool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco, 1228c2ecf20Sopenharmony_ci unsigned long out_min, unsigned long out_max, 1238c2ecf20Sopenharmony_ci dss_hsdiv_calc_func func, void *data) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 1268c2ecf20Sopenharmony_ci int m, m_start, m_stop; 1278c2ecf20Sopenharmony_ci unsigned long out; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci out_min = out_min ? out_min : 1; 1308c2ecf20Sopenharmony_ci out_max = out_max ? out_max : ULONG_MAX; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci m_start = max(DIV_ROUND_UP(clkdco, out_max), 1ul); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci m_stop = min((unsigned)(clkdco / out_min), hw->mX_max); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (m = m_start; m <= m_stop; ++m) { 1378c2ecf20Sopenharmony_ci out = clkdco / m; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (func(m, out, data)) 1408c2ecf20Sopenharmony_ci return true; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return false; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cibool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin, 1478c2ecf20Sopenharmony_ci unsigned long pll_min, unsigned long pll_max, 1488c2ecf20Sopenharmony_ci dss_pll_calc_func func, void *data) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 1518c2ecf20Sopenharmony_ci int n, n_start, n_stop; 1528c2ecf20Sopenharmony_ci int m, m_start, m_stop; 1538c2ecf20Sopenharmony_ci unsigned long fint, clkdco; 1548c2ecf20Sopenharmony_ci unsigned long pll_hw_max; 1558c2ecf20Sopenharmony_ci unsigned long fint_hw_min, fint_hw_max; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pll_hw_max = hw->clkdco_max; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci fint_hw_min = hw->fint_min; 1608c2ecf20Sopenharmony_ci fint_hw_max = hw->fint_max; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); 1638c2ecf20Sopenharmony_ci n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci pll_max = pll_max ? pll_max : ULONG_MAX; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (n = n_start; n <= n_stop; ++n) { 1688c2ecf20Sopenharmony_ci fint = clkin / n; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), 1718c2ecf20Sopenharmony_ci 1ul); 1728c2ecf20Sopenharmony_ci m_stop = min3((unsigned)(pll_max / fint / 2), 1738c2ecf20Sopenharmony_ci (unsigned)(pll_hw_max / fint / 2), 1748c2ecf20Sopenharmony_ci hw->m_max); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for (m = m_start; m <= m_stop; ++m) { 1778c2ecf20Sopenharmony_ci clkdco = 2 * m * fint; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (func(n, m, fint, clkdco, data)) 1808c2ecf20Sopenharmony_ci return true; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return false; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int wait_for_bit_change(void __iomem *reg, int bitnum, int value) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci unsigned long timeout; 1908c2ecf20Sopenharmony_ci ktime_t wait; 1918c2ecf20Sopenharmony_ci int t; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* first busyloop to see if the bit changes right away */ 1948c2ecf20Sopenharmony_ci t = 100; 1958c2ecf20Sopenharmony_ci while (t-- > 0) { 1968c2ecf20Sopenharmony_ci if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value) 1978c2ecf20Sopenharmony_ci return value; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* then loop for 500ms, sleeping for 1ms in between */ 2018c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 2028c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 2038c2ecf20Sopenharmony_ci if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value) 2048c2ecf20Sopenharmony_ci return value; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci wait = ns_to_ktime(1000 * 1000); 2078c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 2088c2ecf20Sopenharmony_ci schedule_hrtimeout(&wait, HRTIMER_MODE_REL); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return !value; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciint dss_pll_wait_reset_done(struct dss_pll *pll) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci void __iomem *base = pll->base; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_STATUS, 0, 1) != 1) 2198c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int t = 100; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci while (t-- > 0) { 2298c2ecf20Sopenharmony_ci u32 v = readl_relaxed(pll->base + PLL_STATUS); 2308c2ecf20Sopenharmony_ci v &= hsdiv_ack_mask; 2318c2ecf20Sopenharmony_ci if (v == hsdiv_ack_mask) 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciint dss_pll_write_config_type_a(struct dss_pll *pll, 2398c2ecf20Sopenharmony_ci const struct dss_pll_clock_info *cinfo) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 2428c2ecf20Sopenharmony_ci void __iomem *base = pll->base; 2438c2ecf20Sopenharmony_ci int r = 0; 2448c2ecf20Sopenharmony_ci u32 l; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci l = 0; 2478c2ecf20Sopenharmony_ci if (hw->has_stopmode) 2488c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 0, 0); /* PLL_STOPMODE */ 2498c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->n - 1, hw->n_msb, hw->n_lsb); /* PLL_REGN */ 2508c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->m, hw->m_msb, hw->m_lsb); /* PLL_REGM */ 2518c2ecf20Sopenharmony_ci /* M4 */ 2528c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[0] ? cinfo->mX[0] - 1 : 0, 2538c2ecf20Sopenharmony_ci hw->mX_msb[0], hw->mX_lsb[0]); 2548c2ecf20Sopenharmony_ci /* M5 */ 2558c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[1] ? cinfo->mX[1] - 1 : 0, 2568c2ecf20Sopenharmony_ci hw->mX_msb[1], hw->mX_lsb[1]); 2578c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION1); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci l = 0; 2608c2ecf20Sopenharmony_ci /* M6 */ 2618c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[2] ? cinfo->mX[2] - 1 : 0, 2628c2ecf20Sopenharmony_ci hw->mX_msb[2], hw->mX_lsb[2]); 2638c2ecf20Sopenharmony_ci /* M7 */ 2648c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[3] ? cinfo->mX[3] - 1 : 0, 2658c2ecf20Sopenharmony_ci hw->mX_msb[3], hw->mX_lsb[3]); 2668c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION3); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION2); 2698c2ecf20Sopenharmony_ci if (hw->has_freqsel) { 2708c2ecf20Sopenharmony_ci u32 f = cinfo->fint < 1000000 ? 0x3 : 2718c2ecf20Sopenharmony_ci cinfo->fint < 1250000 ? 0x4 : 2728c2ecf20Sopenharmony_ci cinfo->fint < 1500000 ? 0x5 : 2738c2ecf20Sopenharmony_ci cinfo->fint < 1750000 ? 0x6 : 2748c2ecf20Sopenharmony_ci 0x7; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci l = FLD_MOD(l, f, 4, 1); /* PLL_FREQSEL */ 2778c2ecf20Sopenharmony_ci } else if (hw->has_selfreqdco) { 2788c2ecf20Sopenharmony_ci u32 f = cinfo->clkdco < hw->clkdco_low ? 0x2 : 0x4; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci l = FLD_MOD(l, f, 3, 1); /* PLL_SELFREQDCO */ 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 13, 13); /* PLL_REFEN */ 2838c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 14, 14); /* PHY_CLKINEN */ 2848c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 16, 16); /* M4_CLOCK_EN */ 2858c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 18, 18); /* M5_CLOCK_EN */ 2868c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 20, 20); /* HSDIVBYPASS */ 2878c2ecf20Sopenharmony_ci if (hw->has_refsel) 2888c2ecf20Sopenharmony_ci l = FLD_MOD(l, 3, 22, 21); /* REFSEL = sysclk */ 2898c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 23, 23); /* M6_CLOCK_EN */ 2908c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */ 2918c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION2); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci writel_relaxed(1, base + PLL_GO); /* PLL_GO */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { 2968c2ecf20Sopenharmony_ci DSSERR("DSS DPLL GO bit not going down.\n"); 2978c2ecf20Sopenharmony_ci r = -EIO; 2988c2ecf20Sopenharmony_ci goto err; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { 3028c2ecf20Sopenharmony_ci DSSERR("cannot lock DSS DPLL\n"); 3038c2ecf20Sopenharmony_ci r = -EIO; 3048c2ecf20Sopenharmony_ci goto err; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION2); 3088c2ecf20Sopenharmony_ci l = FLD_MOD(l, 1, 14, 14); /* PHY_CLKINEN */ 3098c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[0] ? 1 : 0, 16, 16); /* M4_CLOCK_EN */ 3108c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[1] ? 1 : 0, 18, 18); /* M5_CLOCK_EN */ 3118c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0, 20, 20); /* HSDIVBYPASS */ 3128c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[2] ? 1 : 0, 23, 23); /* M6_CLOCK_EN */ 3138c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[3] ? 1 : 0, 25, 25); /* M7_CLOCK_EN */ 3148c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION2); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci r = dss_wait_hsdiv_ack(pll, 3178c2ecf20Sopenharmony_ci (cinfo->mX[0] ? BIT(7) : 0) | 3188c2ecf20Sopenharmony_ci (cinfo->mX[1] ? BIT(8) : 0) | 3198c2ecf20Sopenharmony_ci (cinfo->mX[2] ? BIT(10) : 0) | 3208c2ecf20Sopenharmony_ci (cinfo->mX[3] ? BIT(11) : 0)); 3218c2ecf20Sopenharmony_ci if (r) { 3228c2ecf20Sopenharmony_ci DSSERR("failed to enable HSDIV clocks\n"); 3238c2ecf20Sopenharmony_ci goto err; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cierr: 3278c2ecf20Sopenharmony_ci return r; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ciint dss_pll_write_config_type_b(struct dss_pll *pll, 3318c2ecf20Sopenharmony_ci const struct dss_pll_clock_info *cinfo) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci const struct dss_pll_hw *hw = pll->hw; 3348c2ecf20Sopenharmony_ci void __iomem *base = pll->base; 3358c2ecf20Sopenharmony_ci u32 l; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci l = 0; 3388c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->m, 20, 9); /* PLL_REGM */ 3398c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->n - 1, 8, 1); /* PLL_REGN */ 3408c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION1); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION2); 3438c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ 3448c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x1, 13, 13); /* PLL_REFEN */ 3458c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x0, 14, 14); /* PHY_CLKINEN */ 3468c2ecf20Sopenharmony_ci if (hw->has_refsel) 3478c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x3, 22, 21); /* REFSEL = SYSCLK */ 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* PLL_SELFREQDCO */ 3508c2ecf20Sopenharmony_ci if (cinfo->clkdco > hw->clkdco_low) 3518c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x4, 3, 1); 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci l = FLD_MOD(l, 0x2, 3, 1); 3548c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION2); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION3); 3578c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->sd, 17, 10); /* PLL_REGSD */ 3588c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION3); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci l = readl_relaxed(base + PLL_CONFIGURATION4); 3618c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mX[0], 24, 18); /* PLL_REGM2 */ 3628c2ecf20Sopenharmony_ci l = FLD_MOD(l, cinfo->mf, 17, 0); /* PLL_REGM_F */ 3638c2ecf20Sopenharmony_ci writel_relaxed(l, base + PLL_CONFIGURATION4); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci writel_relaxed(1, base + PLL_GO); /* PLL_GO */ 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { 3688c2ecf20Sopenharmony_ci DSSERR("DSS DPLL GO bit not going down.\n"); 3698c2ecf20Sopenharmony_ci return -EIO; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { 3738c2ecf20Sopenharmony_ci DSSERR("cannot lock DSS DPLL\n"); 3748c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 379