diff options
author | Paul Cercueil <paul@crapouillou.net> | 2021-05-30 17:49:19 +0100 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2021-06-27 19:49:17 -0700 |
commit | 249592bf6d5d52cacdc2f5a07f23368fc1b11324 (patch) | |
tree | 037c1a9a90e52d1d249d83c6996dd65214f92794 /drivers/clk/ingenic/cgu.c | |
parent | 2e1ae04f7fe049bb012c273e5281a3c145924ea1 (diff) | |
download | lwn-249592bf6d5d52cacdc2f5a07f23368fc1b11324.tar.gz lwn-249592bf6d5d52cacdc2f5a07f23368fc1b11324.zip |
clk: Support bypassing dividers
When a clock is declared as both CGU_CLK_DIV and CGU_CLK_MUX, the CGU
code expects the mux to be applied first, the divider second.
On the JZ4760, and maybe on some other SoCs, some clocks also have a mux
setting and a divider, but the divider is not applied to all parents
selectable from the mux.
This could be solved by creating two clocks, one with CGU_CLK_DIV and
one with CGU_CLK_MUX, but that would increase the number of clocks.
Instead, add a 8-bit mask to CGU_CLK_DIV clocks. If the bit
corresponding to the parent clock's index is set, the divider is
bypassed.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Link: https://lore.kernel.org/r/20210530164923.18134-3-paul@crapouillou.net
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Diffstat (limited to 'drivers/clk/ingenic/cgu.c')
-rw-r--r-- | drivers/clk/ingenic/cgu.c | 33 |
1 files changed, 22 insertions, 11 deletions
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index c8e9cb6c8e39..0619d45a950c 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -369,18 +369,23 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) struct ingenic_cgu *cgu = ingenic_clk->cgu; unsigned long rate = parent_rate; u32 div_reg, div; + u8 parent; if (clk_info->type & CGU_CLK_DIV) { - div_reg = readl(cgu->base + clk_info->div.reg); - div = (div_reg >> clk_info->div.shift) & - GENMASK(clk_info->div.bits - 1, 0); + parent = ingenic_clk_get_parent(hw); - if (clk_info->div.div_table) - div = clk_info->div.div_table[div]; - else - div = (div + 1) * clk_info->div.div; + if (!(clk_info->div.bypass_mask & BIT(parent))) { + div_reg = readl(cgu->base + clk_info->div.reg); + div = (div_reg >> clk_info->div.shift) & + GENMASK(clk_info->div.bits - 1, 0); + + if (clk_info->div.div_table) + div = clk_info->div.div_table[div]; + else + div = (div + 1) * clk_info->div.div; - rate /= div; + rate /= div; + } } else if (clk_info->type & CGU_CLK_FIXDIV) { rate /= clk_info->fixdiv.div; } @@ -410,10 +415,16 @@ ingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info, } static unsigned -ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, +ingenic_clk_calc_div(struct clk_hw *hw, + const struct ingenic_cgu_clk_info *clk_info, unsigned long parent_rate, unsigned long req_rate) { unsigned int div, hw_div; + u8 parent; + + parent = ingenic_clk_get_parent(hw); + if (clk_info->div.bypass_mask & BIT(parent)) + return 1; /* calculate the divide */ div = DIV_ROUND_UP(parent_rate, req_rate); @@ -448,7 +459,7 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate, unsigned int div = 1; if (clk_info->type & CGU_CLK_DIV) - div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); + div = ingenic_clk_calc_div(hw, clk_info, *parent_rate, req_rate); else if (clk_info->type & CGU_CLK_FIXDIV) div = clk_info->fixdiv.div; else if (clk_hw_can_set_rate_parent(hw)) @@ -480,7 +491,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, int ret = 0; if (clk_info->type & CGU_CLK_DIV) { - div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); + div = ingenic_clk_calc_div(hw, clk_info, parent_rate, req_rate); rate = DIV_ROUND_UP(parent_rate, div); if (rate != req_rate) |