Skip to content

Commit 7681f64

Browse files
shubhraamdbebarino
authored andcommitted
clk: clocking-wizard: calculate dividers fractional parts
Calculate dividers fractional parts to optimally modulate output frequency. Clocking wizard supports having multiplier m and divisors d and o. Currently the fractional parts of m and o are not utilised. For the pixel clock usecases a higher accuracy is needed.. Adding support for m and o to have fractional values. Co-developed-by: Anatoliy Klymenko <[email protected]> Signed-off-by: Anatoliy Klymenko <[email protected]> Signed-off-by: Shubhrajyoti Datta <[email protected]> Tested-by: Anatoliy Klymenko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent b00b08a commit 7681f64

File tree

1 file changed

+69
-52
lines changed

1 file changed

+69
-52
lines changed

drivers/clk/xilinx/clk-xlnx-clock-wizard.c

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
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)
@@ -87,14 +89,14 @@
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
*/
160164
struct 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

409417
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,
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

581579
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,
646644

647645
static 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

680697
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,
682699
.set_rate = clk_wzrd_dynamic_all_ver,
683700
.recalc_rate = clk_wzrd_recalc_rate_all_ver,
684701
};

0 commit comments

Comments
 (0)