162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 Google, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/printk.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "clk.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define PLL_STATUS 0x0 1762306a36Sopenharmony_ci#define PLL_STATUS_LOCK BIT(0) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define PLL_CTRL1 0x4 2062306a36Sopenharmony_ci#define PLL_CTRL1_REFDIV_SHIFT 0 2162306a36Sopenharmony_ci#define PLL_CTRL1_REFDIV_MASK 0x3f 2262306a36Sopenharmony_ci#define PLL_CTRL1_FBDIV_SHIFT 6 2362306a36Sopenharmony_ci#define PLL_CTRL1_FBDIV_MASK 0xfff 2462306a36Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV1_SHIFT 18 2562306a36Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV1_MASK 0x7 2662306a36Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV2_SHIFT 21 2762306a36Sopenharmony_ci#define PLL_INT_CTRL1_POSTDIV2_MASK 0x7 2862306a36Sopenharmony_ci#define PLL_INT_CTRL1_PD BIT(24) 2962306a36Sopenharmony_ci#define PLL_INT_CTRL1_DSMPD BIT(25) 3062306a36Sopenharmony_ci#define PLL_INT_CTRL1_FOUTPOSTDIVPD BIT(26) 3162306a36Sopenharmony_ci#define PLL_INT_CTRL1_FOUTVCOPD BIT(27) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define PLL_CTRL2 0x8 3462306a36Sopenharmony_ci#define PLL_FRAC_CTRL2_FRAC_SHIFT 0 3562306a36Sopenharmony_ci#define PLL_FRAC_CTRL2_FRAC_MASK 0xffffff 3662306a36Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT 24 3762306a36Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV1_MASK 0x7 3862306a36Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT 27 3962306a36Sopenharmony_ci#define PLL_FRAC_CTRL2_POSTDIV2_MASK 0x7 4062306a36Sopenharmony_ci#define PLL_INT_CTRL2_BYPASS BIT(28) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define PLL_CTRL3 0xc 4362306a36Sopenharmony_ci#define PLL_FRAC_CTRL3_PD BIT(0) 4462306a36Sopenharmony_ci#define PLL_FRAC_CTRL3_DACPD BIT(1) 4562306a36Sopenharmony_ci#define PLL_FRAC_CTRL3_DSMPD BIT(2) 4662306a36Sopenharmony_ci#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD BIT(3) 4762306a36Sopenharmony_ci#define PLL_FRAC_CTRL3_FOUT4PHASEPD BIT(4) 4862306a36Sopenharmony_ci#define PLL_FRAC_CTRL3_FOUTVCOPD BIT(5) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define PLL_CTRL4 0x10 5162306a36Sopenharmony_ci#define PLL_FRAC_CTRL4_BYPASS BIT(28) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define MIN_PFD 9600000UL 5462306a36Sopenharmony_ci#define MIN_VCO_LA 400000000UL 5562306a36Sopenharmony_ci#define MAX_VCO_LA 1600000000UL 5662306a36Sopenharmony_ci#define MIN_VCO_FRAC_INT 600000000UL 5762306a36Sopenharmony_ci#define MAX_VCO_FRAC_INT 1600000000UL 5862306a36Sopenharmony_ci#define MIN_VCO_FRAC_FRAC 600000000UL 5962306a36Sopenharmony_ci#define MAX_VCO_FRAC_FRAC 2400000000UL 6062306a36Sopenharmony_ci#define MIN_OUTPUT_LA 8000000UL 6162306a36Sopenharmony_ci#define MAX_OUTPUT_LA 1600000000UL 6262306a36Sopenharmony_ci#define MIN_OUTPUT_FRAC 12000000UL 6362306a36Sopenharmony_ci#define MAX_OUTPUT_FRAC 1600000000UL 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Fractional PLL operating modes */ 6662306a36Sopenharmony_cienum pll_mode { 6762306a36Sopenharmony_ci PLL_MODE_FRAC, 6862306a36Sopenharmony_ci PLL_MODE_INT, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct pistachio_clk_pll { 7262306a36Sopenharmony_ci struct clk_hw hw; 7362306a36Sopenharmony_ci void __iomem *base; 7462306a36Sopenharmony_ci struct pistachio_pll_rate_table *rates; 7562306a36Sopenharmony_ci unsigned int nr_rates; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci return readl(pll->base + reg); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci writel(val, pll->base + reg); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic inline void pll_lock(struct pistachio_clk_pll *pll) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) 9162306a36Sopenharmony_ci cpu_relax(); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic inline u64 do_div_round_closest(u64 dividend, u64 divisor) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci dividend += divisor / 2; 9762306a36Sopenharmony_ci return div64_u64(dividend, divisor); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci return container_of(hw, struct pistachio_clk_pll, hw); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 10862306a36Sopenharmony_ci u32 val; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD; 11162306a36Sopenharmony_ci return val ? PLL_MODE_INT : PLL_MODE_FRAC; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 11762306a36Sopenharmony_ci u32 val; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3); 12062306a36Sopenharmony_ci if (mode == PLL_MODE_INT) 12162306a36Sopenharmony_ci val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD; 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL3); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct pistachio_pll_rate_table * 12962306a36Sopenharmony_cipll_get_params(struct pistachio_clk_pll *pll, unsigned long fref, 13062306a36Sopenharmony_ci unsigned long fout) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci unsigned int i; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < pll->nr_rates; i++) { 13562306a36Sopenharmony_ci if (pll->rates[i].fref == fref && pll->rates[i].fout == fout) 13662306a36Sopenharmony_ci return &pll->rates[i]; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic long pll_round_rate(struct clk_hw *hw, unsigned long rate, 14362306a36Sopenharmony_ci unsigned long *parent_rate) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 14662306a36Sopenharmony_ci unsigned int i; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (i = 0; i < pll->nr_rates; i++) { 14962306a36Sopenharmony_ci if (i > 0 && pll->rates[i].fref == *parent_rate && 15062306a36Sopenharmony_ci pll->rates[i].fout <= rate) 15162306a36Sopenharmony_ci return pll->rates[i - 1].fout; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return pll->rates[0].fout; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int pll_gf40lp_frac_enable(struct clk_hw *hw) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 16062306a36Sopenharmony_ci u32 val; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3); 16362306a36Sopenharmony_ci val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | 16462306a36Sopenharmony_ci PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD); 16562306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL3); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL4); 16862306a36Sopenharmony_ci val &= ~PLL_FRAC_CTRL4_BYPASS; 16962306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL4); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci pll_lock(pll); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void pll_gf40lp_frac_disable(struct clk_hw *hw) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 17962306a36Sopenharmony_ci u32 val; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL3); 18262306a36Sopenharmony_ci val |= PLL_FRAC_CTRL3_PD; 18362306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL3); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int pll_gf40lp_frac_is_enabled(struct clk_hw *hw) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, 19462306a36Sopenharmony_ci unsigned long parent_rate) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 19762306a36Sopenharmony_ci struct pistachio_pll_rate_table *params; 19862306a36Sopenharmony_ci int enabled = pll_gf40lp_frac_is_enabled(hw); 19962306a36Sopenharmony_ci u64 val, vco, old_postdiv1, old_postdiv2; 20062306a36Sopenharmony_ci const char *name = clk_hw_get_name(hw); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) 20362306a36Sopenharmony_ci return -EINVAL; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci params = pll_get_params(pll, parent_rate, rate); 20662306a36Sopenharmony_ci if (!params || !params->refdiv) 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* calculate vco */ 21062306a36Sopenharmony_ci vco = params->fref; 21162306a36Sopenharmony_ci vco *= (params->fbdiv << 24) + params->frac; 21262306a36Sopenharmony_ci vco = div64_u64(vco, params->refdiv << 24); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) 21562306a36Sopenharmony_ci pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco, 21662306a36Sopenharmony_ci MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci val = div64_u64(params->fref, params->refdiv); 21962306a36Sopenharmony_ci if (val < MIN_PFD) 22062306a36Sopenharmony_ci pr_warn("%s: PFD %llu is too low (min %lu)\n", 22162306a36Sopenharmony_ci name, val, MIN_PFD); 22262306a36Sopenharmony_ci if (val > vco / 16) 22362306a36Sopenharmony_ci pr_warn("%s: PFD %llu is too high (max %llu)\n", 22462306a36Sopenharmony_ci name, val, vco / 16); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 22762306a36Sopenharmony_ci val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | 22862306a36Sopenharmony_ci (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT)); 22962306a36Sopenharmony_ci val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) | 23062306a36Sopenharmony_ci (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT); 23162306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL2); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & 23662306a36Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV1_MASK; 23762306a36Sopenharmony_ci old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & 23862306a36Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV2_MASK; 23962306a36Sopenharmony_ci if (enabled && 24062306a36Sopenharmony_ci (params->postdiv1 != old_postdiv1 || 24162306a36Sopenharmony_ci params->postdiv2 != old_postdiv2)) 24262306a36Sopenharmony_ci pr_warn("%s: changing postdiv while PLL is enabled\n", name); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (params->postdiv2 > params->postdiv1) 24562306a36Sopenharmony_ci pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | 24862306a36Sopenharmony_ci (PLL_FRAC_CTRL2_POSTDIV1_MASK << 24962306a36Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | 25062306a36Sopenharmony_ci (PLL_FRAC_CTRL2_POSTDIV2_MASK << 25162306a36Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV2_SHIFT)); 25262306a36Sopenharmony_ci val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) | 25362306a36Sopenharmony_ci (params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | 25462306a36Sopenharmony_ci (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); 25562306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL2); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* set operating mode */ 25862306a36Sopenharmony_ci if (params->frac) 25962306a36Sopenharmony_ci pll_frac_set_mode(hw, PLL_MODE_FRAC); 26062306a36Sopenharmony_ci else 26162306a36Sopenharmony_ci pll_frac_set_mode(hw, PLL_MODE_INT); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (enabled) 26462306a36Sopenharmony_ci pll_lock(pll); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, 27062306a36Sopenharmony_ci unsigned long parent_rate) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 27362306a36Sopenharmony_ci u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 27662306a36Sopenharmony_ci prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; 27762306a36Sopenharmony_ci fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL2); 28062306a36Sopenharmony_ci postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & 28162306a36Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV1_MASK; 28262306a36Sopenharmony_ci postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & 28362306a36Sopenharmony_ci PLL_FRAC_CTRL2_POSTDIV2_MASK; 28462306a36Sopenharmony_ci frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* get operating mode (int/frac) and calculate rate accordingly */ 28762306a36Sopenharmony_ci rate = parent_rate; 28862306a36Sopenharmony_ci if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) 28962306a36Sopenharmony_ci rate *= (fbdiv << 24) + frac; 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci rate *= (fbdiv << 24); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return rate; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic const struct clk_ops pll_gf40lp_frac_ops = { 29962306a36Sopenharmony_ci .enable = pll_gf40lp_frac_enable, 30062306a36Sopenharmony_ci .disable = pll_gf40lp_frac_disable, 30162306a36Sopenharmony_ci .is_enabled = pll_gf40lp_frac_is_enabled, 30262306a36Sopenharmony_ci .recalc_rate = pll_gf40lp_frac_recalc_rate, 30362306a36Sopenharmony_ci .round_rate = pll_round_rate, 30462306a36Sopenharmony_ci .set_rate = pll_gf40lp_frac_set_rate, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct clk_ops pll_gf40lp_frac_fixed_ops = { 30862306a36Sopenharmony_ci .enable = pll_gf40lp_frac_enable, 30962306a36Sopenharmony_ci .disable = pll_gf40lp_frac_disable, 31062306a36Sopenharmony_ci .is_enabled = pll_gf40lp_frac_is_enabled, 31162306a36Sopenharmony_ci .recalc_rate = pll_gf40lp_frac_recalc_rate, 31262306a36Sopenharmony_ci}; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int pll_gf40lp_laint_enable(struct clk_hw *hw) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 31762306a36Sopenharmony_ci u32 val; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 32062306a36Sopenharmony_ci val &= ~(PLL_INT_CTRL1_PD | 32162306a36Sopenharmony_ci PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD); 32262306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL2); 32562306a36Sopenharmony_ci val &= ~PLL_INT_CTRL2_BYPASS; 32662306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL2); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci pll_lock(pll); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void pll_gf40lp_laint_disable(struct clk_hw *hw) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 33662306a36Sopenharmony_ci u32 val; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 33962306a36Sopenharmony_ci val |= PLL_INT_CTRL1_PD; 34062306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int pll_gf40lp_laint_is_enabled(struct clk_hw *hw) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, 35162306a36Sopenharmony_ci unsigned long parent_rate) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 35462306a36Sopenharmony_ci struct pistachio_pll_rate_table *params; 35562306a36Sopenharmony_ci int enabled = pll_gf40lp_laint_is_enabled(hw); 35662306a36Sopenharmony_ci u32 val, vco, old_postdiv1, old_postdiv2; 35762306a36Sopenharmony_ci const char *name = clk_hw_get_name(hw); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci params = pll_get_params(pll, parent_rate, rate); 36362306a36Sopenharmony_ci if (!params || !params->refdiv) 36462306a36Sopenharmony_ci return -EINVAL; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci vco = div_u64(params->fref * params->fbdiv, params->refdiv); 36762306a36Sopenharmony_ci if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) 36862306a36Sopenharmony_ci pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, 36962306a36Sopenharmony_ci MIN_VCO_LA, MAX_VCO_LA); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci val = div_u64(params->fref, params->refdiv); 37262306a36Sopenharmony_ci if (val < MIN_PFD) 37362306a36Sopenharmony_ci pr_warn("%s: PFD %u is too low (min %lu)\n", 37462306a36Sopenharmony_ci name, val, MIN_PFD); 37562306a36Sopenharmony_ci if (val > vco / 16) 37662306a36Sopenharmony_ci pr_warn("%s: PFD %u is too high (max %u)\n", 37762306a36Sopenharmony_ci name, val, vco / 16); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & 38262306a36Sopenharmony_ci PLL_INT_CTRL1_POSTDIV1_MASK; 38362306a36Sopenharmony_ci old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & 38462306a36Sopenharmony_ci PLL_INT_CTRL1_POSTDIV2_MASK; 38562306a36Sopenharmony_ci if (enabled && 38662306a36Sopenharmony_ci (params->postdiv1 != old_postdiv1 || 38762306a36Sopenharmony_ci params->postdiv2 != old_postdiv2)) 38862306a36Sopenharmony_ci pr_warn("%s: changing postdiv while PLL is enabled\n", name); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (params->postdiv2 > params->postdiv1) 39162306a36Sopenharmony_ci pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | 39462306a36Sopenharmony_ci (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | 39562306a36Sopenharmony_ci (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | 39662306a36Sopenharmony_ci (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT)); 39762306a36Sopenharmony_ci val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) | 39862306a36Sopenharmony_ci (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) | 39962306a36Sopenharmony_ci (params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) | 40062306a36Sopenharmony_ci (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); 40162306a36Sopenharmony_ci pll_writel(pll, val, PLL_CTRL1); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (enabled) 40462306a36Sopenharmony_ci pll_lock(pll); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw, 41062306a36Sopenharmony_ci unsigned long parent_rate) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 41362306a36Sopenharmony_ci u32 val, prediv, fbdiv, postdiv1, postdiv2; 41462306a36Sopenharmony_ci u64 rate = parent_rate; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci val = pll_readl(pll, PLL_CTRL1); 41762306a36Sopenharmony_ci prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; 41862306a36Sopenharmony_ci fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK; 41962306a36Sopenharmony_ci postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & 42062306a36Sopenharmony_ci PLL_INT_CTRL1_POSTDIV1_MASK; 42162306a36Sopenharmony_ci postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & 42262306a36Sopenharmony_ci PLL_INT_CTRL1_POSTDIV2_MASK; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci rate *= fbdiv; 42562306a36Sopenharmony_ci rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return rate; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const struct clk_ops pll_gf40lp_laint_ops = { 43162306a36Sopenharmony_ci .enable = pll_gf40lp_laint_enable, 43262306a36Sopenharmony_ci .disable = pll_gf40lp_laint_disable, 43362306a36Sopenharmony_ci .is_enabled = pll_gf40lp_laint_is_enabled, 43462306a36Sopenharmony_ci .recalc_rate = pll_gf40lp_laint_recalc_rate, 43562306a36Sopenharmony_ci .round_rate = pll_round_rate, 43662306a36Sopenharmony_ci .set_rate = pll_gf40lp_laint_set_rate, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const struct clk_ops pll_gf40lp_laint_fixed_ops = { 44062306a36Sopenharmony_ci .enable = pll_gf40lp_laint_enable, 44162306a36Sopenharmony_ci .disable = pll_gf40lp_laint_disable, 44262306a36Sopenharmony_ci .is_enabled = pll_gf40lp_laint_is_enabled, 44362306a36Sopenharmony_ci .recalc_rate = pll_gf40lp_laint_recalc_rate, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic struct clk *pll_register(const char *name, const char *parent_name, 44762306a36Sopenharmony_ci unsigned long flags, void __iomem *base, 44862306a36Sopenharmony_ci enum pistachio_pll_type type, 44962306a36Sopenharmony_ci struct pistachio_pll_rate_table *rates, 45062306a36Sopenharmony_ci unsigned int nr_rates) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct pistachio_clk_pll *pll; 45362306a36Sopenharmony_ci struct clk_init_data init; 45462306a36Sopenharmony_ci struct clk *clk; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci pll = kzalloc(sizeof(*pll), GFP_KERNEL); 45762306a36Sopenharmony_ci if (!pll) 45862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci init.name = name; 46162306a36Sopenharmony_ci init.flags = flags | CLK_GET_RATE_NOCACHE; 46262306a36Sopenharmony_ci init.parent_names = &parent_name; 46362306a36Sopenharmony_ci init.num_parents = 1; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci switch (type) { 46662306a36Sopenharmony_ci case PLL_GF40LP_FRAC: 46762306a36Sopenharmony_ci if (rates) 46862306a36Sopenharmony_ci init.ops = &pll_gf40lp_frac_ops; 46962306a36Sopenharmony_ci else 47062306a36Sopenharmony_ci init.ops = &pll_gf40lp_frac_fixed_ops; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci case PLL_GF40LP_LAINT: 47362306a36Sopenharmony_ci if (rates) 47462306a36Sopenharmony_ci init.ops = &pll_gf40lp_laint_ops; 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci init.ops = &pll_gf40lp_laint_fixed_ops; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci default: 47962306a36Sopenharmony_ci pr_err("Unrecognized PLL type %u\n", type); 48062306a36Sopenharmony_ci kfree(pll); 48162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci pll->hw.init = &init; 48562306a36Sopenharmony_ci pll->base = base; 48662306a36Sopenharmony_ci pll->rates = rates; 48762306a36Sopenharmony_ci pll->nr_rates = nr_rates; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci clk = clk_register(NULL, &pll->hw); 49062306a36Sopenharmony_ci if (IS_ERR(clk)) 49162306a36Sopenharmony_ci kfree(pll); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return clk; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_civoid pistachio_clk_register_pll(struct pistachio_clk_provider *p, 49762306a36Sopenharmony_ci struct pistachio_pll *pll, 49862306a36Sopenharmony_ci unsigned int num) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct clk *clk; 50162306a36Sopenharmony_ci unsigned int i; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci for (i = 0; i < num; i++) { 50462306a36Sopenharmony_ci clk = pll_register(pll[i].name, pll[i].parent, 50562306a36Sopenharmony_ci 0, p->base + pll[i].reg_base, 50662306a36Sopenharmony_ci pll[i].type, pll[i].rates, 50762306a36Sopenharmony_ci pll[i].nr_rates); 50862306a36Sopenharmony_ci p->clk_data.clks[pll[i].id] = clk; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci} 511