|
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] = { \ |
|
55 | 62 | .parents = { [0] = _parent }, \ |
56 | 63 | } |
57 | 64 |
|
| 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 | + |
58 | 72 | #define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ |
59 | 73 | .name = _name, \ |
60 | 74 | .flags = 0, \ |
@@ -225,7 +239,7 @@ static const struct { |
225 | 239 | JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, |
226 | 240 | JH7100_CLK_OSC_SYS, |
227 | 241 | 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), |
229 | 243 | JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), |
230 | 244 | JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), |
231 | 245 | 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, |
440 | 454 | return 0; |
441 | 455 | } |
442 | 456 |
|
| 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 | + |
443 | 500 | static u8 jh7100_clk_get_parent(struct clk_hw *hw) |
444 | 501 | { |
445 | 502 | struct jh7100_clk *clk = jh7100_clk_from(hw); |
@@ -526,6 +583,13 @@ static const struct clk_ops jh7100_clk_div_ops = { |
526 | 583 | .debug_init = jh7100_clk_debug_init, |
527 | 584 | }; |
528 | 585 |
|
| 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 | + |
529 | 593 | static const struct clk_ops jh7100_clk_gdiv_ops = { |
530 | 594 | .enable = jh7100_clk_enable, |
531 | 595 | .disable = jh7100_clk_disable, |
@@ -564,6 +628,8 @@ static const struct clk_ops *__init jh7100_clk_ops(u32 max) |
564 | 628 | if (max & JH7100_CLK_DIV_MASK) { |
565 | 629 | if (max & JH7100_CLK_ENABLE) |
566 | 630 | return &jh7100_clk_gdiv_ops; |
| 631 | + if (max == JH7100_CLK_FRAC_MAX) |
| 632 | + return &jh7100_clk_fdiv_ops; |
567 | 633 | return &jh7100_clk_div_ops; |
568 | 634 | } |
569 | 635 |
|
|
0 commit comments