2222#include <linux/device.h>
2323#include <linux/init.h>
2424#include <linux/iopoll.h>
25+ #include <linux/math64.h>
2526#include <linux/mod_devicetable.h>
2627#include <linux/module.h>
2728#include <linux/of.h>
7475#define MSTOP_OFF (conf ) FIELD_GET(GENMASK(31, 16), (conf))
7576#define MSTOP_MASK (conf ) FIELD_GET(GENMASK(15, 0), (conf))
7677
78+ #define PLL5_FOUTVCO_MIN 800000000
79+ #define PLL5_FOUTVCO_MAX 3000000000
80+ #define PLL5_POSTDIV_MIN 1
81+ #define PLL5_POSTDIV_MAX 7
82+ #define PLL5_REFDIV_MIN 1
83+ #define PLL5_REFDIV_MAX 2
84+ #define PLL5_INTIN_MIN 20
85+ #define PLL5_INTIN_MAX 320
86+ #define PLL5_HSCLK_MIN 10000000
87+ #define PLL5_HSCLK_MAX 187500000
88+
7789/**
7890 * struct clk_hw_data - clock hardware data
7991 * @hw: clock hw
@@ -129,6 +141,12 @@ struct rzg2l_pll5_param {
129141 u8 pl5_spread ;
130142};
131143
144+ /* PLL5 output will be used for DPI or MIPI-DSI */
145+ static int dsi_div_target = PLL5_TARGET_DPI ;
146+
147+ /* Required division ratio for MIPI D-PHY clock depending on number of lanes and bpp. */
148+ static u8 dsi_div_ab_desired ;
149+
132150struct rzg2l_pll5_mux_dsi_div_param {
133151 u8 clksrc ;
134152 u8 dsi_div_a ;
@@ -170,6 +188,11 @@ struct rzg2l_cpg_priv {
170188 struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params ;
171189};
172190
191+ static inline u8 rzg2l_cpg_div_ab (u8 a , u8 b )
192+ {
193+ return (b + 1 ) << a ;
194+ }
195+
173196static void rzg2l_cpg_del_clk_provider (void * data )
174197{
175198 of_clk_del_provider (data );
@@ -556,24 +579,121 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core,
556579 return clk_hw -> clk ;
557580}
558581
582+ /*
583+ * VCO-->[POSTDIV1,2]--FOUTPOSTDIV--------------->|
584+ * | |-->[1/(DSI DIV A * B)]--> MIPI_DSI_VCLK
585+ * |-->[1/2]--FOUT1PH0-->|
586+ * |
587+ * |------->[1/16]--------------------------------> hsclk (MIPI-PHY)
588+ */
559589static unsigned long
560- rzg2l_cpg_get_foutpostdiv_rate (struct rzg2l_pll5_param * params ,
590+ rzg2l_cpg_get_foutpostdiv_rate (struct rzg2l_cpg_priv * priv ,
591+ struct rzg2l_pll5_param * params ,
561592 unsigned long rate )
562593{
563- unsigned long foutpostdiv_rate , foutvco_rate ;
594+ const u32 extal_hz = EXTAL_FREQ_IN_MEGA_HZ * MEGA ;
595+ unsigned long foutpostdiv_rate ;
596+ unsigned int a , b , odd ;
597+ unsigned long hsclk ;
598+ u8 dsi_div_ab_calc ;
599+ u64 foutvco_rate ;
600+
601+ if (dsi_div_target == PLL5_TARGET_DSI ) {
602+ /* Check hsclk */
603+ hsclk = rate * dsi_div_ab_desired / 16 ;
604+ if (hsclk < PLL5_HSCLK_MIN || hsclk > PLL5_HSCLK_MAX ) {
605+ dev_err (priv -> dev , "hsclk out of range\n" );
606+ return 0 ;
607+ }
608+
609+ /* Determine the correct clock source based on even/odd of the divider */
610+ odd = dsi_div_ab_desired & 1 ;
611+ if (odd ) {
612+ priv -> mux_dsi_div_params .clksrc = 0 ; /* FOUTPOSTDIV */
613+ dsi_div_ab_calc = dsi_div_ab_desired ;
614+ } else {
615+ priv -> mux_dsi_div_params .clksrc = 1 ; /* FOUT1PH0 */
616+ dsi_div_ab_calc = dsi_div_ab_desired / 2 ;
617+ }
618+
619+ /* Calculate the DIV_DSI_A and DIV_DSI_B based on the desired divider */
620+ for (a = 0 ; a < 4 ; a ++ ) {
621+ /* FOUT1PH0: Max output of DIV_DSI_A is 750MHz so at least 1/2 to be safe */
622+ if (!odd && a == 0 )
623+ continue ;
624+
625+ /* FOUTPOSTDIV: DIV_DSI_A must always be 1/1 */
626+ if (odd && a != 0 )
627+ break ;
628+
629+ for (b = 0 ; b < 16 ; b ++ ) {
630+ /* FOUTPOSTDIV: DIV_DSI_B must always be odd divider 1/(b+1) */
631+ if (odd && b & 1 )
632+ continue ;
633+
634+ if (rzg2l_cpg_div_ab (a , b ) == dsi_div_ab_calc ) {
635+ priv -> mux_dsi_div_params .dsi_div_a = a ;
636+ priv -> mux_dsi_div_params .dsi_div_b = b ;
637+ goto calc_pll_clk ;
638+ }
639+ }
640+ }
641+
642+ dev_err (priv -> dev , "Failed to calculate DIV_DSI_A,B\n" );
643+
644+ return 0 ;
645+ } else if (dsi_div_target == PLL5_TARGET_DPI ) {
646+ /* Fixed settings for DPI */
647+ priv -> mux_dsi_div_params .clksrc = 0 ;
648+ priv -> mux_dsi_div_params .dsi_div_a = 3 ; /* Divided by 8 */
649+ priv -> mux_dsi_div_params .dsi_div_b = 0 ; /* Divided by 1 */
650+ dsi_div_ab_desired = rzg2l_cpg_div_ab (priv -> mux_dsi_div_params .dsi_div_a ,
651+ priv -> mux_dsi_div_params .dsi_div_b );
652+ }
564653
565- params -> pl5_intin = rate / MEGA ;
566- params -> pl5_fracin = div_u64 (((u64 )rate % MEGA ) << 24 , MEGA );
567- params -> pl5_refdiv = 2 ;
568- params -> pl5_postdiv1 = 1 ;
569- params -> pl5_postdiv2 = 1 ;
654+ calc_pll_clk :
655+ /* PLL5 (MIPI_DSI_PLLCLK) = VCO / POSTDIV1 / POSTDIV2 */
656+ for (params -> pl5_postdiv1 = PLL5_POSTDIV_MIN ;
657+ params -> pl5_postdiv1 <= PLL5_POSTDIV_MAX ;
658+ params -> pl5_postdiv1 ++ ) {
659+ for (params -> pl5_postdiv2 = PLL5_POSTDIV_MIN ;
660+ params -> pl5_postdiv2 <= PLL5_POSTDIV_MAX ;
661+ params -> pl5_postdiv2 ++ ) {
662+ foutvco_rate = rate * params -> pl5_postdiv1 * params -> pl5_postdiv2 *
663+ dsi_div_ab_desired ;
664+ if (foutvco_rate <= PLL5_FOUTVCO_MIN || foutvco_rate >= PLL5_FOUTVCO_MAX )
665+ continue ;
666+
667+ for (params -> pl5_refdiv = PLL5_REFDIV_MIN ;
668+ params -> pl5_refdiv <= PLL5_REFDIV_MAX ;
669+ params -> pl5_refdiv ++ ) {
670+ u32 rem ;
671+
672+ params -> pl5_intin = div_u64_rem (foutvco_rate * params -> pl5_refdiv ,
673+ extal_hz , & rem );
674+
675+ if (params -> pl5_intin < PLL5_INTIN_MIN ||
676+ params -> pl5_intin > PLL5_INTIN_MAX )
677+ continue ;
678+
679+ params -> pl5_fracin = div_u64 ((u64 )rem << 24 , extal_hz );
680+
681+ goto clk_valid ;
682+ }
683+ }
684+ }
685+
686+ dev_err (priv -> dev , "Failed to calculate PLL5 settings\n" );
687+ return 0 ;
688+
689+ clk_valid :
570690 params -> pl5_spread = 0x16 ;
571691
572692 foutvco_rate = div_u64 (mul_u32_u32 (EXTAL_FREQ_IN_MEGA_HZ * MEGA ,
573693 (params -> pl5_intin << 24 ) + params -> pl5_fracin ),
574694 params -> pl5_refdiv ) >> 24 ;
575- foutpostdiv_rate = DIV_ROUND_CLOSEST (foutvco_rate ,
576- params -> pl5_postdiv1 * params -> pl5_postdiv2 );
695+ foutpostdiv_rate = DIV_U64_ROUND_CLOSEST (foutvco_rate ,
696+ params -> pl5_postdiv1 * params -> pl5_postdiv2 );
577697
578698 return foutpostdiv_rate ;
579699}
@@ -607,7 +727,7 @@ static unsigned long rzg2l_cpg_get_vclk_parent_rate(struct clk_hw *hw,
607727 struct rzg2l_pll5_param params ;
608728 unsigned long parent_rate ;
609729
610- parent_rate = rzg2l_cpg_get_foutpostdiv_rate (& params , rate );
730+ parent_rate = rzg2l_cpg_get_foutpostdiv_rate (priv , & params , rate );
611731
612732 if (priv -> mux_dsi_div_params .clksrc )
613733 parent_rate /= 2 ;
@@ -623,9 +743,19 @@ static int rzg2l_cpg_dsi_div_determine_rate(struct clk_hw *hw,
623743
624744 req -> best_parent_rate = rzg2l_cpg_get_vclk_parent_rate (hw , req -> rate );
625745
746+ if (!req -> best_parent_rate )
747+ return - EINVAL ;
748+
626749 return 0 ;
627750}
628751
752+ void rzg2l_cpg_dsi_div_set_divider (u8 divider , int target )
753+ {
754+ dsi_div_ab_desired = divider ;
755+ dsi_div_target = target ;
756+ }
757+ EXPORT_SYMBOL_GPL (rzg2l_cpg_dsi_div_set_divider );
758+
629759static int rzg2l_cpg_dsi_div_set_rate (struct clk_hw * hw ,
630760 unsigned long rate ,
631761 unsigned long parent_rate )
@@ -796,22 +926,6 @@ struct sipll5 {
796926
797927#define to_sipll5 (_hw ) container_of(_hw, struct sipll5, hw)
798928
799- static unsigned long rzg2l_cpg_get_vclk_rate (struct clk_hw * hw ,
800- unsigned long rate )
801- {
802- struct sipll5 * sipll5 = to_sipll5 (hw );
803- struct rzg2l_cpg_priv * priv = sipll5 -> priv ;
804- unsigned long vclk ;
805-
806- vclk = rate / ((1 << priv -> mux_dsi_div_params .dsi_div_a ) *
807- (priv -> mux_dsi_div_params .dsi_div_b + 1 ));
808-
809- if (priv -> mux_dsi_div_params .clksrc )
810- vclk /= 2 ;
811-
812- return vclk ;
813- }
814-
815929static unsigned long rzg2l_cpg_sipll5_recalc_rate (struct clk_hw * hw ,
816930 unsigned long parent_rate )
817931{
@@ -856,9 +970,9 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
856970 if (!rate )
857971 return - EINVAL ;
858972
859- vclk_rate = rzg2l_cpg_get_vclk_rate ( hw , rate ) ;
973+ vclk_rate = rate / dsi_div_ab_desired ;
860974 sipll5 -> foutpostdiv_rate =
861- rzg2l_cpg_get_foutpostdiv_rate (& params , vclk_rate );
975+ rzg2l_cpg_get_foutpostdiv_rate (priv , & params , vclk_rate );
862976
863977 /* Put PLL5 into standby mode */
864978 writel (CPG_SIPLL5_STBY_RESETB_WEN , priv -> base + CPG_SIPLL5_STBY );
@@ -945,9 +1059,7 @@ rzg2l_cpg_sipll5_register(const struct cpg_core_clk *core,
9451059 if (ret )
9461060 return ERR_PTR (ret );
9471061
948- priv -> mux_dsi_div_params .clksrc = 1 ; /* Use clk src 1 for DSI */
949- priv -> mux_dsi_div_params .dsi_div_a = 1 ; /* Divided by 2 */
950- priv -> mux_dsi_div_params .dsi_div_b = 2 ; /* Divided by 3 */
1062+ rzg2l_cpg_dsi_div_set_divider (8 , PLL5_TARGET_DPI );
9511063
9521064 return clk_hw -> clk ;
9531065}
0 commit comments