511 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Google, Inc.4 */5 6#define pr_fmt(fmt) "%s: " fmt, __func__7 8#include <linux/clk-provider.h>9#include <linux/io.h>10#include <linux/kernel.h>11#include <linux/printk.h>12#include <linux/slab.h>13 14#include "clk.h"15 16#define PLL_STATUS 0x017#define PLL_STATUS_LOCK BIT(0)18 19#define PLL_CTRL1 0x420#define PLL_CTRL1_REFDIV_SHIFT 021#define PLL_CTRL1_REFDIV_MASK 0x3f22#define PLL_CTRL1_FBDIV_SHIFT 623#define PLL_CTRL1_FBDIV_MASK 0xfff24#define PLL_INT_CTRL1_POSTDIV1_SHIFT 1825#define PLL_INT_CTRL1_POSTDIV1_MASK 0x726#define PLL_INT_CTRL1_POSTDIV2_SHIFT 2127#define PLL_INT_CTRL1_POSTDIV2_MASK 0x728#define PLL_INT_CTRL1_PD BIT(24)29#define PLL_INT_CTRL1_DSMPD BIT(25)30#define PLL_INT_CTRL1_FOUTPOSTDIVPD BIT(26)31#define PLL_INT_CTRL1_FOUTVCOPD BIT(27)32 33#define PLL_CTRL2 0x834#define PLL_FRAC_CTRL2_FRAC_SHIFT 035#define PLL_FRAC_CTRL2_FRAC_MASK 0xffffff36#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT 2437#define PLL_FRAC_CTRL2_POSTDIV1_MASK 0x738#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT 2739#define PLL_FRAC_CTRL2_POSTDIV2_MASK 0x740#define PLL_INT_CTRL2_BYPASS BIT(28)41 42#define PLL_CTRL3 0xc43#define PLL_FRAC_CTRL3_PD BIT(0)44#define PLL_FRAC_CTRL3_DACPD BIT(1)45#define PLL_FRAC_CTRL3_DSMPD BIT(2)46#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD BIT(3)47#define PLL_FRAC_CTRL3_FOUT4PHASEPD BIT(4)48#define PLL_FRAC_CTRL3_FOUTVCOPD BIT(5)49 50#define PLL_CTRL4 0x1051#define PLL_FRAC_CTRL4_BYPASS BIT(28)52 53#define MIN_PFD 9600000UL54#define MIN_VCO_LA 400000000UL55#define MAX_VCO_LA 1600000000UL56#define MIN_VCO_FRAC_INT 600000000UL57#define MAX_VCO_FRAC_INT 1600000000UL58#define MIN_VCO_FRAC_FRAC 600000000UL59#define MAX_VCO_FRAC_FRAC 2400000000UL60#define MIN_OUTPUT_LA 8000000UL61#define MAX_OUTPUT_LA 1600000000UL62#define MIN_OUTPUT_FRAC 12000000UL63#define MAX_OUTPUT_FRAC 1600000000UL64 65/* Fractional PLL operating modes */66enum pll_mode {67 PLL_MODE_FRAC,68 PLL_MODE_INT,69};70 71struct pistachio_clk_pll {72 struct clk_hw hw;73 void __iomem *base;74 struct pistachio_pll_rate_table *rates;75 unsigned int nr_rates;76};77 78static inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg)79{80 return readl(pll->base + reg);81}82 83static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg)84{85 writel(val, pll->base + reg);86}87 88static inline void pll_lock(struct pistachio_clk_pll *pll)89{90 while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))91 cpu_relax();92}93 94static inline u64 do_div_round_closest(u64 dividend, u64 divisor)95{96 dividend += divisor / 2;97 return div64_u64(dividend, divisor);98}99 100static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)101{102 return container_of(hw, struct pistachio_clk_pll, hw);103}104 105static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw)106{107 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);108 u32 val;109 110 val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD;111 return val ? PLL_MODE_INT : PLL_MODE_FRAC;112}113 114static inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode)115{116 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);117 u32 val;118 119 val = pll_readl(pll, PLL_CTRL3);120 if (mode == PLL_MODE_INT)121 val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD;122 else123 val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD);124 125 pll_writel(pll, val, PLL_CTRL3);126}127 128static struct pistachio_pll_rate_table *129pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref,130 unsigned long fout)131{132 unsigned int i;133 134 for (i = 0; i < pll->nr_rates; i++) {135 if (pll->rates[i].fref == fref && pll->rates[i].fout == fout)136 return &pll->rates[i];137 }138 139 return NULL;140}141 142static long pll_round_rate(struct clk_hw *hw, unsigned long rate,143 unsigned long *parent_rate)144{145 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);146 unsigned int i;147 148 for (i = 0; i < pll->nr_rates; i++) {149 if (i > 0 && pll->rates[i].fref == *parent_rate &&150 pll->rates[i].fout <= rate)151 return pll->rates[i - 1].fout;152 }153 154 return pll->rates[0].fout;155}156 157static int pll_gf40lp_frac_enable(struct clk_hw *hw)158{159 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);160 u32 val;161 162 val = pll_readl(pll, PLL_CTRL3);163 val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD |164 PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD);165 pll_writel(pll, val, PLL_CTRL3);166 167 val = pll_readl(pll, PLL_CTRL4);168 val &= ~PLL_FRAC_CTRL4_BYPASS;169 pll_writel(pll, val, PLL_CTRL4);170 171 pll_lock(pll);172 173 return 0;174}175 176static void pll_gf40lp_frac_disable(struct clk_hw *hw)177{178 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);179 u32 val;180 181 val = pll_readl(pll, PLL_CTRL3);182 val |= PLL_FRAC_CTRL3_PD;183 pll_writel(pll, val, PLL_CTRL3);184}185 186static int pll_gf40lp_frac_is_enabled(struct clk_hw *hw)187{188 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);189 190 return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD);191}192 193static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,194 unsigned long parent_rate)195{196 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);197 struct pistachio_pll_rate_table *params;198 int enabled = pll_gf40lp_frac_is_enabled(hw);199 u64 val, vco, old_postdiv1, old_postdiv2;200 const char *name = clk_hw_get_name(hw);201 202 if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC)203 return -EINVAL;204 205 params = pll_get_params(pll, parent_rate, rate);206 if (!params || !params->refdiv)207 return -EINVAL;208 209 /* calculate vco */210 vco = params->fref;211 vco *= (params->fbdiv << 24) + params->frac;212 vco = div64_u64(vco, params->refdiv << 24);213 214 if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC)215 pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco,216 MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC);217 218 val = div64_u64(params->fref, params->refdiv);219 if (val < MIN_PFD)220 pr_warn("%s: PFD %llu is too low (min %lu)\n",221 name, val, MIN_PFD);222 if (val > vco / 16)223 pr_warn("%s: PFD %llu is too high (max %llu)\n",224 name, val, vco / 16);225 226 val = pll_readl(pll, PLL_CTRL1);227 val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |228 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT));229 val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |230 (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT);231 pll_writel(pll, val, PLL_CTRL1);232 233 val = pll_readl(pll, PLL_CTRL2);234 235 old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &236 PLL_FRAC_CTRL2_POSTDIV1_MASK;237 old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &238 PLL_FRAC_CTRL2_POSTDIV2_MASK;239 if (enabled &&240 (params->postdiv1 != old_postdiv1 ||241 params->postdiv2 != old_postdiv2))242 pr_warn("%s: changing postdiv while PLL is enabled\n", name);243 244 if (params->postdiv2 > params->postdiv1)245 pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);246 247 val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) |248 (PLL_FRAC_CTRL2_POSTDIV1_MASK <<249 PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |250 (PLL_FRAC_CTRL2_POSTDIV2_MASK <<251 PLL_FRAC_CTRL2_POSTDIV2_SHIFT));252 val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) |253 (params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |254 (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT);255 pll_writel(pll, val, PLL_CTRL2);256 257 /* set operating mode */258 if (params->frac)259 pll_frac_set_mode(hw, PLL_MODE_FRAC);260 else261 pll_frac_set_mode(hw, PLL_MODE_INT);262 263 if (enabled)264 pll_lock(pll);265 266 return 0;267}268 269static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,270 unsigned long parent_rate)271{272 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);273 u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate;274 275 val = pll_readl(pll, PLL_CTRL1);276 prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;277 fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;278 279 val = pll_readl(pll, PLL_CTRL2);280 postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &281 PLL_FRAC_CTRL2_POSTDIV1_MASK;282 postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &283 PLL_FRAC_CTRL2_POSTDIV2_MASK;284 frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK;285 286 /* get operating mode (int/frac) and calculate rate accordingly */287 rate = parent_rate;288 if (pll_frac_get_mode(hw) == PLL_MODE_FRAC)289 rate *= (fbdiv << 24) + frac;290 else291 rate *= (fbdiv << 24);292 293 rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24);294 295 return rate;296}297 298static const struct clk_ops pll_gf40lp_frac_ops = {299 .enable = pll_gf40lp_frac_enable,300 .disable = pll_gf40lp_frac_disable,301 .is_enabled = pll_gf40lp_frac_is_enabled,302 .recalc_rate = pll_gf40lp_frac_recalc_rate,303 .round_rate = pll_round_rate,304 .set_rate = pll_gf40lp_frac_set_rate,305};306 307static const struct clk_ops pll_gf40lp_frac_fixed_ops = {308 .enable = pll_gf40lp_frac_enable,309 .disable = pll_gf40lp_frac_disable,310 .is_enabled = pll_gf40lp_frac_is_enabled,311 .recalc_rate = pll_gf40lp_frac_recalc_rate,312};313 314static int pll_gf40lp_laint_enable(struct clk_hw *hw)315{316 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);317 u32 val;318 319 val = pll_readl(pll, PLL_CTRL1);320 val &= ~(PLL_INT_CTRL1_PD |321 PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD);322 pll_writel(pll, val, PLL_CTRL1);323 324 val = pll_readl(pll, PLL_CTRL2);325 val &= ~PLL_INT_CTRL2_BYPASS;326 pll_writel(pll, val, PLL_CTRL2);327 328 pll_lock(pll);329 330 return 0;331}332 333static void pll_gf40lp_laint_disable(struct clk_hw *hw)334{335 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);336 u32 val;337 338 val = pll_readl(pll, PLL_CTRL1);339 val |= PLL_INT_CTRL1_PD;340 pll_writel(pll, val, PLL_CTRL1);341}342 343static int pll_gf40lp_laint_is_enabled(struct clk_hw *hw)344{345 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);346 347 return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD);348}349 350static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,351 unsigned long parent_rate)352{353 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);354 struct pistachio_pll_rate_table *params;355 int enabled = pll_gf40lp_laint_is_enabled(hw);356 u32 val, vco, old_postdiv1, old_postdiv2;357 const char *name = clk_hw_get_name(hw);358 359 if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA)360 return -EINVAL;361 362 params = pll_get_params(pll, parent_rate, rate);363 if (!params || !params->refdiv)364 return -EINVAL;365 366 vco = div_u64(params->fref * params->fbdiv, params->refdiv);367 if (vco < MIN_VCO_LA || vco > MAX_VCO_LA)368 pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,369 MIN_VCO_LA, MAX_VCO_LA);370 371 val = div_u64(params->fref, params->refdiv);372 if (val < MIN_PFD)373 pr_warn("%s: PFD %u is too low (min %lu)\n",374 name, val, MIN_PFD);375 if (val > vco / 16)376 pr_warn("%s: PFD %u is too high (max %u)\n",377 name, val, vco / 16);378 379 val = pll_readl(pll, PLL_CTRL1);380 381 old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &382 PLL_INT_CTRL1_POSTDIV1_MASK;383 old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &384 PLL_INT_CTRL1_POSTDIV2_MASK;385 if (enabled &&386 (params->postdiv1 != old_postdiv1 ||387 params->postdiv2 != old_postdiv2))388 pr_warn("%s: changing postdiv while PLL is enabled\n", name);389 390 if (params->postdiv2 > params->postdiv1)391 pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);392 393 val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |394 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) |395 (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) |396 (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT));397 val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |398 (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) |399 (params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) |400 (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT);401 pll_writel(pll, val, PLL_CTRL1);402 403 if (enabled)404 pll_lock(pll);405 406 return 0;407}408 409static unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw,410 unsigned long parent_rate)411{412 struct pistachio_clk_pll *pll = to_pistachio_pll(hw);413 u32 val, prediv, fbdiv, postdiv1, postdiv2;414 u64 rate = parent_rate;415 416 val = pll_readl(pll, PLL_CTRL1);417 prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;418 fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;419 postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &420 PLL_INT_CTRL1_POSTDIV1_MASK;421 postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &422 PLL_INT_CTRL1_POSTDIV2_MASK;423 424 rate *= fbdiv;425 rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2);426 427 return rate;428}429 430static const struct clk_ops pll_gf40lp_laint_ops = {431 .enable = pll_gf40lp_laint_enable,432 .disable = pll_gf40lp_laint_disable,433 .is_enabled = pll_gf40lp_laint_is_enabled,434 .recalc_rate = pll_gf40lp_laint_recalc_rate,435 .round_rate = pll_round_rate,436 .set_rate = pll_gf40lp_laint_set_rate,437};438 439static const struct clk_ops pll_gf40lp_laint_fixed_ops = {440 .enable = pll_gf40lp_laint_enable,441 .disable = pll_gf40lp_laint_disable,442 .is_enabled = pll_gf40lp_laint_is_enabled,443 .recalc_rate = pll_gf40lp_laint_recalc_rate,444};445 446static struct clk *pll_register(const char *name, const char *parent_name,447 unsigned long flags, void __iomem *base,448 enum pistachio_pll_type type,449 struct pistachio_pll_rate_table *rates,450 unsigned int nr_rates)451{452 struct pistachio_clk_pll *pll;453 struct clk_init_data init;454 struct clk *clk;455 456 pll = kzalloc(sizeof(*pll), GFP_KERNEL);457 if (!pll)458 return ERR_PTR(-ENOMEM);459 460 init.name = name;461 init.flags = flags | CLK_GET_RATE_NOCACHE;462 init.parent_names = &parent_name;463 init.num_parents = 1;464 465 switch (type) {466 case PLL_GF40LP_FRAC:467 if (rates)468 init.ops = &pll_gf40lp_frac_ops;469 else470 init.ops = &pll_gf40lp_frac_fixed_ops;471 break;472 case PLL_GF40LP_LAINT:473 if (rates)474 init.ops = &pll_gf40lp_laint_ops;475 else476 init.ops = &pll_gf40lp_laint_fixed_ops;477 break;478 default:479 pr_err("Unrecognized PLL type %u\n", type);480 kfree(pll);481 return ERR_PTR(-EINVAL);482 }483 484 pll->hw.init = &init;485 pll->base = base;486 pll->rates = rates;487 pll->nr_rates = nr_rates;488 489 clk = clk_register(NULL, &pll->hw);490 if (IS_ERR(clk))491 kfree(pll);492 493 return clk;494}495 496void pistachio_clk_register_pll(struct pistachio_clk_provider *p,497 struct pistachio_pll *pll,498 unsigned int num)499{500 struct clk *clk;501 unsigned int i;502 503 for (i = 0; i < num; i++) {504 clk = pll_register(pll[i].name, pll[i].parent,505 0, p->base + pll[i].reg_base,506 pll[i].type, pll[i].rates,507 pll[i].nr_rates);508 p->clk_data.clks[pll[i].id] = clk;509 }510}511