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> 358eed1db1SJerome Brunet #include <linux/rational.h> 367a29a869SCarlo Caione 37889c2b7eSJerome Brunet #include "clk-regmap.h" 38889c2b7eSJerome Brunet #include "clk-pll.h" 397a29a869SCarlo Caione 40722825dcSJerome Brunet static inline struct meson_clk_pll_data * 41722825dcSJerome Brunet meson_clk_pll_data(struct clk_regmap *clk) 42722825dcSJerome Brunet { 43722825dcSJerome Brunet return (struct meson_clk_pll_data *)clk->data; 44722825dcSJerome Brunet } 457a29a869SCarlo Caione 468eed1db1SJerome Brunet static int __pll_round_closest_mult(struct meson_clk_pll_data *pll) 478eed1db1SJerome Brunet { 488eed1db1SJerome Brunet if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) && 498eed1db1SJerome Brunet !MESON_PARM_APPLICABLE(&pll->frac)) 508eed1db1SJerome Brunet return 1; 518eed1db1SJerome Brunet 528eed1db1SJerome Brunet return 0; 538eed1db1SJerome Brunet } 548eed1db1SJerome Brunet 558289aafaSJerome Brunet static unsigned long __pll_params_to_rate(unsigned long parent_rate, 568eed1db1SJerome Brunet unsigned int m, unsigned int n, 578eed1db1SJerome Brunet unsigned int frac, 588289aafaSJerome Brunet struct meson_clk_pll_data *pll) 598289aafaSJerome Brunet { 608eed1db1SJerome Brunet u64 rate = (u64)parent_rate * m; 618289aafaSJerome Brunet 628289aafaSJerome Brunet if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { 638289aafaSJerome Brunet u64 frac_rate = (u64)parent_rate * frac; 648289aafaSJerome Brunet 658289aafaSJerome Brunet rate += DIV_ROUND_UP_ULL(frac_rate, 668289aafaSJerome Brunet (1 << pll->frac.width)); 678289aafaSJerome Brunet } 688289aafaSJerome Brunet 698eed1db1SJerome Brunet return DIV_ROUND_UP_ULL(rate, n); 708289aafaSJerome Brunet } 718289aafaSJerome Brunet 727a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, 737a29a869SCarlo Caione unsigned long parent_rate) 747a29a869SCarlo Caione { 75722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 76722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 778eed1db1SJerome Brunet unsigned int m, n, frac; 787a29a869SCarlo Caione 798eed1db1SJerome Brunet n = meson_parm_read(clk->map, &pll->n); 808eed1db1SJerome Brunet m = meson_parm_read(clk->map, &pll->m); 817d3142e5SJerome Brunet 828289aafaSJerome Brunet frac = MESON_PARM_APPLICABLE(&pll->frac) ? 838289aafaSJerome Brunet meson_parm_read(clk->map, &pll->frac) : 848289aafaSJerome Brunet 0; 8594aa8a41SJerome Brunet 868eed1db1SJerome Brunet return __pll_params_to_rate(parent_rate, m, n, frac, pll); 8794aa8a41SJerome Brunet } 8894aa8a41SJerome Brunet 898eed1db1SJerome Brunet static unsigned int __pll_params_with_frac(unsigned long rate, 908289aafaSJerome Brunet unsigned long parent_rate, 918eed1db1SJerome Brunet unsigned int m, 928eed1db1SJerome Brunet unsigned int n, 938289aafaSJerome Brunet struct meson_clk_pll_data *pll) 948289aafaSJerome Brunet { 958eed1db1SJerome Brunet unsigned int frac_max = (1 << pll->frac.width); 968eed1db1SJerome Brunet u64 val = (u64)rate * n; 978eed1db1SJerome Brunet 988eed1db1SJerome Brunet /* Bail out if we are already over the requested rate */ 998eed1db1SJerome Brunet if (rate < parent_rate * m / n) 1008eed1db1SJerome Brunet return 0; 1018289aafaSJerome Brunet 1020a1be867SJerome Brunet if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) 1030a1be867SJerome Brunet val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); 1040a1be867SJerome Brunet else 1058289aafaSJerome Brunet val = div_u64(val * frac_max, parent_rate); 1060a1be867SJerome Brunet 1078eed1db1SJerome Brunet val -= m * frac_max; 1088289aafaSJerome Brunet 1098eed1db1SJerome Brunet return min((unsigned int)val, (frac_max - 1)); 1108289aafaSJerome Brunet } 1118289aafaSJerome Brunet 112dd601dbcSJerome Brunet static bool meson_clk_pll_is_better(unsigned long rate, 113dd601dbcSJerome Brunet unsigned long best, 114dd601dbcSJerome Brunet unsigned long now, 1158289aafaSJerome Brunet struct meson_clk_pll_data *pll) 1168289aafaSJerome Brunet { 1178eed1db1SJerome Brunet if (__pll_round_closest_mult(pll)) { 118dd601dbcSJerome Brunet /* Round Closest */ 119dd601dbcSJerome Brunet if (abs(now - rate) < abs(best - rate)) 120dd601dbcSJerome Brunet return true; 1218eed1db1SJerome Brunet } else { 1228eed1db1SJerome Brunet /* Round down */ 123d6f987c8SMartin Blumenstingl if (now <= rate && best < now) 1248eed1db1SJerome Brunet return true; 125dd601dbcSJerome Brunet } 126dd601dbcSJerome Brunet 127dd601dbcSJerome Brunet return false; 128dd601dbcSJerome Brunet } 129dd601dbcSJerome Brunet 1308eed1db1SJerome Brunet static int meson_clk_get_pll_table_index(unsigned int index, 1318eed1db1SJerome Brunet unsigned int *m, 1328eed1db1SJerome Brunet unsigned int *n, 133dd601dbcSJerome Brunet struct meson_clk_pll_data *pll) 134dd601dbcSJerome Brunet { 1358eed1db1SJerome Brunet if (!pll->table[index].n) 1368eed1db1SJerome Brunet return -EINVAL; 1378eed1db1SJerome Brunet 1388eed1db1SJerome Brunet *m = pll->table[index].m; 1398eed1db1SJerome Brunet *n = pll->table[index].n; 1408eed1db1SJerome Brunet 1418eed1db1SJerome Brunet return 0; 1428eed1db1SJerome Brunet } 1438eed1db1SJerome Brunet 1448eed1db1SJerome Brunet static unsigned int meson_clk_get_pll_range_m(unsigned long rate, 1458eed1db1SJerome Brunet unsigned long parent_rate, 1468eed1db1SJerome Brunet unsigned int n, 1478eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 1488eed1db1SJerome Brunet { 1498eed1db1SJerome Brunet u64 val = (u64)rate * n; 1508eed1db1SJerome Brunet 1518eed1db1SJerome Brunet if (__pll_round_closest_mult(pll)) 1528eed1db1SJerome Brunet return DIV_ROUND_CLOSEST_ULL(val, parent_rate); 1538eed1db1SJerome Brunet 1548eed1db1SJerome Brunet return div_u64(val, parent_rate); 1558eed1db1SJerome Brunet } 1568eed1db1SJerome Brunet 1578eed1db1SJerome Brunet static int meson_clk_get_pll_range_index(unsigned long rate, 1588eed1db1SJerome Brunet unsigned long parent_rate, 1598eed1db1SJerome Brunet unsigned int index, 1608eed1db1SJerome Brunet unsigned int *m, 1618eed1db1SJerome Brunet unsigned int *n, 1628eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 1638eed1db1SJerome Brunet { 1648eed1db1SJerome Brunet *n = index + 1; 1658eed1db1SJerome Brunet 1668eed1db1SJerome Brunet /* Check the predivider range */ 1678eed1db1SJerome Brunet if (*n >= (1 << pll->n.width)) 1688eed1db1SJerome Brunet return -EINVAL; 1698eed1db1SJerome Brunet 1708eed1db1SJerome Brunet if (*n == 1) { 1718eed1db1SJerome Brunet /* Get the boundaries out the way */ 1728eed1db1SJerome Brunet if (rate <= pll->range->min * parent_rate) { 1738eed1db1SJerome Brunet *m = pll->range->min; 1748eed1db1SJerome Brunet return -ENODATA; 1758eed1db1SJerome Brunet } else if (rate >= pll->range->max * parent_rate) { 1768eed1db1SJerome Brunet *m = pll->range->max; 1778eed1db1SJerome Brunet return -ENODATA; 1788eed1db1SJerome Brunet } 1798eed1db1SJerome Brunet } 1808eed1db1SJerome Brunet 1818eed1db1SJerome Brunet *m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll); 1828eed1db1SJerome Brunet 1838eed1db1SJerome Brunet /* the pre-divider gives a multiplier too big - stop */ 1848eed1db1SJerome Brunet if (*m >= (1 << pll->m.width)) 1858eed1db1SJerome Brunet return -EINVAL; 1868eed1db1SJerome Brunet 1878eed1db1SJerome Brunet return 0; 1888eed1db1SJerome Brunet } 1898eed1db1SJerome Brunet 1908eed1db1SJerome Brunet static int meson_clk_get_pll_get_index(unsigned long rate, 1918eed1db1SJerome Brunet unsigned long parent_rate, 1928eed1db1SJerome Brunet unsigned int index, 1938eed1db1SJerome Brunet unsigned int *m, 1948eed1db1SJerome Brunet unsigned int *n, 1958eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 1968eed1db1SJerome Brunet { 1978eed1db1SJerome Brunet if (pll->range) 1988eed1db1SJerome Brunet return meson_clk_get_pll_range_index(rate, parent_rate, 1998eed1db1SJerome Brunet index, m, n, pll); 2008eed1db1SJerome Brunet else if (pll->table) 2018eed1db1SJerome Brunet return meson_clk_get_pll_table_index(index, m, n, pll); 2028eed1db1SJerome Brunet 2038eed1db1SJerome Brunet return -EINVAL; 2048eed1db1SJerome Brunet } 2058eed1db1SJerome Brunet 2068eed1db1SJerome Brunet static int meson_clk_get_pll_settings(unsigned long rate, 2078eed1db1SJerome Brunet unsigned long parent_rate, 2088eed1db1SJerome Brunet unsigned int *best_m, 2098eed1db1SJerome Brunet unsigned int *best_n, 2108eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 2118eed1db1SJerome Brunet { 212dd601dbcSJerome Brunet unsigned long best = 0, now = 0; 2138eed1db1SJerome Brunet unsigned int i, m, n; 2148eed1db1SJerome Brunet int ret; 2158289aafaSJerome Brunet 2168eed1db1SJerome Brunet for (i = 0, ret = 0; !ret; i++) { 2178eed1db1SJerome Brunet ret = meson_clk_get_pll_get_index(rate, parent_rate, 2188eed1db1SJerome Brunet i, &m, &n, pll); 2198eed1db1SJerome Brunet if (ret == -EINVAL) 2208eed1db1SJerome Brunet break; 2218289aafaSJerome Brunet 2228eed1db1SJerome Brunet now = __pll_params_to_rate(parent_rate, m, n, 0, pll); 2238eed1db1SJerome Brunet if (meson_clk_pll_is_better(rate, best, now, pll)) { 224dd601dbcSJerome Brunet best = now; 2258eed1db1SJerome Brunet *best_m = m; 2268eed1db1SJerome Brunet *best_n = n; 2278eed1db1SJerome Brunet 2288eed1db1SJerome Brunet if (now == rate) 2298eed1db1SJerome Brunet break; 230dd601dbcSJerome Brunet } 2310a1be867SJerome Brunet } 2328289aafaSJerome Brunet 2338eed1db1SJerome Brunet return best ? 0 : -EINVAL; 2347a29a869SCarlo Caione } 2357a29a869SCarlo Caione 2367a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 2377a29a869SCarlo Caione unsigned long *parent_rate) 2387a29a869SCarlo Caione { 239722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 240722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 2418eed1db1SJerome Brunet unsigned int m, n, frac; 242dd601dbcSJerome Brunet unsigned long round; 2438eed1db1SJerome Brunet int ret; 2447a29a869SCarlo Caione 2458eed1db1SJerome Brunet ret = meson_clk_get_pll_settings(rate, *parent_rate, &m, &n, pll); 2468eed1db1SJerome Brunet if (ret) 247840e1a73SJerome Brunet return meson_clk_pll_recalc_rate(hw, *parent_rate); 248840e1a73SJerome Brunet 2498eed1db1SJerome Brunet round = __pll_params_to_rate(*parent_rate, m, n, 0, pll); 250dd601dbcSJerome Brunet 251dd601dbcSJerome Brunet if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round) 252dd601dbcSJerome Brunet return round; 2537a29a869SCarlo Caione 2548289aafaSJerome Brunet /* 2558289aafaSJerome Brunet * The rate provided by the setting is not an exact match, let's 2568289aafaSJerome Brunet * try to improve the result using the fractional parameter 2578289aafaSJerome Brunet */ 2588eed1db1SJerome Brunet frac = __pll_params_with_frac(rate, *parent_rate, m, n, pll); 2597a29a869SCarlo Caione 2608eed1db1SJerome Brunet return __pll_params_to_rate(*parent_rate, m, n, frac, pll); 2617a29a869SCarlo Caione } 2627a29a869SCarlo Caione 263722825dcSJerome Brunet static int meson_clk_pll_wait_lock(struct clk_hw *hw) 264722825dcSJerome Brunet { 265722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 266722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 267c178b003SJerome Brunet int delay = 24000000; 268722825dcSJerome Brunet 269722825dcSJerome Brunet do { 270722825dcSJerome Brunet /* Is the clock locked now ? */ 271722825dcSJerome Brunet if (meson_parm_read(clk->map, &pll->l)) 272722825dcSJerome Brunet return 0; 273722825dcSJerome Brunet 274722825dcSJerome Brunet delay--; 275722825dcSJerome Brunet } while (delay > 0); 276722825dcSJerome Brunet 27745fcbec7SNeil Armstrong return -ETIMEDOUT; 27845fcbec7SNeil Armstrong } 27945fcbec7SNeil Armstrong 280*89d079dcSJerome Brunet static int meson_clk_pll_init(struct clk_hw *hw) 2817a29a869SCarlo Caione { 282722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 283722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 2847a29a869SCarlo Caione 285722825dcSJerome Brunet if (pll->init_count) { 286722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 287722825dcSJerome Brunet regmap_multi_reg_write(clk->map, pll->init_regs, 288722825dcSJerome Brunet pll->init_count); 289722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 2907a29a869SCarlo Caione } 291*89d079dcSJerome Brunet 292*89d079dcSJerome Brunet return 0; 29345fcbec7SNeil Armstrong } 29445fcbec7SNeil Armstrong 295d6e81845SMartin Blumenstingl static int meson_clk_pll_is_enabled(struct clk_hw *hw) 296d6e81845SMartin Blumenstingl { 297d6e81845SMartin Blumenstingl struct clk_regmap *clk = to_clk_regmap(hw); 298d6e81845SMartin Blumenstingl struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 299d6e81845SMartin Blumenstingl 300d6e81845SMartin Blumenstingl if (meson_parm_read(clk->map, &pll->rst) || 301d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->en) || 302d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->l)) 303d6e81845SMartin Blumenstingl return 0; 304d6e81845SMartin Blumenstingl 305d6e81845SMartin Blumenstingl return 1; 306d6e81845SMartin Blumenstingl } 307d6e81845SMartin Blumenstingl 30839b85002SNeil Armstrong static int meson_clk_pcie_pll_enable(struct clk_hw *hw) 30939b85002SNeil Armstrong { 31039b85002SNeil Armstrong meson_clk_pll_init(hw); 31139b85002SNeil Armstrong 31239b85002SNeil Armstrong if (meson_clk_pll_wait_lock(hw)) 31339b85002SNeil Armstrong return -EIO; 31439b85002SNeil Armstrong 31539b85002SNeil Armstrong return 0; 31639b85002SNeil Armstrong } 31739b85002SNeil Armstrong 318e40c7e3cSJerome Brunet static int meson_clk_pll_enable(struct clk_hw *hw) 319e40c7e3cSJerome Brunet { 320e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 321e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 322e40c7e3cSJerome Brunet 323d6e81845SMartin Blumenstingl /* do nothing if the PLL is already enabled */ 324d6e81845SMartin Blumenstingl if (clk_hw_is_enabled(hw)) 325d6e81845SMartin Blumenstingl return 0; 326d6e81845SMartin Blumenstingl 327e40c7e3cSJerome Brunet /* Make sure the pll is in reset */ 328e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 329e40c7e3cSJerome Brunet 330e40c7e3cSJerome Brunet /* Enable the pll */ 331e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 1); 332e40c7e3cSJerome Brunet 333e40c7e3cSJerome Brunet /* Take the pll out reset */ 334e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 335e40c7e3cSJerome Brunet 336e40c7e3cSJerome Brunet if (meson_clk_pll_wait_lock(hw)) 337e40c7e3cSJerome Brunet return -EIO; 338e40c7e3cSJerome Brunet 339e40c7e3cSJerome Brunet return 0; 340e40c7e3cSJerome Brunet } 341e40c7e3cSJerome Brunet 342e40c7e3cSJerome Brunet static void meson_clk_pll_disable(struct clk_hw *hw) 343e40c7e3cSJerome Brunet { 344e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 345e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 346e40c7e3cSJerome Brunet 347e40c7e3cSJerome Brunet /* Put the pll is in reset */ 348e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 349e40c7e3cSJerome Brunet 350e40c7e3cSJerome Brunet /* Disable the pll */ 351e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 0); 352e40c7e3cSJerome Brunet } 353e40c7e3cSJerome Brunet 3547a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 3557a29a869SCarlo Caione unsigned long parent_rate) 3567a29a869SCarlo Caione { 357722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 358722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 3598eed1db1SJerome Brunet unsigned int enabled, m, n, frac = 0, ret; 3607a29a869SCarlo Caione unsigned long old_rate; 3617a29a869SCarlo Caione 3627a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 3637a29a869SCarlo Caione return -EINVAL; 3647a29a869SCarlo Caione 3657a29a869SCarlo Caione old_rate = rate; 3667a29a869SCarlo Caione 3678eed1db1SJerome Brunet ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll); 3688eed1db1SJerome Brunet if (ret) 3698eed1db1SJerome Brunet return ret; 3707a29a869SCarlo Caione 371e40c7e3cSJerome Brunet enabled = meson_parm_read(clk->map, &pll->en); 372e40c7e3cSJerome Brunet if (enabled) 373e40c7e3cSJerome Brunet meson_clk_pll_disable(hw); 37445fcbec7SNeil Armstrong 3758eed1db1SJerome Brunet meson_parm_write(clk->map, &pll->n, n); 3768eed1db1SJerome Brunet meson_parm_write(clk->map, &pll->m, m); 3777a29a869SCarlo Caione 3788289aafaSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->frac)) { 3798eed1db1SJerome Brunet frac = __pll_params_with_frac(rate, parent_rate, m, n, pll); 3808289aafaSJerome Brunet meson_parm_write(clk->map, &pll->frac, frac); 3818289aafaSJerome Brunet } 3827a29a869SCarlo Caione 383e40c7e3cSJerome Brunet /* If the pll is stopped, bail out now */ 384e40c7e3cSJerome Brunet if (!enabled) 385e40c7e3cSJerome Brunet return 0; 3864a472951SMichael Turquette 387e40c7e3cSJerome Brunet if (meson_clk_pll_enable(hw)) { 3887a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 3897a29a869SCarlo Caione __func__, old_rate); 390722825dcSJerome Brunet /* 391722825dcSJerome Brunet * FIXME: Do we really need/want this HACK ? 392722825dcSJerome Brunet * It looks unsafe. what happens if the clock gets into a 393722825dcSJerome Brunet * broken state and we can't lock back on the old_rate ? Looks 394722825dcSJerome Brunet * like an infinite recursion is possible 395722825dcSJerome Brunet */ 3967a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 3977a29a869SCarlo Caione } 3987a29a869SCarlo Caione 399722825dcSJerome Brunet return 0; 4007a29a869SCarlo Caione } 4017a29a869SCarlo Caione 40239b85002SNeil Armstrong /* 40339b85002SNeil Armstrong * The Meson G12A PCIE PLL is fined tuned to deliver a very precise 40439b85002SNeil Armstrong * 100MHz reference clock for the PCIe Analog PHY, and thus requires 40539b85002SNeil Armstrong * a strict register sequence to enable the PLL. 40639b85002SNeil Armstrong * To simplify, re-use the _init() op to enable the PLL and keep 40739b85002SNeil Armstrong * the other ops except set_rate since the rate is fixed. 40839b85002SNeil Armstrong */ 40939b85002SNeil Armstrong const struct clk_ops meson_clk_pcie_pll_ops = { 41039b85002SNeil Armstrong .recalc_rate = meson_clk_pll_recalc_rate, 41139b85002SNeil Armstrong .round_rate = meson_clk_pll_round_rate, 41239b85002SNeil Armstrong .is_enabled = meson_clk_pll_is_enabled, 41339b85002SNeil Armstrong .enable = meson_clk_pcie_pll_enable, 41439b85002SNeil Armstrong .disable = meson_clk_pll_disable 41539b85002SNeil Armstrong }; 41639b85002SNeil Armstrong EXPORT_SYMBOL_GPL(meson_clk_pcie_pll_ops); 41739b85002SNeil Armstrong 418ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 419722825dcSJerome Brunet .init = meson_clk_pll_init, 4207a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 4217a29a869SCarlo Caione .round_rate = meson_clk_pll_round_rate, 4227a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 423d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 424e40c7e3cSJerome Brunet .enable = meson_clk_pll_enable, 425e40c7e3cSJerome Brunet .disable = meson_clk_pll_disable 4267a29a869SCarlo Caione }; 427889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ops); 4287a29a869SCarlo Caione 429ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 4307a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 431d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 4327a29a869SCarlo Caione }; 433889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); 434889c2b7eSJerome Brunet 435889c2b7eSJerome Brunet MODULE_DESCRIPTION("Amlogic PLL driver"); 436889c2b7eSJerome Brunet MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); 437889c2b7eSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 438889c2b7eSJerome Brunet MODULE_LICENSE("GPL v2"); 439