Skip to content

Commit 73bfc8d

Browse files
esmilbebarino
authored andcommitted
clk: starfive: jh7100: Handle audio_div clock properly
It turns out the audio_div clock is a fractional divider where the lowest byte of the ctrl register is the integer part of the divider and the 2nd byte is the number of 100th added to the divider. The children of this clock is used by the audio peripherals for their sample rate clock, so round to the closest possible rate rather than always rounding down like regular dividers. Fixes: 4210be6 ("clk: starfive: Add JH7100 clock generator driver") Signed-off-by: Emil Renner Berthing <kernel@esmil.dk> Link: https://lore.kernel.org/r/20220126173953.1016706-3-kernel@esmil.dk Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent 40dda35 commit 73bfc8d

1 file changed

Lines changed: 67 additions & 1 deletion

File tree

drivers/clk/starfive/clk-starfive-jh7100.c

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@
3232
#define JH7100_CLK_MUX_MASK GENMASK(27, 24)
3333
#define JH7100_CLK_MUX_SHIFT 24
3434
#define JH7100_CLK_DIV_MASK GENMASK(23, 0)
35+
#define JH7100_CLK_FRAC_MASK GENMASK(15, 8)
36+
#define JH7100_CLK_FRAC_SHIFT 8
37+
#define JH7100_CLK_INT_MASK GENMASK(7, 0)
38+
39+
/* fractional divider min/max */
40+
#define JH7100_CLK_FRAC_MIN 100UL
41+
#define JH7100_CLK_FRAC_MAX 25599UL
3542

3643
/* clock data */
3744
#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \
@@ -55,6 +62,13 @@
5562
.parents = { [0] = _parent }, \
5663
}
5764

65+
#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \
66+
.name = _name, \
67+
.flags = 0, \
68+
.max = JH7100_CLK_FRAC_MAX, \
69+
.parents = { [0] = _parent }, \
70+
}
71+
5872
#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \
5973
.name = _name, \
6074
.flags = 0, \
@@ -225,7 +239,7 @@ static const struct {
225239
JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2,
226240
JH7100_CLK_OSC_SYS,
227241
JH7100_CLK_USBPHY_PLLDIV25M),
228-
JH7100__DIV(JH7100_CLK_AUDIO_DIV, "audio_div", 131072, JH7100_CLK_AUDIO_ROOT),
242+
JH7100_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT),
229243
JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV),
230244
JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD),
231245
JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT),
@@ -440,6 +454,49 @@ static int jh7100_clk_set_rate(struct clk_hw *hw,
440454
return 0;
441455
}
442456

457+
static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw,
458+
unsigned long parent_rate)
459+
{
460+
struct jh7100_clk *clk = jh7100_clk_from(hw);
461+
u32 reg = jh7100_clk_reg_get(clk);
462+
unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) +
463+
((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT);
464+
465+
return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0;
466+
}
467+
468+
static int jh7100_clk_frac_determine_rate(struct clk_hw *hw,
469+
struct clk_rate_request *req)
470+
{
471+
unsigned long parent100 = 100 * req->best_parent_rate;
472+
unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate);
473+
unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate),
474+
JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX);
475+
unsigned long result = parent100 / div100;
476+
477+
/* clamp the result as in jh7100_clk_determine_rate() above */
478+
if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX)
479+
result = parent100 / (div100 + 1);
480+
if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN)
481+
result = parent100 / (div100 - 1);
482+
483+
req->rate = result;
484+
return 0;
485+
}
486+
487+
static int jh7100_clk_frac_set_rate(struct clk_hw *hw,
488+
unsigned long rate,
489+
unsigned long parent_rate)
490+
{
491+
struct jh7100_clk *clk = jh7100_clk_from(hw);
492+
unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate),
493+
JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX);
494+
u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100);
495+
496+
jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value);
497+
return 0;
498+
}
499+
443500
static u8 jh7100_clk_get_parent(struct clk_hw *hw)
444501
{
445502
struct jh7100_clk *clk = jh7100_clk_from(hw);
@@ -526,6 +583,13 @@ static const struct clk_ops jh7100_clk_div_ops = {
526583
.debug_init = jh7100_clk_debug_init,
527584
};
528585

586+
static const struct clk_ops jh7100_clk_fdiv_ops = {
587+
.recalc_rate = jh7100_clk_frac_recalc_rate,
588+
.determine_rate = jh7100_clk_frac_determine_rate,
589+
.set_rate = jh7100_clk_frac_set_rate,
590+
.debug_init = jh7100_clk_debug_init,
591+
};
592+
529593
static const struct clk_ops jh7100_clk_gdiv_ops = {
530594
.enable = jh7100_clk_enable,
531595
.disable = jh7100_clk_disable,
@@ -564,6 +628,8 @@ static const struct clk_ops *__init jh7100_clk_ops(u32 max)
564628
if (max & JH7100_CLK_DIV_MASK) {
565629
if (max & JH7100_CLK_ENABLE)
566630
return &jh7100_clk_gdiv_ops;
631+
if (max == JH7100_CLK_FRAC_MAX)
632+
return &jh7100_clk_fdiv_ops;
567633
return &jh7100_clk_div_ops;
568634
}
569635

0 commit comments

Comments
 (0)