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 */ 1238eed1db1SJerome Brunet 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 280722825dcSJerome Brunet static void 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 } 29145fcbec7SNeil Armstrong } 29245fcbec7SNeil Armstrong 293d6e81845SMartin Blumenstingl static int meson_clk_pll_is_enabled(struct clk_hw *hw) 294d6e81845SMartin Blumenstingl { 295d6e81845SMartin Blumenstingl struct clk_regmap *clk = to_clk_regmap(hw); 296d6e81845SMartin Blumenstingl struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 297d6e81845SMartin Blumenstingl 298d6e81845SMartin Blumenstingl if (meson_parm_read(clk->map, &pll->rst) || 299d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->en) || 300d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->l)) 301d6e81845SMartin Blumenstingl return 0; 302d6e81845SMartin Blumenstingl 303d6e81845SMartin Blumenstingl return 1; 304d6e81845SMartin Blumenstingl } 305d6e81845SMartin Blumenstingl 306*39b85002SNeil Armstrong static int meson_clk_pcie_pll_enable(struct clk_hw *hw) 307*39b85002SNeil Armstrong { 308*39b85002SNeil Armstrong meson_clk_pll_init(hw); 309*39b85002SNeil Armstrong 310*39b85002SNeil Armstrong if (meson_clk_pll_wait_lock(hw)) 311*39b85002SNeil Armstrong return -EIO; 312*39b85002SNeil Armstrong 313*39b85002SNeil Armstrong return 0; 314*39b85002SNeil Armstrong } 315*39b85002SNeil Armstrong 316e40c7e3cSJerome Brunet static int meson_clk_pll_enable(struct clk_hw *hw) 317e40c7e3cSJerome Brunet { 318e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 319e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 320e40c7e3cSJerome Brunet 321d6e81845SMartin Blumenstingl /* do nothing if the PLL is already enabled */ 322d6e81845SMartin Blumenstingl if (clk_hw_is_enabled(hw)) 323d6e81845SMartin Blumenstingl return 0; 324d6e81845SMartin Blumenstingl 325e40c7e3cSJerome Brunet /* Make sure the pll is in reset */ 326e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 327e40c7e3cSJerome Brunet 328e40c7e3cSJerome Brunet /* Enable the pll */ 329e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 1); 330e40c7e3cSJerome Brunet 331e40c7e3cSJerome Brunet /* Take the pll out reset */ 332e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 333e40c7e3cSJerome Brunet 334e40c7e3cSJerome Brunet if (meson_clk_pll_wait_lock(hw)) 335e40c7e3cSJerome Brunet return -EIO; 336e40c7e3cSJerome Brunet 337e40c7e3cSJerome Brunet return 0; 338e40c7e3cSJerome Brunet } 339e40c7e3cSJerome Brunet 340e40c7e3cSJerome Brunet static void meson_clk_pll_disable(struct clk_hw *hw) 341e40c7e3cSJerome Brunet { 342e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 343e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 344e40c7e3cSJerome Brunet 345e40c7e3cSJerome Brunet /* Put the pll is in reset */ 346e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 347e40c7e3cSJerome Brunet 348e40c7e3cSJerome Brunet /* Disable the pll */ 349e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 0); 350e40c7e3cSJerome Brunet } 351e40c7e3cSJerome Brunet 3527a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 3537a29a869SCarlo Caione unsigned long parent_rate) 3547a29a869SCarlo Caione { 355722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 356722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 3578eed1db1SJerome Brunet unsigned int enabled, m, n, frac = 0, ret; 3587a29a869SCarlo Caione unsigned long old_rate; 3597a29a869SCarlo Caione 3607a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 3617a29a869SCarlo Caione return -EINVAL; 3627a29a869SCarlo Caione 3637a29a869SCarlo Caione old_rate = rate; 3647a29a869SCarlo Caione 3658eed1db1SJerome Brunet ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll); 3668eed1db1SJerome Brunet if (ret) 3678eed1db1SJerome Brunet return ret; 3687a29a869SCarlo Caione 369e40c7e3cSJerome Brunet enabled = meson_parm_read(clk->map, &pll->en); 370e40c7e3cSJerome Brunet if (enabled) 371e40c7e3cSJerome Brunet meson_clk_pll_disable(hw); 37245fcbec7SNeil Armstrong 3738eed1db1SJerome Brunet meson_parm_write(clk->map, &pll->n, n); 3748eed1db1SJerome Brunet meson_parm_write(clk->map, &pll->m, m); 3757a29a869SCarlo Caione 3768289aafaSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->frac)) { 3778eed1db1SJerome Brunet frac = __pll_params_with_frac(rate, parent_rate, m, n, pll); 3788289aafaSJerome Brunet meson_parm_write(clk->map, &pll->frac, frac); 3798289aafaSJerome Brunet } 3807a29a869SCarlo Caione 381e40c7e3cSJerome Brunet /* If the pll is stopped, bail out now */ 382e40c7e3cSJerome Brunet if (!enabled) 383e40c7e3cSJerome Brunet return 0; 3844a472951SMichael Turquette 385e40c7e3cSJerome Brunet if (meson_clk_pll_enable(hw)) { 3867a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 3877a29a869SCarlo Caione __func__, old_rate); 388722825dcSJerome Brunet /* 389722825dcSJerome Brunet * FIXME: Do we really need/want this HACK ? 390722825dcSJerome Brunet * It looks unsafe. what happens if the clock gets into a 391722825dcSJerome Brunet * broken state and we can't lock back on the old_rate ? Looks 392722825dcSJerome Brunet * like an infinite recursion is possible 393722825dcSJerome Brunet */ 3947a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 3957a29a869SCarlo Caione } 3967a29a869SCarlo Caione 397722825dcSJerome Brunet return 0; 3987a29a869SCarlo Caione } 3997a29a869SCarlo Caione 400*39b85002SNeil Armstrong /* 401*39b85002SNeil Armstrong * The Meson G12A PCIE PLL is fined tuned to deliver a very precise 402*39b85002SNeil Armstrong * 100MHz reference clock for the PCIe Analog PHY, and thus requires 403*39b85002SNeil Armstrong * a strict register sequence to enable the PLL. 404*39b85002SNeil Armstrong * To simplify, re-use the _init() op to enable the PLL and keep 405*39b85002SNeil Armstrong * the other ops except set_rate since the rate is fixed. 406*39b85002SNeil Armstrong */ 407*39b85002SNeil Armstrong const struct clk_ops meson_clk_pcie_pll_ops = { 408*39b85002SNeil Armstrong .recalc_rate = meson_clk_pll_recalc_rate, 409*39b85002SNeil Armstrong .round_rate = meson_clk_pll_round_rate, 410*39b85002SNeil Armstrong .is_enabled = meson_clk_pll_is_enabled, 411*39b85002SNeil Armstrong .enable = meson_clk_pcie_pll_enable, 412*39b85002SNeil Armstrong .disable = meson_clk_pll_disable 413*39b85002SNeil Armstrong }; 414*39b85002SNeil Armstrong EXPORT_SYMBOL_GPL(meson_clk_pcie_pll_ops); 415*39b85002SNeil Armstrong 416ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 417722825dcSJerome Brunet .init = meson_clk_pll_init, 4187a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 4197a29a869SCarlo Caione .round_rate = meson_clk_pll_round_rate, 4207a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 421d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 422e40c7e3cSJerome Brunet .enable = meson_clk_pll_enable, 423e40c7e3cSJerome Brunet .disable = meson_clk_pll_disable 4247a29a869SCarlo Caione }; 425889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ops); 4267a29a869SCarlo Caione 427ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 4287a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 429d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 4307a29a869SCarlo Caione }; 431889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); 432889c2b7eSJerome Brunet 433889c2b7eSJerome Brunet MODULE_DESCRIPTION("Amlogic PLL driver"); 434889c2b7eSJerome Brunet MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); 435889c2b7eSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 436889c2b7eSJerome Brunet MODULE_LICENSE("GPL v2"); 437