122f65a38SJerome Brunet // SPDX-License-Identifier: GPL-2.0 27a29a869SCarlo Caione /* 37a29a869SCarlo Caione * Copyright (c) 2015 Endless Mobile, Inc. 47a29a869SCarlo Caione * Author: Carlo Caione <carlo@endlessm.com> 57a29a869SCarlo Caione * 68289aafaSJerome Brunet * Copyright (c) 2018 Baylibre, SAS. 78289aafaSJerome Brunet * Author: Jerome Brunet <jbrunet@baylibre.com> 87a29a869SCarlo Caione */ 97a29a869SCarlo Caione 107a29a869SCarlo Caione /* 117a29a869SCarlo Caione * In the most basic form, a Meson PLL is composed as follows: 127a29a869SCarlo Caione * 137a29a869SCarlo Caione * PLL 1487173557SJerome Brunet * +--------------------------------+ 157a29a869SCarlo Caione * | | 1687173557SJerome Brunet * | +--+ | 1787173557SJerome Brunet * in >>-----[ /N ]--->| | +-----+ | 1887173557SJerome Brunet * | | |------| DCO |---->> out 1987173557SJerome Brunet * | +--------->| | +--v--+ | 2087173557SJerome Brunet * | | +--+ | | 2187173557SJerome Brunet * | | | | 2287173557SJerome Brunet * | +--[ *(M + (F/Fmax) ]<--+ | 237a29a869SCarlo Caione * | | 2487173557SJerome Brunet * +--------------------------------+ 257a29a869SCarlo Caione * 2687173557SJerome Brunet * out = in * (m + frac / frac_max) / n 277a29a869SCarlo Caione */ 287a29a869SCarlo Caione 297a29a869SCarlo Caione #include <linux/clk-provider.h> 307a29a869SCarlo Caione #include <linux/delay.h> 317a29a869SCarlo Caione #include <linux/err.h> 327a29a869SCarlo Caione #include <linux/io.h> 3394aa8a41SJerome Brunet #include <linux/math64.h> 347a29a869SCarlo Caione #include <linux/module.h> 357a29a869SCarlo Caione 36*889c2b7eSJerome Brunet #include "clk-regmap.h" 37*889c2b7eSJerome Brunet #include "clk-pll.h" 387a29a869SCarlo Caione 39722825dcSJerome Brunet static inline struct meson_clk_pll_data * 40722825dcSJerome Brunet meson_clk_pll_data(struct clk_regmap *clk) 41722825dcSJerome Brunet { 42722825dcSJerome Brunet return (struct meson_clk_pll_data *)clk->data; 43722825dcSJerome Brunet } 447a29a869SCarlo Caione 458289aafaSJerome Brunet static unsigned long __pll_params_to_rate(unsigned long parent_rate, 46dd601dbcSJerome Brunet const struct pll_params_table *pllt, 478289aafaSJerome Brunet u16 frac, 488289aafaSJerome Brunet struct meson_clk_pll_data *pll) 498289aafaSJerome Brunet { 508289aafaSJerome Brunet u64 rate = (u64)parent_rate * pllt->m; 518289aafaSJerome Brunet 528289aafaSJerome Brunet if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { 538289aafaSJerome Brunet u64 frac_rate = (u64)parent_rate * frac; 548289aafaSJerome Brunet 558289aafaSJerome Brunet rate += DIV_ROUND_UP_ULL(frac_rate, 568289aafaSJerome Brunet (1 << pll->frac.width)); 578289aafaSJerome Brunet } 588289aafaSJerome Brunet 5987173557SJerome Brunet return DIV_ROUND_UP_ULL(rate, pllt->n); 608289aafaSJerome Brunet } 618289aafaSJerome Brunet 627a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, 637a29a869SCarlo Caione unsigned long parent_rate) 647a29a869SCarlo Caione { 65722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 66722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 67dd601dbcSJerome Brunet struct pll_params_table pllt; 688289aafaSJerome Brunet u16 frac; 697a29a869SCarlo Caione 708289aafaSJerome Brunet pllt.n = meson_parm_read(clk->map, &pll->n); 718289aafaSJerome Brunet pllt.m = meson_parm_read(clk->map, &pll->m); 727d3142e5SJerome Brunet 738289aafaSJerome Brunet frac = MESON_PARM_APPLICABLE(&pll->frac) ? 748289aafaSJerome Brunet meson_parm_read(clk->map, &pll->frac) : 758289aafaSJerome Brunet 0; 7694aa8a41SJerome Brunet 778289aafaSJerome Brunet return __pll_params_to_rate(parent_rate, &pllt, frac, pll); 7894aa8a41SJerome Brunet } 7994aa8a41SJerome Brunet 808289aafaSJerome Brunet static u16 __pll_params_with_frac(unsigned long rate, 818289aafaSJerome Brunet unsigned long parent_rate, 82dd601dbcSJerome Brunet const struct pll_params_table *pllt, 838289aafaSJerome Brunet struct meson_clk_pll_data *pll) 848289aafaSJerome Brunet { 858289aafaSJerome Brunet u16 frac_max = (1 << pll->frac.width); 868289aafaSJerome Brunet u64 val = (u64)rate * pllt->n; 878289aafaSJerome Brunet 880a1be867SJerome Brunet if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) 890a1be867SJerome Brunet val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); 900a1be867SJerome Brunet else 918289aafaSJerome Brunet val = div_u64(val * frac_max, parent_rate); 920a1be867SJerome Brunet 938289aafaSJerome Brunet val -= pllt->m * frac_max; 948289aafaSJerome Brunet 958289aafaSJerome Brunet return min((u16)val, (u16)(frac_max - 1)); 968289aafaSJerome Brunet } 978289aafaSJerome Brunet 98dd601dbcSJerome Brunet static bool meson_clk_pll_is_better(unsigned long rate, 99dd601dbcSJerome Brunet unsigned long best, 100dd601dbcSJerome Brunet unsigned long now, 1018289aafaSJerome Brunet struct meson_clk_pll_data *pll) 1028289aafaSJerome Brunet { 103dd601dbcSJerome Brunet if (!(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) || 104dd601dbcSJerome Brunet MESON_PARM_APPLICABLE(&pll->frac)) { 105dd601dbcSJerome Brunet /* Round down */ 106dd601dbcSJerome Brunet if (now < rate && best < now) 107dd601dbcSJerome Brunet return true; 108dd601dbcSJerome Brunet } else { 109dd601dbcSJerome Brunet /* Round Closest */ 110dd601dbcSJerome Brunet if (abs(now - rate) < abs(best - rate)) 111dd601dbcSJerome Brunet return true; 112dd601dbcSJerome Brunet } 113dd601dbcSJerome Brunet 114dd601dbcSJerome Brunet return false; 115dd601dbcSJerome Brunet } 116dd601dbcSJerome Brunet 117dd601dbcSJerome Brunet static const struct pll_params_table * 118dd601dbcSJerome Brunet meson_clk_get_pll_settings(unsigned long rate, 119dd601dbcSJerome Brunet unsigned long parent_rate, 120dd601dbcSJerome Brunet struct meson_clk_pll_data *pll) 121dd601dbcSJerome Brunet { 122dd601dbcSJerome Brunet const struct pll_params_table *table = pll->table; 123dd601dbcSJerome Brunet unsigned long best = 0, now = 0; 124dd601dbcSJerome Brunet unsigned int i, best_i = 0; 1258289aafaSJerome Brunet 1268289aafaSJerome Brunet if (!table) 1278289aafaSJerome Brunet return NULL; 1288289aafaSJerome Brunet 129dd601dbcSJerome Brunet for (i = 0; table[i].n; i++) { 130dd601dbcSJerome Brunet now = __pll_params_to_rate(parent_rate, &table[i], 0, pll); 1318289aafaSJerome Brunet 132dd601dbcSJerome Brunet /* If we get an exact match, don't bother any further */ 133dd601dbcSJerome Brunet if (now == rate) { 134dd601dbcSJerome Brunet return &table[i]; 135dd601dbcSJerome Brunet } else if (meson_clk_pll_is_better(rate, best, now, pll)) { 136dd601dbcSJerome Brunet best = now; 137dd601dbcSJerome Brunet best_i = i; 138dd601dbcSJerome Brunet } 1390a1be867SJerome Brunet } 1408289aafaSJerome Brunet 141dd601dbcSJerome Brunet return (struct pll_params_table *)&table[best_i]; 1427a29a869SCarlo Caione } 1437a29a869SCarlo Caione 1447a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 1457a29a869SCarlo Caione unsigned long *parent_rate) 1467a29a869SCarlo Caione { 147722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 148722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 149dd601dbcSJerome Brunet const struct pll_params_table *pllt = 150dd601dbcSJerome Brunet meson_clk_get_pll_settings(rate, *parent_rate, pll); 151dd601dbcSJerome Brunet unsigned long round; 1528289aafaSJerome Brunet u16 frac; 1537a29a869SCarlo Caione 1548289aafaSJerome Brunet if (!pllt) 155840e1a73SJerome Brunet return meson_clk_pll_recalc_rate(hw, *parent_rate); 156840e1a73SJerome Brunet 157dd601dbcSJerome Brunet round = __pll_params_to_rate(*parent_rate, pllt, 0, pll); 158dd601dbcSJerome Brunet 159dd601dbcSJerome Brunet if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round) 160dd601dbcSJerome Brunet return round; 1617a29a869SCarlo Caione 1628289aafaSJerome Brunet /* 1638289aafaSJerome Brunet * The rate provided by the setting is not an exact match, let's 1648289aafaSJerome Brunet * try to improve the result using the fractional parameter 1658289aafaSJerome Brunet */ 1668289aafaSJerome Brunet frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll); 1677a29a869SCarlo Caione 1688289aafaSJerome Brunet return __pll_params_to_rate(*parent_rate, pllt, frac, pll); 1697a29a869SCarlo Caione } 1707a29a869SCarlo Caione 171722825dcSJerome Brunet static int meson_clk_pll_wait_lock(struct clk_hw *hw) 172722825dcSJerome Brunet { 173722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 174722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 175c178b003SJerome Brunet int delay = 24000000; 176722825dcSJerome Brunet 177722825dcSJerome Brunet do { 178722825dcSJerome Brunet /* Is the clock locked now ? */ 179722825dcSJerome Brunet if (meson_parm_read(clk->map, &pll->l)) 180722825dcSJerome Brunet return 0; 181722825dcSJerome Brunet 182722825dcSJerome Brunet delay--; 183722825dcSJerome Brunet } while (delay > 0); 184722825dcSJerome Brunet 18545fcbec7SNeil Armstrong return -ETIMEDOUT; 18645fcbec7SNeil Armstrong } 18745fcbec7SNeil Armstrong 188722825dcSJerome Brunet static void meson_clk_pll_init(struct clk_hw *hw) 1897a29a869SCarlo Caione { 190722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 191722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 1927a29a869SCarlo Caione 193722825dcSJerome Brunet if (pll->init_count) { 194722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 195722825dcSJerome Brunet regmap_multi_reg_write(clk->map, pll->init_regs, 196722825dcSJerome Brunet pll->init_count); 197722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 1987a29a869SCarlo Caione } 19945fcbec7SNeil Armstrong } 20045fcbec7SNeil Armstrong 201d6e81845SMartin Blumenstingl static int meson_clk_pll_is_enabled(struct clk_hw *hw) 202d6e81845SMartin Blumenstingl { 203d6e81845SMartin Blumenstingl struct clk_regmap *clk = to_clk_regmap(hw); 204d6e81845SMartin Blumenstingl struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 205d6e81845SMartin Blumenstingl 206d6e81845SMartin Blumenstingl if (meson_parm_read(clk->map, &pll->rst) || 207d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->en) || 208d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->l)) 209d6e81845SMartin Blumenstingl return 0; 210d6e81845SMartin Blumenstingl 211d6e81845SMartin Blumenstingl return 1; 212d6e81845SMartin Blumenstingl } 213d6e81845SMartin Blumenstingl 214e40c7e3cSJerome Brunet static int meson_clk_pll_enable(struct clk_hw *hw) 215e40c7e3cSJerome Brunet { 216e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 217e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 218e40c7e3cSJerome Brunet 219d6e81845SMartin Blumenstingl /* do nothing if the PLL is already enabled */ 220d6e81845SMartin Blumenstingl if (clk_hw_is_enabled(hw)) 221d6e81845SMartin Blumenstingl return 0; 222d6e81845SMartin Blumenstingl 223e40c7e3cSJerome Brunet /* Make sure the pll is in reset */ 224e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 225e40c7e3cSJerome Brunet 226e40c7e3cSJerome Brunet /* Enable the pll */ 227e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 1); 228e40c7e3cSJerome Brunet 229e40c7e3cSJerome Brunet /* Take the pll out reset */ 230e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 231e40c7e3cSJerome Brunet 232e40c7e3cSJerome Brunet if (meson_clk_pll_wait_lock(hw)) 233e40c7e3cSJerome Brunet return -EIO; 234e40c7e3cSJerome Brunet 235e40c7e3cSJerome Brunet return 0; 236e40c7e3cSJerome Brunet } 237e40c7e3cSJerome Brunet 238e40c7e3cSJerome Brunet static void meson_clk_pll_disable(struct clk_hw *hw) 239e40c7e3cSJerome Brunet { 240e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 241e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 242e40c7e3cSJerome Brunet 243e40c7e3cSJerome Brunet /* Put the pll is in reset */ 244e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 245e40c7e3cSJerome Brunet 246e40c7e3cSJerome Brunet /* Disable the pll */ 247e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 0); 248e40c7e3cSJerome Brunet } 249e40c7e3cSJerome Brunet 2507a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 2517a29a869SCarlo Caione unsigned long parent_rate) 2527a29a869SCarlo Caione { 253722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 254722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 255dd601dbcSJerome Brunet const struct pll_params_table *pllt; 256e40c7e3cSJerome Brunet unsigned int enabled; 2577a29a869SCarlo Caione unsigned long old_rate; 2588289aafaSJerome Brunet u16 frac = 0; 2597a29a869SCarlo Caione 2607a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 2617a29a869SCarlo Caione return -EINVAL; 2627a29a869SCarlo Caione 2637a29a869SCarlo Caione old_rate = rate; 2647a29a869SCarlo Caione 265dd601dbcSJerome Brunet pllt = meson_clk_get_pll_settings(rate, parent_rate, pll); 266722825dcSJerome Brunet if (!pllt) 2677a29a869SCarlo Caione return -EINVAL; 2687a29a869SCarlo Caione 269e40c7e3cSJerome Brunet enabled = meson_parm_read(clk->map, &pll->en); 270e40c7e3cSJerome Brunet if (enabled) 271e40c7e3cSJerome Brunet meson_clk_pll_disable(hw); 27245fcbec7SNeil Armstrong 273722825dcSJerome Brunet meson_parm_write(clk->map, &pll->n, pllt->n); 274722825dcSJerome Brunet meson_parm_write(clk->map, &pll->m, pllt->m); 2757a29a869SCarlo Caione 2767a29a869SCarlo Caione 2778289aafaSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->frac)) { 2788289aafaSJerome Brunet frac = __pll_params_with_frac(rate, parent_rate, pllt, pll); 2798289aafaSJerome Brunet meson_parm_write(clk->map, &pll->frac, frac); 2808289aafaSJerome Brunet } 2817a29a869SCarlo Caione 282e40c7e3cSJerome Brunet /* If the pll is stopped, bail out now */ 283e40c7e3cSJerome Brunet if (!enabled) 284e40c7e3cSJerome Brunet return 0; 2854a472951SMichael Turquette 286e40c7e3cSJerome Brunet if (meson_clk_pll_enable(hw)) { 2877a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 2887a29a869SCarlo Caione __func__, old_rate); 289722825dcSJerome Brunet /* 290722825dcSJerome Brunet * FIXME: Do we really need/want this HACK ? 291722825dcSJerome Brunet * It looks unsafe. what happens if the clock gets into a 292722825dcSJerome Brunet * broken state and we can't lock back on the old_rate ? Looks 293722825dcSJerome Brunet * like an infinite recursion is possible 294722825dcSJerome Brunet */ 2957a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 2967a29a869SCarlo Caione } 2977a29a869SCarlo Caione 298722825dcSJerome Brunet return 0; 2997a29a869SCarlo Caione } 3007a29a869SCarlo Caione 301ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 302722825dcSJerome Brunet .init = meson_clk_pll_init, 3037a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 3047a29a869SCarlo Caione .round_rate = meson_clk_pll_round_rate, 3057a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 306d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 307e40c7e3cSJerome Brunet .enable = meson_clk_pll_enable, 308e40c7e3cSJerome Brunet .disable = meson_clk_pll_disable 3097a29a869SCarlo Caione }; 310*889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ops); 3117a29a869SCarlo Caione 312ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 3137a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 314d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 3157a29a869SCarlo Caione }; 316*889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); 317*889c2b7eSJerome Brunet 318*889c2b7eSJerome Brunet MODULE_DESCRIPTION("Amlogic PLL driver"); 319*889c2b7eSJerome Brunet MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); 320*889c2b7eSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 321*889c2b7eSJerome Brunet MODULE_LICENSE("GPL v2"); 322