18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Google, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/printk.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "clk.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define PLL_STATUS 0x0 178c2ecf20Sopenharmony_ci#define PLL_STATUS_LOCK BIT(0) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define PLL_CTRL1 0x4 208c2ecf20Sopenharmony_ci#define PLL_CTRL1_REFDIV_SHIFT 0 218c2ecf20Sopenharmony_ci#define PLL_CTRL1_REFDIV_MASK 0x3f 228c2ecf20Sopenharmony_ci#define PLL_CTRL1_FBDIV_SHIFT 6 238c2ecf20Sopenharmony_ci#define PLL_CTRL1_FBDIV_MASK 0xfff 248c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV1_SHIFT 18 258c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV1_MASK 0x7 268c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV2_SHIFT 21 278c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV2_MASK 0x7 288c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_PD BIT(24) 298c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_DSMPD BIT(25) 308c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_FOUTPOSTDIVPD BIT(26) 318c2ecf20Sopenharmony_ci#define PLL_INT_CTRL1_FOUTVCOPD BIT(27) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define PLL_CTRL2 0x8 348c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL2_FRAC_SHIFT 0 358c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL2_FRAC_MASK 0xffffff 368c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT 24 378c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV1_MASK 0x7 388c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT 27 398c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV2_MASK 0x7 408c2ecf20Sopenharmony_ci#define PLL_INT_CTRL2_BYPASS BIT(28) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define PLL_CTRL3 0xc 438c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL3_PD BIT(0) 448c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL3_DACPD BIT(1) 458c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL3_DSMPD BIT(2) 468c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD BIT(3) 478c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL3_FOUT4PHASEPD BIT(4) 488c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL3_FOUTVCOPD BIT(5) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define PLL_CTRL4 0x10 518c2ecf20Sopenharmony_ci#define PLL_FRAC_CTRL4_BYPASS BIT(28) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define MIN_PFD 9600000UL 548c2ecf20Sopenharmony_ci#define MIN_VCO_LA 400000000UL 558c2ecf20Sopenharmony_ci#define MAX_VCO_LA 1600000000UL 568c2ecf20Sopenharmony_ci#define MIN_VCO_FRAC_INT 600000000UL 578c2ecf20Sopenharmony_ci#define MAX_VCO_FRAC_INT 1600000000UL 588c2ecf20Sopenharmony_ci#define MIN_VCO_FRAC_FRAC 600000000UL 598c2ecf20Sopenharmony_ci#define MAX_VCO_FRAC_FRAC 2400000000UL 608c2ecf20Sopenharmony_ci#define MIN_OUTPUT_LA 8000000UL 618c2ecf20Sopenharmony_ci#define MAX_OUTPUT_LA 1600000000UL 628c2ecf20Sopenharmony_ci#define MIN_OUTPUT_FRAC 12000000UL 638c2ecf20Sopenharmony_ci#define MAX_OUTPUT_FRAC 1600000000UL 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Fractional PLL operating modes */ 668c2ecf20Sopenharmony_cienum pll_mode { 678c2ecf20Sopenharmony_ci PLL_MODE_FRAC, 688c2ecf20Sopenharmony_ci PLL_MODE_INT, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct pistachio_clk_pll { 728c2ecf20Sopenharmony_ci struct clk_hw hw; 738c2ecf20Sopenharmony_ci void __iomem *base; 748c2ecf20Sopenharmony_ci struct pistachio_pll_rate_table *rates; 758c2ecf20Sopenharmony_ci unsigned int nr_rates; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return readl(pll->base + reg); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci writel(val, pll->base + reg); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline void pll_lock(struct pistachio_clk_pll *pll) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) 918c2ecf20Sopenharmony_ci cpu_relax(); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline u64 do_div_round_closest(u64 dividend, u64 divisor) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci dividend += divisor / 2; 978c2ecf20Sopenharmony_ci return div64_u64(dividend, divisor); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return container_of(hw, struct pistachio_clk_pll, hw); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1088c2ecf20Sopenharmony_ci u32 val; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD; 1118c2ecf20Sopenharmony_ci return val ? PLL_MODE_INT : PLL_MODE_FRAC; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1178c2ecf20Sopenharmony_ci u32 val; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3); 1208c2ecf20Sopenharmony_ci if (mode == PLL_MODE_INT) 1218c2ecf20Sopenharmony_ci val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD; 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL3); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic struct pistachio_pll_rate_table * 1298c2ecf20Sopenharmony_cipll_get_params(struct pistachio_clk_pll *pll, unsigned long fref, 1308c2ecf20Sopenharmony_ci unsigned long fout) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned int i; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for (i = 0; i < pll->nr_rates; i++) { 1358c2ecf20Sopenharmony_ci if (pll->rates[i].fref == fref && pll->rates[i].fout == fout) 1368c2ecf20Sopenharmony_ci return &pll->rates[i]; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return NULL; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic long pll_round_rate(struct clk_hw *hw, unsigned long rate, 1438c2ecf20Sopenharmony_ci unsigned long *parent_rate) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1468c2ecf20Sopenharmony_ci unsigned int i; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < pll->nr_rates; i++) { 1498c2ecf20Sopenharmony_ci if (i > 0 && pll->rates[i].fref == *parent_rate && 1508c2ecf20Sopenharmony_ci pll->rates[i].fout <= rate) 1518c2ecf20Sopenharmony_ci return pll->rates[i - 1].fout; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return pll->rates[0].fout; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int pll_gf40lp_frac_enable(struct clk_hw *hw) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1608c2ecf20Sopenharmony_ci u32 val; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3); 1638c2ecf20Sopenharmony_ci val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | 1648c2ecf20Sopenharmony_ci PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD); 1658c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL3); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL4); 1688c2ecf20Sopenharmony_ci val &= ~PLL_FRAC_CTRL4_BYPASS; 1698c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL4); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci pll_lock(pll); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void pll_gf40lp_frac_disable(struct clk_hw *hw) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1798c2ecf20Sopenharmony_ci u32 val; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3); 1828c2ecf20Sopenharmony_ci val |= PLL_FRAC_CTRL3_PD; 1838c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL3); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int pll_gf40lp_frac_is_enabled(struct clk_hw *hw) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, 1948c2ecf20Sopenharmony_ci unsigned long parent_rate) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 1978c2ecf20Sopenharmony_ci struct pistachio_pll_rate_table *params; 1988c2ecf20Sopenharmony_ci int enabled = pll_gf40lp_frac_is_enabled(hw); 1998c2ecf20Sopenharmony_ci u64 val, vco, old_postdiv1, old_postdiv2; 2008c2ecf20Sopenharmony_ci const char *name = clk_hw_get_name(hw); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci params = pll_get_params(pll, parent_rate, rate); 2068c2ecf20Sopenharmony_ci if (!params || !params->refdiv) 2078c2ecf20Sopenharmony_ci return -EINVAL; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* calculate vco */ 2108c2ecf20Sopenharmony_ci vco = params->fref; 2118c2ecf20Sopenharmony_ci vco *= (params->fbdiv << 24) + params->frac; 2128c2ecf20Sopenharmony_ci vco = div64_u64(vco, params->refdiv << 24); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) 2158c2ecf20Sopenharmony_ci pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco, 2168c2ecf20Sopenharmony_ci MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci val = div64_u64(params->fref, params->refdiv); 2198c2ecf20Sopenharmony_ci if (val < MIN_PFD) 2208c2ecf20Sopenharmony_ci pr_warn("%s: PFD %llu is too low (min %lu)\n", 2218c2ecf20Sopenharmony_ci name, val, MIN_PFD); 2228c2ecf20Sopenharmony_ci if (val > vco / 16) 2238c2ecf20Sopenharmony_ci pr_warn("%s: PFD %llu is too high (max %llu)\n", 2248c2ecf20Sopenharmony_ci name, val, vco / 16); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 2278c2ecf20Sopenharmony_ci val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | 2288c2ecf20Sopenharmony_ci (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT)); 2298c2ecf20Sopenharmony_ci val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) | 2308c2ecf20Sopenharmony_ci (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT); 2318c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL2); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & 2368c2ecf20Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV1_MASK; 2378c2ecf20Sopenharmony_ci old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & 2388c2ecf20Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV2_MASK; 2398c2ecf20Sopenharmony_ci if (enabled && 2408c2ecf20Sopenharmony_ci (params->postdiv1 != old_postdiv1 || 2418c2ecf20Sopenharmony_ci params->postdiv2 != old_postdiv2)) 2428c2ecf20Sopenharmony_ci pr_warn("%s: changing postdiv while PLL is enabled\n", name); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (params->postdiv2 > params->postdiv1) 2458c2ecf20Sopenharmony_ci pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | 2488c2ecf20Sopenharmony_ci (PLL_FRAC_CTRL2_POSTDIV1_MASK << 2498c2ecf20Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | 2508c2ecf20Sopenharmony_ci (PLL_FRAC_CTRL2_POSTDIV2_MASK << 2518c2ecf20Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV2_SHIFT)); 2528c2ecf20Sopenharmony_ci val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) | 2538c2ecf20Sopenharmony_ci (params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | 2548c2ecf20Sopenharmony_ci (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); 2558c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL2); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* set operating mode */ 2588c2ecf20Sopenharmony_ci if (params->frac) 2598c2ecf20Sopenharmony_ci pll_frac_set_mode(hw, PLL_MODE_FRAC); 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci pll_frac_set_mode(hw, PLL_MODE_INT); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (enabled) 2648c2ecf20Sopenharmony_ci pll_lock(pll); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, 2708c2ecf20Sopenharmony_ci unsigned long parent_rate) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 2738c2ecf20Sopenharmony_ci u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 2768c2ecf20Sopenharmony_ci prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; 2778c2ecf20Sopenharmony_ci fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL2); 2808c2ecf20Sopenharmony_ci postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & 2818c2ecf20Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV1_MASK; 2828c2ecf20Sopenharmony_ci postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & 2838c2ecf20Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV2_MASK; 2848c2ecf20Sopenharmony_ci frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* get operating mode (int/frac) and calculate rate accordingly */ 2878c2ecf20Sopenharmony_ci rate = parent_rate; 2888c2ecf20Sopenharmony_ci if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) 2898c2ecf20Sopenharmony_ci rate *= (fbdiv << 24) + frac; 2908c2ecf20Sopenharmony_ci else 2918c2ecf20Sopenharmony_ci rate *= (fbdiv << 24); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return rate; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic const struct clk_ops pll_gf40lp_frac_ops = { 2998c2ecf20Sopenharmony_ci .enable = pll_gf40lp_frac_enable, 3008c2ecf20Sopenharmony_ci .disable = pll_gf40lp_frac_disable, 3018c2ecf20Sopenharmony_ci .is_enabled = pll_gf40lp_frac_is_enabled, 3028c2ecf20Sopenharmony_ci .recalc_rate = pll_gf40lp_frac_recalc_rate, 3038c2ecf20Sopenharmony_ci .round_rate = pll_round_rate, 3048c2ecf20Sopenharmony_ci .set_rate = pll_gf40lp_frac_set_rate, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic const struct clk_ops pll_gf40lp_frac_fixed_ops = { 3088c2ecf20Sopenharmony_ci .enable = pll_gf40lp_frac_enable, 3098c2ecf20Sopenharmony_ci .disable = pll_gf40lp_frac_disable, 3108c2ecf20Sopenharmony_ci .is_enabled = pll_gf40lp_frac_is_enabled, 3118c2ecf20Sopenharmony_ci .recalc_rate = pll_gf40lp_frac_recalc_rate, 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int pll_gf40lp_laint_enable(struct clk_hw *hw) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 3178c2ecf20Sopenharmony_ci u32 val; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 3208c2ecf20Sopenharmony_ci val &= ~(PLL_INT_CTRL1_PD | 3218c2ecf20Sopenharmony_ci PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD); 3228c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL2); 3258c2ecf20Sopenharmony_ci val &= ~PLL_INT_CTRL2_BYPASS; 3268c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL2); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci pll_lock(pll); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic void pll_gf40lp_laint_disable(struct clk_hw *hw) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 3368c2ecf20Sopenharmony_ci u32 val; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 3398c2ecf20Sopenharmony_ci val |= PLL_INT_CTRL1_PD; 3408c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int pll_gf40lp_laint_is_enabled(struct clk_hw *hw) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, 3518c2ecf20Sopenharmony_ci unsigned long parent_rate) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 3548c2ecf20Sopenharmony_ci struct pistachio_pll_rate_table *params; 3558c2ecf20Sopenharmony_ci int enabled = pll_gf40lp_laint_is_enabled(hw); 3568c2ecf20Sopenharmony_ci u32 val, vco, old_postdiv1, old_postdiv2; 3578c2ecf20Sopenharmony_ci const char *name = clk_hw_get_name(hw); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci params = pll_get_params(pll, parent_rate, rate); 3638c2ecf20Sopenharmony_ci if (!params || !params->refdiv) 3648c2ecf20Sopenharmony_ci return -EINVAL; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci vco = div_u64(params->fref * params->fbdiv, params->refdiv); 3678c2ecf20Sopenharmony_ci if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) 3688c2ecf20Sopenharmony_ci pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, 3698c2ecf20Sopenharmony_ci MIN_VCO_LA, MAX_VCO_LA); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci val = div_u64(params->fref, params->refdiv); 3728c2ecf20Sopenharmony_ci if (val < MIN_PFD) 3738c2ecf20Sopenharmony_ci pr_warn("%s: PFD %u is too low (min %lu)\n", 3748c2ecf20Sopenharmony_ci name, val, MIN_PFD); 3758c2ecf20Sopenharmony_ci if (val > vco / 16) 3768c2ecf20Sopenharmony_ci pr_warn("%s: PFD %u is too high (max %u)\n", 3778c2ecf20Sopenharmony_ci name, val, vco / 16); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & 3828c2ecf20Sopenharmony_ci PLL_INT_CTRL1_POSTDIV1_MASK; 3838c2ecf20Sopenharmony_ci old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & 3848c2ecf20Sopenharmony_ci PLL_INT_CTRL1_POSTDIV2_MASK; 3858c2ecf20Sopenharmony_ci if (enabled && 3868c2ecf20Sopenharmony_ci (params->postdiv1 != old_postdiv1 || 3878c2ecf20Sopenharmony_ci params->postdiv2 != old_postdiv2)) 3888c2ecf20Sopenharmony_ci pr_warn("%s: changing postdiv while PLL is enabled\n", name); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (params->postdiv2 > params->postdiv1) 3918c2ecf20Sopenharmony_ci pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | 3948c2ecf20Sopenharmony_ci (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | 3958c2ecf20Sopenharmony_ci (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | 3968c2ecf20Sopenharmony_ci (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT)); 3978c2ecf20Sopenharmony_ci val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) | 3988c2ecf20Sopenharmony_ci (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) | 3998c2ecf20Sopenharmony_ci (params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) | 4008c2ecf20Sopenharmony_ci (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); 4018c2ecf20Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (enabled) 4048c2ecf20Sopenharmony_ci pll_lock(pll); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw, 4108c2ecf20Sopenharmony_ci unsigned long parent_rate) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 4138c2ecf20Sopenharmony_ci u32 val, prediv, fbdiv, postdiv1, postdiv2; 4148c2ecf20Sopenharmony_ci u64 rate = parent_rate; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 4178c2ecf20Sopenharmony_ci prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; 4188c2ecf20Sopenharmony_ci fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK; 4198c2ecf20Sopenharmony_ci postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & 4208c2ecf20Sopenharmony_ci PLL_INT_CTRL1_POSTDIV1_MASK; 4218c2ecf20Sopenharmony_ci postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & 4228c2ecf20Sopenharmony_ci PLL_INT_CTRL1_POSTDIV2_MASK; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci rate *= fbdiv; 4258c2ecf20Sopenharmony_ci rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return rate; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic const struct clk_ops pll_gf40lp_laint_ops = { 4318c2ecf20Sopenharmony_ci .enable = pll_gf40lp_laint_enable, 4328c2ecf20Sopenharmony_ci .disable = pll_gf40lp_laint_disable, 4338c2ecf20Sopenharmony_ci .is_enabled = pll_gf40lp_laint_is_enabled, 4348c2ecf20Sopenharmony_ci .recalc_rate = pll_gf40lp_laint_recalc_rate, 4358c2ecf20Sopenharmony_ci .round_rate = pll_round_rate, 4368c2ecf20Sopenharmony_ci .set_rate = pll_gf40lp_laint_set_rate, 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic const struct clk_ops pll_gf40lp_laint_fixed_ops = { 4408c2ecf20Sopenharmony_ci .enable = pll_gf40lp_laint_enable, 4418c2ecf20Sopenharmony_ci .disable = pll_gf40lp_laint_disable, 4428c2ecf20Sopenharmony_ci .is_enabled = pll_gf40lp_laint_is_enabled, 4438c2ecf20Sopenharmony_ci .recalc_rate = pll_gf40lp_laint_recalc_rate, 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic struct clk *pll_register(const char *name, const char *parent_name, 4478c2ecf20Sopenharmony_ci unsigned long flags, void __iomem *base, 4488c2ecf20Sopenharmony_ci enum pistachio_pll_type type, 4498c2ecf20Sopenharmony_ci struct pistachio_pll_rate_table *rates, 4508c2ecf20Sopenharmony_ci unsigned int nr_rates) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct pistachio_clk_pll *pll; 4538c2ecf20Sopenharmony_ci struct clk_init_data init; 4548c2ecf20Sopenharmony_ci struct clk *clk; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci pll = kzalloc(sizeof(*pll), GFP_KERNEL); 4578c2ecf20Sopenharmony_ci if (!pll) 4588c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci init.name = name; 4618c2ecf20Sopenharmony_ci init.flags = flags | CLK_GET_RATE_NOCACHE; 4628c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 4638c2ecf20Sopenharmony_ci init.num_parents = 1; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci switch (type) { 4668c2ecf20Sopenharmony_ci case PLL_GF40LP_FRAC: 4678c2ecf20Sopenharmony_ci if (rates) 4688c2ecf20Sopenharmony_ci init.ops = &pll_gf40lp_frac_ops; 4698c2ecf20Sopenharmony_ci else 4708c2ecf20Sopenharmony_ci init.ops = &pll_gf40lp_frac_fixed_ops; 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case PLL_GF40LP_LAINT: 4738c2ecf20Sopenharmony_ci if (rates) 4748c2ecf20Sopenharmony_ci init.ops = &pll_gf40lp_laint_ops; 4758c2ecf20Sopenharmony_ci else 4768c2ecf20Sopenharmony_ci init.ops = &pll_gf40lp_laint_fixed_ops; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci default: 4798c2ecf20Sopenharmony_ci pr_err("Unrecognized PLL type %u\n", type); 4808c2ecf20Sopenharmony_ci kfree(pll); 4818c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci pll->hw.init = &init; 4858c2ecf20Sopenharmony_ci pll->base = base; 4868c2ecf20Sopenharmony_ci pll->rates = rates; 4878c2ecf20Sopenharmony_ci pll->nr_rates = nr_rates; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci clk = clk_register(NULL, &pll->hw); 4908c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 4918c2ecf20Sopenharmony_ci kfree(pll); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return clk; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_civoid pistachio_clk_register_pll(struct pistachio_clk_provider *p, 4978c2ecf20Sopenharmony_ci struct pistachio_pll *pll, 4988c2ecf20Sopenharmony_ci unsigned int num) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct clk *clk; 5018c2ecf20Sopenharmony_ci unsigned int i; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 5048c2ecf20Sopenharmony_ci clk = pll_register(pll[i].name, pll[i].parent, 5058c2ecf20Sopenharmony_ci 0, p->base + pll[i].reg_base, 5068c2ecf20Sopenharmony_ci pll[i].type, pll[i].rates, 5078c2ecf20Sopenharmony_ci pll[i].nr_rates); 5088c2ecf20Sopenharmony_ci p->clk_data.clks[pll[i].id] = clk; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci} 511