5252
5353#define WZRD_CLKFBOUT_MULT_SHIFT 8
5454#define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT)
55+ #define WZRD_CLKFBOUT_MULT_FRAC_MASK GENMASK(25, 16)
56+ #define WZRD_CLKFBOUT_O_MASK GENMASK(7, 0)
5557#define WZRD_CLKFBOUT_L_SHIFT 0
5658#define WZRD_CLKFBOUT_H_SHIFT 8
5759#define WZRD_CLKFBOUT_L_MASK GENMASK(7, 0)
8789#define DIV_O 0x01
8890#define DIV_ALL 0x03
8991
90- #define WZRD_M_MIN 2
91- #define WZRD_M_MAX 128
92- #define WZRD_D_MIN 1
93- #define WZRD_D_MAX 106
94- #define WZRD_VCO_MIN 800000000
95- #define WZRD_VCO_MAX 1600000000
96- #define WZRD_O_MIN 1
97- #define WZRD_O_MAX 128
92+ #define WZRD_M_MIN 2ULL
93+ #define WZRD_M_MAX 128ULL
94+ #define WZRD_D_MIN 1ULL
95+ #define WZRD_D_MAX 106ULL
96+ #define WZRD_VCO_MIN 800000000ULL
97+ #define WZRD_VCO_MAX 1600000000ULL
98+ #define WZRD_O_MIN 2ULL
99+ #define WZRD_O_MAX 128ULL
98100#define VER_WZRD_M_MIN 4
99101#define VER_WZRD_M_MAX 432
100102#define VER_WZRD_D_MIN 1
@@ -153,8 +155,10 @@ struct clk_wzrd {
153155 * @flags: clk_wzrd divider flags
154156 * @table: array of value/divider pairs, last entry should have div = 0
155157 * @m: value of the multiplier
158+ * @m_frac: fractional value of the multiplier
156159 * @d: value of the common divider
157160 * @o: value of the leaf divider
161+ * @o_frac: value of the fractional leaf divider
158162 * @lock: register lock
159163 */
160164struct clk_wzrd_divider {
@@ -166,8 +170,10 @@ struct clk_wzrd_divider {
166170 u8 flags ;
167171 const struct clk_div_table * table ;
168172 u32 m ;
173+ u32 m_frac ;
169174 u32 d ;
170175 u32 o ;
176+ u32 o_frac ;
171177 spinlock_t * lock ; /* divider lock */
172178};
173179
@@ -372,38 +378,40 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate,
372378 unsigned long parent_rate )
373379{
374380 struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
375- u64 vco_freq , freq , diff , vcomin , vcomax ;
376- u32 m , d , o ;
377- u32 mmin , mmax , dmin , dmax , omin , omax ;
381+ u64 vco_freq , freq , diff , vcomin , vcomax , best_diff = -1ULL ;
382+ u64 m , d , o ;
383+ u64 mmin , mmax , dmin , dmax , omin , omax , mdmin , mdmax ;
378384
379- mmin = WZRD_M_MIN ;
380- mmax = WZRD_M_MAX ;
385+ mmin = WZRD_M_MIN << 3 ;
386+ mmax = WZRD_M_MAX << 3 ;
381387 dmin = WZRD_D_MIN ;
382388 dmax = WZRD_D_MAX ;
383- omin = WZRD_O_MIN ;
384- omax = WZRD_O_MAX ;
385- vcomin = WZRD_VCO_MIN ;
386- vcomax = WZRD_VCO_MAX ;
389+ omin = WZRD_O_MIN << 3 ;
390+ omax = WZRD_O_MAX << 3 ;
391+ vcomin = WZRD_VCO_MIN << 3 ;
392+ vcomax = WZRD_VCO_MAX << 3 ;
387393
388394 for (m = mmin ; m <= mmax ; m ++ ) {
389- for (d = dmin ; d <= dmax ; d ++ ) {
390- vco_freq = DIV_ROUND_CLOSEST ((parent_rate * m ), d );
391- if (vco_freq >= vcomin && vco_freq <= vcomax ) {
392- for (o = omin ; o <= omax ; o ++ ) {
393- freq = DIV_ROUND_CLOSEST_ULL (vco_freq , o );
394- diff = abs (freq - rate );
395-
396- if (diff < WZRD_MIN_ERR ) {
397- divider -> m = m ;
398- divider -> d = d ;
399- divider -> o = o ;
400- return 0 ;
401- }
402- }
395+ mdmin = max (dmin , div64_u64 (parent_rate * m + vcomax / 2 , vcomax ));
396+ mdmax = min (dmax , div64_u64 (parent_rate * m + vcomin / 2 , vcomin ));
397+ for (d = mdmin ; d <= mdmax ; d ++ ) {
398+ vco_freq = DIV_ROUND_CLOSEST_ULL ((parent_rate * m ), d );
399+ o = DIV_ROUND_CLOSEST_ULL (vco_freq , rate );
400+ if (o < omin || o > omax )
401+ continue ;
402+ freq = DIV_ROUND_CLOSEST_ULL (vco_freq , o );
403+ diff = freq - rate ;
404+ if (diff < best_diff ) {
405+ best_diff = diff ;
406+ divider -> m = m >> 3 ;
407+ divider -> m_frac = (m - (divider -> m << 3 )) * 125 ;
408+ divider -> d = d ;
409+ divider -> o = o >> 3 ;
410+ divider -> o_frac = (o - (divider -> o << 3 )) * 125 ;
403411 }
404412 }
405413 }
406- return - EBUSY ;
414+ return best_diff < WZRD_MIN_ERR ? 0 : - EBUSY ;
407415}
408416
409417static int clk_wzrd_reconfig (struct clk_wzrd_divider * divider , void __iomem * div_addr )
@@ -496,33 +504,22 @@ static int clk_wzrd_dynamic_all_nolock(struct clk_hw *hw, unsigned long rate,
496504 unsigned long parent_rate )
497505{
498506 struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
499- unsigned long vco_freq , rate_div , clockout0_div ;
500507 void __iomem * div_addr ;
501- u32 reg , pre , f ;
508+ u32 reg ;
502509 int err ;
503510
504511 err = clk_wzrd_get_divisors (hw , rate , parent_rate );
505512 if (err )
506513 return err ;
507514
508- vco_freq = DIV_ROUND_CLOSEST (parent_rate * divider -> m , divider -> d );
509- rate_div = DIV_ROUND_CLOSEST_ULL ((vco_freq * WZRD_FRAC_POINTS ), rate );
510-
511- clockout0_div = div_u64 (rate_div , WZRD_FRAC_POINTS );
512-
513- pre = DIV_ROUND_CLOSEST_ULL (vco_freq * WZRD_FRAC_POINTS , rate );
514- f = (pre - (clockout0_div * WZRD_FRAC_POINTS ));
515- f &= WZRD_CLKOUT_FRAC_MASK ;
516-
517- reg = FIELD_PREP (WZRD_CLKOUT_DIVIDE_MASK , clockout0_div ) |
518- FIELD_PREP (WZRD_CLKOUT0_FRAC_MASK , f );
515+ reg = FIELD_PREP (WZRD_CLKOUT_DIVIDE_MASK , divider -> o ) |
516+ FIELD_PREP (WZRD_CLKOUT0_FRAC_MASK , divider -> o_frac );
519517
520518 writel (reg , divider -> base + WZRD_CLK_CFG_REG (0 , 2 ));
521- /* Set divisor and clear phase offset */
522519 reg = FIELD_PREP (WZRD_CLKFBOUT_MULT_MASK , divider -> m ) |
520+ FIELD_PREP (WZRD_CLKFBOUT_MULT_FRAC_MASK , divider -> m_frac ) |
523521 FIELD_PREP (WZRD_DIVCLK_DIVIDE_MASK , divider -> d );
524522 writel (reg , divider -> base + WZRD_CLK_CFG_REG (0 , 0 ));
525- writel (divider -> o , divider -> base + WZRD_CLK_CFG_REG (0 , 2 ));
526523 writel (0 , divider -> base + WZRD_CLK_CFG_REG (0 , 3 ));
527524 div_addr = divider -> base + WZRD_DR_INIT_REG_OFFSET ;
528525 return clk_wzrd_reconfig (divider , div_addr );
@@ -564,18 +561,19 @@ static unsigned long clk_wzrd_recalc_rate_all(struct clk_hw *hw,
564561 unsigned long parent_rate )
565562{
566563 struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
567- u32 m , d , o , div , reg , f ;
564+ u32 m , d , o , reg , f , mf ;
565+ u64 mul ;
568566
569567 reg = readl (divider -> base + WZRD_CLK_CFG_REG (0 , 0 ));
570568 d = FIELD_GET (WZRD_DIVCLK_DIVIDE_MASK , reg );
571569 m = FIELD_GET (WZRD_CLKFBOUT_MULT_MASK , reg );
570+ mf = FIELD_GET (WZRD_CLKFBOUT_MULT_FRAC_MASK , reg );
572571 reg = readl (divider -> base + WZRD_CLK_CFG_REG (0 , 2 ));
573572 o = FIELD_GET (WZRD_DIVCLK_DIVIDE_MASK , reg );
574573 f = FIELD_GET (WZRD_CLKOUT0_FRAC_MASK , reg );
575574
576- div = DIV_ROUND_CLOSEST (d * (WZRD_FRAC_POINTS * o + f ), WZRD_FRAC_POINTS );
577- return divider_recalc_rate (hw , parent_rate * m , div , divider -> table ,
578- divider -> flags , divider -> width );
575+ mul = m * 1000 + mf ;
576+ return DIV_ROUND_CLOSEST_ULL (parent_rate * mul , d * (o * 1000 + f ));
579577}
580578
581579static unsigned long clk_wzrd_recalc_rate_all_ver (struct clk_hw * hw ,
@@ -646,6 +644,25 @@ static unsigned long clk_wzrd_recalc_rate_all_ver(struct clk_hw *hw,
646644
647645static long clk_wzrd_round_rate_all (struct clk_hw * hw , unsigned long rate ,
648646 unsigned long * prate )
647+ {
648+ struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
649+ u32 m , d , o ;
650+ int err ;
651+
652+ err = clk_wzrd_get_divisors (hw , rate , * prate );
653+ if (err )
654+ return err ;
655+
656+ m = divider -> m ;
657+ d = divider -> d ;
658+ o = divider -> o ;
659+
660+ rate = div_u64 (* prate * (m * 1000 + divider -> m_frac ), d * (o * 1000 + divider -> o_frac ));
661+ return rate ;
662+ }
663+
664+ static long clk_wzrd_ver_round_rate_all (struct clk_hw * hw , unsigned long rate ,
665+ unsigned long * prate )
649666{
650667 struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
651668 unsigned long int_freq ;
@@ -678,7 +695,7 @@ static const struct clk_ops clk_wzrd_ver_divider_ops = {
678695};
679696
680697static const struct clk_ops clk_wzrd_ver_div_all_ops = {
681- .round_rate = clk_wzrd_round_rate_all ,
698+ .round_rate = clk_wzrd_ver_round_rate_all ,
682699 .set_rate = clk_wzrd_dynamic_all_ver ,
683700 .recalc_rate = clk_wzrd_recalc_rate_all_ver ,
684701};
0 commit comments