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

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

DEFINITIONS

This source file includes following definitions.
  1. meson_clk_mpll_data
  2. rate_from_params
  3. params_from_rate
  4. mpll_recalc_rate
  5. mpll_round_rate
  6. mpll_set_rate
  7. mpll_init

   1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
   2 /*
   3  * Copyright (c) 2016 AmLogic, Inc.
   4  * Author: Michael Turquette <mturquette@baylibre.com>
   5  */
   6 
   7 /*
   8  * MultiPhase Locked Loops are outputs from a PLL with additional frequency
   9  * scaling capabilities. MPLL rates are calculated as:
  10  *
  11  * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384)
  12  */
  13 
  14 #include <linux/clk-provider.h>
  15 #include <linux/module.h>
  16 #include <linux/spinlock.h>
  17 
  18 #include "clk-regmap.h"
  19 #include "clk-mpll.h"
  20 
  21 #define SDM_DEN 16384
  22 #define N2_MIN  4
  23 #define N2_MAX  511
  24 
  25 static inline struct meson_clk_mpll_data *
  26 meson_clk_mpll_data(struct clk_regmap *clk)
  27 {
  28         return (struct meson_clk_mpll_data *)clk->data;
  29 }
  30 
  31 static long rate_from_params(unsigned long parent_rate,
  32                              unsigned int sdm,
  33                              unsigned int n2)
  34 {
  35         unsigned long divisor = (SDM_DEN * n2) + sdm;
  36 
  37         if (n2 < N2_MIN)
  38                 return -EINVAL;
  39 
  40         return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor);
  41 }
  42 
  43 static void params_from_rate(unsigned long requested_rate,
  44                              unsigned long parent_rate,
  45                              unsigned int *sdm,
  46                              unsigned int *n2,
  47                              u8 flags)
  48 {
  49         uint64_t div = parent_rate;
  50         uint64_t frac = do_div(div, requested_rate);
  51 
  52         frac *= SDM_DEN;
  53 
  54         if (flags & CLK_MESON_MPLL_ROUND_CLOSEST)
  55                 *sdm = DIV_ROUND_CLOSEST_ULL(frac, requested_rate);
  56         else
  57                 *sdm = DIV_ROUND_UP_ULL(frac, requested_rate);
  58 
  59         if (*sdm == SDM_DEN) {
  60                 *sdm = 0;
  61                 div += 1;
  62         }
  63 
  64         if (div < N2_MIN) {
  65                 *n2 = N2_MIN;
  66                 *sdm = 0;
  67         } else if (div > N2_MAX) {
  68                 *n2 = N2_MAX;
  69                 *sdm = SDM_DEN - 1;
  70         } else {
  71                 *n2 = div;
  72         }
  73 }
  74 
  75 static unsigned long mpll_recalc_rate(struct clk_hw *hw,
  76                 unsigned long parent_rate)
  77 {
  78         struct clk_regmap *clk = to_clk_regmap(hw);
  79         struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
  80         unsigned int sdm, n2;
  81         long rate;
  82 
  83         sdm = meson_parm_read(clk->map, &mpll->sdm);
  84         n2 = meson_parm_read(clk->map, &mpll->n2);
  85 
  86         rate = rate_from_params(parent_rate, sdm, n2);
  87         return rate < 0 ? 0 : rate;
  88 }
  89 
  90 static long mpll_round_rate(struct clk_hw *hw,
  91                             unsigned long rate,
  92                             unsigned long *parent_rate)
  93 {
  94         struct clk_regmap *clk = to_clk_regmap(hw);
  95         struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
  96         unsigned int sdm, n2;
  97 
  98         params_from_rate(rate, *parent_rate, &sdm, &n2, mpll->flags);
  99         return rate_from_params(*parent_rate, sdm, n2);
 100 }
 101 
 102 static int mpll_set_rate(struct clk_hw *hw,
 103                          unsigned long rate,
 104                          unsigned long parent_rate)
 105 {
 106         struct clk_regmap *clk = to_clk_regmap(hw);
 107         struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
 108         unsigned int sdm, n2;
 109         unsigned long flags = 0;
 110 
 111         params_from_rate(rate, parent_rate, &sdm, &n2, mpll->flags);
 112 
 113         if (mpll->lock)
 114                 spin_lock_irqsave(mpll->lock, flags);
 115         else
 116                 __acquire(mpll->lock);
 117 
 118         /* Set the fractional part */
 119         meson_parm_write(clk->map, &mpll->sdm, sdm);
 120 
 121         /* Set the integer divider part */
 122         meson_parm_write(clk->map, &mpll->n2, n2);
 123 
 124         if (mpll->lock)
 125                 spin_unlock_irqrestore(mpll->lock, flags);
 126         else
 127                 __release(mpll->lock);
 128 
 129         return 0;
 130 }
 131 
 132 static void mpll_init(struct clk_hw *hw)
 133 {
 134         struct clk_regmap *clk = to_clk_regmap(hw);
 135         struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
 136 
 137         if (mpll->init_count)
 138                 regmap_multi_reg_write(clk->map, mpll->init_regs,
 139                                        mpll->init_count);
 140 
 141         /* Enable the fractional part */
 142         meson_parm_write(clk->map, &mpll->sdm_en, 1);
 143 
 144         /* Set spread spectrum if possible */
 145         if (MESON_PARM_APPLICABLE(&mpll->ssen)) {
 146                 unsigned int ss =
 147                         mpll->flags & CLK_MESON_MPLL_SPREAD_SPECTRUM ? 1 : 0;
 148                 meson_parm_write(clk->map, &mpll->ssen, ss);
 149         }
 150 
 151         /* Set the magic misc bit if required */
 152         if (MESON_PARM_APPLICABLE(&mpll->misc))
 153                 meson_parm_write(clk->map, &mpll->misc, 1);
 154 }
 155 
 156 const struct clk_ops meson_clk_mpll_ro_ops = {
 157         .recalc_rate    = mpll_recalc_rate,
 158         .round_rate     = mpll_round_rate,
 159 };
 160 EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops);
 161 
 162 const struct clk_ops meson_clk_mpll_ops = {
 163         .recalc_rate    = mpll_recalc_rate,
 164         .round_rate     = mpll_round_rate,
 165         .set_rate       = mpll_set_rate,
 166         .init           = mpll_init,
 167 };
 168 EXPORT_SYMBOL_GPL(meson_clk_mpll_ops);
 169 
 170 MODULE_DESCRIPTION("Amlogic MPLL driver");
 171 MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>");
 172 MODULE_LICENSE("GPL v2");

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