18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file contains the utility functions to register the pll clocks. 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include "clk.h" 168c2ecf20Sopenharmony_ci#include "clk-pll.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PLL_TIMEOUT_MS 10 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct samsung_clk_pll { 218c2ecf20Sopenharmony_ci struct clk_hw hw; 228c2ecf20Sopenharmony_ci void __iomem *lock_reg; 238c2ecf20Sopenharmony_ci void __iomem *con_reg; 248c2ecf20Sopenharmony_ci /* PLL enable control bit offset in @con_reg register */ 258c2ecf20Sopenharmony_ci unsigned short enable_offs; 268c2ecf20Sopenharmony_ci /* PLL lock status bit offset in @con_reg register */ 278c2ecf20Sopenharmony_ci unsigned short lock_offs; 288c2ecf20Sopenharmony_ci enum samsung_pll_type type; 298c2ecf20Sopenharmony_ci unsigned int rate_count; 308c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate_table; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct samsung_pll_rate_table *samsung_get_pll_settings( 368c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll, unsigned long rate) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate_table = pll->rate_table; 398c2ecf20Sopenharmony_ci int i; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for (i = 0; i < pll->rate_count; i++) { 428c2ecf20Sopenharmony_ci if (rate == rate_table[i].rate) 438c2ecf20Sopenharmony_ci return &rate_table[i]; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic long samsung_pll_round_rate(struct clk_hw *hw, 508c2ecf20Sopenharmony_ci unsigned long drate, unsigned long *prate) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 538c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate_table = pll->rate_table; 548c2ecf20Sopenharmony_ci int i; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* Assumming rate_table is in descending order */ 578c2ecf20Sopenharmony_ci for (i = 0; i < pll->rate_count; i++) { 588c2ecf20Sopenharmony_ci if (drate >= rate_table[i].rate) 598c2ecf20Sopenharmony_ci return rate_table[i].rate; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* return minimum supported value */ 638c2ecf20Sopenharmony_ci return rate_table[i - 1].rate; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int samsung_pll3xxx_enable(struct clk_hw *hw) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 698c2ecf20Sopenharmony_ci u32 tmp; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 728c2ecf20Sopenharmony_ci tmp |= BIT(pll->enable_offs); 738c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* wait lock time */ 768c2ecf20Sopenharmony_ci do { 778c2ecf20Sopenharmony_ci cpu_relax(); 788c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 798c2ecf20Sopenharmony_ci } while (!(tmp & BIT(pll->lock_offs))); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void samsung_pll3xxx_disable(struct clk_hw *hw) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 878c2ecf20Sopenharmony_ci u32 tmp; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 908c2ecf20Sopenharmony_ci tmp &= ~BIT(pll->enable_offs); 918c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * PLL2126 Clock Type 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define PLL2126_MDIV_MASK (0xff) 998c2ecf20Sopenharmony_ci#define PLL2126_PDIV_MASK (0x3f) 1008c2ecf20Sopenharmony_ci#define PLL2126_SDIV_MASK (0x3) 1018c2ecf20Sopenharmony_ci#define PLL2126_MDIV_SHIFT (16) 1028c2ecf20Sopenharmony_ci#define PLL2126_PDIV_SHIFT (8) 1038c2ecf20Sopenharmony_ci#define PLL2126_SDIV_SHIFT (0) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic unsigned long samsung_pll2126_recalc_rate(struct clk_hw *hw, 1068c2ecf20Sopenharmony_ci unsigned long parent_rate) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 1098c2ecf20Sopenharmony_ci u32 pll_con, mdiv, pdiv, sdiv; 1108c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 1138c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL2126_MDIV_SHIFT) & PLL2126_MDIV_MASK; 1148c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL2126_PDIV_SHIFT) & PLL2126_PDIV_MASK; 1158c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLL2126_SDIV_SHIFT) & PLL2126_SDIV_MASK; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci fvco *= (mdiv + 8); 1188c2ecf20Sopenharmony_ci do_div(fvco, (pdiv + 2) << sdiv); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return (unsigned long)fvco; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2126_clk_ops = { 1248c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2126_recalc_rate, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* 1288c2ecf20Sopenharmony_ci * PLL3000 Clock Type 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define PLL3000_MDIV_MASK (0xff) 1328c2ecf20Sopenharmony_ci#define PLL3000_PDIV_MASK (0x3) 1338c2ecf20Sopenharmony_ci#define PLL3000_SDIV_MASK (0x3) 1348c2ecf20Sopenharmony_ci#define PLL3000_MDIV_SHIFT (16) 1358c2ecf20Sopenharmony_ci#define PLL3000_PDIV_SHIFT (8) 1368c2ecf20Sopenharmony_ci#define PLL3000_SDIV_SHIFT (0) 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic unsigned long samsung_pll3000_recalc_rate(struct clk_hw *hw, 1398c2ecf20Sopenharmony_ci unsigned long parent_rate) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 1428c2ecf20Sopenharmony_ci u32 pll_con, mdiv, pdiv, sdiv; 1438c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 1468c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL3000_MDIV_SHIFT) & PLL3000_MDIV_MASK; 1478c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL3000_PDIV_SHIFT) & PLL3000_PDIV_MASK; 1488c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLL3000_SDIV_SHIFT) & PLL3000_SDIV_MASK; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci fvco *= (2 * (mdiv + 8)); 1518c2ecf20Sopenharmony_ci do_div(fvco, pdiv << sdiv); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return (unsigned long)fvco; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll3000_clk_ops = { 1578c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll3000_recalc_rate, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * PLL35xx Clock Type 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci/* Maximum lock time can be 270 * PDIV cycles */ 1648c2ecf20Sopenharmony_ci#define PLL35XX_LOCK_FACTOR (270) 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#define PLL35XX_MDIV_MASK (0x3FF) 1678c2ecf20Sopenharmony_ci#define PLL35XX_PDIV_MASK (0x3F) 1688c2ecf20Sopenharmony_ci#define PLL35XX_SDIV_MASK (0x7) 1698c2ecf20Sopenharmony_ci#define PLL35XX_MDIV_SHIFT (16) 1708c2ecf20Sopenharmony_ci#define PLL35XX_PDIV_SHIFT (8) 1718c2ecf20Sopenharmony_ci#define PLL35XX_SDIV_SHIFT (0) 1728c2ecf20Sopenharmony_ci#define PLL35XX_LOCK_STAT_SHIFT (29) 1738c2ecf20Sopenharmony_ci#define PLL35XX_ENABLE_SHIFT (31) 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, 1768c2ecf20Sopenharmony_ci unsigned long parent_rate) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 1798c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con; 1808c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 1838c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; 1848c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; 1858c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci fvco *= mdiv; 1888c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return (unsigned long)fvco; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic inline bool samsung_pll35xx_mp_change( 1948c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate, u32 pll_con) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci u32 old_mdiv, old_pdiv; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; 1998c2ecf20Sopenharmony_ci old_pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, 2058c2ecf20Sopenharmony_ci unsigned long prate) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 2088c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 2098c2ecf20Sopenharmony_ci u32 tmp; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Get required rate settings from table */ 2128c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 2138c2ecf20Sopenharmony_ci if (!rate) { 2148c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 2158c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!(samsung_pll35xx_mp_change(rate, tmp))) { 2228c2ecf20Sopenharmony_ci /* If only s change, change just s value only*/ 2238c2ecf20Sopenharmony_ci tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT); 2248c2ecf20Sopenharmony_ci tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT; 2258c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 2318c2ecf20Sopenharmony_ci writel_relaxed(rate->pdiv * PLL35XX_LOCK_FACTOR, 2328c2ecf20Sopenharmony_ci pll->lock_reg); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Change PLL PMS values */ 2358c2ecf20Sopenharmony_ci tmp &= ~((PLL35XX_MDIV_MASK << PLL35XX_MDIV_SHIFT) | 2368c2ecf20Sopenharmony_ci (PLL35XX_PDIV_MASK << PLL35XX_PDIV_SHIFT) | 2378c2ecf20Sopenharmony_ci (PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT)); 2388c2ecf20Sopenharmony_ci tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) | 2398c2ecf20Sopenharmony_ci (rate->pdiv << PLL35XX_PDIV_SHIFT) | 2408c2ecf20Sopenharmony_ci (rate->sdiv << PLL35XX_SDIV_SHIFT); 2418c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Wait until the PLL is locked if it is enabled. */ 2448c2ecf20Sopenharmony_ci if (tmp & BIT(pll->enable_offs)) { 2458c2ecf20Sopenharmony_ci do { 2468c2ecf20Sopenharmony_ci cpu_relax(); 2478c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 2488c2ecf20Sopenharmony_ci } while (!(tmp & BIT(pll->lock_offs))); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll35xx_clk_ops = { 2548c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll35xx_recalc_rate, 2558c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 2568c2ecf20Sopenharmony_ci .set_rate = samsung_pll35xx_set_rate, 2578c2ecf20Sopenharmony_ci .enable = samsung_pll3xxx_enable, 2588c2ecf20Sopenharmony_ci .disable = samsung_pll3xxx_disable, 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll35xx_clk_min_ops = { 2628c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll35xx_recalc_rate, 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * PLL36xx Clock Type 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci/* Maximum lock time can be 3000 * PDIV cycles */ 2698c2ecf20Sopenharmony_ci#define PLL36XX_LOCK_FACTOR (3000) 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci#define PLL36XX_KDIV_MASK (0xFFFF) 2728c2ecf20Sopenharmony_ci#define PLL36XX_MDIV_MASK (0x1FF) 2738c2ecf20Sopenharmony_ci#define PLL36XX_PDIV_MASK (0x3F) 2748c2ecf20Sopenharmony_ci#define PLL36XX_SDIV_MASK (0x7) 2758c2ecf20Sopenharmony_ci#define PLL36XX_MDIV_SHIFT (16) 2768c2ecf20Sopenharmony_ci#define PLL36XX_PDIV_SHIFT (8) 2778c2ecf20Sopenharmony_ci#define PLL36XX_SDIV_SHIFT (0) 2788c2ecf20Sopenharmony_ci#define PLL36XX_KDIV_SHIFT (0) 2798c2ecf20Sopenharmony_ci#define PLL36XX_LOCK_STAT_SHIFT (29) 2808c2ecf20Sopenharmony_ci#define PLL36XX_ENABLE_SHIFT (31) 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, 2838c2ecf20Sopenharmony_ci unsigned long parent_rate) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 2868c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con0, pll_con1; 2878c2ecf20Sopenharmony_ci s16 kdiv; 2888c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 2918c2ecf20Sopenharmony_ci pll_con1 = readl_relaxed(pll->con_reg + 4); 2928c2ecf20Sopenharmony_ci mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; 2938c2ecf20Sopenharmony_ci pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; 2948c2ecf20Sopenharmony_ci sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK; 2958c2ecf20Sopenharmony_ci kdiv = (s16)(pll_con1 & PLL36XX_KDIV_MASK); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci fvco *= (mdiv << 16) + kdiv; 2988c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 2998c2ecf20Sopenharmony_ci fvco >>= 16; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return (unsigned long)fvco; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic inline bool samsung_pll36xx_mpk_change( 3058c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate, u32 pll_con0, u32 pll_con1) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci u32 old_mdiv, old_pdiv, old_kdiv; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci old_mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; 3108c2ecf20Sopenharmony_ci old_pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; 3118c2ecf20Sopenharmony_ci old_kdiv = (pll_con1 >> PLL36XX_KDIV_SHIFT) & PLL36XX_KDIV_MASK; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || 3148c2ecf20Sopenharmony_ci rate->kdiv != old_kdiv); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate, 3188c2ecf20Sopenharmony_ci unsigned long parent_rate) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 3218c2ecf20Sopenharmony_ci u32 tmp, pll_con0, pll_con1; 3228c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 3258c2ecf20Sopenharmony_ci if (!rate) { 3268c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 3278c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 3288c2ecf20Sopenharmony_ci return -EINVAL; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 3328c2ecf20Sopenharmony_ci pll_con1 = readl_relaxed(pll->con_reg + 4); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!(samsung_pll36xx_mpk_change(rate, pll_con0, pll_con1))) { 3358c2ecf20Sopenharmony_ci /* If only s change, change just s value only*/ 3368c2ecf20Sopenharmony_ci pll_con0 &= ~(PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT); 3378c2ecf20Sopenharmony_ci pll_con0 |= (rate->sdiv << PLL36XX_SDIV_SHIFT); 3388c2ecf20Sopenharmony_ci writel_relaxed(pll_con0, pll->con_reg); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 3448c2ecf20Sopenharmony_ci writel_relaxed(rate->pdiv * PLL36XX_LOCK_FACTOR, pll->lock_reg); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Change PLL PMS values */ 3478c2ecf20Sopenharmony_ci pll_con0 &= ~((PLL36XX_MDIV_MASK << PLL36XX_MDIV_SHIFT) | 3488c2ecf20Sopenharmony_ci (PLL36XX_PDIV_MASK << PLL36XX_PDIV_SHIFT) | 3498c2ecf20Sopenharmony_ci (PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT)); 3508c2ecf20Sopenharmony_ci pll_con0 |= (rate->mdiv << PLL36XX_MDIV_SHIFT) | 3518c2ecf20Sopenharmony_ci (rate->pdiv << PLL36XX_PDIV_SHIFT) | 3528c2ecf20Sopenharmony_ci (rate->sdiv << PLL36XX_SDIV_SHIFT); 3538c2ecf20Sopenharmony_ci writel_relaxed(pll_con0, pll->con_reg); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci pll_con1 &= ~(PLL36XX_KDIV_MASK << PLL36XX_KDIV_SHIFT); 3568c2ecf20Sopenharmony_ci pll_con1 |= rate->kdiv << PLL36XX_KDIV_SHIFT; 3578c2ecf20Sopenharmony_ci writel_relaxed(pll_con1, pll->con_reg + 4); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* wait_lock_time */ 3608c2ecf20Sopenharmony_ci if (pll_con0 & BIT(pll->enable_offs)) { 3618c2ecf20Sopenharmony_ci do { 3628c2ecf20Sopenharmony_ci cpu_relax(); 3638c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 3648c2ecf20Sopenharmony_ci } while (!(tmp & BIT(pll->lock_offs))); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll36xx_clk_ops = { 3718c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll36xx_recalc_rate, 3728c2ecf20Sopenharmony_ci .set_rate = samsung_pll36xx_set_rate, 3738c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 3748c2ecf20Sopenharmony_ci .enable = samsung_pll3xxx_enable, 3758c2ecf20Sopenharmony_ci .disable = samsung_pll3xxx_disable, 3768c2ecf20Sopenharmony_ci}; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll36xx_clk_min_ops = { 3798c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll36xx_recalc_rate, 3808c2ecf20Sopenharmony_ci}; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * PLL45xx Clock Type 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci#define PLL4502_LOCK_FACTOR 400 3868c2ecf20Sopenharmony_ci#define PLL4508_LOCK_FACTOR 240 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci#define PLL45XX_MDIV_MASK (0x3FF) 3898c2ecf20Sopenharmony_ci#define PLL45XX_PDIV_MASK (0x3F) 3908c2ecf20Sopenharmony_ci#define PLL45XX_SDIV_MASK (0x7) 3918c2ecf20Sopenharmony_ci#define PLL45XX_AFC_MASK (0x1F) 3928c2ecf20Sopenharmony_ci#define PLL45XX_MDIV_SHIFT (16) 3938c2ecf20Sopenharmony_ci#define PLL45XX_PDIV_SHIFT (8) 3948c2ecf20Sopenharmony_ci#define PLL45XX_SDIV_SHIFT (0) 3958c2ecf20Sopenharmony_ci#define PLL45XX_AFC_SHIFT (0) 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci#define PLL45XX_ENABLE BIT(31) 3988c2ecf20Sopenharmony_ci#define PLL45XX_LOCKED BIT(29) 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, 4018c2ecf20Sopenharmony_ci unsigned long parent_rate) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 4048c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con; 4058c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 4088c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; 4098c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; 4108c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (pll->type == pll_4508) 4138c2ecf20Sopenharmony_ci sdiv = sdiv - 1; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci fvco *= mdiv; 4168c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return (unsigned long)fvco; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic bool samsung_pll45xx_mp_change(u32 pll_con0, u32 pll_con1, 4228c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci u32 old_mdiv, old_pdiv, old_afc; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci old_mdiv = (pll_con0 >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; 4278c2ecf20Sopenharmony_ci old_pdiv = (pll_con0 >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; 4288c2ecf20Sopenharmony_ci old_afc = (pll_con1 >> PLL45XX_AFC_SHIFT) & PLL45XX_AFC_MASK; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv 4318c2ecf20Sopenharmony_ci || old_afc != rate->afc); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate, 4358c2ecf20Sopenharmony_ci unsigned long prate) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 4388c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 4398c2ecf20Sopenharmony_ci u32 con0, con1; 4408c2ecf20Sopenharmony_ci ktime_t start; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Get required rate settings from table */ 4438c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 4448c2ecf20Sopenharmony_ci if (!rate) { 4458c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 4468c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci con0 = readl_relaxed(pll->con_reg); 4518c2ecf20Sopenharmony_ci con1 = readl_relaxed(pll->con_reg + 0x4); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!(samsung_pll45xx_mp_change(con0, con1, rate))) { 4548c2ecf20Sopenharmony_ci /* If only s change, change just s value only*/ 4558c2ecf20Sopenharmony_ci con0 &= ~(PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT); 4568c2ecf20Sopenharmony_ci con0 |= rate->sdiv << PLL45XX_SDIV_SHIFT; 4578c2ecf20Sopenharmony_ci writel_relaxed(con0, pll->con_reg); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Set PLL PMS values. */ 4638c2ecf20Sopenharmony_ci con0 &= ~((PLL45XX_MDIV_MASK << PLL45XX_MDIV_SHIFT) | 4648c2ecf20Sopenharmony_ci (PLL45XX_PDIV_MASK << PLL45XX_PDIV_SHIFT) | 4658c2ecf20Sopenharmony_ci (PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT)); 4668c2ecf20Sopenharmony_ci con0 |= (rate->mdiv << PLL45XX_MDIV_SHIFT) | 4678c2ecf20Sopenharmony_ci (rate->pdiv << PLL45XX_PDIV_SHIFT) | 4688c2ecf20Sopenharmony_ci (rate->sdiv << PLL45XX_SDIV_SHIFT); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* Set PLL AFC value. */ 4718c2ecf20Sopenharmony_ci con1 = readl_relaxed(pll->con_reg + 0x4); 4728c2ecf20Sopenharmony_ci con1 &= ~(PLL45XX_AFC_MASK << PLL45XX_AFC_SHIFT); 4738c2ecf20Sopenharmony_ci con1 |= (rate->afc << PLL45XX_AFC_SHIFT); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 4768c2ecf20Sopenharmony_ci switch (pll->type) { 4778c2ecf20Sopenharmony_ci case pll_4502: 4788c2ecf20Sopenharmony_ci writel_relaxed(rate->pdiv * PLL4502_LOCK_FACTOR, pll->lock_reg); 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci case pll_4508: 4818c2ecf20Sopenharmony_ci writel_relaxed(rate->pdiv * PLL4508_LOCK_FACTOR, pll->lock_reg); 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci default: 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Set new configuration. */ 4888c2ecf20Sopenharmony_ci writel_relaxed(con1, pll->con_reg + 0x4); 4898c2ecf20Sopenharmony_ci writel_relaxed(con0, pll->con_reg); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Wait for locking. */ 4928c2ecf20Sopenharmony_ci start = ktime_get(); 4938c2ecf20Sopenharmony_ci while (!(readl_relaxed(pll->con_reg) & PLL45XX_LOCKED)) { 4948c2ecf20Sopenharmony_ci ktime_t delta = ktime_sub(ktime_get(), start); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) { 4978c2ecf20Sopenharmony_ci pr_err("%s: could not lock PLL %s\n", 4988c2ecf20Sopenharmony_ci __func__, clk_hw_get_name(hw)); 4998c2ecf20Sopenharmony_ci return -EFAULT; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci cpu_relax(); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll45xx_clk_ops = { 5098c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll45xx_recalc_rate, 5108c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 5118c2ecf20Sopenharmony_ci .set_rate = samsung_pll45xx_set_rate, 5128c2ecf20Sopenharmony_ci}; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll45xx_clk_min_ops = { 5158c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll45xx_recalc_rate, 5168c2ecf20Sopenharmony_ci}; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* 5198c2ecf20Sopenharmony_ci * PLL46xx Clock Type 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci#define PLL46XX_LOCK_FACTOR 3000 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci#define PLL46XX_VSEL_MASK (1) 5248c2ecf20Sopenharmony_ci#define PLL46XX_MDIV_MASK (0x1FF) 5258c2ecf20Sopenharmony_ci#define PLL1460X_MDIV_MASK (0x3FF) 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci#define PLL46XX_PDIV_MASK (0x3F) 5288c2ecf20Sopenharmony_ci#define PLL46XX_SDIV_MASK (0x7) 5298c2ecf20Sopenharmony_ci#define PLL46XX_VSEL_SHIFT (27) 5308c2ecf20Sopenharmony_ci#define PLL46XX_MDIV_SHIFT (16) 5318c2ecf20Sopenharmony_ci#define PLL46XX_PDIV_SHIFT (8) 5328c2ecf20Sopenharmony_ci#define PLL46XX_SDIV_SHIFT (0) 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci#define PLL46XX_KDIV_MASK (0xFFFF) 5358c2ecf20Sopenharmony_ci#define PLL4650C_KDIV_MASK (0xFFF) 5368c2ecf20Sopenharmony_ci#define PLL46XX_KDIV_SHIFT (0) 5378c2ecf20Sopenharmony_ci#define PLL46XX_MFR_MASK (0x3F) 5388c2ecf20Sopenharmony_ci#define PLL46XX_MRR_MASK (0x1F) 5398c2ecf20Sopenharmony_ci#define PLL46XX_KDIV_SHIFT (0) 5408c2ecf20Sopenharmony_ci#define PLL46XX_MFR_SHIFT (16) 5418c2ecf20Sopenharmony_ci#define PLL46XX_MRR_SHIFT (24) 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci#define PLL46XX_ENABLE BIT(31) 5448c2ecf20Sopenharmony_ci#define PLL46XX_LOCKED BIT(29) 5458c2ecf20Sopenharmony_ci#define PLL46XX_VSEL BIT(27) 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, 5488c2ecf20Sopenharmony_ci unsigned long parent_rate) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 5518c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift; 5528c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 5558c2ecf20Sopenharmony_ci pll_con1 = readl_relaxed(pll->con_reg + 4); 5568c2ecf20Sopenharmony_ci mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & ((pll->type == pll_1460x) ? 5578c2ecf20Sopenharmony_ci PLL1460X_MDIV_MASK : PLL46XX_MDIV_MASK); 5588c2ecf20Sopenharmony_ci pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK; 5598c2ecf20Sopenharmony_ci sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK; 5608c2ecf20Sopenharmony_ci kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK : 5618c2ecf20Sopenharmony_ci pll_con1 & PLL46XX_KDIV_MASK; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci shift = ((pll->type == pll_4600) || (pll->type == pll_1460x)) ? 16 : 10; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci fvco *= (mdiv << shift) + kdiv; 5668c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 5678c2ecf20Sopenharmony_ci fvco >>= shift; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return (unsigned long)fvco; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic bool samsung_pll46xx_mpk_change(u32 pll_con0, u32 pll_con1, 5738c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci u32 old_mdiv, old_pdiv, old_kdiv; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci old_mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK; 5788c2ecf20Sopenharmony_ci old_pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK; 5798c2ecf20Sopenharmony_ci old_kdiv = (pll_con1 >> PLL46XX_KDIV_SHIFT) & PLL46XX_KDIV_MASK; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv 5828c2ecf20Sopenharmony_ci || old_kdiv != rate->kdiv); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate, 5868c2ecf20Sopenharmony_ci unsigned long prate) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 5898c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 5908c2ecf20Sopenharmony_ci u32 con0, con1, lock; 5918c2ecf20Sopenharmony_ci ktime_t start; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Get required rate settings from table */ 5948c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 5958c2ecf20Sopenharmony_ci if (!rate) { 5968c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 5978c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 5988c2ecf20Sopenharmony_ci return -EINVAL; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci con0 = readl_relaxed(pll->con_reg); 6028c2ecf20Sopenharmony_ci con1 = readl_relaxed(pll->con_reg + 0x4); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (!(samsung_pll46xx_mpk_change(con0, con1, rate))) { 6058c2ecf20Sopenharmony_ci /* If only s change, change just s value only*/ 6068c2ecf20Sopenharmony_ci con0 &= ~(PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT); 6078c2ecf20Sopenharmony_ci con0 |= rate->sdiv << PLL46XX_SDIV_SHIFT; 6088c2ecf20Sopenharmony_ci writel_relaxed(con0, pll->con_reg); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 6148c2ecf20Sopenharmony_ci lock = rate->pdiv * PLL46XX_LOCK_FACTOR; 6158c2ecf20Sopenharmony_ci if (lock > 0xffff) 6168c2ecf20Sopenharmony_ci /* Maximum lock time bitfield is 16-bit. */ 6178c2ecf20Sopenharmony_ci lock = 0xffff; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* Set PLL PMS and VSEL values. */ 6208c2ecf20Sopenharmony_ci if (pll->type == pll_1460x) { 6218c2ecf20Sopenharmony_ci con0 &= ~((PLL1460X_MDIV_MASK << PLL46XX_MDIV_SHIFT) | 6228c2ecf20Sopenharmony_ci (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT) | 6238c2ecf20Sopenharmony_ci (PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT)); 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci con0 &= ~((PLL46XX_MDIV_MASK << PLL46XX_MDIV_SHIFT) | 6268c2ecf20Sopenharmony_ci (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT) | 6278c2ecf20Sopenharmony_ci (PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT) | 6288c2ecf20Sopenharmony_ci (PLL46XX_VSEL_MASK << PLL46XX_VSEL_SHIFT)); 6298c2ecf20Sopenharmony_ci con0 |= rate->vsel << PLL46XX_VSEL_SHIFT; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci con0 |= (rate->mdiv << PLL46XX_MDIV_SHIFT) | 6338c2ecf20Sopenharmony_ci (rate->pdiv << PLL46XX_PDIV_SHIFT) | 6348c2ecf20Sopenharmony_ci (rate->sdiv << PLL46XX_SDIV_SHIFT); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Set PLL K, MFR and MRR values. */ 6378c2ecf20Sopenharmony_ci con1 = readl_relaxed(pll->con_reg + 0x4); 6388c2ecf20Sopenharmony_ci con1 &= ~((PLL46XX_KDIV_MASK << PLL46XX_KDIV_SHIFT) | 6398c2ecf20Sopenharmony_ci (PLL46XX_MFR_MASK << PLL46XX_MFR_SHIFT) | 6408c2ecf20Sopenharmony_ci (PLL46XX_MRR_MASK << PLL46XX_MRR_SHIFT)); 6418c2ecf20Sopenharmony_ci con1 |= (rate->kdiv << PLL46XX_KDIV_SHIFT) | 6428c2ecf20Sopenharmony_ci (rate->mfr << PLL46XX_MFR_SHIFT) | 6438c2ecf20Sopenharmony_ci (rate->mrr << PLL46XX_MRR_SHIFT); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Write configuration to PLL */ 6468c2ecf20Sopenharmony_ci writel_relaxed(lock, pll->lock_reg); 6478c2ecf20Sopenharmony_ci writel_relaxed(con0, pll->con_reg); 6488c2ecf20Sopenharmony_ci writel_relaxed(con1, pll->con_reg + 0x4); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Wait for locking. */ 6518c2ecf20Sopenharmony_ci start = ktime_get(); 6528c2ecf20Sopenharmony_ci while (!(readl_relaxed(pll->con_reg) & PLL46XX_LOCKED)) { 6538c2ecf20Sopenharmony_ci ktime_t delta = ktime_sub(ktime_get(), start); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) { 6568c2ecf20Sopenharmony_ci pr_err("%s: could not lock PLL %s\n", 6578c2ecf20Sopenharmony_ci __func__, clk_hw_get_name(hw)); 6588c2ecf20Sopenharmony_ci return -EFAULT; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci cpu_relax(); 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll46xx_clk_ops = { 6688c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll46xx_recalc_rate, 6698c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 6708c2ecf20Sopenharmony_ci .set_rate = samsung_pll46xx_set_rate, 6718c2ecf20Sopenharmony_ci}; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll46xx_clk_min_ops = { 6748c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll46xx_recalc_rate, 6758c2ecf20Sopenharmony_ci}; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* 6788c2ecf20Sopenharmony_ci * PLL6552 Clock Type 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci#define PLL6552_MDIV_MASK 0x3ff 6828c2ecf20Sopenharmony_ci#define PLL6552_PDIV_MASK 0x3f 6838c2ecf20Sopenharmony_ci#define PLL6552_SDIV_MASK 0x7 6848c2ecf20Sopenharmony_ci#define PLL6552_MDIV_SHIFT 16 6858c2ecf20Sopenharmony_ci#define PLL6552_MDIV_SHIFT_2416 14 6868c2ecf20Sopenharmony_ci#define PLL6552_PDIV_SHIFT 8 6878c2ecf20Sopenharmony_ci#define PLL6552_PDIV_SHIFT_2416 5 6888c2ecf20Sopenharmony_ci#define PLL6552_SDIV_SHIFT 0 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, 6918c2ecf20Sopenharmony_ci unsigned long parent_rate) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 6948c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con; 6958c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 6988c2ecf20Sopenharmony_ci if (pll->type == pll_6552_s3c2416) { 6998c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL6552_MDIV_SHIFT_2416) & PLL6552_MDIV_MASK; 7008c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL6552_PDIV_SHIFT_2416) & PLL6552_PDIV_MASK; 7018c2ecf20Sopenharmony_ci } else { 7028c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK; 7038c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci fvco *= mdiv; 7088c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return (unsigned long)fvco; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll6552_clk_ops = { 7148c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll6552_recalc_rate, 7158c2ecf20Sopenharmony_ci}; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/* 7188c2ecf20Sopenharmony_ci * PLL6553 Clock Type 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci#define PLL6553_MDIV_MASK 0xff 7228c2ecf20Sopenharmony_ci#define PLL6553_PDIV_MASK 0x3f 7238c2ecf20Sopenharmony_ci#define PLL6553_SDIV_MASK 0x7 7248c2ecf20Sopenharmony_ci#define PLL6553_KDIV_MASK 0xffff 7258c2ecf20Sopenharmony_ci#define PLL6553_MDIV_SHIFT 16 7268c2ecf20Sopenharmony_ci#define PLL6553_PDIV_SHIFT 8 7278c2ecf20Sopenharmony_ci#define PLL6553_SDIV_SHIFT 0 7288c2ecf20Sopenharmony_ci#define PLL6553_KDIV_SHIFT 0 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic unsigned long samsung_pll6553_recalc_rate(struct clk_hw *hw, 7318c2ecf20Sopenharmony_ci unsigned long parent_rate) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 7348c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1; 7358c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 7388c2ecf20Sopenharmony_ci pll_con1 = readl_relaxed(pll->con_reg + 0x4); 7398c2ecf20Sopenharmony_ci mdiv = (pll_con0 >> PLL6553_MDIV_SHIFT) & PLL6553_MDIV_MASK; 7408c2ecf20Sopenharmony_ci pdiv = (pll_con0 >> PLL6553_PDIV_SHIFT) & PLL6553_PDIV_MASK; 7418c2ecf20Sopenharmony_ci sdiv = (pll_con0 >> PLL6553_SDIV_SHIFT) & PLL6553_SDIV_MASK; 7428c2ecf20Sopenharmony_ci kdiv = (pll_con1 >> PLL6553_KDIV_SHIFT) & PLL6553_KDIV_MASK; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci fvco *= (mdiv << 16) + kdiv; 7458c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 7468c2ecf20Sopenharmony_ci fvco >>= 16; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return (unsigned long)fvco; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll6553_clk_ops = { 7528c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll6553_recalc_rate, 7538c2ecf20Sopenharmony_ci}; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/* 7568c2ecf20Sopenharmony_ci * PLL Clock Type of S3C24XX before S3C2443 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci#define PLLS3C2410_MDIV_MASK (0xff) 7608c2ecf20Sopenharmony_ci#define PLLS3C2410_PDIV_MASK (0x1f) 7618c2ecf20Sopenharmony_ci#define PLLS3C2410_SDIV_MASK (0x3) 7628c2ecf20Sopenharmony_ci#define PLLS3C2410_MDIV_SHIFT (12) 7638c2ecf20Sopenharmony_ci#define PLLS3C2410_PDIV_SHIFT (4) 7648c2ecf20Sopenharmony_ci#define PLLS3C2410_SDIV_SHIFT (0) 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci#define PLLS3C2410_ENABLE_REG_OFFSET 0x10 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic unsigned long samsung_s3c2410_pll_recalc_rate(struct clk_hw *hw, 7698c2ecf20Sopenharmony_ci unsigned long parent_rate) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 7728c2ecf20Sopenharmony_ci u32 pll_con, mdiv, pdiv, sdiv; 7738c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 7768c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK; 7778c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK; 7788c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci fvco *= (mdiv + 8); 7818c2ecf20Sopenharmony_ci do_div(fvco, (pdiv + 2) << sdiv); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return (unsigned int)fvco; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic unsigned long samsung_s3c2440_mpll_recalc_rate(struct clk_hw *hw, 7878c2ecf20Sopenharmony_ci unsigned long parent_rate) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 7908c2ecf20Sopenharmony_ci u32 pll_con, mdiv, pdiv, sdiv; 7918c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 7948c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK; 7958c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK; 7968c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci fvco *= (2 * (mdiv + 8)); 7998c2ecf20Sopenharmony_ci do_div(fvco, (pdiv + 2) << sdiv); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return (unsigned int)fvco; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate, 8058c2ecf20Sopenharmony_ci unsigned long prate) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 8088c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 8098c2ecf20Sopenharmony_ci u32 tmp; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* Get required rate settings from table */ 8128c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 8138c2ecf20Sopenharmony_ci if (!rate) { 8148c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 8158c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 8168c2ecf20Sopenharmony_ci return -EINVAL; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* Change PLL PMS values */ 8228c2ecf20Sopenharmony_ci tmp &= ~((PLLS3C2410_MDIV_MASK << PLLS3C2410_MDIV_SHIFT) | 8238c2ecf20Sopenharmony_ci (PLLS3C2410_PDIV_MASK << PLLS3C2410_PDIV_SHIFT) | 8248c2ecf20Sopenharmony_ci (PLLS3C2410_SDIV_MASK << PLLS3C2410_SDIV_SHIFT)); 8258c2ecf20Sopenharmony_ci tmp |= (rate->mdiv << PLLS3C2410_MDIV_SHIFT) | 8268c2ecf20Sopenharmony_ci (rate->pdiv << PLLS3C2410_PDIV_SHIFT) | 8278c2ecf20Sopenharmony_ci (rate->sdiv << PLLS3C2410_SDIV_SHIFT); 8288c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* Time to settle according to the manual */ 8318c2ecf20Sopenharmony_ci udelay(300); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic int samsung_s3c2410_pll_enable(struct clk_hw *hw, int bit, bool enable) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 8398c2ecf20Sopenharmony_ci u32 pll_en = readl_relaxed(pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET); 8408c2ecf20Sopenharmony_ci u32 pll_en_orig = pll_en; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (enable) 8438c2ecf20Sopenharmony_ci pll_en &= ~BIT(bit); 8448c2ecf20Sopenharmony_ci else 8458c2ecf20Sopenharmony_ci pll_en |= BIT(bit); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci writel_relaxed(pll_en, pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* if we started the UPLL, then allow to settle */ 8508c2ecf20Sopenharmony_ci if (enable && (pll_en_orig & BIT(bit))) 8518c2ecf20Sopenharmony_ci udelay(300); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return 0; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic int samsung_s3c2410_mpll_enable(struct clk_hw *hw) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci return samsung_s3c2410_pll_enable(hw, 5, true); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic void samsung_s3c2410_mpll_disable(struct clk_hw *hw) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci samsung_s3c2410_pll_enable(hw, 5, false); 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int samsung_s3c2410_upll_enable(struct clk_hw *hw) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci return samsung_s3c2410_pll_enable(hw, 7, true); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic void samsung_s3c2410_upll_disable(struct clk_hw *hw) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci samsung_s3c2410_pll_enable(hw, 7, false); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_s3c2410_mpll_clk_min_ops = { 8778c2ecf20Sopenharmony_ci .recalc_rate = samsung_s3c2410_pll_recalc_rate, 8788c2ecf20Sopenharmony_ci .enable = samsung_s3c2410_mpll_enable, 8798c2ecf20Sopenharmony_ci .disable = samsung_s3c2410_mpll_disable, 8808c2ecf20Sopenharmony_ci}; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_s3c2410_upll_clk_min_ops = { 8838c2ecf20Sopenharmony_ci .recalc_rate = samsung_s3c2410_pll_recalc_rate, 8848c2ecf20Sopenharmony_ci .enable = samsung_s3c2410_upll_enable, 8858c2ecf20Sopenharmony_ci .disable = samsung_s3c2410_upll_disable, 8868c2ecf20Sopenharmony_ci}; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_s3c2440_mpll_clk_min_ops = { 8898c2ecf20Sopenharmony_ci .recalc_rate = samsung_s3c2440_mpll_recalc_rate, 8908c2ecf20Sopenharmony_ci .enable = samsung_s3c2410_mpll_enable, 8918c2ecf20Sopenharmony_ci .disable = samsung_s3c2410_mpll_disable, 8928c2ecf20Sopenharmony_ci}; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_s3c2410_mpll_clk_ops = { 8958c2ecf20Sopenharmony_ci .recalc_rate = samsung_s3c2410_pll_recalc_rate, 8968c2ecf20Sopenharmony_ci .enable = samsung_s3c2410_mpll_enable, 8978c2ecf20Sopenharmony_ci .disable = samsung_s3c2410_mpll_disable, 8988c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 8998c2ecf20Sopenharmony_ci .set_rate = samsung_s3c2410_pll_set_rate, 9008c2ecf20Sopenharmony_ci}; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_s3c2410_upll_clk_ops = { 9038c2ecf20Sopenharmony_ci .recalc_rate = samsung_s3c2410_pll_recalc_rate, 9048c2ecf20Sopenharmony_ci .enable = samsung_s3c2410_upll_enable, 9058c2ecf20Sopenharmony_ci .disable = samsung_s3c2410_upll_disable, 9068c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 9078c2ecf20Sopenharmony_ci .set_rate = samsung_s3c2410_pll_set_rate, 9088c2ecf20Sopenharmony_ci}; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_s3c2440_mpll_clk_ops = { 9118c2ecf20Sopenharmony_ci .recalc_rate = samsung_s3c2440_mpll_recalc_rate, 9128c2ecf20Sopenharmony_ci .enable = samsung_s3c2410_mpll_enable, 9138c2ecf20Sopenharmony_ci .disable = samsung_s3c2410_mpll_disable, 9148c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 9158c2ecf20Sopenharmony_ci .set_rate = samsung_s3c2410_pll_set_rate, 9168c2ecf20Sopenharmony_ci}; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci/* 9198c2ecf20Sopenharmony_ci * PLL2550x Clock Type 9208c2ecf20Sopenharmony_ci */ 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci#define PLL2550X_R_MASK (0x1) 9238c2ecf20Sopenharmony_ci#define PLL2550X_P_MASK (0x3F) 9248c2ecf20Sopenharmony_ci#define PLL2550X_M_MASK (0x3FF) 9258c2ecf20Sopenharmony_ci#define PLL2550X_S_MASK (0x7) 9268c2ecf20Sopenharmony_ci#define PLL2550X_R_SHIFT (20) 9278c2ecf20Sopenharmony_ci#define PLL2550X_P_SHIFT (14) 9288c2ecf20Sopenharmony_ci#define PLL2550X_M_SHIFT (4) 9298c2ecf20Sopenharmony_ci#define PLL2550X_S_SHIFT (0) 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw, 9328c2ecf20Sopenharmony_ci unsigned long parent_rate) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 9358c2ecf20Sopenharmony_ci u32 r, p, m, s, pll_stat; 9368c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci pll_stat = readl_relaxed(pll->con_reg); 9398c2ecf20Sopenharmony_ci r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK; 9408c2ecf20Sopenharmony_ci if (!r) 9418c2ecf20Sopenharmony_ci return 0; 9428c2ecf20Sopenharmony_ci p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK; 9438c2ecf20Sopenharmony_ci m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK; 9448c2ecf20Sopenharmony_ci s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci fvco *= m; 9478c2ecf20Sopenharmony_ci do_div(fvco, (p << s)); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci return (unsigned long)fvco; 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2550x_clk_ops = { 9538c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2550x_recalc_rate, 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/* 9578c2ecf20Sopenharmony_ci * PLL2550xx Clock Type 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci/* Maximum lock time can be 270 * PDIV cycles */ 9618c2ecf20Sopenharmony_ci#define PLL2550XX_LOCK_FACTOR 270 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci#define PLL2550XX_M_MASK 0x3FF 9648c2ecf20Sopenharmony_ci#define PLL2550XX_P_MASK 0x3F 9658c2ecf20Sopenharmony_ci#define PLL2550XX_S_MASK 0x7 9668c2ecf20Sopenharmony_ci#define PLL2550XX_LOCK_STAT_MASK 0x1 9678c2ecf20Sopenharmony_ci#define PLL2550XX_M_SHIFT 9 9688c2ecf20Sopenharmony_ci#define PLL2550XX_P_SHIFT 3 9698c2ecf20Sopenharmony_ci#define PLL2550XX_S_SHIFT 0 9708c2ecf20Sopenharmony_ci#define PLL2550XX_LOCK_STAT_SHIFT 21 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic unsigned long samsung_pll2550xx_recalc_rate(struct clk_hw *hw, 9738c2ecf20Sopenharmony_ci unsigned long parent_rate) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 9768c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con; 9778c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci pll_con = readl_relaxed(pll->con_reg); 9808c2ecf20Sopenharmony_ci mdiv = (pll_con >> PLL2550XX_M_SHIFT) & PLL2550XX_M_MASK; 9818c2ecf20Sopenharmony_ci pdiv = (pll_con >> PLL2550XX_P_SHIFT) & PLL2550XX_P_MASK; 9828c2ecf20Sopenharmony_ci sdiv = (pll_con >> PLL2550XX_S_SHIFT) & PLL2550XX_S_MASK; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci fvco *= mdiv; 9858c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return (unsigned long)fvco; 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic inline bool samsung_pll2550xx_mp_change(u32 mdiv, u32 pdiv, u32 pll_con) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci u32 old_mdiv, old_pdiv; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci old_mdiv = (pll_con >> PLL2550XX_M_SHIFT) & PLL2550XX_M_MASK; 9958c2ecf20Sopenharmony_ci old_pdiv = (pll_con >> PLL2550XX_P_SHIFT) & PLL2550XX_P_MASK; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return mdiv != old_mdiv || pdiv != old_pdiv; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int samsung_pll2550xx_set_rate(struct clk_hw *hw, unsigned long drate, 10018c2ecf20Sopenharmony_ci unsigned long prate) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 10048c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 10058c2ecf20Sopenharmony_ci u32 tmp; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Get required rate settings from table */ 10088c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 10098c2ecf20Sopenharmony_ci if (!rate) { 10108c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 10118c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 10128c2ecf20Sopenharmony_ci return -EINVAL; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (!(samsung_pll2550xx_mp_change(rate->mdiv, rate->pdiv, tmp))) { 10188c2ecf20Sopenharmony_ci /* If only s change, change just s value only*/ 10198c2ecf20Sopenharmony_ci tmp &= ~(PLL2550XX_S_MASK << PLL2550XX_S_SHIFT); 10208c2ecf20Sopenharmony_ci tmp |= rate->sdiv << PLL2550XX_S_SHIFT; 10218c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci return 0; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 10278c2ecf20Sopenharmony_ci writel_relaxed(rate->pdiv * PLL2550XX_LOCK_FACTOR, pll->lock_reg); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* Change PLL PMS values */ 10308c2ecf20Sopenharmony_ci tmp &= ~((PLL2550XX_M_MASK << PLL2550XX_M_SHIFT) | 10318c2ecf20Sopenharmony_ci (PLL2550XX_P_MASK << PLL2550XX_P_SHIFT) | 10328c2ecf20Sopenharmony_ci (PLL2550XX_S_MASK << PLL2550XX_S_SHIFT)); 10338c2ecf20Sopenharmony_ci tmp |= (rate->mdiv << PLL2550XX_M_SHIFT) | 10348c2ecf20Sopenharmony_ci (rate->pdiv << PLL2550XX_P_SHIFT) | 10358c2ecf20Sopenharmony_ci (rate->sdiv << PLL2550XX_S_SHIFT); 10368c2ecf20Sopenharmony_ci writel_relaxed(tmp, pll->con_reg); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci /* wait_lock_time */ 10398c2ecf20Sopenharmony_ci do { 10408c2ecf20Sopenharmony_ci cpu_relax(); 10418c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 10428c2ecf20Sopenharmony_ci } while (!(tmp & (PLL2550XX_LOCK_STAT_MASK 10438c2ecf20Sopenharmony_ci << PLL2550XX_LOCK_STAT_SHIFT))); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci return 0; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2550xx_clk_ops = { 10498c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2550xx_recalc_rate, 10508c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 10518c2ecf20Sopenharmony_ci .set_rate = samsung_pll2550xx_set_rate, 10528c2ecf20Sopenharmony_ci}; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2550xx_clk_min_ops = { 10558c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2550xx_recalc_rate, 10568c2ecf20Sopenharmony_ci}; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/* 10598c2ecf20Sopenharmony_ci * PLL2650x Clock Type 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci/* Maximum lock time can be 3000 * PDIV cycles */ 10638c2ecf20Sopenharmony_ci#define PLL2650X_LOCK_FACTOR 3000 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci#define PLL2650X_M_MASK 0x1ff 10668c2ecf20Sopenharmony_ci#define PLL2650X_P_MASK 0x3f 10678c2ecf20Sopenharmony_ci#define PLL2650X_S_MASK 0x7 10688c2ecf20Sopenharmony_ci#define PLL2650X_K_MASK 0xffff 10698c2ecf20Sopenharmony_ci#define PLL2650X_LOCK_STAT_MASK 0x1 10708c2ecf20Sopenharmony_ci#define PLL2650X_M_SHIFT 16 10718c2ecf20Sopenharmony_ci#define PLL2650X_P_SHIFT 8 10728c2ecf20Sopenharmony_ci#define PLL2650X_S_SHIFT 0 10738c2ecf20Sopenharmony_ci#define PLL2650X_K_SHIFT 0 10748c2ecf20Sopenharmony_ci#define PLL2650X_LOCK_STAT_SHIFT 29 10758c2ecf20Sopenharmony_ci#define PLL2650X_PLL_ENABLE_SHIFT 31 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic unsigned long samsung_pll2650x_recalc_rate(struct clk_hw *hw, 10788c2ecf20Sopenharmony_ci unsigned long parent_rate) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 10818c2ecf20Sopenharmony_ci u64 fout = parent_rate; 10828c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con0, pll_con1; 10838c2ecf20Sopenharmony_ci s16 kdiv; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 10868c2ecf20Sopenharmony_ci mdiv = (pll_con0 >> PLL2650X_M_SHIFT) & PLL2650X_M_MASK; 10878c2ecf20Sopenharmony_ci pdiv = (pll_con0 >> PLL2650X_P_SHIFT) & PLL2650X_P_MASK; 10888c2ecf20Sopenharmony_ci sdiv = (pll_con0 >> PLL2650X_S_SHIFT) & PLL2650X_S_MASK; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci pll_con1 = readl_relaxed(pll->con_reg + 4); 10918c2ecf20Sopenharmony_ci kdiv = (s16)((pll_con1 >> PLL2650X_K_SHIFT) & PLL2650X_K_MASK); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci fout *= (mdiv << 16) + kdiv; 10948c2ecf20Sopenharmony_ci do_div(fout, (pdiv << sdiv)); 10958c2ecf20Sopenharmony_ci fout >>= 16; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return (unsigned long)fout; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic int samsung_pll2650x_set_rate(struct clk_hw *hw, unsigned long drate, 11018c2ecf20Sopenharmony_ci unsigned long prate) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 11048c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 11058c2ecf20Sopenharmony_ci u32 con0, con1; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* Get required rate settings from table */ 11088c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 11098c2ecf20Sopenharmony_ci if (!rate) { 11108c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 11118c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 11128c2ecf20Sopenharmony_ci return -EINVAL; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci con0 = readl_relaxed(pll->con_reg); 11168c2ecf20Sopenharmony_ci con1 = readl_relaxed(pll->con_reg + 4); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 11198c2ecf20Sopenharmony_ci writel_relaxed(rate->pdiv * PLL2650X_LOCK_FACTOR, pll->lock_reg); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* Change PLL PMS values */ 11228c2ecf20Sopenharmony_ci con0 &= ~((PLL2650X_M_MASK << PLL2650X_M_SHIFT) | 11238c2ecf20Sopenharmony_ci (PLL2650X_P_MASK << PLL2650X_P_SHIFT) | 11248c2ecf20Sopenharmony_ci (PLL2650X_S_MASK << PLL2650X_S_SHIFT)); 11258c2ecf20Sopenharmony_ci con0 |= (rate->mdiv << PLL2650X_M_SHIFT) | 11268c2ecf20Sopenharmony_ci (rate->pdiv << PLL2650X_P_SHIFT) | 11278c2ecf20Sopenharmony_ci (rate->sdiv << PLL2650X_S_SHIFT); 11288c2ecf20Sopenharmony_ci con0 |= (1 << PLL2650X_PLL_ENABLE_SHIFT); 11298c2ecf20Sopenharmony_ci writel_relaxed(con0, pll->con_reg); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci con1 &= ~(PLL2650X_K_MASK << PLL2650X_K_SHIFT); 11328c2ecf20Sopenharmony_ci con1 |= ((rate->kdiv & PLL2650X_K_MASK) << PLL2650X_K_SHIFT); 11338c2ecf20Sopenharmony_ci writel_relaxed(con1, pll->con_reg + 4); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci do { 11368c2ecf20Sopenharmony_ci cpu_relax(); 11378c2ecf20Sopenharmony_ci con0 = readl_relaxed(pll->con_reg); 11388c2ecf20Sopenharmony_ci } while (!(con0 & (PLL2650X_LOCK_STAT_MASK 11398c2ecf20Sopenharmony_ci << PLL2650X_LOCK_STAT_SHIFT))); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci return 0; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2650x_clk_ops = { 11458c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2650x_recalc_rate, 11468c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 11478c2ecf20Sopenharmony_ci .set_rate = samsung_pll2650x_set_rate, 11488c2ecf20Sopenharmony_ci}; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2650x_clk_min_ops = { 11518c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2650x_recalc_rate, 11528c2ecf20Sopenharmony_ci}; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci/* 11558c2ecf20Sopenharmony_ci * PLL2650XX Clock Type 11568c2ecf20Sopenharmony_ci */ 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/* Maximum lock time can be 3000 * PDIV cycles */ 11598c2ecf20Sopenharmony_ci#define PLL2650XX_LOCK_FACTOR 3000 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci#define PLL2650XX_MDIV_SHIFT 9 11628c2ecf20Sopenharmony_ci#define PLL2650XX_PDIV_SHIFT 3 11638c2ecf20Sopenharmony_ci#define PLL2650XX_SDIV_SHIFT 0 11648c2ecf20Sopenharmony_ci#define PLL2650XX_KDIV_SHIFT 0 11658c2ecf20Sopenharmony_ci#define PLL2650XX_MDIV_MASK 0x1ff 11668c2ecf20Sopenharmony_ci#define PLL2650XX_PDIV_MASK 0x3f 11678c2ecf20Sopenharmony_ci#define PLL2650XX_SDIV_MASK 0x7 11688c2ecf20Sopenharmony_ci#define PLL2650XX_KDIV_MASK 0xffff 11698c2ecf20Sopenharmony_ci#define PLL2650XX_PLL_ENABLE_SHIFT 23 11708c2ecf20Sopenharmony_ci#define PLL2650XX_PLL_LOCKTIME_SHIFT 21 11718c2ecf20Sopenharmony_ci#define PLL2650XX_PLL_FOUTMASK_SHIFT 31 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic unsigned long samsung_pll2650xx_recalc_rate(struct clk_hw *hw, 11748c2ecf20Sopenharmony_ci unsigned long parent_rate) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 11778c2ecf20Sopenharmony_ci u32 mdiv, pdiv, sdiv, pll_con0, pll_con2; 11788c2ecf20Sopenharmony_ci s16 kdiv; 11798c2ecf20Sopenharmony_ci u64 fvco = parent_rate; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 11828c2ecf20Sopenharmony_ci pll_con2 = readl_relaxed(pll->con_reg + 8); 11838c2ecf20Sopenharmony_ci mdiv = (pll_con0 >> PLL2650XX_MDIV_SHIFT) & PLL2650XX_MDIV_MASK; 11848c2ecf20Sopenharmony_ci pdiv = (pll_con0 >> PLL2650XX_PDIV_SHIFT) & PLL2650XX_PDIV_MASK; 11858c2ecf20Sopenharmony_ci sdiv = (pll_con0 >> PLL2650XX_SDIV_SHIFT) & PLL2650XX_SDIV_MASK; 11868c2ecf20Sopenharmony_ci kdiv = (s16)(pll_con2 & PLL2650XX_KDIV_MASK); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci fvco *= (mdiv << 16) + kdiv; 11898c2ecf20Sopenharmony_ci do_div(fvco, (pdiv << sdiv)); 11908c2ecf20Sopenharmony_ci fvco >>= 16; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return (unsigned long)fvco; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int samsung_pll2650xx_set_rate(struct clk_hw *hw, unsigned long drate, 11968c2ecf20Sopenharmony_ci unsigned long parent_rate) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll = to_clk_pll(hw); 11998c2ecf20Sopenharmony_ci u32 tmp, pll_con0, pll_con2; 12008c2ecf20Sopenharmony_ci const struct samsung_pll_rate_table *rate; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci rate = samsung_get_pll_settings(pll, drate); 12038c2ecf20Sopenharmony_ci if (!rate) { 12048c2ecf20Sopenharmony_ci pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, 12058c2ecf20Sopenharmony_ci drate, clk_hw_get_name(hw)); 12068c2ecf20Sopenharmony_ci return -EINVAL; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci pll_con0 = readl_relaxed(pll->con_reg); 12108c2ecf20Sopenharmony_ci pll_con2 = readl_relaxed(pll->con_reg + 8); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* Change PLL PMS values */ 12138c2ecf20Sopenharmony_ci pll_con0 &= ~(PLL2650XX_MDIV_MASK << PLL2650XX_MDIV_SHIFT | 12148c2ecf20Sopenharmony_ci PLL2650XX_PDIV_MASK << PLL2650XX_PDIV_SHIFT | 12158c2ecf20Sopenharmony_ci PLL2650XX_SDIV_MASK << PLL2650XX_SDIV_SHIFT); 12168c2ecf20Sopenharmony_ci pll_con0 |= rate->mdiv << PLL2650XX_MDIV_SHIFT; 12178c2ecf20Sopenharmony_ci pll_con0 |= rate->pdiv << PLL2650XX_PDIV_SHIFT; 12188c2ecf20Sopenharmony_ci pll_con0 |= rate->sdiv << PLL2650XX_SDIV_SHIFT; 12198c2ecf20Sopenharmony_ci pll_con0 |= 1 << PLL2650XX_PLL_ENABLE_SHIFT; 12208c2ecf20Sopenharmony_ci pll_con0 |= 1 << PLL2650XX_PLL_FOUTMASK_SHIFT; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci pll_con2 &= ~(PLL2650XX_KDIV_MASK << PLL2650XX_KDIV_SHIFT); 12238c2ecf20Sopenharmony_ci pll_con2 |= ((~(rate->kdiv) + 1) & PLL2650XX_KDIV_MASK) 12248c2ecf20Sopenharmony_ci << PLL2650XX_KDIV_SHIFT; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* Set PLL lock time. */ 12278c2ecf20Sopenharmony_ci writel_relaxed(PLL2650XX_LOCK_FACTOR * rate->pdiv, pll->lock_reg); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci writel_relaxed(pll_con0, pll->con_reg); 12308c2ecf20Sopenharmony_ci writel_relaxed(pll_con2, pll->con_reg + 8); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci do { 12338c2ecf20Sopenharmony_ci tmp = readl_relaxed(pll->con_reg); 12348c2ecf20Sopenharmony_ci } while (!(tmp & (0x1 << PLL2650XX_PLL_LOCKTIME_SHIFT))); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci return 0; 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2650xx_clk_ops = { 12408c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2650xx_recalc_rate, 12418c2ecf20Sopenharmony_ci .set_rate = samsung_pll2650xx_set_rate, 12428c2ecf20Sopenharmony_ci .round_rate = samsung_pll_round_rate, 12438c2ecf20Sopenharmony_ci}; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic const struct clk_ops samsung_pll2650xx_clk_min_ops = { 12468c2ecf20Sopenharmony_ci .recalc_rate = samsung_pll2650xx_recalc_rate, 12478c2ecf20Sopenharmony_ci}; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, 12508c2ecf20Sopenharmony_ci const struct samsung_pll_clock *pll_clk, 12518c2ecf20Sopenharmony_ci void __iomem *base) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci struct samsung_clk_pll *pll; 12548c2ecf20Sopenharmony_ci struct clk_init_data init; 12558c2ecf20Sopenharmony_ci int ret, len; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci pll = kzalloc(sizeof(*pll), GFP_KERNEL); 12588c2ecf20Sopenharmony_ci if (!pll) { 12598c2ecf20Sopenharmony_ci pr_err("%s: could not allocate pll clk %s\n", 12608c2ecf20Sopenharmony_ci __func__, pll_clk->name); 12618c2ecf20Sopenharmony_ci return; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci init.name = pll_clk->name; 12658c2ecf20Sopenharmony_ci init.flags = pll_clk->flags; 12668c2ecf20Sopenharmony_ci init.parent_names = &pll_clk->parent_name; 12678c2ecf20Sopenharmony_ci init.num_parents = 1; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (pll_clk->rate_table) { 12708c2ecf20Sopenharmony_ci /* find count of rates in rate_table */ 12718c2ecf20Sopenharmony_ci for (len = 0; pll_clk->rate_table[len].rate != 0; ) 12728c2ecf20Sopenharmony_ci len++; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci pll->rate_count = len; 12758c2ecf20Sopenharmony_ci pll->rate_table = kmemdup(pll_clk->rate_table, 12768c2ecf20Sopenharmony_ci pll->rate_count * 12778c2ecf20Sopenharmony_ci sizeof(struct samsung_pll_rate_table), 12788c2ecf20Sopenharmony_ci GFP_KERNEL); 12798c2ecf20Sopenharmony_ci WARN(!pll->rate_table, 12808c2ecf20Sopenharmony_ci "%s: could not allocate rate table for %s\n", 12818c2ecf20Sopenharmony_ci __func__, pll_clk->name); 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci switch (pll_clk->type) { 12858c2ecf20Sopenharmony_ci case pll_2126: 12868c2ecf20Sopenharmony_ci init.ops = &samsung_pll2126_clk_ops; 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci case pll_3000: 12898c2ecf20Sopenharmony_ci init.ops = &samsung_pll3000_clk_ops; 12908c2ecf20Sopenharmony_ci break; 12918c2ecf20Sopenharmony_ci /* clk_ops for 35xx and 2550 are similar */ 12928c2ecf20Sopenharmony_ci case pll_35xx: 12938c2ecf20Sopenharmony_ci case pll_2550: 12948c2ecf20Sopenharmony_ci case pll_1450x: 12958c2ecf20Sopenharmony_ci case pll_1451x: 12968c2ecf20Sopenharmony_ci case pll_1452x: 12978c2ecf20Sopenharmony_ci pll->enable_offs = PLL35XX_ENABLE_SHIFT; 12988c2ecf20Sopenharmony_ci pll->lock_offs = PLL35XX_LOCK_STAT_SHIFT; 12998c2ecf20Sopenharmony_ci if (!pll->rate_table) 13008c2ecf20Sopenharmony_ci init.ops = &samsung_pll35xx_clk_min_ops; 13018c2ecf20Sopenharmony_ci else 13028c2ecf20Sopenharmony_ci init.ops = &samsung_pll35xx_clk_ops; 13038c2ecf20Sopenharmony_ci break; 13048c2ecf20Sopenharmony_ci case pll_4500: 13058c2ecf20Sopenharmony_ci init.ops = &samsung_pll45xx_clk_min_ops; 13068c2ecf20Sopenharmony_ci break; 13078c2ecf20Sopenharmony_ci case pll_4502: 13088c2ecf20Sopenharmony_ci case pll_4508: 13098c2ecf20Sopenharmony_ci if (!pll->rate_table) 13108c2ecf20Sopenharmony_ci init.ops = &samsung_pll45xx_clk_min_ops; 13118c2ecf20Sopenharmony_ci else 13128c2ecf20Sopenharmony_ci init.ops = &samsung_pll45xx_clk_ops; 13138c2ecf20Sopenharmony_ci break; 13148c2ecf20Sopenharmony_ci /* clk_ops for 36xx and 2650 are similar */ 13158c2ecf20Sopenharmony_ci case pll_36xx: 13168c2ecf20Sopenharmony_ci case pll_2650: 13178c2ecf20Sopenharmony_ci pll->enable_offs = PLL36XX_ENABLE_SHIFT; 13188c2ecf20Sopenharmony_ci pll->lock_offs = PLL36XX_LOCK_STAT_SHIFT; 13198c2ecf20Sopenharmony_ci if (!pll->rate_table) 13208c2ecf20Sopenharmony_ci init.ops = &samsung_pll36xx_clk_min_ops; 13218c2ecf20Sopenharmony_ci else 13228c2ecf20Sopenharmony_ci init.ops = &samsung_pll36xx_clk_ops; 13238c2ecf20Sopenharmony_ci break; 13248c2ecf20Sopenharmony_ci case pll_6552: 13258c2ecf20Sopenharmony_ci case pll_6552_s3c2416: 13268c2ecf20Sopenharmony_ci init.ops = &samsung_pll6552_clk_ops; 13278c2ecf20Sopenharmony_ci break; 13288c2ecf20Sopenharmony_ci case pll_6553: 13298c2ecf20Sopenharmony_ci init.ops = &samsung_pll6553_clk_ops; 13308c2ecf20Sopenharmony_ci break; 13318c2ecf20Sopenharmony_ci case pll_4600: 13328c2ecf20Sopenharmony_ci case pll_4650: 13338c2ecf20Sopenharmony_ci case pll_4650c: 13348c2ecf20Sopenharmony_ci case pll_1460x: 13358c2ecf20Sopenharmony_ci if (!pll->rate_table) 13368c2ecf20Sopenharmony_ci init.ops = &samsung_pll46xx_clk_min_ops; 13378c2ecf20Sopenharmony_ci else 13388c2ecf20Sopenharmony_ci init.ops = &samsung_pll46xx_clk_ops; 13398c2ecf20Sopenharmony_ci break; 13408c2ecf20Sopenharmony_ci case pll_s3c2410_mpll: 13418c2ecf20Sopenharmony_ci if (!pll->rate_table) 13428c2ecf20Sopenharmony_ci init.ops = &samsung_s3c2410_mpll_clk_min_ops; 13438c2ecf20Sopenharmony_ci else 13448c2ecf20Sopenharmony_ci init.ops = &samsung_s3c2410_mpll_clk_ops; 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci case pll_s3c2410_upll: 13478c2ecf20Sopenharmony_ci if (!pll->rate_table) 13488c2ecf20Sopenharmony_ci init.ops = &samsung_s3c2410_upll_clk_min_ops; 13498c2ecf20Sopenharmony_ci else 13508c2ecf20Sopenharmony_ci init.ops = &samsung_s3c2410_upll_clk_ops; 13518c2ecf20Sopenharmony_ci break; 13528c2ecf20Sopenharmony_ci case pll_s3c2440_mpll: 13538c2ecf20Sopenharmony_ci if (!pll->rate_table) 13548c2ecf20Sopenharmony_ci init.ops = &samsung_s3c2440_mpll_clk_min_ops; 13558c2ecf20Sopenharmony_ci else 13568c2ecf20Sopenharmony_ci init.ops = &samsung_s3c2440_mpll_clk_ops; 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci case pll_2550x: 13598c2ecf20Sopenharmony_ci init.ops = &samsung_pll2550x_clk_ops; 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci case pll_2550xx: 13628c2ecf20Sopenharmony_ci if (!pll->rate_table) 13638c2ecf20Sopenharmony_ci init.ops = &samsung_pll2550xx_clk_min_ops; 13648c2ecf20Sopenharmony_ci else 13658c2ecf20Sopenharmony_ci init.ops = &samsung_pll2550xx_clk_ops; 13668c2ecf20Sopenharmony_ci break; 13678c2ecf20Sopenharmony_ci case pll_2650x: 13688c2ecf20Sopenharmony_ci if (!pll->rate_table) 13698c2ecf20Sopenharmony_ci init.ops = &samsung_pll2650x_clk_min_ops; 13708c2ecf20Sopenharmony_ci else 13718c2ecf20Sopenharmony_ci init.ops = &samsung_pll2650x_clk_ops; 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci case pll_2650xx: 13748c2ecf20Sopenharmony_ci if (!pll->rate_table) 13758c2ecf20Sopenharmony_ci init.ops = &samsung_pll2650xx_clk_min_ops; 13768c2ecf20Sopenharmony_ci else 13778c2ecf20Sopenharmony_ci init.ops = &samsung_pll2650xx_clk_ops; 13788c2ecf20Sopenharmony_ci break; 13798c2ecf20Sopenharmony_ci default: 13808c2ecf20Sopenharmony_ci pr_warn("%s: Unknown pll type for pll clk %s\n", 13818c2ecf20Sopenharmony_ci __func__, pll_clk->name); 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci pll->hw.init = &init; 13858c2ecf20Sopenharmony_ci pll->type = pll_clk->type; 13868c2ecf20Sopenharmony_ci pll->lock_reg = base + pll_clk->lock_offset; 13878c2ecf20Sopenharmony_ci pll->con_reg = base + pll_clk->con_offset; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci ret = clk_hw_register(ctx->dev, &pll->hw); 13908c2ecf20Sopenharmony_ci if (ret) { 13918c2ecf20Sopenharmony_ci pr_err("%s: failed to register pll clock %s : %d\n", 13928c2ecf20Sopenharmony_ci __func__, pll_clk->name, ret); 13938c2ecf20Sopenharmony_ci kfree(pll->rate_table); 13948c2ecf20Sopenharmony_ci kfree(pll); 13958c2ecf20Sopenharmony_ci return; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci samsung_clk_add_lookup(ctx, &pll->hw, pll_clk->id); 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_civoid __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, 14028c2ecf20Sopenharmony_ci const struct samsung_pll_clock *pll_list, 14038c2ecf20Sopenharmony_ci unsigned int nr_pll, void __iomem *base) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci int cnt; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci for (cnt = 0; cnt < nr_pll; cnt++) 14088c2ecf20Sopenharmony_ci _samsung_clk_register_pll(ctx, &pll_list[cnt], base); 14098c2ecf20Sopenharmony_ci} 1410