Re: [PATCH 1/2] clk: axi-clkgen: Add support for fractional dividers

2020-10-13 Thread Stephen Boyd
Quoting Alexandru Ardelean (2020-10-01 01:59:47)
> From: Lars-Peter Clausen 
> 
> The axi-clkgen has (optional) fractional dividers on the output clock
> divider and feedback clock divider path. Utilizing the fractional dividers
> allows for a better resolution of the output clock, being able to
> synthesize more frequencies.
> 
> Rework the driver support to support the fractional register fields, both
> for setting a new rate as well as reading back the current rate from the
> hardware.
> 
> For setting the rate if no perfect divider settings were found in
> non-fractional mode try again in fractional mode and see if better settings
> can be found. This appears to be the recommended mode of operation.
> 
> Signed-off-by: Lars-Peter Clausen 
> Signed-off-by: Alexandru Ardelean 
> ---

Applied to clk-next


[PATCH 1/2] clk: axi-clkgen: Add support for fractional dividers

2020-10-01 Thread Alexandru Ardelean
From: Lars-Peter Clausen 

The axi-clkgen has (optional) fractional dividers on the output clock
divider and feedback clock divider path. Utilizing the fractional dividers
allows for a better resolution of the output clock, being able to
synthesize more frequencies.

Rework the driver support to support the fractional register fields, both
for setting a new rate as well as reading back the current rate from the
hardware.

For setting the rate if no perfect divider settings were found in
non-fractional mode try again in fractional mode and see if better settings
can be found. This appears to be the recommended mode of operation.

Signed-off-by: Lars-Peter Clausen 
Signed-off-by: Alexandru Ardelean 
---

Series split away from [1]:
  
https://lore.kernel.org/linux-clk/20200929144417.89816-9-alexandru.ardel...@analog.com/T/#t

After a review on that, it was concluded that dt-binding conversion to
yaml format would be a good idea.
The conversion went out via:
  
https://lore.kernel.org/linux-clk/20201001085035.82938-1-alexandru.ardel...@analog.com/T/#u

It will be reviewed by Rob Herring when he can get to it.
The first 2 patches from the series [1] are independent from dt-binding
context.

 drivers/clk/clk-axi-clkgen.c | 180 +--
 1 file changed, 129 insertions(+), 51 deletions(-)

diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 96f351785b41..1df03cc6d089 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -27,8 +27,10 @@
 
 #define AXI_CLKGEN_V2_DRP_STATUS_BUSY  BIT(16)
 
+#define MMCM_REG_CLKOUT5_2 0x07
 #define MMCM_REG_CLKOUT0_1 0x08
 #define MMCM_REG_CLKOUT0_2 0x09
+#define MMCM_REG_CLKOUT6_2 0x13
 #define MMCM_REG_CLK_FB1   0x14
 #define MMCM_REG_CLK_FB2   0x15
 #define MMCM_REG_CLK_DIV   0x16
@@ -40,6 +42,7 @@
 
 #define MMCM_CLKOUT_NOCOUNTBIT(6)
 
+#define MMCM_CLK_DIV_DIVIDEBIT(11)
 #define MMCM_CLK_DIV_NOCOUNT   BIT(12)
 
 struct axi_clkgen {
@@ -107,6 +110,8 @@ static void axi_clkgen_calc_params(unsigned long fin, 
unsigned long fout,
unsigned long d, d_min, d_max, _d_min, _d_max;
unsigned long m, m_min, m_max;
unsigned long f, dout, best_f, fvco;
+   unsigned long fract_shift = 0;
+   unsigned long fvco_min_fract, fvco_max_fract;
 
fin /= 1000;
fout /= 1000;
@@ -119,42 +124,89 @@ static void axi_clkgen_calc_params(unsigned long fin, 
unsigned long fout,
d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
d_max = min_t(unsigned long, fin / fpfd_min, 80);
 
-   m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
-   m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
+again:
+   fvco_min_fract = fvco_min << fract_shift;
+   fvco_max_fract = fvco_max << fract_shift;
+
+   m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 
1);
+   m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << 
fract_shift);
 
for (m = m_min; m <= m_max; m++) {
-   _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
-   _d_max = min(d_max, fin * m / fvco_min);
+   _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract));
+   _d_max = min(d_max, fin * m / fvco_min_fract);
 
for (d = _d_min; d <= _d_max; d++) {
fvco = fin * m / d;
 
dout = DIV_ROUND_CLOSEST(fvco, fout);
-   dout = clamp_t(unsigned long, dout, 1, 128);
+   dout = clamp_t(unsigned long, dout, 1, 128 << 
fract_shift);
f = fvco / dout;
if (abs(f - fout) < abs(best_f - fout)) {
best_f = f;
*best_d = d;
-   *best_m = m;
-   *best_dout = dout;
+   *best_m = m << (3 - fract_shift);
+   *best_dout = dout << (3 - fract_shift);
if (best_f == fout)
return;
}
}
}
+
+   /* Lets see if we find a better setting in fractional mode */
+   if (fract_shift == 0) {
+   fract_shift = 3;
+   goto again;
+   }
 }
 
-static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
-   unsigned int *high, unsigned int *edge, unsigned int *nocount)
+struct axi_clkgen_div_params {
+   unsigned int low;
+   unsigned int high;
+   unsigned int edge;
+   unsigned int nocount;
+   unsigned int frac_en;
+   unsigned int frac;
+   unsigned int frac_wf_f;
+   unsigned int frac_wf_r;
+   unsigned int frac_phase;
+};
+
+static void axi_clkgen_calc_clk_params(unsigned int divider,
+   unsigned int frac_divider, struct axi_clkgen_div_