[PATCH] clk: samsung: add set_rate and round_rate callbacks for pll45xx

Yadwinder Singh Brar yadi.brar01 at gmail.com
Thu Jul 11 06:44:26 EDT 2013


Hi Thomas,

On Thu, Jul 11, 2013 at 2:57 PM, Thomas Abraham
<thomas.abraham at linaro.org> wrote:
> Add support for set_rate and round_rate callbacks for pll45xx pll. This allows
> configuring pll45xx to generate a desired clock output.
>
> Signed-off-by: Thomas Abraham <thomas.abraham at linaro.org>
> ---
>
> The set_rate and round_rate callbacks in this patch for pll45xx are handled
> slightly differently from the way it is done in the (not yet merged) patch
> series from Yadwinder
> (http://www.mail-archive.com/linux-samsung-soc@vger.kernel.org/msg19540.html)
>
> In this patch, the pll lookup table is kept as part of the pll configuration
> code itself, since the pll is just a hardware block which takes an input
> frequency and configuration values to genertate a output clock frequency.
> Given a set of inputs, a pll of a given type will generate the same output
> regardless of the SoC it is used in. So instead of supplying the pll lookup

Tomasz also raised similar point earlier in discussion on that series.
That time a question which remained unanswered for which we were waiting
(as requested from hardware guys also) was that :
Whether we need to stick to recommended values only or we can drive a
particular output frequency with a given input frequency with any possible
set of (P, M, S) values ?

Theoretically it seems possible but from manual its seems to prefer
recommended values.
Also I am not sure whether same values are always recommended in user manuals
for different SoCs using same PLL type.

> table from per-soc clock driver code (as done in the Yadwinder's patch series),
> the pll lookup table can be coupled with the pll code itself, saving duplication
> of pll lookup table for every SoC the pll is used with.

If we don't need to stick to recommended table, definitely it will
save lot of duplication.
In-fact then we can use same pll lookup table for other PLLs also
which have same calculation
equation for output frequency.


Regards,
Yadwinder

>
>  drivers/clk/samsung/clk-pll.c |   88 +++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/samsung/clk-pll.h |   15 +++++++
>  2 files changed, 103 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
> index 362f12d..4940936 100644
> --- a/drivers/clk/samsung/clk-pll.c
> +++ b/drivers/clk/samsung/clk-pll.c
> @@ -172,6 +172,8 @@ struct clk * __init samsung_clk_register_pll36xx(const char *name,
>   * PLL45xx Clock Type
>   */
>
> +#define PLL45XX_EN_MASK                (1 << 31)
> +#define PLL45XX_LOCK_MASK      (1 << 29)
>  #define PLL45XX_MDIV_MASK      (0x3FF)
>  #define PLL45XX_PDIV_MASK      (0x3F)
>  #define PLL45XX_SDIV_MASK      (0x7)
> @@ -187,6 +189,90 @@ struct samsung_clk_pll45xx {
>
>  #define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
>
> +/* a sorted table of freq supported by pll45xx with 24mhz parent clock */
> +static struct pll45xx_freq_lookup pll45xx_freq_lookup_24mhz[] = {
> +       PLL45XX_PMS(1000000000, 6, 250, 1),
> +       PLL45XX_PMS(800000000, 6, 200, 1),
> +       PLL45XX_PMS(500000000, 6, 250, 2),
> +       PLL45XX_PMS(400000000, 6, 200, 2),
> +       PLL45XX_PMS(200000000, 6, 200, 3),
> +};
> +
> +static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                       unsigned long prate)
> +{
> +       struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
> +       struct pll45xx_freq_lookup *f;
> +       unsigned long timeout, pll_con, cnt, idx;
> +
> +       /* select a lookup table based on parent clock frequency */
> +       switch (prate) {
> +       case 24000000:
> +               f = pll45xx_freq_lookup_24mhz;
> +               cnt = ARRAY_SIZE(pll45xx_freq_lookup_24mhz);
> +               break;
> +       default:
> +               pr_err("%s: unsupported parent clock rate, failed to set rate",
> +                                       __func__);
> +               return -EINVAL;
> +       }
> +
> +       /* check if the requested freq is in the list of supported freq */
> +       for (idx = 0; idx < cnt; idx++, f++)
> +               if (f->target_freq == rate)
> +                       break;
> +
> +       if (idx == cnt) {
> +               pr_err("%s: unspported clock speed %ld requested\n",
> +                               __func__, rate);
> +               return -EINVAL;
> +       }
> +
> +       /* first, disable the output of the pll */
> +       writel(readl(pll->con_reg) & ~PLL45XX_EN_MASK, (void *)pll->con_reg);
> +
> +       /* write the new pll configuration values */
> +       pll_con = (f->pdiv << PLL45XX_PDIV_SHIFT) |
> +                       (f->mdiv << PLL45XX_MDIV_SHIFT) |
> +                       (f->sdiv << PLL45XX_SDIV_SHIFT);
> +       writel(pll_con, (void *)pll->con_reg);
> +
> +       /* enable the pll and wait for it to stabilize */
> +       writel(pll_con | PLL45XX_EN_MASK, (void *)pll->con_reg);
> +       timeout = jiffies + msecs_to_jiffies(20);
> +       while (time_before(jiffies, timeout))
> +               if (readl(pll->con_reg) & PLL45XX_LOCK_MASK)
> +                       return 0;
> +       return -EBUSY;
> +}
> +
> +static long samsung_pll45xx_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                               unsigned long *prate)
> +{
> +       struct pll45xx_freq_lookup *f;
> +       unsigned long cnt;
> +
> +       /* select a lookup table based on parent clock frequency */
> +       switch (*prate) {
> +       case 24000000:
> +               f = pll45xx_freq_lookup_24mhz;
> +               cnt = ARRAY_SIZE(pll45xx_freq_lookup_24mhz);
> +               break;
> +       default:
> +               pr_err("%s: unsupported parent clock rate", __func__);
> +               return *prate;
> +       }
> +
> +       /* find the nearest possible clock output that can be supported */
> +       while (cnt-- > 0) {
> +               if (rate >= f->target_freq)
> +                       return f->target_freq;
> +               f++;
> +       }
> +
> +       return (--f)->target_freq;
> +}
> +
>  static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
>                                 unsigned long parent_rate)
>  {
> @@ -209,6 +295,8 @@ static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
>  }
>
>  static const struct clk_ops samsung_pll45xx_clk_ops = {
> +       .set_rate = samsung_pll45xx_set_rate,
> +       .round_rate = samsung_pll45xx_round_rate,
>         .recalc_rate = samsung_pll45xx_recalc_rate,
>  };
>
> diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h
> index f33786e..fb687ec 100644
> --- a/drivers/clk/samsung/clk-pll.h
> +++ b/drivers/clk/samsung/clk-pll.h
> @@ -18,6 +18,21 @@ enum pll45xx_type {
>         pll_4508
>  };
>
> +struct pll45xx_freq_lookup {
> +       unsigned long   target_freq;
> +       unsigned long   pdiv;
> +       unsigned long   mdiv;
> +       unsigned long   sdiv;
> +};
> +
> +#define PLL45XX_PMS(f, p, m, s)                \
> +       {                                       \
> +               .target_freq    = f,            \
> +               .pdiv           = p,            \
> +               .mdiv           = m,            \
> +               .sdiv           = s,            \
> +       }
> +
>  enum pll46xx_type {
>         pll_4600,
>         pll_4650,
> --
> 1.7.5.4
>



More information about the linux-arm-kernel mailing list