52
52
53
53
#define WZRD_CLKFBOUT_MULT_SHIFT 8
54
54
#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)
55
57
#define WZRD_CLKFBOUT_L_SHIFT 0
56
58
#define WZRD_CLKFBOUT_H_SHIFT 8
57
59
#define WZRD_CLKFBOUT_L_MASK GENMASK(7, 0)
87
89
#define DIV_O 0x01
88
90
#define DIV_ALL 0x03
89
91
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
98
100
#define VER_WZRD_M_MIN 4
99
101
#define VER_WZRD_M_MAX 432
100
102
#define VER_WZRD_D_MIN 1
@@ -153,8 +155,10 @@ struct clk_wzrd {
153
155
* @flags: clk_wzrd divider flags
154
156
* @table: array of value/divider pairs, last entry should have div = 0
155
157
* @m: value of the multiplier
158
+ * @m_frac: fractional value of the multiplier
156
159
* @d: value of the common divider
157
160
* @o: value of the leaf divider
161
+ * @o_frac: value of the fractional leaf divider
158
162
* @lock: register lock
159
163
*/
160
164
struct clk_wzrd_divider {
@@ -166,8 +170,10 @@ struct clk_wzrd_divider {
166
170
u8 flags ;
167
171
const struct clk_div_table * table ;
168
172
u32 m ;
173
+ u32 m_frac ;
169
174
u32 d ;
170
175
u32 o ;
176
+ u32 o_frac ;
171
177
spinlock_t * lock ; /* divider lock */
172
178
};
173
179
@@ -372,38 +378,40 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate,
372
378
unsigned long parent_rate )
373
379
{
374
380
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 ;
378
384
379
- mmin = WZRD_M_MIN ;
380
- mmax = WZRD_M_MAX ;
385
+ mmin = WZRD_M_MIN << 3 ;
386
+ mmax = WZRD_M_MAX << 3 ;
381
387
dmin = WZRD_D_MIN ;
382
388
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 ;
387
393
388
394
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 ;
403
411
}
404
412
}
405
413
}
406
- return - EBUSY ;
414
+ return best_diff < WZRD_MIN_ERR ? 0 : - EBUSY ;
407
415
}
408
416
409
417
static 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,
496
504
unsigned long parent_rate )
497
505
{
498
506
struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
499
- unsigned long vco_freq , rate_div , clockout0_div ;
500
507
void __iomem * div_addr ;
501
- u32 reg , pre , f ;
508
+ u32 reg ;
502
509
int err ;
503
510
504
511
err = clk_wzrd_get_divisors (hw , rate , parent_rate );
505
512
if (err )
506
513
return err ;
507
514
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 );
519
517
520
518
writel (reg , divider -> base + WZRD_CLK_CFG_REG (0 , 2 ));
521
- /* Set divisor and clear phase offset */
522
519
reg = FIELD_PREP (WZRD_CLKFBOUT_MULT_MASK , divider -> m ) |
520
+ FIELD_PREP (WZRD_CLKFBOUT_MULT_FRAC_MASK , divider -> m_frac ) |
523
521
FIELD_PREP (WZRD_DIVCLK_DIVIDE_MASK , divider -> d );
524
522
writel (reg , divider -> base + WZRD_CLK_CFG_REG (0 , 0 ));
525
- writel (divider -> o , divider -> base + WZRD_CLK_CFG_REG (0 , 2 ));
526
523
writel (0 , divider -> base + WZRD_CLK_CFG_REG (0 , 3 ));
527
524
div_addr = divider -> base + WZRD_DR_INIT_REG_OFFSET ;
528
525
return clk_wzrd_reconfig (divider , div_addr );
@@ -564,18 +561,19 @@ static unsigned long clk_wzrd_recalc_rate_all(struct clk_hw *hw,
564
561
unsigned long parent_rate )
565
562
{
566
563
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 ;
568
566
569
567
reg = readl (divider -> base + WZRD_CLK_CFG_REG (0 , 0 ));
570
568
d = FIELD_GET (WZRD_DIVCLK_DIVIDE_MASK , reg );
571
569
m = FIELD_GET (WZRD_CLKFBOUT_MULT_MASK , reg );
570
+ mf = FIELD_GET (WZRD_CLKFBOUT_MULT_FRAC_MASK , reg );
572
571
reg = readl (divider -> base + WZRD_CLK_CFG_REG (0 , 2 ));
573
572
o = FIELD_GET (WZRD_DIVCLK_DIVIDE_MASK , reg );
574
573
f = FIELD_GET (WZRD_CLKOUT0_FRAC_MASK , reg );
575
574
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 ));
579
577
}
580
578
581
579
static 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,
646
644
647
645
static long clk_wzrd_round_rate_all (struct clk_hw * hw , unsigned long rate ,
648
646
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 )
649
666
{
650
667
struct clk_wzrd_divider * divider = to_clk_wzrd_divider (hw );
651
668
unsigned long int_freq ;
@@ -678,7 +695,7 @@ static const struct clk_ops clk_wzrd_ver_divider_ops = {
678
695
};
679
696
680
697
static 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 ,
682
699
.set_rate = clk_wzrd_dynamic_all_ver ,
683
700
.recalc_rate = clk_wzrd_recalc_rate_all_ver ,
684
701
};
0 commit comments