Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 4210be668a09 ("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>

authored by

Emil Renner Berthing and committed by
Stephen Boyd
73bfc8d7 40dda353

+67 -1
+67 -1
drivers/clk/starfive/clk-starfive-jh7100.c
··· 32 32 #define JH7100_CLK_MUX_MASK GENMASK(27, 24) 33 33 #define JH7100_CLK_MUX_SHIFT 24 34 34 #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 35 42 36 43 /* clock data */ 37 44 #define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ ··· 59 52 .name = _name, \ 60 53 .flags = _flags, \ 61 54 .max = JH7100_CLK_ENABLE | (_max), \ 55 + .parents = { [0] = _parent }, \ 56 + } 57 + 58 + #define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ 59 + .name = _name, \ 60 + .flags = 0, \ 61 + .max = JH7100_CLK_FRAC_MAX, \ 62 62 .parents = { [0] = _parent }, \ 63 63 } 64 64 ··· 239 225 JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, 240 226 JH7100_CLK_OSC_SYS, 241 227 JH7100_CLK_USBPHY_PLLDIV25M), 242 - JH7100__DIV(JH7100_CLK_AUDIO_DIV, "audio_div", 131072, JH7100_CLK_AUDIO_ROOT), 228 + JH7100_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), 243 229 JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), 244 230 JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), 245 231 JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), ··· 454 440 return 0; 455 441 } 456 442 443 + static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, 444 + unsigned long parent_rate) 445 + { 446 + struct jh7100_clk *clk = jh7100_clk_from(hw); 447 + u32 reg = jh7100_clk_reg_get(clk); 448 + unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + 449 + ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); 450 + 451 + return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; 452 + } 453 + 454 + static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, 455 + struct clk_rate_request *req) 456 + { 457 + unsigned long parent100 = 100 * req->best_parent_rate; 458 + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); 459 + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), 460 + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); 461 + unsigned long result = parent100 / div100; 462 + 463 + /* clamp the result as in jh7100_clk_determine_rate() above */ 464 + if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) 465 + result = parent100 / (div100 + 1); 466 + if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) 467 + result = parent100 / (div100 - 1); 468 + 469 + req->rate = result; 470 + return 0; 471 + } 472 + 473 + static int jh7100_clk_frac_set_rate(struct clk_hw *hw, 474 + unsigned long rate, 475 + unsigned long parent_rate) 476 + { 477 + struct jh7100_clk *clk = jh7100_clk_from(hw); 478 + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), 479 + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); 480 + u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); 481 + 482 + jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); 483 + return 0; 484 + } 485 + 457 486 static u8 jh7100_clk_get_parent(struct clk_hw *hw) 458 487 { 459 488 struct jh7100_clk *clk = jh7100_clk_from(hw); ··· 583 526 .debug_init = jh7100_clk_debug_init, 584 527 }; 585 528 529 + static const struct clk_ops jh7100_clk_fdiv_ops = { 530 + .recalc_rate = jh7100_clk_frac_recalc_rate, 531 + .determine_rate = jh7100_clk_frac_determine_rate, 532 + .set_rate = jh7100_clk_frac_set_rate, 533 + .debug_init = jh7100_clk_debug_init, 534 + }; 535 + 586 536 static const struct clk_ops jh7100_clk_gdiv_ops = { 587 537 .enable = jh7100_clk_enable, 588 538 .disable = jh7100_clk_disable, ··· 628 564 if (max & JH7100_CLK_DIV_MASK) { 629 565 if (max & JH7100_CLK_ENABLE) 630 566 return &jh7100_clk_gdiv_ops; 567 + if (max == JH7100_CLK_FRAC_MAX) 568 + return &jh7100_clk_fdiv_ops; 631 569 return &jh7100_clk_div_ops; 632 570 } 633 571