summaryrefslogtreecommitdiff
path: root/drivers/clk/meson/clk-pll.c
diff options
context:
space:
mode:
authorJerome Brunet <jbrunet@baylibre.com>2018-02-12 17:58:42 +0300
committerNeil Armstrong <narmstrong@baylibre.com>2018-03-13 12:04:03 +0300
commit722825dcd54b2e427c1aee54a7992eb4ab04a49d (patch)
tree5abe9c46643a0681d86e24f214816a8ffb911fa3 /drivers/clk/meson/clk-pll.c
parent88a4e1283681e0f07048b2bd867cc81fbbae57cc (diff)
downloadlinux-722825dcd54b2e427c1aee54a7992eb4ab04a49d.tar.xz
clk: meson: migrate plls clocks to clk_regmap
Rework meson pll driver to use clk_regmap and move meson8b, gxbb and axg's clock using meson_clk_pll to clk_regmap. This rework is not just about clk_regmap, there a serious clean-up of the driver code: * Add lock and reset field: Previously inferred from the n field. * Simplify the reset logic: Code seemed to apply reset differently but in fact it was always the same -> assert reset, apply params, de-assert reset. The 2 lock checking loops have been kept for now, as they seem to be necessary. * Do the sequence of init register pokes only at .init() instead of in .set_rate(). Redoing the init on every set_rate() is not necessary Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Diffstat (limited to 'drivers/clk/meson/clk-pll.c')
-rw-r--r--drivers/clk/meson/clk-pll.c243
1 files changed, 93 insertions, 150 deletions
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 218c769c6d50..f3d909719111 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -42,52 +42,36 @@
#include "clkc.h"
-#define MESON_PLL_RESET BIT(29)
-#define MESON_PLL_LOCK BIT(31)
-
-#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
+static inline struct meson_clk_pll_data *
+meson_clk_pll_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_pll_data *)clk->data;
+}
static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct meson_clk_pll *pll = to_meson_clk_pll(hw);
- struct parm *p;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
u64 rate;
u16 n, m, frac = 0, od, od2 = 0, od3 = 0;
- u32 reg;
-
- p = &pll->n;
- reg = readl(pll->base + p->reg_off);
- n = PARM_GET(p->width, p->shift, reg);
- p = &pll->m;
- reg = readl(pll->base + p->reg_off);
- m = PARM_GET(p->width, p->shift, reg);
+ n = meson_parm_read(clk->map, &pll->n);
+ m = meson_parm_read(clk->map, &pll->m);
+ od = meson_parm_read(clk->map, &pll->od);
- p = &pll->od;
- reg = readl(pll->base + p->reg_off);
- od = PARM_GET(p->width, p->shift, reg);
+ if (MESON_PARM_APPLICABLE(&pll->od2))
+ od2 = meson_parm_read(clk->map, &pll->od2);
- p = &pll->od2;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- od2 = PARM_GET(p->width, p->shift, reg);
- }
-
- p = &pll->od3;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- od3 = PARM_GET(p->width, p->shift, reg);
- }
+ if (MESON_PARM_APPLICABLE(&pll->od3))
+ od3 = meson_parm_read(clk->map, &pll->od3);
rate = (u64)m * parent_rate;
- p = &pll->frac;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- frac = PARM_GET(p->width, p->shift, reg);
+ if (MESON_PARM_APPLICABLE(&pll->frac)) {
+ frac = meson_parm_read(clk->map, &pll->frac);
- rate += mul_u64_u32_shr(parent_rate, frac, p->width);
+ rate += mul_u64_u32_shr(parent_rate, frac, pll->frac.width);
}
return div_u64(rate, n) >> od >> od2 >> od3;
@@ -96,177 +80,136 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct meson_clk_pll *pll = to_meson_clk_pll(hw);
- const struct pll_rate_table *rate_table = pll->rate_table;
- int i;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ const struct pll_rate_table *pllt;
/*
* if the table is missing, just return the current rate
* since we don't have the other available frequencies
*/
- if (!rate_table)
+ if (!pll->table)
return meson_clk_pll_recalc_rate(hw, *parent_rate);
- for (i = 0; i < pll->rate_count; i++) {
- if (rate <= rate_table[i].rate)
- return rate_table[i].rate;
+ for (pllt = pll->table; pllt->rate; pllt++) {
+ if (rate <= pllt->rate)
+ return pllt->rate;
}
/* else return the smallest value */
- return rate_table[0].rate;
+ return pll->table[0].rate;
}
-static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
- unsigned long rate)
+static const struct pll_rate_table *
+meson_clk_get_pll_settings(const struct pll_rate_table *table,
+ unsigned long rate)
{
- const struct pll_rate_table *rate_table = pll->rate_table;
- int i;
+ const struct pll_rate_table *pllt;
- if (!rate_table)
+ if (!table)
return NULL;
- for (i = 0; i < pll->rate_count; i++) {
- if (rate == rate_table[i].rate)
- return &rate_table[i];
+ for (pllt = table; pllt->rate; pllt++) {
+ if (rate == pllt->rate)
+ return pllt;
}
+
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)
+static int meson_clk_pll_wait_lock(struct clk_hw *hw)
{
- 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)
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ int delay = pll->flags & CLK_MESON_PLL_LOCK_LOOP_RST ?
+ 100 : 24000000;
+
+ do {
+ /* Specific wait loop for GXL/GXM GP0 PLL */
+ if (pll->flags & CLK_MESON_PLL_LOCK_LOOP_RST) {
+ /* Procedure taken from the vendor kernel */
+ meson_parm_write(clk->map, &pll->rst, 1);
+ udelay(10);
+ meson_parm_write(clk->map, &pll->rst, 0);
+ mdelay(1);
+ }
+
+ /* Is the clock locked now ? */
+ if (meson_parm_read(clk->map, &pll->l))
return 0;
- delay--;
- }
- return -ETIMEDOUT;
-}
-static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
- struct parm *p_n)
-{
- int delay = 24000000;
- u32 reg;
-
- while (delay > 0) {
- reg = readl(pll->base + p_n->reg_off);
-
- if (reg & MESON_PLL_LOCK)
- return 0;
delay--;
- }
+ } while (delay > 0);
+
return -ETIMEDOUT;
}
-static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
+static void meson_clk_pll_init(struct clk_hw *hw)
{
- 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);
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+
+ if (pll->init_count) {
+ meson_parm_write(clk->map, &pll->rst, 1);
+ regmap_multi_reg_write(clk->map, pll->init_regs,
+ pll->init_count);
+ meson_parm_write(clk->map, &pll->rst, 0);
+ }
}
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct meson_clk_pll *pll = to_meson_clk_pll(hw);
- struct parm *p;
- const struct pll_rate_table *rate_set;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ const struct pll_rate_table *pllt;
unsigned long old_rate;
- int ret = 0;
- u32 reg;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
old_rate = rate;
- rate_set = meson_clk_get_pll_settings(pll, rate);
- if (!rate_set)
+ pllt = meson_clk_get_pll_settings(pll->table, rate);
+ if (!pllt)
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);
- /* 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);
-
- p = &pll->m;
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
- writel(reg, pll->base + p->reg_off);
-
- p = &pll->od;
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
- writel(reg, pll->base + p->reg_off);
-
- p = &pll->od2;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
- writel(reg, pll->base + p->reg_off);
- }
+ /* Put the pll in reset to write the params */
+ meson_parm_write(clk->map, &pll->rst, 1);
- p = &pll->od3;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->od3);
- writel(reg, pll->base + p->reg_off);
- }
+ meson_parm_write(clk->map, &pll->n, pllt->n);
+ meson_parm_write(clk->map, &pll->m, pllt->m);
+ meson_parm_write(clk->map, &pll->od, pllt->od);
- p = &pll->frac;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
- writel(reg, pll->base + p->reg_off);
- }
+ if (MESON_PARM_APPLICABLE(&pll->od2))
+ meson_parm_write(clk->map, &pll->od2, pllt->od2);
- p = &pll->n;
- /* 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 (MESON_PARM_APPLICABLE(&pll->od3))
+ meson_parm_write(clk->map, &pll->od3, pllt->od3);
+
+ if (MESON_PARM_APPLICABLE(&pll->frac))
+ meson_parm_write(clk->map, &pll->frac, pllt->frac);
+
+ /* make sure the reset is cleared at this point */
+ meson_parm_write(clk->map, &pll->rst, 0);
- /* 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) {
+ if (meson_clk_pll_wait_lock(hw)) {
pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
__func__, old_rate);
+ /*
+ * FIXME: Do we really need/want this HACK ?
+ * It looks unsafe. what happens if the clock gets into a
+ * broken state and we can't lock back on the old_rate ? Looks
+ * like an infinite recursion is possible
+ */
meson_clk_pll_set_rate(hw, old_rate, parent_rate);
}
- return ret;
+ return 0;
}
const struct clk_ops meson_clk_pll_ops = {
+ .init = meson_clk_pll_init,
.recalc_rate = meson_clk_pll_recalc_rate,
.round_rate = meson_clk_pll_round_rate,
.set_rate = meson_clk_pll_set_rate,