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>
This commit is contained in:
Paul Cercueil 2021-05-30 17:49:19 +01:00 committed by Stephen Boyd
parent 2e1ae04f7f
commit 249592bf6d
5 changed files with 42 additions and 29 deletions

View file

@ -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);
rate /= div;
if (clk_info->div.div_table)
div = clk_info->div.div_table[div];
else
div = (div + 1) * clk_info->div.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)

View file

@ -84,6 +84,7 @@ struct ingenic_cgu_mux_info {
* isn't one
* @busy_bit: the index of the busy bit within reg, or -1 if there isn't one
* @stop_bit: the index of the stop bit within reg, or -1 if there isn't one
* @bypass_mask: mask of parent clocks for which the divider does not apply
* @div_table: optional table to map the value read from the register to the
* actual divider value
*/
@ -95,6 +96,7 @@ struct ingenic_cgu_div_info {
s8 ce_bit;
s8 busy_bit;
s8 stop_bit;
u8 bypass_mask;
const u8 *div_table;
};

View file

@ -80,7 +80,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
"pll half", CGU_CLK_DIV,
.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1,
CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,
jz4725b_cgu_pll_half_div_table,
},
},
@ -89,7 +89,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
"cclk", CGU_CLK_DIV,
.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
jz4725b_cgu_cpccr_div_table,
},
},
@ -98,7 +98,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
"hclk", CGU_CLK_DIV,
.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
jz4725b_cgu_cpccr_div_table,
},
},
@ -107,7 +107,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
"pclk", CGU_CLK_DIV,
.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
jz4725b_cgu_cpccr_div_table,
},
},
@ -116,7 +116,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
"mclk", CGU_CLK_DIV,
.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
jz4725b_cgu_cpccr_div_table,
},
},
@ -125,7 +125,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
"ipu", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
jz4725b_cgu_cpccr_div_table,
},
.gate = { CGU_REG_CLKGR, 13 },

View file

@ -95,7 +95,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
"pll half", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1,
CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,
jz4740_cgu_pll_half_div_table,
},
},
@ -104,7 +104,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
"cclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
jz4740_cgu_cpccr_div_table,
},
},
@ -113,7 +113,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
"hclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
jz4740_cgu_cpccr_div_table,
},
},
@ -122,7 +122,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
"pclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
jz4740_cgu_cpccr_div_table,
},
},
@ -131,7 +131,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
"mclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
jz4740_cgu_cpccr_div_table,
},
},
@ -140,7 +140,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
"lcd", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
.div = {
CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1,
CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1, 0,
jz4740_cgu_cpccr_div_table,
},
.gate = { CGU_REG_CLKGR, 10 },

View file

@ -152,7 +152,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
"cclk", CGU_CLK_DIV,
.parents = { JZ4770_CLK_PLL0, },
.div = {
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
jz4770_cgu_cpccr_div_table,
},
},
@ -160,7 +160,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
"h0clk", CGU_CLK_DIV,
.parents = { JZ4770_CLK_PLL0, },
.div = {
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
jz4770_cgu_cpccr_div_table,
},
},
@ -168,7 +168,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
"h1clk", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4770_CLK_PLL0, },
.div = {
CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1, 0,
jz4770_cgu_cpccr_div_table,
},
.gate = { CGU_REG_CLKGR1, 7 },
@ -177,7 +177,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
"h2clk", CGU_CLK_DIV,
.parents = { JZ4770_CLK_PLL0, },
.div = {
CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
jz4770_cgu_cpccr_div_table,
},
},
@ -185,7 +185,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
"c1clk", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4770_CLK_PLL0, },
.div = {
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
jz4770_cgu_cpccr_div_table,
},
.gate = { CGU_REG_OPCR, 31, true }, // disable CCLK stop on idle
@ -194,7 +194,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
"pclk", CGU_CLK_DIV,
.parents = { JZ4770_CLK_PLL0, },
.div = {
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1,
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
jz4770_cgu_cpccr_div_table,
},
},