root/drivers/clk/meson/clk-pll.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. meson_clk_pll_data
  2. __pll_round_closest_mult
  3. __pll_params_to_rate
  4. meson_clk_pll_recalc_rate
  5. __pll_params_with_frac
  6. meson_clk_pll_is_better
  7. meson_clk_get_pll_table_index
  8. meson_clk_get_pll_range_m
  9. meson_clk_get_pll_range_index
  10. meson_clk_get_pll_get_index
  11. meson_clk_get_pll_settings
  12. meson_clk_pll_round_rate
  13. meson_clk_pll_wait_lock
  14. meson_clk_pll_init
  15. meson_clk_pll_is_enabled
  16. meson_clk_pcie_pll_enable
  17. meson_clk_pll_enable
  18. meson_clk_pll_disable
  19. meson_clk_pll_set_rate

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (c) 2015 Endless Mobile, Inc.
   4  * Author: Carlo Caione <carlo@endlessm.com>
   5  *
   6  * Copyright (c) 2018 Baylibre, SAS.
   7  * Author: Jerome Brunet <jbrunet@baylibre.com>
   8  */
   9 
  10 /*
  11  * In the most basic form, a Meson PLL is composed as follows:
  12  *
  13  *                     PLL
  14  *        +--------------------------------+
  15  *        |                                |
  16  *        |             +--+               |
  17  *  in >>-----[ /N ]--->|  |      +-----+  |
  18  *        |             |  |------| DCO |---->> out
  19  *        |  +--------->|  |      +--v--+  |
  20  *        |  |          +--+         |     |
  21  *        |  |                       |     |
  22  *        |  +--[ *(M + (F/Fmax) ]<--+     |
  23  *        |                                |
  24  *        +--------------------------------+
  25  *
  26  * out = in * (m + frac / frac_max) / n
  27  */
  28 
  29 #include <linux/clk-provider.h>
  30 #include <linux/delay.h>
  31 #include <linux/err.h>
  32 #include <linux/io.h>
  33 #include <linux/math64.h>
  34 #include <linux/module.h>
  35 #include <linux/rational.h>
  36 
  37 #include "clk-regmap.h"
  38 #include "clk-pll.h"
  39 
  40 static inline struct meson_clk_pll_data *
  41 meson_clk_pll_data(struct clk_regmap *clk)
  42 {
  43         return (struct meson_clk_pll_data *)clk->data;
  44 }
  45 
  46 static int __pll_round_closest_mult(struct meson_clk_pll_data *pll)
  47 {
  48         if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) &&
  49             !MESON_PARM_APPLICABLE(&pll->frac))
  50                 return 1;
  51 
  52         return 0;
  53 }
  54 
  55 static unsigned long __pll_params_to_rate(unsigned long parent_rate,
  56                                           unsigned int m, unsigned int n,
  57                                           unsigned int frac,
  58                                           struct meson_clk_pll_data *pll)
  59 {
  60         u64 rate = (u64)parent_rate * m;
  61 
  62         if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
  63                 u64 frac_rate = (u64)parent_rate * frac;
  64 
  65                 rate += DIV_ROUND_UP_ULL(frac_rate,
  66                                          (1 << pll->frac.width));
  67         }
  68 
  69         return DIV_ROUND_UP_ULL(rate, n);
  70 }
  71 
  72 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
  73                                                 unsigned long parent_rate)
  74 {
  75         struct clk_regmap *clk = to_clk_regmap(hw);
  76         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
  77         unsigned int m, n, frac;
  78 
  79         n = meson_parm_read(clk->map, &pll->n);
  80 
  81         /*
  82          * On some HW, N is set to zero on init. This value is invalid as
  83          * it would result in a division by zero. The rate can't be
  84          * calculated in this case
  85          */
  86         if (n == 0)
  87                 return 0;
  88 
  89         m = meson_parm_read(clk->map, &pll->m);
  90 
  91         frac = MESON_PARM_APPLICABLE(&pll->frac) ?
  92                 meson_parm_read(clk->map, &pll->frac) :
  93                 0;
  94 
  95         return __pll_params_to_rate(parent_rate, m, n, frac, pll);
  96 }
  97 
  98 static unsigned int __pll_params_with_frac(unsigned long rate,
  99                                            unsigned long parent_rate,
 100                                            unsigned int m,
 101                                            unsigned int n,
 102                                            struct meson_clk_pll_data *pll)
 103 {
 104         unsigned int frac_max = (1 << pll->frac.width);
 105         u64 val = (u64)rate * n;
 106 
 107         /* Bail out if we are already over the requested rate */
 108         if (rate < parent_rate * m / n)
 109                 return 0;
 110 
 111         if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
 112                 val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate);
 113         else
 114                 val = div_u64(val * frac_max, parent_rate);
 115 
 116         val -= m * frac_max;
 117 
 118         return min((unsigned int)val, (frac_max - 1));
 119 }
 120 
 121 static bool meson_clk_pll_is_better(unsigned long rate,
 122                                     unsigned long best,
 123                                     unsigned long now,
 124                                     struct meson_clk_pll_data *pll)
 125 {
 126         if (__pll_round_closest_mult(pll)) {
 127                 /* Round Closest */
 128                 if (abs(now - rate) < abs(best - rate))
 129                         return true;
 130         } else {
 131                 /* Round down */
 132                 if (now <= rate && best < now)
 133                         return true;
 134         }
 135 
 136         return false;
 137 }
 138 
 139 static int meson_clk_get_pll_table_index(unsigned int index,
 140                                          unsigned int *m,
 141                                          unsigned int *n,
 142                                          struct meson_clk_pll_data *pll)
 143 {
 144         if (!pll->table[index].n)
 145                 return -EINVAL;
 146 
 147         *m = pll->table[index].m;
 148         *n = pll->table[index].n;
 149 
 150         return 0;
 151 }
 152 
 153 static unsigned int meson_clk_get_pll_range_m(unsigned long rate,
 154                                               unsigned long parent_rate,
 155                                               unsigned int n,
 156                                               struct meson_clk_pll_data *pll)
 157 {
 158         u64 val = (u64)rate * n;
 159 
 160         if (__pll_round_closest_mult(pll))
 161                 return DIV_ROUND_CLOSEST_ULL(val, parent_rate);
 162 
 163         return div_u64(val,  parent_rate);
 164 }
 165 
 166 static int meson_clk_get_pll_range_index(unsigned long rate,
 167                                          unsigned long parent_rate,
 168                                          unsigned int index,
 169                                          unsigned int *m,
 170                                          unsigned int *n,
 171                                          struct meson_clk_pll_data *pll)
 172 {
 173         *n = index + 1;
 174 
 175         /* Check the predivider range */
 176         if (*n >= (1 << pll->n.width))
 177                 return -EINVAL;
 178 
 179         if (*n == 1) {
 180                 /* Get the boundaries out the way */
 181                 if (rate <= pll->range->min * parent_rate) {
 182                         *m = pll->range->min;
 183                         return -ENODATA;
 184                 } else if (rate >= pll->range->max * parent_rate) {
 185                         *m = pll->range->max;
 186                         return -ENODATA;
 187                 }
 188         }
 189 
 190         *m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll);
 191 
 192         /* the pre-divider gives a multiplier too big - stop */
 193         if (*m >= (1 << pll->m.width))
 194                 return -EINVAL;
 195 
 196         return 0;
 197 }
 198 
 199 static int meson_clk_get_pll_get_index(unsigned long rate,
 200                                        unsigned long parent_rate,
 201                                        unsigned int index,
 202                                        unsigned int *m,
 203                                        unsigned int *n,
 204                                        struct meson_clk_pll_data *pll)
 205 {
 206         if (pll->range)
 207                 return meson_clk_get_pll_range_index(rate, parent_rate,
 208                                                      index, m, n, pll);
 209         else if (pll->table)
 210                 return meson_clk_get_pll_table_index(index, m, n, pll);
 211 
 212         return -EINVAL;
 213 }
 214 
 215 static int meson_clk_get_pll_settings(unsigned long rate,
 216                                       unsigned long parent_rate,
 217                                       unsigned int *best_m,
 218                                       unsigned int *best_n,
 219                                       struct meson_clk_pll_data *pll)
 220 {
 221         unsigned long best = 0, now = 0;
 222         unsigned int i, m, n;
 223         int ret;
 224 
 225         for (i = 0, ret = 0; !ret; i++) {
 226                 ret = meson_clk_get_pll_get_index(rate, parent_rate,
 227                                                   i, &m, &n, pll);
 228                 if (ret == -EINVAL)
 229                         break;
 230 
 231                 now = __pll_params_to_rate(parent_rate, m, n, 0, pll);
 232                 if (meson_clk_pll_is_better(rate, best, now, pll)) {
 233                         best = now;
 234                         *best_m = m;
 235                         *best_n = n;
 236 
 237                         if (now == rate)
 238                                 break;
 239                 }
 240         }
 241 
 242         return best ? 0 : -EINVAL;
 243 }
 244 
 245 static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 246                                      unsigned long *parent_rate)
 247 {
 248         struct clk_regmap *clk = to_clk_regmap(hw);
 249         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 250         unsigned int m, n, frac;
 251         unsigned long round;
 252         int ret;
 253 
 254         ret = meson_clk_get_pll_settings(rate, *parent_rate, &m, &n, pll);
 255         if (ret)
 256                 return meson_clk_pll_recalc_rate(hw, *parent_rate);
 257 
 258         round = __pll_params_to_rate(*parent_rate, m, n, 0, pll);
 259 
 260         if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round)
 261                 return round;
 262 
 263         /*
 264          * The rate provided by the setting is not an exact match, let's
 265          * try to improve the result using the fractional parameter
 266          */
 267         frac = __pll_params_with_frac(rate, *parent_rate, m, n, pll);
 268 
 269         return __pll_params_to_rate(*parent_rate, m, n, frac, pll);
 270 }
 271 
 272 static int meson_clk_pll_wait_lock(struct clk_hw *hw)
 273 {
 274         struct clk_regmap *clk = to_clk_regmap(hw);
 275         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 276         int delay = 24000000;
 277 
 278         do {
 279                 /* Is the clock locked now ? */
 280                 if (meson_parm_read(clk->map, &pll->l))
 281                         return 0;
 282 
 283                 delay--;
 284         } while (delay > 0);
 285 
 286         return -ETIMEDOUT;
 287 }
 288 
 289 static void meson_clk_pll_init(struct clk_hw *hw)
 290 {
 291         struct clk_regmap *clk = to_clk_regmap(hw);
 292         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 293 
 294         if (pll->init_count) {
 295                 meson_parm_write(clk->map, &pll->rst, 1);
 296                 regmap_multi_reg_write(clk->map, pll->init_regs,
 297                                        pll->init_count);
 298                 meson_parm_write(clk->map, &pll->rst, 0);
 299         }
 300 }
 301 
 302 static int meson_clk_pll_is_enabled(struct clk_hw *hw)
 303 {
 304         struct clk_regmap *clk = to_clk_regmap(hw);
 305         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 306 
 307         if (meson_parm_read(clk->map, &pll->rst) ||
 308             !meson_parm_read(clk->map, &pll->en) ||
 309             !meson_parm_read(clk->map, &pll->l))
 310                 return 0;
 311 
 312         return 1;
 313 }
 314 
 315 static int meson_clk_pcie_pll_enable(struct clk_hw *hw)
 316 {
 317         meson_clk_pll_init(hw);
 318 
 319         if (meson_clk_pll_wait_lock(hw))
 320                 return -EIO;
 321 
 322         return 0;
 323 }
 324 
 325 static int meson_clk_pll_enable(struct clk_hw *hw)
 326 {
 327         struct clk_regmap *clk = to_clk_regmap(hw);
 328         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 329 
 330         /* do nothing if the PLL is already enabled */
 331         if (clk_hw_is_enabled(hw))
 332                 return 0;
 333 
 334         /* Make sure the pll is in reset */
 335         meson_parm_write(clk->map, &pll->rst, 1);
 336 
 337         /* Enable the pll */
 338         meson_parm_write(clk->map, &pll->en, 1);
 339 
 340         /* Take the pll out reset */
 341         meson_parm_write(clk->map, &pll->rst, 0);
 342 
 343         if (meson_clk_pll_wait_lock(hw))
 344                 return -EIO;
 345 
 346         return 0;
 347 }
 348 
 349 static void meson_clk_pll_disable(struct clk_hw *hw)
 350 {
 351         struct clk_regmap *clk = to_clk_regmap(hw);
 352         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 353 
 354         /* Put the pll is in reset */
 355         meson_parm_write(clk->map, &pll->rst, 1);
 356 
 357         /* Disable the pll */
 358         meson_parm_write(clk->map, &pll->en, 0);
 359 }
 360 
 361 static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 362                                   unsigned long parent_rate)
 363 {
 364         struct clk_regmap *clk = to_clk_regmap(hw);
 365         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
 366         unsigned int enabled, m, n, frac = 0, ret;
 367         unsigned long old_rate;
 368 
 369         if (parent_rate == 0 || rate == 0)
 370                 return -EINVAL;
 371 
 372         old_rate = rate;
 373 
 374         ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll);
 375         if (ret)
 376                 return ret;
 377 
 378         enabled = meson_parm_read(clk->map, &pll->en);
 379         if (enabled)
 380                 meson_clk_pll_disable(hw);
 381 
 382         meson_parm_write(clk->map, &pll->n, n);
 383         meson_parm_write(clk->map, &pll->m, m);
 384 
 385         if (MESON_PARM_APPLICABLE(&pll->frac)) {
 386                 frac = __pll_params_with_frac(rate, parent_rate, m, n, pll);
 387                 meson_parm_write(clk->map, &pll->frac, frac);
 388         }
 389 
 390         /* If the pll is stopped, bail out now */
 391         if (!enabled)
 392                 return 0;
 393 
 394         if (meson_clk_pll_enable(hw)) {
 395                 pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
 396                         __func__, old_rate);
 397                 /*
 398                  * FIXME: Do we really need/want this HACK ?
 399                  * It looks unsafe. what happens if the clock gets into a
 400                  * broken state and we can't lock back on the old_rate ? Looks
 401                  * like an infinite recursion is possible
 402                  */
 403                 meson_clk_pll_set_rate(hw, old_rate, parent_rate);
 404         }
 405 
 406         return 0;
 407 }
 408 
 409 /*
 410  * The Meson G12A PCIE PLL is fined tuned to deliver a very precise
 411  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
 412  * a strict register sequence to enable the PLL.
 413  * To simplify, re-use the _init() op to enable the PLL and keep
 414  * the other ops except set_rate since the rate is fixed.
 415  */
 416 const struct clk_ops meson_clk_pcie_pll_ops = {
 417         .recalc_rate    = meson_clk_pll_recalc_rate,
 418         .round_rate     = meson_clk_pll_round_rate,
 419         .is_enabled     = meson_clk_pll_is_enabled,
 420         .enable         = meson_clk_pcie_pll_enable,
 421         .disable        = meson_clk_pll_disable
 422 };
 423 EXPORT_SYMBOL_GPL(meson_clk_pcie_pll_ops);
 424 
 425 const struct clk_ops meson_clk_pll_ops = {
 426         .init           = meson_clk_pll_init,
 427         .recalc_rate    = meson_clk_pll_recalc_rate,
 428         .round_rate     = meson_clk_pll_round_rate,
 429         .set_rate       = meson_clk_pll_set_rate,
 430         .is_enabled     = meson_clk_pll_is_enabled,
 431         .enable         = meson_clk_pll_enable,
 432         .disable        = meson_clk_pll_disable
 433 };
 434 EXPORT_SYMBOL_GPL(meson_clk_pll_ops);
 435 
 436 const struct clk_ops meson_clk_pll_ro_ops = {
 437         .recalc_rate    = meson_clk_pll_recalc_rate,
 438         .is_enabled     = meson_clk_pll_is_enabled,
 439 };
 440 EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops);
 441 
 442 MODULE_DESCRIPTION("Amlogic PLL driver");
 443 MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
 444 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 445 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */