diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index b7e66c9dd9f2..220b432d1f4a 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -52,6 +52,11 @@ struct rockchip_clk_pll { #define to_rockchip_clk_pll_nb(nb) \ container_of(nb, struct rockchip_clk_pll, clk_nb) +static void rockchip_rk3366_pll_get_params(struct rockchip_clk_pll *pll, + struct rockchip_pll_rate_table *rate); +static int rockchip_rk3366_pll_set_params(struct rockchip_clk_pll *pll, + const struct rockchip_pll_rate_table *rate); + static const struct rockchip_pll_rate_table *rockchip_get_pll_settings( struct rockchip_clk_pll *pll, unsigned long rate) { @@ -162,7 +167,10 @@ static unsigned long rockchip_rk3036_pll_recalc_rate(struct clk_hw *hw, struct rockchip_pll_rate_table cur; u64 rate64 = prate; - rockchip_rk3036_pll_get_params(pll, &cur); + if (pll->type == pll_rk3366) + rockchip_rk3366_pll_get_params(pll, &cur); + else + rockchip_rk3036_pll_get_params(pll, &cur); rate64 *= cur.fbdiv; do_div(rate64, cur.refdiv); @@ -265,6 +273,9 @@ static int rockchip_rk3036_pll_set_rate(struct clk_hw *hw, unsigned long drate, return -EINVAL; } + if (pll->type == pll_rk3366) + return rockchip_rk3366_pll_set_params(pll, rate); + return rockchip_rk3036_pll_set_params(pll, rate); } @@ -312,7 +323,10 @@ static void rockchip_rk3036_pll_init(struct clk_hw *hw) if (!rate) return; - rockchip_rk3036_pll_get_params(pll, &cur); + if (pll->type == pll_rk3366) + rockchip_rk3366_pll_get_params(pll, &cur); + else + rockchip_rk3036_pll_get_params(pll, &cur); pr_debug("%s: pll %s@%lu: Hz\n", __func__, __clk_get_name(hw->clk), drate); @@ -336,7 +350,10 @@ static void rockchip_rk3036_pll_init(struct clk_hw *hw) pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n", __func__, __clk_get_name(hw->clk)); - rockchip_rk3036_pll_set_params(pll, rate); + if (pll->type == pll_rk3366) + rockchip_rk3366_pll_set_params(pll, rate); + else + rockchip_rk3036_pll_set_params(pll, rate); } } @@ -586,6 +603,157 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { .init = rockchip_rk3066_pll_init, }; +/** + * PLL used in RK3366 + */ + +#define RK3366_PLLCON(i) (i * 0x4) +#define RK3366_PLLCON0_FBDIV_MASK 0xfff +#define RK3366_PLLCON0_FBDIV_SHIFT 0 +#define RK3366_PLLCON0_POSTDIV1_MASK 0x7 +#define RK3366_PLLCON0_POSTDIV1_SHIFT 12 +#define RK3366_PLLCON1_REFDIV_MASK 0x3f +#define RK3366_PLLCON1_REFDIV_SHIFT 0 +#define RK3366_PLLCON1_POSTDIV2_MASK 0x7 +#define RK3366_PLLCON1_POSTDIV2_SHIFT 6 +#define RK3366_PLLCON2_FRAC_MASK 0xffffff +#define RK3366_PLLCON2_FRAC_SHIFT 0 +#define RK3366_PLLCON3_DSMPD_MASK 0x1 +#define RK3366_PLLCON3_DSMPD_SHIFT 2 + +#define RK3366_PLLCON3_PWRDOWN (1 << 0) + +static void rockchip_rk3366_pll_get_params(struct rockchip_clk_pll *pll, + struct rockchip_pll_rate_table *rate) +{ + u32 pllcon; + + pllcon = readl_relaxed(pll->reg_base + RK3366_PLLCON(0)); + rate->fbdiv = ((pllcon >> RK3366_PLLCON0_FBDIV_SHIFT) + & RK3366_PLLCON0_FBDIV_MASK); + rate->postdiv1 = ((pllcon >> RK3366_PLLCON0_POSTDIV1_SHIFT) + & RK3366_PLLCON0_POSTDIV1_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3366_PLLCON(1)); + rate->refdiv = ((pllcon >> RK3366_PLLCON1_REFDIV_SHIFT) + & RK3366_PLLCON1_REFDIV_MASK); + rate->postdiv2 = ((pllcon >> RK3366_PLLCON1_POSTDIV2_SHIFT) + & RK3366_PLLCON1_POSTDIV2_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3366_PLLCON(2)); + rate->frac = ((pllcon >> RK3366_PLLCON2_FRAC_SHIFT) + & RK3366_PLLCON2_FRAC_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3366_PLLCON(3)); + rate->dsmpd = ((pllcon >> RK3366_PLLCON3_DSMPD_SHIFT) + & RK3366_PLLCON3_DSMPD_MASK); +} + +static int rockchip_rk3366_pll_set_params(struct rockchip_clk_pll *pll, + const struct rockchip_pll_rate_table *rate) +{ + 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; + u32 pllcon; + int rate_change_remuxed = 0; + int cur_parent; + int ret; + + pr_debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n", + __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv, + rate->postdiv2, rate->dsmpd, rate->frac); + + rockchip_rk3366_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); + rate_change_remuxed = 1; + } + + /* update pll values */ + writel_relaxed(HIWORD_UPDATE(rate->fbdiv, RK3366_PLLCON0_FBDIV_MASK, + RK3366_PLLCON0_FBDIV_SHIFT) | + HIWORD_UPDATE(rate->postdiv1, RK3366_PLLCON0_POSTDIV1_MASK, + RK3366_PLLCON0_POSTDIV1_SHIFT), + pll->reg_base + RK3366_PLLCON(0)); + + writel_relaxed(HIWORD_UPDATE(rate->refdiv, RK3366_PLLCON1_REFDIV_MASK, + RK3366_PLLCON1_REFDIV_SHIFT) | + HIWORD_UPDATE(rate->postdiv2, RK3366_PLLCON1_POSTDIV2_MASK, + RK3366_PLLCON1_POSTDIV2_SHIFT), + pll->reg_base + RK3366_PLLCON(1)); + + /* GPLL CON2 is not HIWORD_MASK */ + pllcon = readl_relaxed(pll->reg_base + RK3366_PLLCON(2)); + pllcon &= ~(RK3366_PLLCON2_FRAC_MASK << RK3366_PLLCON2_FRAC_SHIFT); + pllcon |= rate->frac << RK3366_PLLCON2_FRAC_SHIFT; + writel_relaxed(pllcon, pll->reg_base + RK3366_PLLCON(2)); + + writel_relaxed(HIWORD_UPDATE(rate->dsmpd, RK3366_PLLCON3_DSMPD_MASK, + RK3366_PLLCON3_DSMPD_SHIFT), + pll->reg_base + RK3366_PLLCON(3)); + + /* wait for the pll to lock */ + ret = rockchip_pll_wait_lock(pll); + if (ret) { + pr_warn("%s: pll update unsucessful, trying to restore old params\n", + __func__); + rockchip_rk3366_pll_set_params(pll, &cur); + } + + if (rate_change_remuxed) + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); + + return ret; +} + +static int rockchip_rk3366_pll_enable(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + + writel(HIWORD_UPDATE(0, RK3366_PLLCON3_PWRDOWN, 0), + pll->reg_base + RK3366_PLLCON(3)); + + return 0; +} + +static void rockchip_rk3366_pll_disable(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + + writel(HIWORD_UPDATE(RK3366_PLLCON3_PWRDOWN, + RK3366_PLLCON3_PWRDOWN, 0), + pll->reg_base + RK3366_PLLCON(3)); +} + +static int rockchip_rk3366_pll_is_enabled(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + u32 pllcon = readl(pll->reg_base + RK3366_PLLCON(3)); + + return !(pllcon & RK3366_PLLCON3_PWRDOWN); +} + +static const struct clk_ops rockchip_rk3366_pll_clk_norate_ops = { + .recalc_rate = rockchip_rk3036_pll_recalc_rate, + .enable = rockchip_rk3366_pll_enable, + .disable = rockchip_rk3366_pll_disable, + .is_enabled = rockchip_rk3366_pll_is_enabled, +}; + +static const struct clk_ops rockchip_rk3366_pll_clk_ops = { + .recalc_rate = rockchip_rk3036_pll_recalc_rate, + .round_rate = rockchip_pll_round_rate, + .set_rate = rockchip_rk3036_pll_set_rate, + .enable = rockchip_rk3366_pll_enable, + .disable = rockchip_rk3366_pll_disable, + .is_enabled = rockchip_rk3366_pll_is_enabled, + .init = rockchip_rk3036_pll_init, +}; + /* * Common registering of pll clocks */ @@ -626,7 +794,8 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, pll_mux->lock = lock; pll_mux->hw.init = &init; - if (pll_type == pll_rk3036 || pll_type == pll_rk3066) + if (pll_type == pll_rk3036 || pll_type == pll_rk3066 || + pll_type == pll_rk3366) pll_mux->flags |= CLK_MUX_HIWORD_MASK; /* the actual muxing is xin24m, pll-output, xin32k */ @@ -683,6 +852,12 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, else init.ops = &rockchip_rk3066_pll_clk_ops; break; + case pll_rk3366: + if (!pll->rate_table) + init.ops = &rockchip_rk3366_pll_clk_norate_ops; + else + init.ops = &rockchip_rk3366_pll_clk_ops; + break; default: pr_warn("%s: Unknown pll type for pll clk %s\n", __func__, name); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 61e7b2d995f1..423e49cc06ed 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -79,6 +79,7 @@ struct clk; enum rockchip_pll_type { pll_rk3036, pll_rk3066, + pll_rk3366, }; #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \