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: renesas: rzg2l: Remove DSI clock rate restrictions

Convert the limited MIPI clock calculations to a full range of settings
based on math including H/W limitation validation.
Since the required DSI division setting must be specified from external
sources before calculations, expose a new API to set it.

Signed-off-by: Chris Brandt <chris.brandt@renesas.com>
Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
Tested-by: Biju Das <biju.das.jz@bp.renesas.com>
Reviewed-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Tested-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://patch.msgid.link/20251124131003.992554-2-chris.brandt@renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>

authored by

Chris Brandt and committed by
Geert Uytterhoeven
5a4326f2 879e9fc8

+154 -31
+143 -31
drivers/clk/renesas/rzg2l-cpg.c
··· 22 22 #include <linux/device.h> 23 23 #include <linux/init.h> 24 24 #include <linux/iopoll.h> 25 + #include <linux/math64.h> 25 26 #include <linux/mod_devicetable.h> 26 27 #include <linux/module.h> 27 28 #include <linux/of.h> ··· 74 73 75 74 #define MSTOP_OFF(conf) FIELD_GET(GENMASK(31, 16), (conf)) 76 75 #define MSTOP_MASK(conf) FIELD_GET(GENMASK(15, 0), (conf)) 76 + 77 + #define PLL5_FOUTVCO_MIN 800000000 78 + #define PLL5_FOUTVCO_MAX 3000000000 79 + #define PLL5_POSTDIV_MIN 1 80 + #define PLL5_POSTDIV_MAX 7 81 + #define PLL5_REFDIV_MIN 1 82 + #define PLL5_REFDIV_MAX 2 83 + #define PLL5_INTIN_MIN 20 84 + #define PLL5_INTIN_MAX 320 85 + #define PLL5_HSCLK_MIN 10000000 86 + #define PLL5_HSCLK_MAX 187500000 77 87 78 88 /** 79 89 * struct clk_hw_data - clock hardware data ··· 141 129 u8 pl5_spread; 142 130 }; 143 131 132 + /* PLL5 output will be used for DPI or MIPI-DSI */ 133 + static int dsi_div_target = PLL5_TARGET_DPI; 134 + 135 + /* Required division ratio for MIPI D-PHY clock depending on number of lanes and bpp. */ 136 + static u8 dsi_div_ab_desired; 137 + 144 138 struct rzg2l_pll5_mux_dsi_div_param { 145 139 u8 clksrc; 146 140 u8 dsi_div_a; ··· 187 169 188 170 struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params; 189 171 }; 172 + 173 + static inline u8 rzg2l_cpg_div_ab(u8 a, u8 b) 174 + { 175 + return (b + 1) << a; 176 + } 190 177 191 178 static void rzg2l_cpg_del_clk_provider(void *data) 192 179 { ··· 579 556 return clk_hw->clk; 580 557 } 581 558 559 + /* 560 + * VCO-->[POSTDIV1,2]--FOUTPOSTDIV--------------->| 561 + * | |-->[1/(DSI DIV A * B)]--> MIPI_DSI_VCLK 562 + * |-->[1/2]--FOUT1PH0-->| 563 + * | 564 + * |------->[1/16]--------------------------------> hsclk (MIPI-PHY) 565 + */ 582 566 static unsigned long 583 - rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_pll5_param *params, 567 + rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_cpg_priv *priv, 568 + struct rzg2l_pll5_param *params, 584 569 unsigned long rate) 585 570 { 586 - unsigned long foutpostdiv_rate, foutvco_rate; 571 + const u32 extal_hz = EXTAL_FREQ_IN_MEGA_HZ * MEGA; 572 + unsigned long foutpostdiv_rate; 573 + unsigned int a, b, odd; 574 + unsigned long hsclk; 575 + u8 dsi_div_ab_calc; 576 + u64 foutvco_rate; 587 577 588 - params->pl5_intin = rate / MEGA; 589 - params->pl5_fracin = div_u64(((u64)rate % MEGA) << 24, MEGA); 590 - params->pl5_refdiv = 2; 591 - params->pl5_postdiv1 = 1; 592 - params->pl5_postdiv2 = 1; 578 + if (dsi_div_target == PLL5_TARGET_DSI) { 579 + /* Check hsclk */ 580 + hsclk = rate * dsi_div_ab_desired / 16; 581 + if (hsclk < PLL5_HSCLK_MIN || hsclk > PLL5_HSCLK_MAX) { 582 + dev_err(priv->dev, "hsclk out of range\n"); 583 + return 0; 584 + } 585 + 586 + /* Determine the correct clock source based on even/odd of the divider */ 587 + odd = dsi_div_ab_desired & 1; 588 + if (odd) { 589 + priv->mux_dsi_div_params.clksrc = 0; /* FOUTPOSTDIV */ 590 + dsi_div_ab_calc = dsi_div_ab_desired; 591 + } else { 592 + priv->mux_dsi_div_params.clksrc = 1; /* FOUT1PH0 */ 593 + dsi_div_ab_calc = dsi_div_ab_desired / 2; 594 + } 595 + 596 + /* Calculate the DIV_DSI_A and DIV_DSI_B based on the desired divider */ 597 + for (a = 0; a < 4; a++) { 598 + /* FOUT1PH0: Max output of DIV_DSI_A is 750MHz so at least 1/2 to be safe */ 599 + if (!odd && a == 0) 600 + continue; 601 + 602 + /* FOUTPOSTDIV: DIV_DSI_A must always be 1/1 */ 603 + if (odd && a != 0) 604 + break; 605 + 606 + for (b = 0; b < 16; b++) { 607 + /* FOUTPOSTDIV: DIV_DSI_B must always be odd divider 1/(b+1) */ 608 + if (odd && b & 1) 609 + continue; 610 + 611 + if (rzg2l_cpg_div_ab(a, b) == dsi_div_ab_calc) { 612 + priv->mux_dsi_div_params.dsi_div_a = a; 613 + priv->mux_dsi_div_params.dsi_div_b = b; 614 + goto calc_pll_clk; 615 + } 616 + } 617 + } 618 + 619 + dev_err(priv->dev, "Failed to calculate DIV_DSI_A,B\n"); 620 + 621 + return 0; 622 + } else if (dsi_div_target == PLL5_TARGET_DPI) { 623 + /* Fixed settings for DPI */ 624 + priv->mux_dsi_div_params.clksrc = 0; 625 + priv->mux_dsi_div_params.dsi_div_a = 3; /* Divided by 8 */ 626 + priv->mux_dsi_div_params.dsi_div_b = 0; /* Divided by 1 */ 627 + dsi_div_ab_desired = rzg2l_cpg_div_ab(priv->mux_dsi_div_params.dsi_div_a, 628 + priv->mux_dsi_div_params.dsi_div_b); 629 + } 630 + 631 + calc_pll_clk: 632 + /* PLL5 (MIPI_DSI_PLLCLK) = VCO / POSTDIV1 / POSTDIV2 */ 633 + for (params->pl5_postdiv1 = PLL5_POSTDIV_MIN; 634 + params->pl5_postdiv1 <= PLL5_POSTDIV_MAX; 635 + params->pl5_postdiv1++) { 636 + for (params->pl5_postdiv2 = PLL5_POSTDIV_MIN; 637 + params->pl5_postdiv2 <= PLL5_POSTDIV_MAX; 638 + params->pl5_postdiv2++) { 639 + foutvco_rate = rate * params->pl5_postdiv1 * params->pl5_postdiv2 * 640 + dsi_div_ab_desired; 641 + if (foutvco_rate <= PLL5_FOUTVCO_MIN || foutvco_rate >= PLL5_FOUTVCO_MAX) 642 + continue; 643 + 644 + for (params->pl5_refdiv = PLL5_REFDIV_MIN; 645 + params->pl5_refdiv <= PLL5_REFDIV_MAX; 646 + params->pl5_refdiv++) { 647 + u32 rem; 648 + 649 + params->pl5_intin = div_u64_rem(foutvco_rate * params->pl5_refdiv, 650 + extal_hz, &rem); 651 + 652 + if (params->pl5_intin < PLL5_INTIN_MIN || 653 + params->pl5_intin > PLL5_INTIN_MAX) 654 + continue; 655 + 656 + params->pl5_fracin = div_u64((u64)rem << 24, extal_hz); 657 + 658 + goto clk_valid; 659 + } 660 + } 661 + } 662 + 663 + dev_err(priv->dev, "Failed to calculate PLL5 settings\n"); 664 + return 0; 665 + 666 + clk_valid: 593 667 params->pl5_spread = 0x16; 594 668 595 669 foutvco_rate = div_u64(mul_u32_u32(EXTAL_FREQ_IN_MEGA_HZ * MEGA, 596 670 (params->pl5_intin << 24) + params->pl5_fracin), 597 671 params->pl5_refdiv) >> 24; 598 - foutpostdiv_rate = DIV_ROUND_CLOSEST(foutvco_rate, 599 - params->pl5_postdiv1 * params->pl5_postdiv2); 672 + foutpostdiv_rate = DIV_U64_ROUND_CLOSEST(foutvco_rate, 673 + params->pl5_postdiv1 * params->pl5_postdiv2); 600 674 601 675 return foutpostdiv_rate; 602 676 } ··· 727 607 struct rzg2l_pll5_param params; 728 608 unsigned long parent_rate; 729 609 730 - parent_rate = rzg2l_cpg_get_foutpostdiv_rate(&params, rate); 610 + parent_rate = rzg2l_cpg_get_foutpostdiv_rate(priv, &params, rate); 731 611 732 612 if (priv->mux_dsi_div_params.clksrc) 733 613 parent_rate /= 2; ··· 743 623 744 624 req->best_parent_rate = rzg2l_cpg_get_vclk_parent_rate(hw, req->rate); 745 625 626 + if (!req->best_parent_rate) 627 + return -EINVAL; 628 + 746 629 return 0; 747 630 } 631 + 632 + void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) 633 + { 634 + dsi_div_ab_desired = divider; 635 + dsi_div_target = target; 636 + } 637 + EXPORT_SYMBOL_GPL(rzg2l_cpg_dsi_div_set_divider); 748 638 749 639 static int rzg2l_cpg_dsi_div_set_rate(struct clk_hw *hw, 750 640 unsigned long rate, ··· 926 796 927 797 #define to_sipll5(_hw) container_of(_hw, struct sipll5, hw) 928 798 929 - static unsigned long rzg2l_cpg_get_vclk_rate(struct clk_hw *hw, 930 - unsigned long rate) 931 - { 932 - struct sipll5 *sipll5 = to_sipll5(hw); 933 - struct rzg2l_cpg_priv *priv = sipll5->priv; 934 - unsigned long vclk; 935 - 936 - vclk = rate / ((1 << priv->mux_dsi_div_params.dsi_div_a) * 937 - (priv->mux_dsi_div_params.dsi_div_b + 1)); 938 - 939 - if (priv->mux_dsi_div_params.clksrc) 940 - vclk /= 2; 941 - 942 - return vclk; 943 - } 944 - 945 799 static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw, 946 800 unsigned long parent_rate) 947 801 { ··· 970 856 if (!rate) 971 857 return -EINVAL; 972 858 973 - vclk_rate = rzg2l_cpg_get_vclk_rate(hw, rate); 859 + vclk_rate = rate / dsi_div_ab_desired; 974 860 sipll5->foutpostdiv_rate = 975 - rzg2l_cpg_get_foutpostdiv_rate(&params, vclk_rate); 861 + rzg2l_cpg_get_foutpostdiv_rate(priv, &params, vclk_rate); 976 862 977 863 /* Put PLL5 into standby mode */ 978 864 writel(CPG_SIPLL5_STBY_RESETB_WEN, priv->base + CPG_SIPLL5_STBY); ··· 1059 945 if (ret) 1060 946 return ERR_PTR(ret); 1061 947 1062 - priv->mux_dsi_div_params.clksrc = 1; /* Use clk src 1 for DSI */ 1063 - priv->mux_dsi_div_params.dsi_div_a = 1; /* Divided by 2 */ 1064 - priv->mux_dsi_div_params.dsi_div_b = 2; /* Divided by 3 */ 948 + rzg2l_cpg_dsi_div_set_divider(8, PLL5_TARGET_DPI); 1065 949 1066 950 return clk_hw->clk; 1067 951 }
+11
include/linux/clk/renesas.h
··· 35 35 #define cpg_mssr_detach_dev NULL 36 36 #endif 37 37 38 + enum { 39 + PLL5_TARGET_DPI, 40 + PLL5_TARGET_DSI 41 + }; 42 + 43 + #ifdef CONFIG_CLK_RZG2L 44 + void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target); 45 + #else 46 + static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { } 47 + #endif 48 + 38 49 /** 39 50 * struct rzv2h_pll_limits - PLL parameter constraints 40 51 *