From the definition of register, The i2s_bclk is a non-linear,
discrete divider clock.
The following table shows the correspondence between index
and frequency division coefficients:
| index | div |
|-------|-------|
| 0 | 2 |
| 1 | 4 |
| 2 | 6 |
| 3 | 8 |
But from a software perspective and this table, dividing the
actual div value by 2 is sufficient to obtain a continuous
divider clock.
Rather than introducing a new clock type to handle this case,
a factor parameter has been added to CCU_DIV_GATE_DEFINE.
Suggested-by: Haylen Chu <heylenay@4d2.org>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
drivers/clk/spacemit/ccu_mix.c | 7 ++++++-
drivers/clk/spacemit/ccu_mix.h | 4 +++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c
index 9b852aa61f78aed5256bfe6fc3b01932d6db6256..dbd2cf234bf81d8e110b19868ff9af7373e2ab55 100644
--- a/drivers/clk/spacemit/ccu_mix.c
+++ b/drivers/clk/spacemit/ccu_mix.c
@@ -56,7 +56,10 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
val = ccu_read(&mix->common, ctrl) >> div->shift;
val &= (1 << div->width) - 1;
- return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width);
+ if (!div->factor)
+ return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width);
+
+ return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width) / div->factor;
}
/*
@@ -115,6 +118,8 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long rate,
for (int j = 1; j <= div_max; j++) {
unsigned long tmp = DIV_ROUND_CLOSEST_ULL(parent_rate, j);
+ if (mix->div.factor)
+ tmp /= mix->div.factor;
if (abs(tmp - rate) < abs(best_rate - rate)) {
best_rate = tmp;
diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h
index 54d40cd39b2752260f57d2a96eb8d3eed8116ecd..7dd00d24ec4b1dab70663b9cb7b9ebb02abeaecb 100644
--- a/drivers/clk/spacemit/ccu_mix.h
+++ b/drivers/clk/spacemit/ccu_mix.h
@@ -34,6 +34,7 @@ struct ccu_mux_config {
struct ccu_div_config {
u8 shift;
u8 width;
+ unsigned int factor;
};
struct ccu_mix {
@@ -130,10 +131,11 @@ static struct ccu_mix _name = { \
}
#define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, \
- _mask_gate, _flags) \
+ _mask_gate, _factor, _flags) \
static struct ccu_mix _name = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.div = CCU_DIV_INIT(_shift, _width), \
+ .div.factor = _factor, \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_gate_ops, \
--
2.50.1
On Mon, Aug 11, 2025 at 10:04:29PM +0800, Troy Mitchell wrote: > From the definition of register, The i2s_bclk is a non-linear, > discrete divider clock. > > The following table shows the correspondence between index > and frequency division coefficients: > > | index | div | > |-------|-------| > | 0 | 2 | > | 1 | 4 | > | 2 | 6 | > | 3 | 8 | > > But from a software perspective and this table, dividing the > actual div value by 2 is sufficient to obtain a continuous > divider clock. > > Rather than introducing a new clock type to handle this case, > a factor parameter has been added to CCU_DIV_GATE_DEFINE. Actually, I expected you to represent the factor simply with CCU_FACTOR_DEFINE, introducing no new clock-calculation code... > Suggested-by: Haylen Chu <heylenay@4d2.org> > Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com> > --- > drivers/clk/spacemit/ccu_mix.c | 7 ++++++- > drivers/clk/spacemit/ccu_mix.h | 4 +++- > 2 files changed, 9 insertions(+), 2 deletions(-) > > diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c > index 9b852aa61f78aed5256bfe6fc3b01932d6db6256..dbd2cf234bf81d8e110b19868ff9af7373e2ab55 100644 > --- a/drivers/clk/spacemit/ccu_mix.c > +++ b/drivers/clk/spacemit/ccu_mix.c > @@ -56,7 +56,10 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, > val = ccu_read(&mix->common, ctrl) >> div->shift; > val &= (1 << div->width) - 1; > > - return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width); > + if (!div->factor) > + return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width); Please adapt all div-related macros to make them assign one to the factor, which helps you get rid of this if and the one in ccu_mix_calc_best_rate(). > + return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width) / div->factor; > } > > /* > @@ -115,6 +118,8 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long rate, > > for (int j = 1; j <= div_max; j++) { > unsigned long tmp = DIV_ROUND_CLOSEST_ULL(parent_rate, j); > + if (mix->div.factor) ---- this if ------ > + tmp /= mix->div.factor; > > if (abs(tmp - rate) < abs(best_rate - rate)) { > best_rate = tmp; > diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h > index 54d40cd39b2752260f57d2a96eb8d3eed8116ecd..7dd00d24ec4b1dab70663b9cb7b9ebb02abeaecb 100644 > --- a/drivers/clk/spacemit/ccu_mix.h > +++ b/drivers/clk/spacemit/ccu_mix.h > @@ -34,6 +34,7 @@ struct ccu_mux_config { > struct ccu_div_config { > u8 shift; > u8 width; > + unsigned int factor; > }; > > struct ccu_mix { > @@ -130,10 +131,11 @@ static struct ccu_mix _name = { \ > } > > #define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, \ > - _mask_gate, _flags) \ > + _mask_gate, _factor, _flags) \ This isn't that consistent: why could only divider-gate come with a factor? This is another reason why I think representing the factor separately with the CCU_FACTOR_DEFINE() macro is better. > static struct ccu_mix _name = { \ > .gate = CCU_GATE_INIT(_mask_gate), \ > .div = CCU_DIV_INIT(_shift, _width), \ > + .div.factor = _factor, \ > .common = { \ > .reg_ctrl = _reg_ctrl, \ > CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_gate_ops, \ > > -- > 2.50.1 > Best regards, Haylen Chu
On Mon, Aug 18, 2025 at 02:20:03AM +0000, Haylen Chu wrote: > On Mon, Aug 11, 2025 at 10:04:29PM +0800, Troy Mitchell wrote: > > From the definition of register, The i2s_bclk is a non-linear, > > discrete divider clock. > > > > The following table shows the correspondence between index > > and frequency division coefficients: > > > > | index | div | > > |-------|-------| > > | 0 | 2 | > > | 1 | 4 | > > | 2 | 6 | > > | 3 | 8 | > > > > But from a software perspective and this table, dividing the > > actual div value by 2 is sufficient to obtain a continuous > > divider clock. > > > > Rather than introducing a new clock type to handle this case, > > a factor parameter has been added to CCU_DIV_GATE_DEFINE. > > Actually, I expected you to represent the factor simply with > CCU_FACTOR_DEFINE, introducing no new clock-calculation code... I'll change it in the next version. > > > Suggested-by: Haylen Chu <heylenay@4d2.org> > > Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com> > > --- > > drivers/clk/spacemit/ccu_mix.c | 7 ++++++- > > drivers/clk/spacemit/ccu_mix.h | 4 +++- > > 2 files changed, 9 insertions(+), 2 deletions(-) > > > > diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c > > index 9b852aa61f78aed5256bfe6fc3b01932d6db6256..dbd2cf234bf81d8e110b19868ff9af7373e2ab55 100644 > > --- a/drivers/clk/spacemit/ccu_mix.c > > +++ b/drivers/clk/spacemit/ccu_mix.c > > @@ -56,7 +56,10 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, > > val = ccu_read(&mix->common, ctrl) >> div->shift; > > val &= (1 << div->width) - 1; > > > > - return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width); > > + if (!div->factor) > > + return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width); > > Please adapt all div-related macros to make them assign one to the > factor, which helps you get rid of this if and the one in > ccu_mix_calc_best_rate(). > > > + return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width) / div->factor; > > } > > > > /* > > @@ -115,6 +118,8 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long rate, > > > > for (int j = 1; j <= div_max; j++) { > > unsigned long tmp = DIV_ROUND_CLOSEST_ULL(parent_rate, j); > > + if (mix->div.factor) > ---- this if ------ > > > + tmp /= mix->div.factor; > > > > if (abs(tmp - rate) < abs(best_rate - rate)) { > > best_rate = tmp; > > diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h > > index 54d40cd39b2752260f57d2a96eb8d3eed8116ecd..7dd00d24ec4b1dab70663b9cb7b9ebb02abeaecb 100644 > > --- a/drivers/clk/spacemit/ccu_mix.h > > +++ b/drivers/clk/spacemit/ccu_mix.h > > @@ -34,6 +34,7 @@ struct ccu_mux_config { > > struct ccu_div_config { > > u8 shift; > > u8 width; > > + unsigned int factor; > > }; > > > > struct ccu_mix { > > @@ -130,10 +131,11 @@ static struct ccu_mix _name = { \ > > } > > > > #define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, \ > > - _mask_gate, _flags) \ > > + _mask_gate, _factor, _flags) \ > > This isn't that consistent: why could only divider-gate come with a > factor? This is another reason why I think representing the factor > separately with the CCU_FACTOR_DEFINE() macro is better. > > > static struct ccu_mix _name = { \ > > .gate = CCU_GATE_INIT(_mask_gate), \ > > .div = CCU_DIV_INIT(_shift, _width), \ > > + .div.factor = _factor, \ > > .common = { \ > > .reg_ctrl = _reg_ctrl, \ > > CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_gate_ops, \ > > > > -- > > 2.50.1 > > > > Best regards, > Haylen Chu > > _______________________________________________ > linux-riscv mailing list > linux-riscv@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-riscv
© 2016 - 2025 Red Hat, Inc.