clk: meson: Add support for parameters for specific PLLs
In recent Amlogic GXBB, GXL and GXM SoCs, the GP0 PLL needs some specific parameters in order to initialize and lock correctly. This patch adds an optional PARAM table used to initialize the PLL to a default value with it's parameters in order to achieve to desired frequency. The GP0 PLL in GXBB, GXL/GXM also needs some tweaks in the initialization steps, and these are exposed along the PARAM table. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Michael Turquette <mturquette@baylibre.com> Link: lkml.kernel.org/r/1490178747-14837-2-git-send-email-narmstrong@baylibre.com
This commit is contained in:

committed by
Kevin Hilman

parent
fac9a55b66
commit
45fcbec70c
@@ -116,6 +116,30 @@ static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Specific wait loop for GXL/GXM GP0 PLL */
|
||||
static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
|
||||
struct parm *p_n)
|
||||
{
|
||||
int delay = 100;
|
||||
u32 reg;
|
||||
|
||||
while (delay > 0) {
|
||||
reg = readl(pll->base + p_n->reg_off);
|
||||
writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
|
||||
udelay(10);
|
||||
writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);
|
||||
|
||||
/* This delay comes from AMLogic tree clk-gp0-gxl driver */
|
||||
mdelay(1);
|
||||
|
||||
reg = readl(pll->base + p_n->reg_off);
|
||||
if (reg & MESON_PLL_LOCK)
|
||||
return 0;
|
||||
delay--;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
|
||||
struct parm *p_n)
|
||||
{
|
||||
@@ -132,6 +156,15 @@ static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < pll->params.params_count ; ++i)
|
||||
writel(pll->params.params_table[i].value,
|
||||
pll->base + pll->params.params_table[i].reg_off);
|
||||
}
|
||||
|
||||
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -151,10 +184,16 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
if (!rate_set)
|
||||
return -EINVAL;
|
||||
|
||||
/* Initialize the PLL in a clean state if specified */
|
||||
if (pll->params.params_count)
|
||||
meson_clk_pll_init_params(pll);
|
||||
|
||||
/* PLL reset */
|
||||
p = &pll->n;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
|
||||
/* If no_init_reset is provided, avoid resetting at this point */
|
||||
if (!pll->params.no_init_reset)
|
||||
writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
|
||||
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
@@ -184,7 +223,17 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
}
|
||||
|
||||
p = &pll->n;
|
||||
ret = meson_clk_pll_wait_lock(pll, p);
|
||||
/* If clear_reset_for_lock is provided, remove the reset bit here */
|
||||
if (pll->params.clear_reset_for_lock) {
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
|
||||
}
|
||||
|
||||
/* If reset_lock_loop, use a special loop including resetting */
|
||||
if (pll->params.reset_lock_loop)
|
||||
ret = meson_clk_pll_wait_lock_reset(pll, p);
|
||||
else
|
||||
ret = meson_clk_pll_wait_lock(pll, p);
|
||||
if (ret) {
|
||||
pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
|
||||
__func__, old_rate);
|
||||
|
Reference in New Issue
Block a user