summaryrefslogtreecommitdiff
path: root/drivers/clk/ingenic/cgu.c
diff options
context:
space:
mode:
authorPaul Cercueil <paul@crapouillou.net>2021-05-30 17:49:19 +0100
committerStephen Boyd <sboyd@kernel.org>2021-06-27 19:49:17 -0700
commit249592bf6d5d52cacdc2f5a07f23368fc1b11324 (patch)
tree037c1a9a90e52d1d249d83c6996dd65214f92794 /drivers/clk/ingenic/cgu.c
parent2e1ae04f7fe049bb012c273e5281a3c145924ea1 (diff)
downloadlwn-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.c33
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)