diff options
Diffstat (limited to 'kernel/drivers/clk/rockchip/clk-pll.c')
-rw-r--r-- | kernel/drivers/clk/rockchip/clk-pll.c | 221 |
1 files changed, 117 insertions, 104 deletions
diff --git a/kernel/drivers/clk/rockchip/clk-pll.c b/kernel/drivers/clk/rockchip/clk-pll.c index f8d3baf27..4881eb8a1 100644 --- a/kernel/drivers/clk/rockchip/clk-pll.c +++ b/kernel/drivers/clk/rockchip/clk-pll.c @@ -17,7 +17,6 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/delay.h> -#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/regmap.h> #include "clk.h" @@ -121,73 +120,72 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) #define RK3066_PLLCON0_NR_SHIFT 8 #define RK3066_PLLCON1_NF_MASK 0x1fff #define RK3066_PLLCON1_NF_SHIFT 0 -#define RK3066_PLLCON2_BWADJ_MASK 0xfff -#define RK3066_PLLCON2_BWADJ_SHIFT 0 +#define RK3066_PLLCON2_NB_MASK 0xfff +#define RK3066_PLLCON2_NB_SHIFT 0 #define RK3066_PLLCON3_RESET (1 << 5) #define RK3066_PLLCON3_PWRDOWN (1 << 1) #define RK3066_PLLCON3_BYPASS (1 << 0) +static void rockchip_rk3066_pll_get_params(struct rockchip_clk_pll *pll, + struct rockchip_pll_rate_table *rate) +{ + u32 pllcon; + + pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); + rate->nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT) + & RK3066_PLLCON0_NR_MASK) + 1; + rate->no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT) + & RK3066_PLLCON0_OD_MASK) + 1; + + pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); + rate->nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT) + & RK3066_PLLCON1_NF_MASK) + 1; + + pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2)); + rate->nb = ((pllcon >> RK3066_PLLCON2_NB_SHIFT) + & RK3066_PLLCON2_NB_MASK) + 1; +} + static unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw, unsigned long prate) { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); - u64 nf, nr, no, rate64 = prate; + struct rockchip_pll_rate_table cur; + u64 rate64 = prate; u32 pllcon; pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(3)); if (pllcon & RK3066_PLLCON3_BYPASS) { pr_debug("%s: pll %s is bypassed\n", __func__, - __clk_get_name(hw->clk)); + clk_hw_get_name(hw)); return prate; } - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); - nf = (pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK; - - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); - nr = (pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK; - no = (pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK; + rockchip_rk3066_pll_get_params(pll, &cur); - rate64 *= (nf + 1); - do_div(rate64, nr + 1); - do_div(rate64, no + 1); + rate64 *= cur.nf; + do_div(rate64, cur.nr); + do_div(rate64, cur.no); return (unsigned long)rate64; } -static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) +static int rockchip_rk3066_pll_set_params(struct rockchip_clk_pll *pll, + const struct rockchip_pll_rate_table *rate) { - struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); - const struct rockchip_pll_rate_table *rate; - unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); - struct regmap *grf = rockchip_clk_get_grf(); - struct clk_mux *pll_mux = &pll->pll_mux; const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; + struct clk_mux *pll_mux = &pll->pll_mux; + struct rockchip_pll_rate_table cur; int rate_change_remuxed = 0; int cur_parent; int ret; - if (IS_ERR(grf)) { - pr_debug("%s: grf regmap not available, aborting rate change\n", - __func__); - return PTR_ERR(grf); - } - - pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", - __func__, __clk_get_name(hw->clk), old_rate, drate, prate); - - /* Get required rate settings from table */ - rate = rockchip_get_pll_settings(pll, drate); - if (!rate) { - pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, - drate, __clk_get_name(hw->clk)); - return -EINVAL; - } - pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", __func__, rate->rate, rate->nr, rate->no, rate->nf); + rockchip_rk3066_pll_get_params(pll, &cur); + cur.rate = 0; + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); if (cur_parent == PLL_MODE_NORM) { pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); @@ -208,8 +206,8 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, writel_relaxed(HIWORD_UPDATE(rate->nf - 1, RK3066_PLLCON1_NF_MASK, RK3066_PLLCON1_NF_SHIFT), pll->reg_base + RK3066_PLLCON(1)); - writel_relaxed(HIWORD_UPDATE(rate->bwadj, RK3066_PLLCON2_BWADJ_MASK, - RK3066_PLLCON2_BWADJ_SHIFT), + writel_relaxed(HIWORD_UPDATE(rate->nb - 1, RK3066_PLLCON2_NB_MASK, + RK3066_PLLCON2_NB_SHIFT), pll->reg_base + RK3066_PLLCON(2)); /* leave reset and wait the reset_delay */ @@ -220,9 +218,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, /* wait for the pll to lock */ ret = rockchip_pll_wait_lock(pll); if (ret) { - pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", - __func__, old_rate); - rockchip_rk3066_pll_set_rate(hw, old_rate, prate); + pr_warn("%s: pll update unsucessful, trying to restore old params\n", + __func__); + rockchip_rk3066_pll_set_params(pll, &cur); } if (rate_change_remuxed) @@ -231,6 +229,34 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, return ret; } +static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + const struct rockchip_pll_rate_table *rate; + unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); + struct regmap *grf = rockchip_clk_get_grf(); + + if (IS_ERR(grf)) { + pr_debug("%s: grf regmap not available, aborting rate change\n", + __func__); + return PTR_ERR(grf); + } + + pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", + __func__, clk_hw_get_name(hw), old_rate, drate, prate); + + /* Get required rate settings from table */ + rate = rockchip_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk_hw_get_name(hw)); + return -EINVAL; + } + + return rockchip_rk3066_pll_set_params(pll, rate); +} + static int rockchip_rk3066_pll_enable(struct clk_hw *hw) { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); @@ -262,48 +288,34 @@ static void rockchip_rk3066_pll_init(struct clk_hw *hw) { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); const struct rockchip_pll_rate_table *rate; - unsigned int nf, nr, no, bwadj; + struct rockchip_pll_rate_table cur; unsigned long drate; - u32 pllcon; if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE)) return; - drate = __clk_get_rate(hw->clk); + drate = clk_hw_get_rate(hw); rate = rockchip_get_pll_settings(pll, drate); /* when no rate setting for the current rate, rely on clk_set_rate */ if (!rate) return; - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); - nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK) + 1; - no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK) + 1; + rockchip_rk3066_pll_get_params(pll, &cur); - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); - nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK) + 1; + pr_debug("%s: pll %s@%lu: nr (%d:%d); no (%d:%d); nf(%d:%d), nb(%d:%d)\n", + __func__, clk_hw_get_name(hw), drate, rate->nr, cur.nr, + rate->no, cur.no, rate->nf, cur.nf, rate->nb, cur.nb); + if (rate->nr != cur.nr || rate->no != cur.no || rate->nf != cur.nf + || rate->nb != cur.nb) { + struct regmap *grf = rockchip_clk_get_grf(); - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2)); - bwadj = (pllcon >> RK3066_PLLCON2_BWADJ_SHIFT) & RK3066_PLLCON2_BWADJ_MASK; - - pr_debug("%s: pll %s@%lu: nr (%d:%d); no (%d:%d); nf(%d:%d), bwadj(%d:%d)\n", - __func__, __clk_get_name(hw->clk), drate, rate->nr, nr, - rate->no, no, rate->nf, nf, rate->bwadj, bwadj); - if (rate->nr != nr || rate->no != no || rate->nf != nf - || rate->bwadj != bwadj) { - struct clk *parent = __clk_get_parent(hw->clk); - unsigned long prate; - - if (!parent) { - pr_warn("%s: parent of %s not available\n", - __func__, __clk_get_name(hw->clk)); + if (IS_ERR(grf)) return; - } pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n", - __func__, __clk_get_name(hw->clk)); - prate = __clk_get_rate(parent); - rockchip_rk3066_pll_set_rate(hw, drate, prate); + __func__, clk_hw_get_name(hw)); + rockchip_rk3066_pll_set_params(pll, rate); } } @@ -329,10 +341,10 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { */ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int mode_offset, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int mode_offset, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock) { const char *pll_parents[3]; @@ -354,6 +366,35 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, if (!pll) return ERR_PTR(-ENOMEM); + /* create the mux on top of the real pll */ + pll->pll_mux_ops = &clk_mux_ops; + pll_mux = &pll->pll_mux; + pll_mux->reg = base + mode_offset; + pll_mux->shift = mode_shift; + pll_mux->mask = PLL_MODE_MASK; + pll_mux->flags = 0; + pll_mux->lock = lock; + pll_mux->hw.init = &init; + + if (pll_type == pll_rk3066) + pll_mux->flags |= CLK_MUX_HIWORD_MASK; + + /* the actual muxing is xin24m, pll-output, xin32k */ + pll_parents[0] = parent_names[0]; + pll_parents[1] = pll_name; + pll_parents[2] = parent_names[1]; + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.ops = pll->pll_mux_ops; + init.parent_names = pll_parents; + init.num_parents = ARRAY_SIZE(pll_parents); + + mux_clk = clk_register(NULL, &pll_mux->hw); + if (IS_ERR(mux_clk)) + goto err_mux; + + /* now create the actual pll */ init.name = pll_name; /* keep all plls untouched for now */ @@ -399,47 +440,19 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, pll->flags = clk_pll_flags; pll->lock = lock; - /* create the mux on top of the real pll */ - pll->pll_mux_ops = &clk_mux_ops; - pll_mux = &pll->pll_mux; - pll_mux->reg = base + mode_offset; - pll_mux->shift = mode_shift; - pll_mux->mask = PLL_MODE_MASK; - pll_mux->flags = 0; - pll_mux->lock = lock; - pll_mux->hw.init = &init; - - if (pll_type == pll_rk3066) - pll_mux->flags |= CLK_MUX_HIWORD_MASK; - pll_clk = clk_register(NULL, &pll->hw); if (IS_ERR(pll_clk)) { pr_err("%s: failed to register pll clock %s : %ld\n", __func__, name, PTR_ERR(pll_clk)); - mux_clk = pll_clk; goto err_pll; } - /* the actual muxing is xin24m, pll-output, xin32k */ - pll_parents[0] = parent_names[0]; - pll_parents[1] = pll_name; - pll_parents[2] = parent_names[1]; - - init.name = name; - init.flags = CLK_SET_RATE_PARENT; - init.ops = pll->pll_mux_ops; - init.parent_names = pll_parents; - init.num_parents = ARRAY_SIZE(pll_parents); - - mux_clk = clk_register(NULL, &pll_mux->hw); - if (IS_ERR(mux_clk)) - goto err_mux; - return mux_clk; -err_mux: - clk_unregister(pll_clk); err_pll: + clk_unregister(mux_clk); + mux_clk = pll_clk; +err_mux: kfree(pll); return mux_clk; } |