Skip to content

Commit 595c88c

Browse files
shubhraamdbebarino
authored andcommitted
clocking-wizard: Support higher frequency accuracy
Change the multipliers and divisors to support a higher frequency accuracy if there is only one output. Currently only O is changed now we are changing M, D and O. For multiple output case the earlier behavior is retained. Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> Link: https://lore.kernel.org/r/20230327062637.22237-1-shubhrajyoti.datta@amd.com Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent 57e3bbd commit 595c88c

File tree

1 file changed

+204
-24
lines changed

1 file changed

+204
-24
lines changed

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

Lines changed: 204 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
*
99
*/
1010

11+
#include <linux/bitfield.h>
1112
#include <linux/platform_device.h>
1213
#include <linux/clk.h>
1314
#include <linux/clk-provider.h>
1415
#include <linux/slab.h>
1516
#include <linux/io.h>
1617
#include <linux/of.h>
18+
#include <linux/math64.h>
1719
#include <linux/module.h>
1820
#include <linux/err.h>
1921
#include <linux/iopoll.h>
@@ -37,6 +39,7 @@
3739
#define WZRD_CLKOUT_DIVIDE_MASK (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
3840
#define WZRD_CLKOUT_FRAC_SHIFT 8
3941
#define WZRD_CLKOUT_FRAC_MASK 0x3ff
42+
#define WZRD_CLKOUT0_FRAC_MASK GENMASK(17, 8)
4043

4144
#define WZRD_DR_MAX_INT_DIV_VALUE 255
4245
#define WZRD_DR_STATUS_REG_OFFSET 0x04
@@ -49,6 +52,22 @@
4952

5053
#define WZRD_USEC_POLL 10
5154
#define WZRD_TIMEOUT_POLL 1000
55+
56+
/* Divider limits, from UG572 Table 3-4 for Ultrascale+ */
57+
#define DIV_O 0x01
58+
#define DIV_ALL 0x03
59+
60+
#define WZRD_M_MIN 2
61+
#define WZRD_M_MAX 128
62+
#define WZRD_D_MIN 1
63+
#define WZRD_D_MAX 106
64+
#define WZRD_VCO_MIN 800000000
65+
#define WZRD_VCO_MAX 1600000000
66+
#define WZRD_O_MIN 1
67+
#define WZRD_O_MAX 128
68+
#define WZRD_MIN_ERR 20000
69+
#define WZRD_FRAC_POINTS 1000
70+
5271
/* Get the mask from width */
5372
#define div_mask(width) ((1 << (width)) - 1)
5473

@@ -97,6 +116,9 @@ struct clk_wzrd {
97116
* @width: width of the divider bit field
98117
* @flags: clk_wzrd divider flags
99118
* @table: array of value/divider pairs, last entry should have div = 0
119+
* @m: value of the multiplier
120+
* @d: value of the common divider
121+
* @o: value of the leaf divider
100122
* @lock: register lock
101123
*/
102124
struct clk_wzrd_divider {
@@ -107,6 +129,9 @@ struct clk_wzrd_divider {
107129
u8 width;
108130
u8 flags;
109131
const struct clk_div_table *table;
132+
u32 m;
133+
u32 d;
134+
u32 o;
110135
spinlock_t *lock; /* divider lock */
111136
};
112137

@@ -198,12 +223,155 @@ static long clk_wzrd_round_rate(struct clk_hw *hw, unsigned long rate,
198223
return *prate / div;
199224
}
200225

226+
static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate,
227+
unsigned long parent_rate)
228+
{
229+
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
230+
unsigned long vco_freq, freq, diff;
231+
u32 m, d, o;
232+
233+
for (m = WZRD_M_MIN; m <= WZRD_M_MAX; m++) {
234+
for (d = WZRD_D_MIN; d <= WZRD_D_MAX; d++) {
235+
vco_freq = DIV_ROUND_CLOSEST((parent_rate * m), d);
236+
if (vco_freq >= WZRD_VCO_MIN && vco_freq <= WZRD_VCO_MAX) {
237+
for (o = WZRD_O_MIN; o <= WZRD_O_MAX; o++) {
238+
freq = DIV_ROUND_CLOSEST_ULL(vco_freq, o);
239+
diff = abs(freq - rate);
240+
241+
if (diff < WZRD_MIN_ERR) {
242+
divider->m = m;
243+
divider->d = d;
244+
divider->o = o;
245+
return 0;
246+
}
247+
}
248+
}
249+
}
250+
}
251+
return -EBUSY;
252+
}
253+
254+
static int clk_wzrd_dynamic_all_nolock(struct clk_hw *hw, unsigned long rate,
255+
unsigned long parent_rate)
256+
{
257+
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
258+
unsigned long vco_freq, rate_div, clockout0_div;
259+
u32 reg, pre, value, f;
260+
int err;
261+
262+
err = clk_wzrd_get_divisors(hw, rate, parent_rate);
263+
if (err)
264+
return err;
265+
266+
vco_freq = DIV_ROUND_CLOSEST(parent_rate * divider->m, divider->d);
267+
rate_div = DIV_ROUND_CLOSEST_ULL((vco_freq * WZRD_FRAC_POINTS), rate);
268+
269+
clockout0_div = div_u64(rate_div, WZRD_FRAC_POINTS);
270+
271+
pre = DIV_ROUND_CLOSEST_ULL(vco_freq * WZRD_FRAC_POINTS, rate);
272+
f = (pre - (clockout0_div * WZRD_FRAC_POINTS));
273+
f &= WZRD_CLKOUT_FRAC_MASK;
274+
275+
reg = FIELD_PREP(WZRD_CLKOUT_DIVIDE_MASK, clockout0_div) |
276+
FIELD_PREP(WZRD_CLKOUT0_FRAC_MASK, f);
277+
278+
writel(reg, divider->base + WZRD_CLK_CFG_REG(2));
279+
/* Set divisor and clear phase offset */
280+
reg = FIELD_PREP(WZRD_CLKFBOUT_MULT_MASK, divider->m) |
281+
FIELD_PREP(WZRD_DIVCLK_DIVIDE_MASK, divider->d);
282+
writel(reg, divider->base + WZRD_CLK_CFG_REG(0));
283+
writel(divider->o, divider->base + WZRD_CLK_CFG_REG(2));
284+
writel(0, divider->base + WZRD_CLK_CFG_REG(3));
285+
/* Check status register */
286+
err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
287+
value & WZRD_DR_LOCK_BIT_MASK,
288+
WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
289+
if (err)
290+
return -ETIMEDOUT;
291+
292+
/* Initiate reconfiguration */
293+
writel(WZRD_DR_BEGIN_DYNA_RECONF,
294+
divider->base + WZRD_DR_INIT_REG_OFFSET);
295+
296+
/* Check status register */
297+
return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
298+
value & WZRD_DR_LOCK_BIT_MASK,
299+
WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
300+
}
301+
302+
static int clk_wzrd_dynamic_all(struct clk_hw *hw, unsigned long rate,
303+
unsigned long parent_rate)
304+
{
305+
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
306+
unsigned long flags = 0;
307+
int ret;
308+
309+
spin_lock_irqsave(divider->lock, flags);
310+
311+
ret = clk_wzrd_dynamic_all_nolock(hw, rate, parent_rate);
312+
313+
spin_unlock_irqrestore(divider->lock, flags);
314+
315+
return ret;
316+
}
317+
318+
static unsigned long clk_wzrd_recalc_rate_all(struct clk_hw *hw,
319+
unsigned long parent_rate)
320+
{
321+
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
322+
u32 m, d, o, div, reg, f;
323+
324+
reg = readl(divider->base + WZRD_CLK_CFG_REG(0));
325+
d = FIELD_GET(WZRD_DIVCLK_DIVIDE_MASK, reg);
326+
m = FIELD_GET(WZRD_CLKFBOUT_MULT_MASK, reg);
327+
reg = readl(divider->base + WZRD_CLK_CFG_REG(2));
328+
o = FIELD_GET(WZRD_DIVCLK_DIVIDE_MASK, reg);
329+
f = FIELD_GET(WZRD_CLKOUT0_FRAC_MASK, reg);
330+
331+
div = DIV_ROUND_CLOSEST(d * (WZRD_FRAC_POINTS * o + f), WZRD_FRAC_POINTS);
332+
return divider_recalc_rate(hw, parent_rate * m, div, divider->table,
333+
divider->flags, divider->width);
334+
}
335+
336+
static long clk_wzrd_round_rate_all(struct clk_hw *hw, unsigned long rate,
337+
unsigned long *prate)
338+
{
339+
struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
340+
unsigned long int_freq;
341+
u32 m, d, o, div, f;
342+
int err;
343+
344+
err = clk_wzrd_get_divisors(hw, rate, *prate);
345+
if (err)
346+
return err;
347+
348+
m = divider->m;
349+
d = divider->d;
350+
o = divider->o;
351+
352+
div = d * o;
353+
int_freq = divider_recalc_rate(hw, *prate * m, div, divider->table,
354+
divider->flags, divider->width);
355+
356+
if (rate > int_freq) {
357+
f = DIV_ROUND_CLOSEST_ULL(rate * WZRD_FRAC_POINTS, int_freq);
358+
rate = DIV_ROUND_CLOSEST(int_freq * f, WZRD_FRAC_POINTS);
359+
}
360+
return rate;
361+
}
362+
201363
static const struct clk_ops clk_wzrd_clk_divider_ops = {
202364
.round_rate = clk_wzrd_round_rate,
203365
.set_rate = clk_wzrd_dynamic_reconfig,
204366
.recalc_rate = clk_wzrd_recalc_rate,
205367
};
206368

369+
static const struct clk_ops clk_wzrd_clk_div_all_ops = {
370+
.round_rate = clk_wzrd_round_rate_all,
371+
.set_rate = clk_wzrd_dynamic_all,
372+
.recalc_rate = clk_wzrd_recalc_rate_all,
373+
};
374+
207375
static unsigned long clk_wzrd_recalc_ratef(struct clk_hw *hw,
208376
unsigned long parent_rate)
209377
{
@@ -280,7 +448,7 @@ static struct clk *clk_wzrd_register_divf(struct device *dev,
280448
void __iomem *base, u16 offset,
281449
u8 shift, u8 width,
282450
u8 clk_divider_flags,
283-
const struct clk_div_table *table,
451+
u32 div_type,
284452
spinlock_t *lock)
285453
{
286454
struct clk_wzrd_divider *div;
@@ -307,7 +475,6 @@ static struct clk *clk_wzrd_register_divf(struct device *dev,
307475
div->flags = clk_divider_flags;
308476
div->lock = lock;
309477
div->hw.init = &init;
310-
div->table = table;
311478

312479
hw = &div->hw;
313480
ret = devm_clk_hw_register(dev, hw);
@@ -324,7 +491,7 @@ static struct clk *clk_wzrd_register_divider(struct device *dev,
324491
void __iomem *base, u16 offset,
325492
u8 shift, u8 width,
326493
u8 clk_divider_flags,
327-
const struct clk_div_table *table,
494+
u32 div_type,
328495
spinlock_t *lock)
329496
{
330497
struct clk_wzrd_divider *div;
@@ -337,7 +504,12 @@ static struct clk *clk_wzrd_register_divider(struct device *dev,
337504
return ERR_PTR(-ENOMEM);
338505

339506
init.name = name;
340-
init.ops = &clk_wzrd_clk_divider_ops;
507+
if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
508+
init.ops = &clk_divider_ro_ops;
509+
else if (div_type == DIV_O)
510+
init.ops = &clk_wzrd_clk_divider_ops;
511+
else
512+
init.ops = &clk_wzrd_clk_div_all_ops;
341513
init.flags = flags;
342514
init.parent_names = &parent_name;
343515
init.num_parents = 1;
@@ -349,7 +521,6 @@ static struct clk *clk_wzrd_register_divider(struct device *dev,
349521
div->flags = clk_divider_flags;
350522
div->lock = lock;
351523
div->hw.init = &init;
352-
div->table = table;
353524

354525
hw = &div->hw;
355526
ret = devm_clk_hw_register(dev, hw);
@@ -425,6 +596,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
425596
const char *clk_name;
426597
void __iomem *ctrl_reg;
427598
struct clk_wzrd *clk_wzrd;
599+
const char *clkout_name;
428600
struct device_node *np = pdev->dev.of_node;
429601
int nr_outputs;
430602
unsigned long flags = 0;
@@ -469,27 +641,38 @@ static int clk_wzrd_probe(struct platform_device *pdev)
469641
goto err_disable_clk;
470642
}
471643

644+
ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs);
645+
if (ret || nr_outputs > WZRD_NUM_OUTPUTS) {
646+
ret = -EINVAL;
647+
goto err_disable_clk;
648+
}
649+
650+
clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_out0", dev_name(&pdev->dev));
651+
if (nr_outputs == 1) {
652+
clk_wzrd->clkout[0] = clk_wzrd_register_divider
653+
(&pdev->dev, clkout_name,
654+
__clk_get_name(clk_wzrd->clk_in1), 0,
655+
clk_wzrd->base, WZRD_CLK_CFG_REG(3),
656+
WZRD_CLKOUT_DIVIDE_SHIFT,
657+
WZRD_CLKOUT_DIVIDE_WIDTH,
658+
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
659+
DIV_ALL, &clkwzrd_lock);
660+
661+
goto out;
662+
}
663+
472664
reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0));
473665
reg_f = reg & WZRD_CLKFBOUT_FRAC_MASK;
474666
reg_f = reg_f >> WZRD_CLKFBOUT_FRAC_SHIFT;
475667

476668
reg = reg & WZRD_CLKFBOUT_MULT_MASK;
477669
reg = reg >> WZRD_CLKFBOUT_MULT_SHIFT;
478670
mult = (reg * 1000) + reg_f;
479-
clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
671+
clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
480672
if (!clk_name) {
481673
ret = -ENOMEM;
482674
goto err_disable_clk;
483675
}
484-
485-
ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs);
486-
if (ret || nr_outputs > WZRD_NUM_OUTPUTS) {
487-
ret = -EINVAL;
488-
goto err_disable_clk;
489-
}
490-
if (nr_outputs == 1)
491-
flags = CLK_SET_RATE_PARENT;
492-
493676
clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
494677
(&pdev->dev, clk_name,
495678
__clk_get_name(clk_wzrd->clk_in1),
@@ -500,7 +683,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
500683
goto err_disable_clk;
501684
}
502685

503-
clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
686+
clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
504687
if (!clk_name) {
505688
ret = -ENOMEM;
506689
goto err_rm_int_clk;
@@ -521,9 +704,8 @@ static int clk_wzrd_probe(struct platform_device *pdev)
521704

522705
/* register div per output */
523706
for (i = nr_outputs - 1; i >= 0 ; i--) {
524-
const char *clkout_name;
525-
526-
clkout_name = kasprintf(GFP_KERNEL, "%s_out%d", dev_name(&pdev->dev), i);
707+
clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
708+
"%s_out%d", dev_name(&pdev->dev), i);
527709
if (!clkout_name) {
528710
ret = -ENOMEM;
529711
goto err_rm_int_clk;
@@ -537,7 +719,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
537719
WZRD_CLKOUT_DIVIDE_SHIFT,
538720
WZRD_CLKOUT_DIVIDE_WIDTH,
539721
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
540-
NULL, &clkwzrd_lock);
722+
DIV_O, &clkwzrd_lock);
541723
else
542724
clk_wzrd->clkout[i] = clk_wzrd_register_divider
543725
(&pdev->dev, clkout_name,
@@ -546,7 +728,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
546728
WZRD_CLKOUT_DIVIDE_SHIFT,
547729
WZRD_CLKOUT_DIVIDE_WIDTH,
548730
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
549-
NULL, &clkwzrd_lock);
731+
DIV_O, &clkwzrd_lock);
550732
if (IS_ERR(clk_wzrd->clkout[i])) {
551733
int j;
552734

@@ -559,8 +741,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
559741
}
560742
}
561743

562-
kfree(clk_name);
563-
744+
out:
564745
clk_wzrd->clk_data.clks = clk_wzrd->clkout;
565746
clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout);
566747
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data);
@@ -585,7 +766,6 @@ static int clk_wzrd_probe(struct platform_device *pdev)
585766
err_rm_int_clks:
586767
clk_unregister(clk_wzrd->clks_internal[1]);
587768
err_rm_int_clk:
588-
kfree(clk_name);
589769
clk_unregister(clk_wzrd->clks_internal[0]);
590770
err_disable_clk:
591771
clk_disable_unprepare(clk_wzrd->axi_clk);

0 commit comments

Comments
 (0)