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 36889c2b7eSJerome Brunet #include "clk-regmap.h" 37889c2b7eSJerome 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 458eed1db1SJerome Brunet static int __pll_round_closest_mult(struct meson_clk_pll_data *pll) 468eed1db1SJerome Brunet { 478eed1db1SJerome Brunet if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) && 488eed1db1SJerome Brunet !MESON_PARM_APPLICABLE(&pll->frac)) 498eed1db1SJerome Brunet return 1; 508eed1db1SJerome Brunet 518eed1db1SJerome Brunet return 0; 528eed1db1SJerome Brunet } 538eed1db1SJerome Brunet 548289aafaSJerome Brunet static unsigned long __pll_params_to_rate(unsigned long parent_rate, 558eed1db1SJerome Brunet unsigned int m, unsigned int n, 568eed1db1SJerome Brunet unsigned int frac, 578289aafaSJerome Brunet struct meson_clk_pll_data *pll) 588289aafaSJerome Brunet { 598eed1db1SJerome Brunet u64 rate = (u64)parent_rate * m; 608289aafaSJerome Brunet 618289aafaSJerome Brunet if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { 628289aafaSJerome Brunet u64 frac_rate = (u64)parent_rate * frac; 638289aafaSJerome Brunet 648289aafaSJerome Brunet rate += DIV_ROUND_UP_ULL(frac_rate, 658289aafaSJerome Brunet (1 << pll->frac.width)); 668289aafaSJerome Brunet } 678289aafaSJerome Brunet 688eed1db1SJerome Brunet return DIV_ROUND_UP_ULL(rate, n); 698289aafaSJerome Brunet } 708289aafaSJerome Brunet 717a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, 727a29a869SCarlo Caione unsigned long parent_rate) 737a29a869SCarlo Caione { 74722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 75722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 768eed1db1SJerome Brunet unsigned int m, n, frac; 777a29a869SCarlo Caione 788eed1db1SJerome Brunet n = meson_parm_read(clk->map, &pll->n); 79d8488a41SRemi Pommarel 80d8488a41SRemi Pommarel /* 81d8488a41SRemi Pommarel * On some HW, N is set to zero on init. This value is invalid as 82d8488a41SRemi Pommarel * it would result in a division by zero. The rate can't be 83d8488a41SRemi Pommarel * calculated in this case 84d8488a41SRemi Pommarel */ 85d8488a41SRemi Pommarel if (n == 0) 86d8488a41SRemi Pommarel return 0; 87d8488a41SRemi Pommarel 888eed1db1SJerome Brunet m = meson_parm_read(clk->map, &pll->m); 897d3142e5SJerome Brunet 908289aafaSJerome Brunet frac = MESON_PARM_APPLICABLE(&pll->frac) ? 918289aafaSJerome Brunet meson_parm_read(clk->map, &pll->frac) : 928289aafaSJerome Brunet 0; 9394aa8a41SJerome Brunet 948eed1db1SJerome Brunet return __pll_params_to_rate(parent_rate, m, n, frac, pll); 9594aa8a41SJerome Brunet } 9694aa8a41SJerome Brunet 978eed1db1SJerome Brunet static unsigned int __pll_params_with_frac(unsigned long rate, 988289aafaSJerome Brunet unsigned long parent_rate, 998eed1db1SJerome Brunet unsigned int m, 1008eed1db1SJerome Brunet unsigned int n, 1018289aafaSJerome Brunet struct meson_clk_pll_data *pll) 1028289aafaSJerome Brunet { 1038eed1db1SJerome Brunet unsigned int frac_max = (1 << pll->frac.width); 1048eed1db1SJerome Brunet u64 val = (u64)rate * n; 1058eed1db1SJerome Brunet 1068eed1db1SJerome Brunet /* Bail out if we are already over the requested rate */ 1078eed1db1SJerome Brunet if (rate < parent_rate * m / n) 1088eed1db1SJerome Brunet return 0; 1098289aafaSJerome Brunet 1100a1be867SJerome Brunet if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) 1110a1be867SJerome Brunet val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); 1120a1be867SJerome Brunet else 1138289aafaSJerome Brunet val = div_u64(val * frac_max, parent_rate); 1140a1be867SJerome Brunet 1158eed1db1SJerome Brunet val -= m * frac_max; 1168289aafaSJerome Brunet 1178eed1db1SJerome Brunet return min((unsigned int)val, (frac_max - 1)); 1188289aafaSJerome Brunet } 1198289aafaSJerome Brunet 120dd601dbcSJerome Brunet static bool meson_clk_pll_is_better(unsigned long rate, 121dd601dbcSJerome Brunet unsigned long best, 122dd601dbcSJerome Brunet unsigned long now, 1238289aafaSJerome Brunet struct meson_clk_pll_data *pll) 1248289aafaSJerome Brunet { 1258eed1db1SJerome Brunet if (__pll_round_closest_mult(pll)) { 126dd601dbcSJerome Brunet /* Round Closest */ 127dd601dbcSJerome Brunet if (abs(now - rate) < abs(best - rate)) 128dd601dbcSJerome Brunet return true; 1298eed1db1SJerome Brunet } else { 1308eed1db1SJerome Brunet /* Round down */ 131d6f987c8SMartin Blumenstingl if (now <= rate && best < now) 1328eed1db1SJerome Brunet return true; 133dd601dbcSJerome Brunet } 134dd601dbcSJerome Brunet 135dd601dbcSJerome Brunet return false; 136dd601dbcSJerome Brunet } 137dd601dbcSJerome Brunet 1388eed1db1SJerome Brunet static int meson_clk_get_pll_table_index(unsigned int index, 1398eed1db1SJerome Brunet unsigned int *m, 1408eed1db1SJerome Brunet unsigned int *n, 141dd601dbcSJerome Brunet struct meson_clk_pll_data *pll) 142dd601dbcSJerome Brunet { 1438eed1db1SJerome Brunet if (!pll->table[index].n) 1448eed1db1SJerome Brunet return -EINVAL; 1458eed1db1SJerome Brunet 1468eed1db1SJerome Brunet *m = pll->table[index].m; 1478eed1db1SJerome Brunet *n = pll->table[index].n; 1488eed1db1SJerome Brunet 1498eed1db1SJerome Brunet return 0; 1508eed1db1SJerome Brunet } 1518eed1db1SJerome Brunet 1528eed1db1SJerome Brunet static unsigned int meson_clk_get_pll_range_m(unsigned long rate, 1538eed1db1SJerome Brunet unsigned long parent_rate, 1548eed1db1SJerome Brunet unsigned int n, 1558eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 1568eed1db1SJerome Brunet { 1578eed1db1SJerome Brunet u64 val = (u64)rate * n; 1588eed1db1SJerome Brunet 1598eed1db1SJerome Brunet if (__pll_round_closest_mult(pll)) 1608eed1db1SJerome Brunet return DIV_ROUND_CLOSEST_ULL(val, parent_rate); 1618eed1db1SJerome Brunet 1628eed1db1SJerome Brunet return div_u64(val, parent_rate); 1638eed1db1SJerome Brunet } 1648eed1db1SJerome Brunet 1658eed1db1SJerome Brunet static int meson_clk_get_pll_range_index(unsigned long rate, 1668eed1db1SJerome Brunet unsigned long parent_rate, 1678eed1db1SJerome Brunet unsigned int index, 1688eed1db1SJerome Brunet unsigned int *m, 1698eed1db1SJerome Brunet unsigned int *n, 1708eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 1718eed1db1SJerome Brunet { 1728eed1db1SJerome Brunet *n = index + 1; 1738eed1db1SJerome Brunet 1748eed1db1SJerome Brunet /* Check the predivider range */ 1758eed1db1SJerome Brunet if (*n >= (1 << pll->n.width)) 1768eed1db1SJerome Brunet return -EINVAL; 1778eed1db1SJerome Brunet 1788eed1db1SJerome Brunet if (*n == 1) { 1798eed1db1SJerome Brunet /* Get the boundaries out the way */ 1808eed1db1SJerome Brunet if (rate <= pll->range->min * parent_rate) { 1818eed1db1SJerome Brunet *m = pll->range->min; 1828eed1db1SJerome Brunet return -ENODATA; 1838eed1db1SJerome Brunet } else if (rate >= pll->range->max * parent_rate) { 1848eed1db1SJerome Brunet *m = pll->range->max; 1858eed1db1SJerome Brunet return -ENODATA; 1868eed1db1SJerome Brunet } 1878eed1db1SJerome Brunet } 1888eed1db1SJerome Brunet 1898eed1db1SJerome Brunet *m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll); 1908eed1db1SJerome Brunet 1918eed1db1SJerome Brunet /* the pre-divider gives a multiplier too big - stop */ 1928eed1db1SJerome Brunet if (*m >= (1 << pll->m.width)) 1938eed1db1SJerome Brunet return -EINVAL; 1948eed1db1SJerome Brunet 1958eed1db1SJerome Brunet return 0; 1968eed1db1SJerome Brunet } 1978eed1db1SJerome Brunet 1988eed1db1SJerome Brunet static int meson_clk_get_pll_get_index(unsigned long rate, 1998eed1db1SJerome Brunet unsigned long parent_rate, 2008eed1db1SJerome Brunet unsigned int index, 2018eed1db1SJerome Brunet unsigned int *m, 2028eed1db1SJerome Brunet unsigned int *n, 2038eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 2048eed1db1SJerome Brunet { 2058eed1db1SJerome Brunet if (pll->range) 2068eed1db1SJerome Brunet return meson_clk_get_pll_range_index(rate, parent_rate, 2078eed1db1SJerome Brunet index, m, n, pll); 2088eed1db1SJerome Brunet else if (pll->table) 2098eed1db1SJerome Brunet return meson_clk_get_pll_table_index(index, m, n, pll); 2108eed1db1SJerome Brunet 2118eed1db1SJerome Brunet return -EINVAL; 2128eed1db1SJerome Brunet } 2138eed1db1SJerome Brunet 2148eed1db1SJerome Brunet static int meson_clk_get_pll_settings(unsigned long rate, 2158eed1db1SJerome Brunet unsigned long parent_rate, 2168eed1db1SJerome Brunet unsigned int *best_m, 2178eed1db1SJerome Brunet unsigned int *best_n, 2188eed1db1SJerome Brunet struct meson_clk_pll_data *pll) 2198eed1db1SJerome Brunet { 220dd601dbcSJerome Brunet unsigned long best = 0, now = 0; 2218eed1db1SJerome Brunet unsigned int i, m, n; 2228eed1db1SJerome Brunet int ret; 2238289aafaSJerome Brunet 2248eed1db1SJerome Brunet for (i = 0, ret = 0; !ret; i++) { 2258eed1db1SJerome Brunet ret = meson_clk_get_pll_get_index(rate, parent_rate, 2268eed1db1SJerome Brunet i, &m, &n, pll); 2278eed1db1SJerome Brunet if (ret == -EINVAL) 2288eed1db1SJerome Brunet break; 2298289aafaSJerome Brunet 2308eed1db1SJerome Brunet now = __pll_params_to_rate(parent_rate, m, n, 0, pll); 2318eed1db1SJerome Brunet if (meson_clk_pll_is_better(rate, best, now, pll)) { 232dd601dbcSJerome Brunet best = now; 2338eed1db1SJerome Brunet *best_m = m; 2348eed1db1SJerome Brunet *best_n = n; 2358eed1db1SJerome Brunet 2368eed1db1SJerome Brunet if (now == rate) 2378eed1db1SJerome Brunet break; 238dd601dbcSJerome Brunet } 2390a1be867SJerome Brunet } 2408289aafaSJerome Brunet 2418eed1db1SJerome Brunet return best ? 0 : -EINVAL; 2427a29a869SCarlo Caione } 2437a29a869SCarlo Caione 2444cbf0cd6SMartin Blumenstingl static int meson_clk_pll_determine_rate(struct clk_hw *hw, 2454cbf0cd6SMartin Blumenstingl struct clk_rate_request *req) 2467a29a869SCarlo Caione { 247722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 248722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 2498eed1db1SJerome Brunet unsigned int m, n, frac; 250dd601dbcSJerome Brunet unsigned long round; 2518eed1db1SJerome Brunet int ret; 2527a29a869SCarlo Caione 2534cbf0cd6SMartin Blumenstingl ret = meson_clk_get_pll_settings(req->rate, req->best_parent_rate, 2544cbf0cd6SMartin Blumenstingl &m, &n, pll); 2558eed1db1SJerome Brunet if (ret) 2564cbf0cd6SMartin Blumenstingl return ret; 257840e1a73SJerome Brunet 2584cbf0cd6SMartin Blumenstingl round = __pll_params_to_rate(req->best_parent_rate, m, n, 0, pll); 259dd601dbcSJerome Brunet 2604cbf0cd6SMartin Blumenstingl if (!MESON_PARM_APPLICABLE(&pll->frac) || req->rate == round) { 2614cbf0cd6SMartin Blumenstingl req->rate = round; 2624cbf0cd6SMartin Blumenstingl return 0; 2634cbf0cd6SMartin Blumenstingl } 2647a29a869SCarlo Caione 2658289aafaSJerome Brunet /* 2668289aafaSJerome Brunet * The rate provided by the setting is not an exact match, let's 2678289aafaSJerome Brunet * try to improve the result using the fractional parameter 2688289aafaSJerome Brunet */ 2694cbf0cd6SMartin Blumenstingl frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, n, pll); 2704cbf0cd6SMartin Blumenstingl req->rate = __pll_params_to_rate(req->best_parent_rate, m, n, frac, pll); 2717a29a869SCarlo Caione 2724cbf0cd6SMartin Blumenstingl return 0; 2737a29a869SCarlo Caione } 2747a29a869SCarlo Caione 275722825dcSJerome Brunet static int meson_clk_pll_wait_lock(struct clk_hw *hw) 276722825dcSJerome Brunet { 277722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 278722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 27919648dddSHeiner Kallweit int delay = 5000; 280722825dcSJerome Brunet 281722825dcSJerome Brunet do { 28219648dddSHeiner Kallweit /* Is the clock locked now ? Time out after 100ms. */ 283722825dcSJerome Brunet if (meson_parm_read(clk->map, &pll->l)) 284722825dcSJerome Brunet return 0; 285722825dcSJerome Brunet 28619648dddSHeiner Kallweit udelay(20); 28719648dddSHeiner Kallweit } while (--delay); 288722825dcSJerome Brunet 28945fcbec7SNeil Armstrong return -ETIMEDOUT; 29045fcbec7SNeil Armstrong } 29145fcbec7SNeil Armstrong 29289d079dcSJerome Brunet static int meson_clk_pll_init(struct clk_hw *hw) 2937a29a869SCarlo Caione { 294722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 295722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 2967a29a869SCarlo Caione 297722825dcSJerome Brunet if (pll->init_count) { 29802f1e17cSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->rst)) 299722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 30002f1e17cSDmitry Rokosov 301722825dcSJerome Brunet regmap_multi_reg_write(clk->map, pll->init_regs, 302722825dcSJerome Brunet pll->init_count); 30302f1e17cSDmitry Rokosov 30402f1e17cSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->rst)) 305722825dcSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 3067a29a869SCarlo Caione } 30789d079dcSJerome Brunet 30889d079dcSJerome Brunet return 0; 30945fcbec7SNeil Armstrong } 31045fcbec7SNeil Armstrong 311d6e81845SMartin Blumenstingl static int meson_clk_pll_is_enabled(struct clk_hw *hw) 312d6e81845SMartin Blumenstingl { 313d6e81845SMartin Blumenstingl struct clk_regmap *clk = to_clk_regmap(hw); 314d6e81845SMartin Blumenstingl struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 315d6e81845SMartin Blumenstingl 31602f1e17cSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->rst) && 31702f1e17cSDmitry Rokosov meson_parm_read(clk->map, &pll->rst)) 31802f1e17cSDmitry Rokosov return 0; 31902f1e17cSDmitry Rokosov 32002f1e17cSDmitry Rokosov if (!meson_parm_read(clk->map, &pll->en) || 321d6e81845SMartin Blumenstingl !meson_parm_read(clk->map, &pll->l)) 322d6e81845SMartin Blumenstingl return 0; 323d6e81845SMartin Blumenstingl 324d6e81845SMartin Blumenstingl return 1; 325d6e81845SMartin Blumenstingl } 326d6e81845SMartin Blumenstingl 32739b85002SNeil Armstrong static int meson_clk_pcie_pll_enable(struct clk_hw *hw) 32839b85002SNeil Armstrong { 329d73406edSHeiner Kallweit int retries = 10; 330d73406edSHeiner Kallweit 331d73406edSHeiner Kallweit do { 33239b85002SNeil Armstrong meson_clk_pll_init(hw); 333d73406edSHeiner Kallweit if (!meson_clk_pll_wait_lock(hw)) 33439b85002SNeil Armstrong return 0; 335d73406edSHeiner Kallweit pr_info("Retry enabling PCIe PLL clock\n"); 336d73406edSHeiner Kallweit } while (--retries); 337d73406edSHeiner Kallweit 338d73406edSHeiner Kallweit return -EIO; 33939b85002SNeil Armstrong } 34039b85002SNeil Armstrong 341e40c7e3cSJerome Brunet static int meson_clk_pll_enable(struct clk_hw *hw) 342e40c7e3cSJerome Brunet { 343e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 344e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 345e40c7e3cSJerome Brunet 346d6e81845SMartin Blumenstingl /* do nothing if the PLL is already enabled */ 347d6e81845SMartin Blumenstingl if (clk_hw_is_enabled(hw)) 348d6e81845SMartin Blumenstingl return 0; 349d6e81845SMartin Blumenstingl 350e40c7e3cSJerome Brunet /* Make sure the pll is in reset */ 35102f1e17cSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->rst)) 352e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 353e40c7e3cSJerome Brunet 354e40c7e3cSJerome Brunet /* Enable the pll */ 355e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 1); 356e40c7e3cSJerome Brunet 357e40c7e3cSJerome Brunet /* Take the pll out reset */ 35802f1e17cSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->rst)) 359e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 0); 360e40c7e3cSJerome Brunet 361b6ec400aSDmitry Rokosov /* 362b6ec400aSDmitry Rokosov * Compared with the previous SoCs, self-adaption current module 363b6ec400aSDmitry Rokosov * is newly added for A1, keep the new power-on sequence to enable the 364b6ec400aSDmitry Rokosov * PLL. The sequence is: 365b6ec400aSDmitry Rokosov * 1. enable the pll, delay for 10us 366b6ec400aSDmitry Rokosov * 2. enable the pll self-adaption current module, delay for 40us 367b6ec400aSDmitry Rokosov * 3. enable the lock detect module 368b6ec400aSDmitry Rokosov */ 369b6ec400aSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->current_en)) { 370b6ec400aSDmitry Rokosov usleep_range(10, 20); 371b6ec400aSDmitry Rokosov meson_parm_write(clk->map, &pll->current_en, 1); 372b6ec400aSDmitry Rokosov usleep_range(40, 50); 373*b8533204SJiapeng Chong } 374b6ec400aSDmitry Rokosov 375b6ec400aSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->l_detect)) { 376b6ec400aSDmitry Rokosov meson_parm_write(clk->map, &pll->l_detect, 1); 377b6ec400aSDmitry Rokosov meson_parm_write(clk->map, &pll->l_detect, 0); 378b6ec400aSDmitry Rokosov } 379b6ec400aSDmitry Rokosov 380e40c7e3cSJerome Brunet if (meson_clk_pll_wait_lock(hw)) 381e40c7e3cSJerome Brunet return -EIO; 382e40c7e3cSJerome Brunet 383e40c7e3cSJerome Brunet return 0; 384e40c7e3cSJerome Brunet } 385e40c7e3cSJerome Brunet 386e40c7e3cSJerome Brunet static void meson_clk_pll_disable(struct clk_hw *hw) 387e40c7e3cSJerome Brunet { 388e40c7e3cSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 389e40c7e3cSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 390e40c7e3cSJerome Brunet 391e40c7e3cSJerome Brunet /* Put the pll is in reset */ 39202f1e17cSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->rst)) 393e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->rst, 1); 394e40c7e3cSJerome Brunet 395e40c7e3cSJerome Brunet /* Disable the pll */ 396e40c7e3cSJerome Brunet meson_parm_write(clk->map, &pll->en, 0); 397b6ec400aSDmitry Rokosov 398b6ec400aSDmitry Rokosov /* Disable PLL internal self-adaption current module */ 399b6ec400aSDmitry Rokosov if (MESON_PARM_APPLICABLE(&pll->current_en)) 400b6ec400aSDmitry Rokosov meson_parm_write(clk->map, &pll->current_en, 0); 401e40c7e3cSJerome Brunet } 402e40c7e3cSJerome Brunet 4037a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 4047a29a869SCarlo Caione unsigned long parent_rate) 4057a29a869SCarlo Caione { 406722825dcSJerome Brunet struct clk_regmap *clk = to_clk_regmap(hw); 407722825dcSJerome Brunet struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); 4089e717285SMartin Blumenstingl unsigned int enabled, m, n, frac = 0; 4097a29a869SCarlo Caione unsigned long old_rate; 4109e717285SMartin Blumenstingl int ret; 4117a29a869SCarlo Caione 4127a29a869SCarlo Caione if (parent_rate == 0 || rate == 0) 4137a29a869SCarlo Caione return -EINVAL; 4147a29a869SCarlo Caione 4152f290b7cSMartin Blumenstingl old_rate = clk_hw_get_rate(hw); 4167a29a869SCarlo Caione 4178eed1db1SJerome Brunet ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll); 4188eed1db1SJerome Brunet if (ret) 4198eed1db1SJerome Brunet return ret; 4207a29a869SCarlo Caione 421e40c7e3cSJerome Brunet enabled = meson_parm_read(clk->map, &pll->en); 422e40c7e3cSJerome Brunet if (enabled) 423e40c7e3cSJerome Brunet meson_clk_pll_disable(hw); 42445fcbec7SNeil Armstrong 4258eed1db1SJerome Brunet meson_parm_write(clk->map, &pll->n, n); 4268eed1db1SJerome Brunet meson_parm_write(clk->map, &pll->m, m); 4277a29a869SCarlo Caione 4288289aafaSJerome Brunet if (MESON_PARM_APPLICABLE(&pll->frac)) { 4298eed1db1SJerome Brunet frac = __pll_params_with_frac(rate, parent_rate, m, n, pll); 4308289aafaSJerome Brunet meson_parm_write(clk->map, &pll->frac, frac); 4318289aafaSJerome Brunet } 4327a29a869SCarlo Caione 433e40c7e3cSJerome Brunet /* If the pll is stopped, bail out now */ 434e40c7e3cSJerome Brunet if (!enabled) 435e40c7e3cSJerome Brunet return 0; 4364a472951SMichael Turquette 437ccdc1f08SMartin Blumenstingl ret = meson_clk_pll_enable(hw); 438ccdc1f08SMartin Blumenstingl if (ret) { 4397a29a869SCarlo Caione pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", 4407a29a869SCarlo Caione __func__, old_rate); 441722825dcSJerome Brunet /* 442722825dcSJerome Brunet * FIXME: Do we really need/want this HACK ? 443722825dcSJerome Brunet * It looks unsafe. what happens if the clock gets into a 444722825dcSJerome Brunet * broken state and we can't lock back on the old_rate ? Looks 445722825dcSJerome Brunet * like an infinite recursion is possible 446722825dcSJerome Brunet */ 4477a29a869SCarlo Caione meson_clk_pll_set_rate(hw, old_rate, parent_rate); 4487a29a869SCarlo Caione } 4497a29a869SCarlo Caione 450ccdc1f08SMartin Blumenstingl return ret; 4517a29a869SCarlo Caione } 4527a29a869SCarlo Caione 45339b85002SNeil Armstrong /* 45439b85002SNeil Armstrong * The Meson G12A PCIE PLL is fined tuned to deliver a very precise 45539b85002SNeil Armstrong * 100MHz reference clock for the PCIe Analog PHY, and thus requires 45639b85002SNeil Armstrong * a strict register sequence to enable the PLL. 45739b85002SNeil Armstrong * To simplify, re-use the _init() op to enable the PLL and keep 45839b85002SNeil Armstrong * the other ops except set_rate since the rate is fixed. 45939b85002SNeil Armstrong */ 46039b85002SNeil Armstrong const struct clk_ops meson_clk_pcie_pll_ops = { 46139b85002SNeil Armstrong .recalc_rate = meson_clk_pll_recalc_rate, 4624cbf0cd6SMartin Blumenstingl .determine_rate = meson_clk_pll_determine_rate, 46339b85002SNeil Armstrong .is_enabled = meson_clk_pll_is_enabled, 46439b85002SNeil Armstrong .enable = meson_clk_pcie_pll_enable, 46539b85002SNeil Armstrong .disable = meson_clk_pll_disable 46639b85002SNeil Armstrong }; 46739b85002SNeil Armstrong EXPORT_SYMBOL_GPL(meson_clk_pcie_pll_ops); 46839b85002SNeil Armstrong 469ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = { 470722825dcSJerome Brunet .init = meson_clk_pll_init, 4717a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 4724cbf0cd6SMartin Blumenstingl .determine_rate = meson_clk_pll_determine_rate, 4737a29a869SCarlo Caione .set_rate = meson_clk_pll_set_rate, 474d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 475e40c7e3cSJerome Brunet .enable = meson_clk_pll_enable, 476e40c7e3cSJerome Brunet .disable = meson_clk_pll_disable 4777a29a869SCarlo Caione }; 478889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ops); 4797a29a869SCarlo Caione 480ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = { 4817a29a869SCarlo Caione .recalc_rate = meson_clk_pll_recalc_rate, 482d6e81845SMartin Blumenstingl .is_enabled = meson_clk_pll_is_enabled, 4837a29a869SCarlo Caione }; 484889c2b7eSJerome Brunet EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); 485889c2b7eSJerome Brunet 486889c2b7eSJerome Brunet MODULE_DESCRIPTION("Amlogic PLL driver"); 487889c2b7eSJerome Brunet MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); 488889c2b7eSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 489889c2b7eSJerome Brunet MODULE_LICENSE("GPL v2"); 490