diff options
-rw-r--r-- | drivers/clk/rockchip/clk-cpu.c | 29 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-pll.c | 301 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3036.c | 21 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3188.c | 51 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3228.c | 21 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3288.c | 23 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3368.c | 28 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.c | 146 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.h | 84 |
9 files changed, 569 insertions, 135 deletions
diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 4e73ed5cab58..4bb130cd0062 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -158,12 +158,16 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask, reg_data->div_core_shift) | - HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), + HIWORD_UPDATE(reg_data->mux_core_alt, + reg_data->mux_core_mask, + reg_data->mux_core_shift), cpuclk->reg_base + reg_data->core_reg); } else { /* select alternate parent */ - writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), - cpuclk->reg_base + reg_data->core_reg); + writel(HIWORD_UPDATE(reg_data->mux_core_alt, + reg_data->mux_core_mask, + reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); } spin_unlock_irqrestore(cpuclk->lock, flags); @@ -198,7 +202,9 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, writel(HIWORD_UPDATE(0, reg_data->div_core_mask, reg_data->div_core_shift) | - HIWORD_UPDATE(0, 1, reg_data->mux_core_shift), + HIWORD_UPDATE(reg_data->mux_core_main, + reg_data->mux_core_mask, + reg_data->mux_core_shift), cpuclk->reg_base + reg_data->core_reg); if (ndata->old_rate > ndata->new_rate) @@ -252,7 +258,7 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, return ERR_PTR(-ENOMEM); init.name = name; - init.parent_names = &parent_names[0]; + init.parent_names = &parent_names[reg_data->mux_core_main]; init.num_parents = 1; init.ops = &rockchip_cpuclk_ops; @@ -270,10 +276,10 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb; cpuclk->hw.init = &init; - cpuclk->alt_parent = __clk_lookup(parent_names[1]); + cpuclk->alt_parent = __clk_lookup(parent_names[reg_data->mux_core_alt]); if (!cpuclk->alt_parent) { - pr_err("%s: could not lookup alternate parent\n", - __func__); + pr_err("%s: could not lookup alternate parent: (%d)\n", + __func__, reg_data->mux_core_alt); ret = -EINVAL; goto free_cpuclk; } @@ -285,10 +291,11 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, goto free_cpuclk; } - clk = __clk_lookup(parent_names[0]); + clk = __clk_lookup(parent_names[reg_data->mux_core_main]); if (!clk) { - pr_err("%s: could not lookup parent clock %s\n", - __func__, parent_names[0]); + pr_err("%s: could not lookup parent clock: (%d) %s\n", + __func__, reg_data->mux_core_main, + parent_names[reg_data->mux_core_main]); ret = -EINVAL; goto free_alt_parent; } diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 5de797e34d54..128a6b2fca7f 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -46,6 +46,8 @@ struct rockchip_clk_pll { const struct rockchip_pll_rate_table *rate_table; unsigned int rate_count; spinlock_t *lock; + + struct rockchip_clk_provider *ctx; }; #define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw) @@ -90,7 +92,7 @@ static long rockchip_pll_round_rate(struct clk_hw *hw, */ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) { - struct regmap *grf = rockchip_clk_get_grf(); + struct regmap *grf = rockchip_clk_get_grf(pll->ctx); unsigned int val; int delay = 24000000, ret; @@ -251,7 +253,7 @@ static int rockchip_rk3036_pll_set_rate(struct clk_hw *hw, unsigned long drate, struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); const struct rockchip_pll_rate_table *rate; unsigned long old_rate = rockchip_rk3036_pll_recalc_rate(hw, prate); - struct regmap *grf = rockchip_clk_get_grf(); + struct regmap *grf = rockchip_clk_get_grf(pll->ctx); if (IS_ERR(grf)) { pr_debug("%s: grf regmap not available, aborting rate change\n", @@ -490,7 +492,7 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, 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 regmap *grf = rockchip_clk_get_grf(pll->ctx); if (IS_ERR(grf)) { pr_debug("%s: grf regmap not available, aborting rate change\n", @@ -563,7 +565,7 @@ static void rockchip_rk3066_pll_init(struct clk_hw *hw) 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(); + struct regmap *grf = rockchip_clk_get_grf(pll->ctx); if (IS_ERR(grf)) return; @@ -591,16 +593,278 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { .init = rockchip_rk3066_pll_init, }; +/** + * PLL used in RK3399 + */ + +#define RK3399_PLLCON(i) (i * 0x4) +#define RK3399_PLLCON0_FBDIV_MASK 0xfff +#define RK3399_PLLCON0_FBDIV_SHIFT 0 +#define RK3399_PLLCON1_REFDIV_MASK 0x3f +#define RK3399_PLLCON1_REFDIV_SHIFT 0 +#define RK3399_PLLCON1_POSTDIV1_MASK 0x7 +#define RK3399_PLLCON1_POSTDIV1_SHIFT 8 +#define RK3399_PLLCON1_POSTDIV2_MASK 0x7 +#define RK3399_PLLCON1_POSTDIV2_SHIFT 12 +#define RK3399_PLLCON2_FRAC_MASK 0xffffff +#define RK3399_PLLCON2_FRAC_SHIFT 0 +#define RK3399_PLLCON2_LOCK_STATUS BIT(31) +#define RK3399_PLLCON3_PWRDOWN BIT(0) +#define RK3399_PLLCON3_DSMPD_MASK 0x1 +#define RK3399_PLLCON3_DSMPD_SHIFT 3 + +static int rockchip_rk3399_pll_wait_lock(struct rockchip_clk_pll *pll) +{ + u32 pllcon; + int delay = 24000000; + + /* poll check the lock status in rk3399 xPLLCON2 */ + while (delay > 0) { + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2)); + if (pllcon & RK3399_PLLCON2_LOCK_STATUS) + return 0; + + delay--; + } + + pr_err("%s: timeout waiting for pll to lock\n", __func__); + return -ETIMEDOUT; +} + +static void rockchip_rk3399_pll_get_params(struct rockchip_clk_pll *pll, + struct rockchip_pll_rate_table *rate) +{ + u32 pllcon; + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(0)); + rate->fbdiv = ((pllcon >> RK3399_PLLCON0_FBDIV_SHIFT) + & RK3399_PLLCON0_FBDIV_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(1)); + rate->refdiv = ((pllcon >> RK3399_PLLCON1_REFDIV_SHIFT) + & RK3399_PLLCON1_REFDIV_MASK); + rate->postdiv1 = ((pllcon >> RK3399_PLLCON1_POSTDIV1_SHIFT) + & RK3399_PLLCON1_POSTDIV1_MASK); + rate->postdiv2 = ((pllcon >> RK3399_PLLCON1_POSTDIV2_SHIFT) + & RK3399_PLLCON1_POSTDIV2_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2)); + rate->frac = ((pllcon >> RK3399_PLLCON2_FRAC_SHIFT) + & RK3399_PLLCON2_FRAC_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(3)); + rate->dsmpd = ((pllcon >> RK3399_PLLCON3_DSMPD_SHIFT) + & RK3399_PLLCON3_DSMPD_MASK); +} + +static unsigned long rockchip_rk3399_pll_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + struct rockchip_pll_rate_table cur; + u64 rate64 = prate; + + rockchip_rk3399_pll_get_params(pll, &cur); + + rate64 *= cur.fbdiv; + do_div(rate64, cur.refdiv); + + if (cur.dsmpd == 0) { + /* fractional mode */ + u64 frac_rate64 = prate * cur.frac; + + do_div(frac_rate64, cur.refdiv); + rate64 += frac_rate64 >> 24; + } + + do_div(rate64, cur.postdiv1); + do_div(rate64, cur.postdiv2); + + return (unsigned long)rate64; +} + +static int rockchip_rk3399_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_rk3399_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, RK3399_PLLCON0_FBDIV_MASK, + RK3399_PLLCON0_FBDIV_SHIFT), + pll->reg_base + RK3399_PLLCON(0)); + + writel_relaxed(HIWORD_UPDATE(rate->refdiv, RK3399_PLLCON1_REFDIV_MASK, + RK3399_PLLCON1_REFDIV_SHIFT) | + HIWORD_UPDATE(rate->postdiv1, RK3399_PLLCON1_POSTDIV1_MASK, + RK3399_PLLCON1_POSTDIV1_SHIFT) | + HIWORD_UPDATE(rate->postdiv2, RK3399_PLLCON1_POSTDIV2_MASK, + RK3399_PLLCON1_POSTDIV2_SHIFT), + pll->reg_base + RK3399_PLLCON(1)); + + /* xPLL CON2 is not HIWORD_MASK */ + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2)); + pllcon &= ~(RK3399_PLLCON2_FRAC_MASK << RK3399_PLLCON2_FRAC_SHIFT); + pllcon |= rate->frac << RK3399_PLLCON2_FRAC_SHIFT; + writel_relaxed(pllcon, pll->reg_base + RK3399_PLLCON(2)); + + writel_relaxed(HIWORD_UPDATE(rate->dsmpd, RK3399_PLLCON3_DSMPD_MASK, + RK3399_PLLCON3_DSMPD_SHIFT), + pll->reg_base + RK3399_PLLCON(3)); + + /* wait for the pll to lock */ + ret = rockchip_rk3399_pll_wait_lock(pll); + if (ret) { + pr_warn("%s: pll update unsuccessful, trying to restore old params\n", + __func__); + rockchip_rk3399_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_rk3399_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_rk3399_pll_recalc_rate(hw, prate); + + 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; + } + + return rockchip_rk3399_pll_set_params(pll, rate); +} + +static int rockchip_rk3399_pll_enable(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + + writel(HIWORD_UPDATE(0, RK3399_PLLCON3_PWRDOWN, 0), + pll->reg_base + RK3399_PLLCON(3)); + + return 0; +} + +static void rockchip_rk3399_pll_disable(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + + writel(HIWORD_UPDATE(RK3399_PLLCON3_PWRDOWN, + RK3399_PLLCON3_PWRDOWN, 0), + pll->reg_base + RK3399_PLLCON(3)); +} + +static int rockchip_rk3399_pll_is_enabled(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + u32 pllcon = readl(pll->reg_base + RK3399_PLLCON(3)); + + return !(pllcon & RK3399_PLLCON3_PWRDOWN); +} + +static void rockchip_rk3399_pll_init(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + const struct rockchip_pll_rate_table *rate; + struct rockchip_pll_rate_table cur; + unsigned long drate; + + if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE)) + return; + + 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; + + rockchip_rk3399_pll_get_params(pll, &cur); + + pr_debug("%s: pll %s@%lu: Hz\n", __func__, __clk_get_name(hw->clk), + drate); + pr_debug("old - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n", + cur.fbdiv, cur.postdiv1, cur.refdiv, cur.postdiv2, + cur.dsmpd, cur.frac); + pr_debug("new - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n", + rate->fbdiv, rate->postdiv1, rate->refdiv, rate->postdiv2, + rate->dsmpd, rate->frac); + + if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 || + rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 || + rate->dsmpd != cur.dsmpd || rate->frac != cur.frac) { + struct clk *parent = clk_get_parent(hw->clk); + + if (!parent) { + pr_warn("%s: parent of %s not available\n", + __func__, __clk_get_name(hw->clk)); + return; + } + + pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n", + __func__, __clk_get_name(hw->clk)); + rockchip_rk3399_pll_set_params(pll, rate); + } +} + +static const struct clk_ops rockchip_rk3399_pll_clk_norate_ops = { + .recalc_rate = rockchip_rk3399_pll_recalc_rate, + .enable = rockchip_rk3399_pll_enable, + .disable = rockchip_rk3399_pll_disable, + .is_enabled = rockchip_rk3399_pll_is_enabled, +}; + +static const struct clk_ops rockchip_rk3399_pll_clk_ops = { + .recalc_rate = rockchip_rk3399_pll_recalc_rate, + .round_rate = rockchip_pll_round_rate, + .set_rate = rockchip_rk3399_pll_set_rate, + .enable = rockchip_rk3399_pll_enable, + .disable = rockchip_rk3399_pll_disable, + .is_enabled = rockchip_rk3399_pll_is_enabled, + .init = rockchip_rk3399_pll_init, +}; + /* * Common registering of pll clocks */ -struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, +struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, + enum rockchip_pll_type pll_type, 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) + u8 num_parents, 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) { const char *pll_parents[3]; struct clk_init_data init; @@ -624,14 +888,16 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, /* 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->reg = ctx->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->lock = &ctx->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_rk3399) pll_mux->flags |= CLK_MUX_HIWORD_MASK; /* the actual muxing is xin24m, pll-output, xin32k */ @@ -688,6 +954,12 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, else init.ops = &rockchip_rk3066_pll_clk_ops; break; + case pll_rk3399: + if (!pll->rate_table) + init.ops = &rockchip_rk3399_pll_clk_norate_ops; + else + init.ops = &rockchip_rk3399_pll_clk_ops; + break; default: pr_warn("%s: Unknown pll type for pll clk %s\n", __func__, name); @@ -695,11 +967,12 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, pll->hw.init = &init; pll->type = pll_type; - pll->reg_base = base + con_offset; + pll->reg_base = ctx->reg_base + con_offset; pll->lock_offset = grf_lock_offset; pll->lock_shift = lock_shift; pll->flags = clk_pll_flags; - pll->lock = lock; + pll->lock = &ctx->lock; + pll->ctx = ctx; pll_clk = clk_register(NULL, &pll->hw); if (IS_ERR(pll_clk)) { diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c index 7cdb2d61f3e0..924f560dcf80 100644 --- a/drivers/clk/rockchip/clk-rk3036.c +++ b/drivers/clk/rockchip/clk-rk3036.c @@ -113,7 +113,10 @@ static const struct rockchip_cpuclk_reg_data rk3036_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 7, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "xin24m", "xin24m" }; @@ -437,6 +440,7 @@ static const char *const rk3036_critical_clocks[] __initconst = { static void __init rk3036_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; struct clk *clk; @@ -446,22 +450,27 @@ static void __init rk3036_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1); if (IS_ERR(clk)) pr_warn("%s: could not register clock usb480m: %ld\n", __func__, PTR_ERR(clk)); - rockchip_clk_register_plls(rk3036_pll_clks, + rockchip_clk_register_plls(ctx, rk3036_pll_clks, ARRAY_SIZE(rk3036_pll_clks), RK3036_GRF_SOC_STATUS0); - rockchip_clk_register_branches(rk3036_clk_branches, + rockchip_clk_register_branches(ctx, rk3036_clk_branches, ARRAY_SIZE(rk3036_clk_branches)); rockchip_clk_protect_critical(rk3036_critical_clocks, ARRAY_SIZE(rk3036_critical_clocks)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3036_cpuclk_data, rk3036_cpuclk_rates, ARRAY_SIZE(rk3036_cpuclk_rates)); @@ -469,6 +478,8 @@ static void __init rk3036_clk_init(struct device_node *np) rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 40bab3901491..d0e722a0e8cf 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -155,7 +155,10 @@ static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 8, + .mux_core_mask = 0x1, }; #define RK3188_DIV_ACLK_CORE_MASK 0x7 @@ -191,7 +194,10 @@ static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 9, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 8, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "xin24m", "xin32k" }; @@ -753,57 +759,75 @@ static const char *const rk3188_critical_clocks[] __initconst = { "hclk_cpubus" }; -static void __init rk3188_common_clk_init(struct device_node *np) +static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; reg_base = of_iomap(np, 0); if (!reg_base) { pr_err("%s: could not map cru region\n", __func__); - return; + return ERR_PTR(-ENOMEM); } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return ERR_PTR(-ENOMEM); + } - rockchip_clk_register_branches(common_clk_branches, + rockchip_clk_register_branches(ctx, common_clk_branches, ARRAY_SIZE(common_clk_branches)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL); + + return ctx; } static void __init rk3066a_clk_init(struct device_node *np) { - rk3188_common_clk_init(np); - rockchip_clk_register_plls(rk3066_pll_clks, + struct rockchip_clk_provider *ctx; + + ctx = rk3188_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_plls(ctx, rk3066_pll_clks, ARRAY_SIZE(rk3066_pll_clks), RK3066_GRF_SOC_STATUS); - rockchip_clk_register_branches(rk3066a_clk_branches, + rockchip_clk_register_branches(ctx, rk3066a_clk_branches, ARRAY_SIZE(rk3066a_clk_branches)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3066_cpuclk_data, rk3066_cpuclk_rates, ARRAY_SIZE(rk3066_cpuclk_rates)); rockchip_clk_protect_critical(rk3188_critical_clocks, ARRAY_SIZE(rk3188_critical_clocks)); + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); static void __init rk3188a_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; struct clk *clk1, *clk2; unsigned long rate; int ret; - rk3188_common_clk_init(np); - rockchip_clk_register_plls(rk3188_pll_clks, + ctx = rk3188_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_plls(ctx, rk3188_pll_clks, ARRAY_SIZE(rk3188_pll_clks), RK3188_GRF_SOC_STATUS); - rockchip_clk_register_branches(rk3188_clk_branches, + rockchip_clk_register_branches(ctx, rk3188_clk_branches, ARRAY_SIZE(rk3188_clk_branches)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3188_cpuclk_data, rk3188_cpuclk_rates, ARRAY_SIZE(rk3188_cpuclk_rates)); @@ -827,6 +851,7 @@ static void __init rk3188a_clk_init(struct device_node *np) rockchip_clk_protect_critical(rk3188_critical_clocks, ARRAY_SIZE(rk3188_critical_clocks)); + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c index 7702d2855e9c..016bdb0b793a 100644 --- a/drivers/clk/rockchip/clk-rk3228.c +++ b/drivers/clk/rockchip/clk-rk3228.c @@ -111,7 +111,10 @@ static const struct rockchip_cpuclk_reg_data rk3228_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 6, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "clk_24m", "xin24m" }; @@ -625,6 +628,7 @@ static const char *const rk3228_critical_clocks[] __initconst = { static void __init rk3228_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; reg_base = of_iomap(np, 0); @@ -633,17 +637,22 @@ static void __init rk3228_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } - rockchip_clk_register_plls(rk3228_pll_clks, + rockchip_clk_register_plls(ctx, rk3228_pll_clks, ARRAY_SIZE(rk3228_pll_clks), RK3228_GRF_SOC_STATUS0); - rockchip_clk_register_branches(rk3228_clk_branches, + rockchip_clk_register_branches(ctx, rk3228_clk_branches, ARRAY_SIZE(rk3228_clk_branches)); rockchip_clk_protect_critical(rk3228_critical_clocks, ARRAY_SIZE(rk3228_critical_clocks)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3228_cpuclk_data, rk3228_cpuclk_rates, ARRAY_SIZE(rk3228_cpuclk_rates)); @@ -651,6 +660,8 @@ static void __init rk3228_clk_init(struct device_node *np) rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK3228_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK3228_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 3cb72163a512..39af05a589b3 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -165,7 +165,10 @@ static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = { .core_reg = RK3288_CLKSEL_CON(0), .div_core_shift = 8, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 15, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "xin24m", "xin32k" }; @@ -878,6 +881,7 @@ static struct syscore_ops rk3288_clk_syscore_ops = { static void __init rk3288_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; struct clk *clk; rk3288_cru_base = of_iomap(np, 0); @@ -886,7 +890,12 @@ static void __init rk3288_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(rk3288_cru_base); + return; + } /* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */ clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1); @@ -894,17 +903,17 @@ static void __init rk3288_clk_init(struct device_node *np) pr_warn("%s: could not register clock pclk_wdt: %ld\n", __func__, PTR_ERR(clk)); else - rockchip_clk_add_lookup(clk, PCLK_WDT); + rockchip_clk_add_lookup(ctx, clk, PCLK_WDT); - rockchip_clk_register_plls(rk3288_pll_clks, + rockchip_clk_register_plls(ctx, rk3288_pll_clks, ARRAY_SIZE(rk3288_pll_clks), RK3288_GRF_SOC_STATUS1); - rockchip_clk_register_branches(rk3288_clk_branches, + rockchip_clk_register_branches(ctx, rk3288_clk_branches, ARRAY_SIZE(rk3288_clk_branches)); rockchip_clk_protect_critical(rk3288_critical_clocks, ARRAY_SIZE(rk3288_critical_clocks)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3288_cpuclk_data, rk3288_cpuclk_rates, ARRAY_SIZE(rk3288_cpuclk_rates)); @@ -913,8 +922,10 @@ static void __init rk3288_clk_init(struct device_node *np) rk3288_cru_base + RK3288_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK3288_GLB_SRST_FST, + rockchip_register_restart_notifier(ctx, RK3288_GLB_SRST_FST, rk3288_clk_shutdown); register_syscore_ops(&rk3288_clk_syscore_ops); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c index a2bb12200465..6cb474c593e7 100644 --- a/drivers/clk/rockchip/clk-rk3368.c +++ b/drivers/clk/rockchip/clk-rk3368.c @@ -165,14 +165,20 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkb_data = { .core_reg = RK3368_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 7, + .mux_core_mask = 0x1, }; static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = { .core_reg = RK3368_CLKSEL_CON(2), .div_core_shift = 0, + .mux_core_alt = 1, + .mux_core_main = 0, .div_core_mask = 0x1f, .mux_core_shift = 7, + .mux_core_mask = 0x1, }; #define RK3368_DIV_ACLKM_MASK 0x1f @@ -856,6 +862,7 @@ static const char *const rk3368_critical_clocks[] __initconst = { static void __init rk3368_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; struct clk *clk; @@ -865,7 +872,12 @@ static void __init rk3368_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } /* Watchdog pclk is controlled by sgrf_soc_con3[7]. */ clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1); @@ -873,22 +885,22 @@ static void __init rk3368_clk_init(struct device_node *np) pr_warn("%s: could not register clock pclk_wdt: %ld\n", __func__, PTR_ERR(clk)); else - rockchip_clk_add_lookup(clk, PCLK_WDT); + rockchip_clk_add_lookup(ctx, clk, PCLK_WDT); - rockchip_clk_register_plls(rk3368_pll_clks, + rockchip_clk_register_plls(ctx, rk3368_pll_clks, ARRAY_SIZE(rk3368_pll_clks), RK3368_GRF_SOC_STATUS0); - rockchip_clk_register_branches(rk3368_clk_branches, + rockchip_clk_register_branches(ctx, rk3368_clk_branches, ARRAY_SIZE(rk3368_clk_branches)); rockchip_clk_protect_critical(rk3368_critical_clocks, ARRAY_SIZE(rk3368_critical_clocks)); - rockchip_clk_register_armclk(ARMCLKB, "armclkb", + rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb", mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p), &rk3368_cpuclkb_data, rk3368_cpuclkb_rates, ARRAY_SIZE(rk3368_cpuclkb_rates)); - rockchip_clk_register_armclk(ARMCLKL, "armclkl", + rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl", mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p), &rk3368_cpuclkl_data, rk3368_cpuclkl_rates, ARRAY_SIZE(rk3368_cpuclkl_rates)); @@ -896,6 +908,8 @@ static void __init rk3368_clk_init(struct device_node *np) rockchip_register_softrst(np, 15, reg_base + RK3368_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK3368_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK3368_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3368_cru, "rockchip,rk3368-cru", rk3368_clk_init); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index ec06350c78c4..277f9270bf72 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -2,6 +2,9 @@ * Copyright (c) 2014 MundoReader S.L. * Author: Heiko Stuebner <heiko@sntech.de> * + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Xing Zheng <zhengxing@rock-chips.com> + * * based on * * samsung/clk.c @@ -157,7 +160,8 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, return notifier_from_errno(ret); } -static struct clk *rockchip_clk_register_frac_branch(const char *name, +static struct clk *rockchip_clk_register_frac_branch( + struct rockchip_clk_provider *ctx, const char *name, const char *const *parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 div_flags, int gate_offset, u8 gate_shift, u8 gate_flags, @@ -250,7 +254,7 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name, if (IS_ERR(mux_clk)) return clk; - rockchip_clk_add_lookup(mux_clk, child->id); + rockchip_clk_add_lookup(ctx, mux_clk, child->id); /* notifier on the fraction divider to catch rate changes */ if (frac->mux_frac_idx >= 0) { @@ -314,66 +318,92 @@ static struct clk *rockchip_clk_register_factor_branch(const char *name, return clk; } -static DEFINE_SPINLOCK(clk_lock); -static struct clk **clk_table; -static void __iomem *reg_base; -static struct clk_onecell_data clk_data; -static struct device_node *cru_node; -static struct regmap *grf; - -void __init rockchip_clk_init(struct device_node *np, void __iomem *base, - unsigned long nr_clks) +struct rockchip_clk_provider * __init rockchip_clk_init(struct device_node *np, + void __iomem *base, unsigned long nr_clks) { - reg_base = base; - cru_node = np; - grf = ERR_PTR(-EPROBE_DEFER); + struct rockchip_clk_provider *ctx; + struct clk **clk_table; + int i; + + ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL); + if (!ctx) { + pr_err("%s: Could not allocate clock provider context\n", + __func__); + return ERR_PTR(-ENOMEM); + } clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); - if (!clk_table) - pr_err("%s: could not allocate clock lookup table\n", __func__); + if (!clk_table) { + pr_err("%s: Could not allocate clock lookup table\n", + __func__); + goto err_free; + } - clk_data.clks = clk_table; - clk_data.clk_num = nr_clks; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + for (i = 0; i < nr_clks; ++i) + clk_table[i] = ERR_PTR(-ENOENT); + + ctx->reg_base = base; + ctx->clk_data.clks = clk_table; + ctx->clk_data.clk_num = nr_clks; + ctx->cru_node = np; + ctx->grf = ERR_PTR(-EPROBE_DEFER); + spin_lock_init(&ctx->lock); + + return ctx; + +err_free: + kfree(ctx); + return ERR_PTR(-ENOMEM); +} + +void __init rockchip_clk_of_add_provider(struct device_node *np, + struct rockchip_clk_provider *ctx) +{ + if (of_clk_add_provider(np, of_clk_src_onecell_get, + &ctx->clk_data)) + pr_err("%s: could not register clk provider\n", __func__); } -struct regmap *rockchip_clk_get_grf(void) +struct regmap *rockchip_clk_get_grf(struct rockchip_clk_provider *ctx) { - if (IS_ERR(grf)) - grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf"); - return grf; + if (IS_ERR(ctx->grf)) + ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, "rockchip,grf"); + return ctx->grf; } -void rockchip_clk_add_lookup(struct clk *clk, unsigned int id) +void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, + struct clk *clk, unsigned int id) { - if (clk_table && id) - clk_table[id] = clk; + if (ctx->clk_data.clks && id) + ctx->clk_data.clks[id] = clk; } -void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list, +void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, + struct rockchip_pll_clock *list, unsigned int nr_pll, int grf_lock_offset) { struct clk *clk; int idx; for (idx = 0; idx < nr_pll; idx++, list++) { - clk = rockchip_clk_register_pll(list->type, list->name, + clk = rockchip_clk_register_pll(ctx, list->type, list->name, list->parent_names, list->num_parents, - reg_base, list->con_offset, grf_lock_offset, + list->con_offset, grf_lock_offset, list->lock_shift, list->mode_offset, list->mode_shift, list->rate_table, - list->pll_flags, &clk_lock); + list->pll_flags); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, list->name); continue; } - rockchip_clk_add_lookup(clk, list->id); + rockchip_clk_add_lookup(ctx, clk, list->id); } } void __init rockchip_clk_register_branches( + struct rockchip_clk_provider *ctx, struct rockchip_clk_branch *list, unsigned int nr_clk) { @@ -389,56 +419,56 @@ void __init rockchip_clk_register_branches( case branch_mux: clk = clk_register_mux(NULL, list->name, list->parent_names, list->num_parents, - flags, reg_base + list->muxdiv_offset, + flags, ctx->reg_base + list->muxdiv_offset, list->mux_shift, list->mux_width, - list->mux_flags, &clk_lock); + list->mux_flags, &ctx->lock); break; case branch_divider: if (list->div_table) clk = clk_register_divider_table(NULL, list->name, list->parent_names[0], - flags, reg_base + list->muxdiv_offset, + flags, ctx->reg_base + list->muxdiv_offset, list->div_shift, list->div_width, list->div_flags, list->div_table, - &clk_lock); + &ctx->lock); else clk = clk_register_divider(NULL, list->name, list->parent_names[0], flags, - reg_base + list->muxdiv_offset, + ctx->reg_base + list->muxdiv_offset, list->div_shift, list->div_width, - list->div_flags, &clk_lock); + list->div_flags, &ctx->lock); break; case branch_fraction_divider: - clk = rockchip_clk_register_frac_branch(list->name, + clk = rockchip_clk_register_frac_branch(ctx, list->name, list->parent_names, list->num_parents, - reg_base, list->muxdiv_offset, list->div_flags, + ctx->reg_base, list->muxdiv_offset, list->div_flags, list->gate_offset, list->gate_shift, list->gate_flags, flags, list->child, - &clk_lock); + &ctx->lock); break; case branch_gate: flags |= CLK_SET_RATE_PARENT; clk = clk_register_gate(NULL, list->name, list->parent_names[0], flags, - reg_base + list->gate_offset, - list->gate_shift, list->gate_flags, &clk_lock); + ctx->reg_base + list->gate_offset, + list->gate_shift, list->gate_flags, &ctx->lock); break; case branch_composite: clk = rockchip_clk_register_branch(list->name, list->parent_names, list->num_parents, - reg_base, list->muxdiv_offset, list->mux_shift, + ctx->reg_base, list->muxdiv_offset, list->mux_shift, list->mux_width, list->mux_flags, list->div_shift, list->div_width, list->div_flags, list->div_table, list->gate_offset, list->gate_shift, - list->gate_flags, flags, &clk_lock); + list->gate_flags, flags, &ctx->lock); break; case branch_mmc: clk = rockchip_clk_register_mmc( list->name, list->parent_names, list->num_parents, - reg_base + list->muxdiv_offset, + ctx->reg_base + list->muxdiv_offset, list->div_shift ); break; @@ -446,16 +476,16 @@ void __init rockchip_clk_register_branches( clk = rockchip_clk_register_inverter( list->name, list->parent_names, list->num_parents, - reg_base + list->muxdiv_offset, - list->div_shift, list->div_flags, &clk_lock); + ctx->reg_base + list->muxdiv_offset, + list->div_shift, list->div_flags, &ctx->lock); break; case branch_factor: clk = rockchip_clk_register_factor_branch( list->name, list->parent_names, - list->num_parents, reg_base, + list->num_parents, ctx->reg_base, list->div_shift, list->div_width, list->gate_offset, list->gate_shift, - list->gate_flags, flags, &clk_lock); + list->gate_flags, flags, &ctx->lock); break; } @@ -472,11 +502,12 @@ void __init rockchip_clk_register_branches( continue; } - rockchip_clk_add_lookup(clk, list->id); + rockchip_clk_add_lookup(ctx, clk, list->id); } } -void __init rockchip_clk_register_armclk(unsigned int lookup_id, +void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, @@ -486,15 +517,15 @@ void __init rockchip_clk_register_armclk(unsigned int lookup_id, struct clk *clk; clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, - reg_data, rates, nrates, reg_base, - &clk_lock); + reg_data, rates, nrates, ctx->reg_base, + &ctx->lock); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s: %ld\n", __func__, name, PTR_ERR(clk)); return; } - rockchip_clk_add_lookup(clk, lookup_id); + rockchip_clk_add_lookup(ctx, clk, lookup_id); } void __init rockchip_clk_protect_critical(const char *const clocks[], @@ -511,6 +542,7 @@ void __init rockchip_clk_protect_critical(const char *const clocks[], } } +static void __iomem *rst_base; static unsigned int reg_restart; static void (*cb_restart)(void); static int rockchip_restart_notify(struct notifier_block *this, @@ -519,7 +551,7 @@ static int rockchip_restart_notify(struct notifier_block *this, if (cb_restart) cb_restart(); - writel(0xfdb9, reg_base + reg_restart); + writel(0xfdb9, rst_base + reg_restart); return NOTIFY_DONE; } @@ -528,10 +560,12 @@ static struct notifier_block rockchip_restart_handler = { .priority = 128, }; -void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void)) +void __init rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, + unsigned int reg, void (*cb)(void)) { int ret; + rst_base = ctx->reg_base; reg_restart = reg; cb_restart = cb; ret = register_restart_handler(&rockchip_restart_handler); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 39c198bbcbee..cb6a63963693 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -27,6 +27,7 @@ #define CLK_ROCKCHIP_CLK_H #include <linux/io.h> +#include <linux/clk-provider.h> struct clk; @@ -95,6 +96,7 @@ struct clk; enum rockchip_pll_type { pll_rk3036, pll_rk3066, + pll_rk3399, }; #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ @@ -127,13 +129,29 @@ enum rockchip_pll_type { .nb = _nb, \ } +/** + * struct rockchip_clk_provider - information about clock provider + * @reg_base: virtual address for the register base. + * @clk_data: holds clock related data like clk* and number of clocks. + * @cru_node: device-node of the clock-provider + * @grf: regmap of the general-register-files syscon + * @lock: maintains exclusion between callbacks for a given clock-provider. + */ +struct rockchip_clk_provider { + void __iomem *reg_base; + struct clk_onecell_data clk_data; + struct device_node *cru_node; + struct regmap *grf; + spinlock_t lock; +}; + struct rockchip_pll_rate_table { unsigned long rate; unsigned int nr; unsigned int nf; unsigned int no; unsigned int nb; - /* for RK3036 */ + /* for RK3036/RK3399 */ unsigned int fbdiv; unsigned int postdiv1; unsigned int refdiv; @@ -143,10 +161,11 @@ struct rockchip_pll_rate_table { }; /** - * struct rockchip_pll_clock: information about pll clock + * struct rockchip_pll_clock - information about pll clock * @id: platform specific id of the clock. * @name: name of this pll clock. - * @parent_name: name of the parent clock. + * @parent_names: name of the parent clock. + * @num_parents: number of parents * @flags: optional flags for basic clock. * @con_offset: offset of the register for configuring the PLL. * @mode_offset: offset of the register for configuring the PLL-mode. @@ -194,12 +213,13 @@ struct rockchip_pll_clock { .rate_table = _rtable, \ } -struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, +struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, + enum rockchip_pll_type pll_type, 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 reg_mode, - int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags, spinlock_t *lock); + u8 num_parents, 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); struct rockchip_cpuclk_clksel { int reg; @@ -213,18 +233,23 @@ struct rockchip_cpuclk_rate_table { }; /** - * struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock + * struct rockchip_cpuclk_reg_data - describes register offsets and masks of the cpuclock * @core_reg: register offset of the core settings register * @div_core_shift: core divider offset used to divide the pll value * @div_core_mask: core divider mask + * @mux_core_alt: mux value to select alternate parent + * @mux_core_main: mux value to select main parent of core * @mux_core_shift: offset of the core multiplexer + * @mux_core_mask: core multiplexer mask */ struct rockchip_cpuclk_reg_data { int core_reg; u8 div_core_shift; u32 div_core_mask; - int mux_core_reg; + u8 mux_core_alt; + u8 mux_core_main; u8 mux_core_shift; + u32 mux_core_mask; }; struct clk *rockchip_clk_register_cpuclk(const char *name, @@ -428,6 +453,22 @@ struct rockchip_clk_branch { .child = ch, \ } +#define COMPOSITE_FRACMUX_NOGATE(_id, cname, pname, f, mo, df, ch) \ + { \ + .id = _id, \ + .branch_type = branch_fraction_divider, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .num_parents = 1, \ + .flags = f, \ + .muxdiv_offset = mo, \ + .div_shift = 16, \ + .div_width = 16, \ + .div_flags = df, \ + .gate_offset = -1, \ + .child = ch, \ + } + #define MUX(_id, cname, pnames, f, o, s, w, mf) \ { \ .id = _id, \ @@ -536,21 +577,28 @@ struct rockchip_clk_branch { .gate_flags = gf, \ } -void rockchip_clk_init(struct device_node *np, void __iomem *base, - unsigned long nr_clks); -struct regmap *rockchip_clk_get_grf(void); -void rockchip_clk_add_lookup(struct clk *clk, unsigned int id); -void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, +struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, + void __iomem *base, unsigned long nr_clks); +void rockchip_clk_of_add_provider(struct device_node *np, + struct rockchip_clk_provider *ctx); +struct regmap *rockchip_clk_get_grf(struct rockchip_clk_provider *ctx); +void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, + struct clk *clk, unsigned int id); +void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *list, unsigned int nr_clk); -void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, +void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, + struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); -void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, +void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates); void rockchip_clk_protect_critical(const char *const clocks[], int nclocks); -void rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void)); +void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, + unsigned int reg, void (*cb)(void)); #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) |