linux-fpga.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver
@ 2020-09-24  6:50 Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 1/6] clk: axi-clkgen: Add support for fractional dividers Alexandru Ardelean
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean

These patches synchronize the driver with the current state in the
Analog Devices Linux tree:
  https://github.com/analogdevicesinc/linux/

They have been in the tree for about 2-3, so they did receive some
testing.

Highlights are:
* Add support for fractional dividers (Lars-Peter Clausen)
* Enable support for ZynqMP (UltraScale) (Dragos Bogdan)
* Support frequency limits for ZynqMP (Mathias Tausen)
  - And continued by Mircea Caprioru, to read them from the IP cores

Changelog v2 -> v3:
* for patch 'include: fpga: adi-axi-common.h: add definitions for supported FPGAs'
  - fix whitespace found by checkpatch
  - add 'Acked-by: Moritz Fischer <mdf@kernel.org>'

Changelog v1 -> v2:
- in patch 'include: fpga: adi-axi-common.h: add definitions for supported FPGAs'
  * converted enums to #define
  * added Intel FPGA definitions
  * added Device-Package definitions
  * added INTEL / XILINX in the define names
 definitions according to:
 https://github.com/analogdevicesinc/hdl/blob/4e438261aa319b1dda4c593c155218a93b1d869b/library/scripts/adi_intel_device_info_enc.tcl
 https://github.com/analogdevicesinc/hdl/blob/4e438261aa319b1dda4c593c155218a93b1d869b/library/scripts/adi_xilinx_device_info_enc.tcl

Dragos Bogdan (1):
  clk: axi-clkgen: add support for ZynqMP (UltraScale)

Lars-Peter Clausen (2):
  clk: axi-clkgen: Add support for fractional dividers
  clk: axi-clkgen: Set power bits for fractional mode

Mathias Tausen (1):
  clk: axi-clkgen: Respect ZYNQMP PFD/VCO frequency limits

Mircea Caprioru (2):
  include: fpga: adi-axi-common.h: add definitions for supported FPGAs
  clk: axi-clkgen: Add support for FPGA info

 drivers/clk/Kconfig                 |   2 +-
 drivers/clk/clk-axi-clkgen.c        | 253 ++++++++++++++++++++++------
 include/linux/fpga/adi-axi-common.h | 103 +++++++++++
 3 files changed, 302 insertions(+), 56 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 1/6] clk: axi-clkgen: Add support for fractional dividers
  2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
@ 2020-09-24  6:50 ` Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 2/6] clk: axi-clkgen: Set power bits for fractional mode Alexandru Ardelean
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean, Lars-Peter Clausen

From: Lars-Peter Clausen <lars@metafoo.de>

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 <lars@metafoo.de>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 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_NOCOUNT	BIT(6)
 
+#define MMCM_CLK_DIV_DIVIDE	BIT(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_params *params)
 {
-	if (divider == 1)
-		*nocount = 1;
-	else
-		*nocount = 0;
 
-	*high = divider / 2;
-	*edge = divider % 2;
-	*low = divider - *high;
+	memset(params, 0x0, sizeof(*params));
+
+	if (divider == 1) {
+		params->nocount = 1;
+		return;
+	}
+
+	if (frac_divider == 0) {
+		params->high = divider / 2;
+		params->edge = divider % 2;
+		params->low = divider - params->high;
+	} else {
+		params->frac_en = 1;
+		params->frac = frac_divider;
+
+		params->high = divider / 2;
+		params->edge = divider % 2;
+		params->low = params->high;
+
+		if (params->edge == 0) {
+			params->high--;
+			params->frac_wf_r = 1;
+		}
+
+		if (params->edge == 0 || frac_divider == 1)
+			params->low--;
+		if (((params->edge == 0) ^ (frac_divider == 1)) ||
+			(divider == 2 && frac_divider == 1))
+			params->frac_wf_f = 1;
+
+		params->frac_phase = params->edge * 4 + frac_divider / 2;
+	}
 }
 
 static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
@@ -246,15 +298,28 @@ static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
 	return container_of(clk_hw, struct axi_clkgen, clk_hw);
 }
 
+static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen,
+	unsigned int reg1, unsigned int reg2, unsigned int reg3,
+	struct axi_clkgen_div_params *params)
+{
+	axi_clkgen_mmcm_write(axi_clkgen, reg1,
+		(params->high << 6) | params->low, 0xefff);
+	axi_clkgen_mmcm_write(axi_clkgen, reg2,
+		(params->frac << 12) | (params->frac_en << 11) |
+		(params->frac_wf_r << 10) | (params->edge << 7) |
+		(params->nocount << 6), 0x7fff);
+	if (reg3 != 0) {
+		axi_clkgen_mmcm_write(axi_clkgen, reg3,
+			(params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00);
+	}
+}
+
 static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
 	unsigned long rate, unsigned long parent_rate)
 {
 	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 	unsigned int d, m, dout;
-	unsigned int nocount;
-	unsigned int high;
-	unsigned int edge;
-	unsigned int low;
+	struct axi_clkgen_div_params params;
 	uint32_t filter;
 	uint32_t lock;
 
@@ -269,21 +334,18 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
 	filter = axi_clkgen_lookup_filter(m - 1);
 	lock = axi_clkgen_lookup_lock(m - 1);
 
-	axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
-	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
-		(high << 6) | low, 0xefff);
-	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
-		(edge << 7) | (nocount << 6), 0x03ff);
+	axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, &params);
+	axi_clkgen_set_div(axi_clkgen,  MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2,
+		MMCM_REG_CLKOUT5_2, &params);
 
-	axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
+	axi_clkgen_calc_clk_params(d, 0, &params);
 	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
-		(edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
+		(params.edge << 13) | (params.nocount << 12) |
+		(params.high << 6) | params.low, 0x3fff);
 
-	axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
-	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
-		(high << 6) | low, 0xefff);
-	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
-		(edge << 7) | (nocount << 6), 0x03ff);
+	axi_clkgen_calc_clk_params(m >> 3, m & 0x7, &params);
+	axi_clkgen_set_div(axi_clkgen,  MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2,
+		MMCM_REG_CLKOUT6_2, &params);
 
 	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
 	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
@@ -313,35 +375,51 @@ static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
 	return min_t(unsigned long long, tmp, LONG_MAX);
 }
 
+static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen,
+	unsigned int reg1, unsigned int reg2)
+{
+	unsigned int val1, val2;
+	unsigned int div;
+
+	axi_clkgen_mmcm_read(axi_clkgen, reg2, &val2);
+	if (val2 & MMCM_CLKOUT_NOCOUNT)
+		return 8;
+
+	axi_clkgen_mmcm_read(axi_clkgen, reg1, &val1);
+
+	div = (val1 & 0x3f) + ((val1 >> 6) & 0x3f);
+	div <<= 3;
+
+	if (val2 & MMCM_CLK_DIV_DIVIDE) {
+		if ((val2 & BIT(7)) && (val2 & 0x7000) != 0x1000)
+			div += 8;
+		else
+			div += 16;
+
+		div += (val2 >> 12) & 0x7;
+	}
+
+	return div;
+}
+
 static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
 	unsigned long parent_rate)
 {
 	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 	unsigned int d, m, dout;
-	unsigned int reg;
 	unsigned long long tmp;
+	unsigned int val;
 
-	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, &reg);
-	if (reg & MMCM_CLKOUT_NOCOUNT) {
-		dout = 1;
-	} else {
-		axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
-		dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
-	}
+	dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1,
+		MMCM_REG_CLKOUT0_2);
+	m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1,
+		MMCM_REG_CLK_FB2);
 
-	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
-	if (reg & MMCM_CLK_DIV_NOCOUNT)
+	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val);
+	if (val & MMCM_CLK_DIV_NOCOUNT)
 		d = 1;
 	else
-		d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
-
-	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, &reg);
-	if (reg & MMCM_CLKOUT_NOCOUNT) {
-		m = 1;
-	} else {
-		axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
-		m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
-	}
+		d = (val & 0x3f) + ((val >> 6) & 0x3f);
 
 	if (d == 0 || dout == 0)
 		return 0;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 2/6] clk: axi-clkgen: Set power bits for fractional mode
  2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 1/6] clk: axi-clkgen: Add support for fractional dividers Alexandru Ardelean
@ 2020-09-24  6:50 ` Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 3/6] clk: axi-clkgen: add support for ZynqMP (UltraScale) Alexandru Ardelean
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean, Lars-Peter Clausen

From: Lars-Peter Clausen <lars@metafoo.de>

Using the fractional dividers requires some additional power bits to be
set.

The fractional power bits are not documented and the current heuristic
for setting them seems be insufficient for some cases. Just always set all
the fractional power bits when in fractional mode.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/clk/clk-axi-clkgen.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 1df03cc6d089..14d803e6af62 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -37,6 +37,7 @@
 #define MMCM_REG_LOCK1		0x18
 #define MMCM_REG_LOCK2		0x19
 #define MMCM_REG_LOCK3		0x1a
+#define MMCM_REG_POWER		0x28
 #define MMCM_REG_FILTER1	0x4e
 #define MMCM_REG_FILTER2	0x4f
 
@@ -320,6 +321,7 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
 	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 	unsigned int d, m, dout;
 	struct axi_clkgen_div_params params;
+	uint32_t power = 0;
 	uint32_t filter;
 	uint32_t lock;
 
@@ -331,6 +333,11 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
 	if (d == 0 || dout == 0 || m == 0)
 		return -EINVAL;
 
+	if ((dout & 0x7) != 0 || (m & 0x7) != 0)
+		power |= 0x9800;
+
+	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_POWER, power, 0x9800);
+
 	filter = axi_clkgen_lookup_filter(m - 1);
 	lock = axi_clkgen_lookup_lock(m - 1);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 3/6] clk: axi-clkgen: add support for ZynqMP (UltraScale)
  2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 1/6] clk: axi-clkgen: Add support for fractional dividers Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 2/6] clk: axi-clkgen: Set power bits for fractional mode Alexandru Ardelean
@ 2020-09-24  6:50 ` Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 4/6] clk: axi-clkgen: Respect ZYNQMP PFD/VCO frequency limits Alexandru Ardelean
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean, Dragos Bogdan

From: Dragos Bogdan <dragos.bogdan@analog.com>

This IP core also works and is supported on the Xilinx ZynqMP (UltraScale)
FPGA boards.
This patch enables the driver to be available on these platforms as well.

Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/clk/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4026fac9fac3..44353f257fe2 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -239,7 +239,7 @@ config CLK_TWL6040
 
 config COMMON_CLK_AXI_CLKGEN
 	tristate "AXI clkgen driver"
-	depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
+	depends on ARCH_ZYNQ || ARCH_ZYNQMP || MICROBLAZE || COMPILE_TEST
 	help
 	  Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
 	  FPGAs. It is commonly used in Analog Devices' reference designs.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 4/6] clk: axi-clkgen: Respect ZYNQMP PFD/VCO frequency limits
  2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
                   ` (2 preceding siblings ...)
  2020-09-24  6:50 ` [PATCH v3 3/6] clk: axi-clkgen: add support for ZynqMP (UltraScale) Alexandru Ardelean
@ 2020-09-24  6:50 ` Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 5/6] include: fpga: adi-axi-common.h: add definitions for supported FPGAs Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info Alexandru Ardelean
  5 siblings, 0 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean, Mathias Tausen

From: Mathias Tausen <mta@gomspace.com>

Since axi-clkgen is now supported on ZYNQMP, make sure the max/min
frequencies of the PFD and VCO are respected.

Signed-off-by: Mathias Tausen <mta@gomspace.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/clk/clk-axi-clkgen.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 14d803e6af62..6ffc19e9d850 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -100,10 +100,17 @@ static uint32_t axi_clkgen_lookup_lock(unsigned int m)
 	return 0x1f1f00fa;
 }
 
+#ifdef ARCH_ZYNQMP
+static const unsigned int fpfd_min = 10000;
+static const unsigned int fpfd_max = 450000;
+static const unsigned int fvco_min = 800000;
+static const unsigned int fvco_max = 1600000;
+#else
 static const unsigned int fpfd_min = 10000;
 static const unsigned int fpfd_max = 300000;
 static const unsigned int fvco_min = 600000;
 static const unsigned int fvco_max = 1200000;
+#endif
 
 static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
 	unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 5/6] include: fpga: adi-axi-common.h: add definitions for supported FPGAs
  2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
                   ` (3 preceding siblings ...)
  2020-09-24  6:50 ` [PATCH v3 4/6] clk: axi-clkgen: Respect ZYNQMP PFD/VCO frequency limits Alexandru Ardelean
@ 2020-09-24  6:50 ` Alexandru Ardelean
  2020-09-24  6:50 ` [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info Alexandru Ardelean
  5 siblings, 0 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean

From: Mircea Caprioru <mircea.caprioru@analog.com>

All (newer) FPGA IP cores supported by Analog Devices, store information in
the synthesized designs. This information describes various parameters,
including the family of boards on which this is deployed, speed-grade, and
so on.

Currently, some of these definitions are deployed mostly on Xilinx boards,
but they have been considered also for FPGA boards from other vendors.

The register definitions are described at this link:
  https://wiki.analog.com/resources/fpga/docs/hdl/regmap
(the 'Base (common to all cores)' section).

Acked-by: Moritz Fischer <mdf@kernel.org>
Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 include/linux/fpga/adi-axi-common.h | 103 ++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/include/linux/fpga/adi-axi-common.h b/include/linux/fpga/adi-axi-common.h
index 141ac3f251e6..1a7f18e3a384 100644
--- a/include/linux/fpga/adi-axi-common.h
+++ b/include/linux/fpga/adi-axi-common.h
@@ -13,6 +13,9 @@
 
 #define ADI_AXI_REG_VERSION			0x0000
 
+#define ADI_AXI_REG_FPGA_INFO			0x001C
+#define ADI_AXI_REG_FPGA_VOLTAGE		0x0140
+
 #define ADI_AXI_PCORE_VER(major, minor, patch)	\
 	(((major) << 16) | ((minor) << 8) | (patch))
 
@@ -20,4 +23,104 @@
 #define ADI_AXI_PCORE_VER_MINOR(version)	(((version) >> 8) & 0xff)
 #define ADI_AXI_PCORE_VER_PATCH(version)	((version) & 0xff)
 
+#define ADI_AXI_INFO_FPGA_VOLTAGE(val)		((val) & 0xffff)
+
+#define ADI_AXI_INFO_FPGA_TECH(info)		(((info) >> 24) & 0xff)
+#define ADI_AXI_INFO_FPGA_FAMILY(info)		(((info) >> 16) & 0xff)
+#define ADI_AXI_INFO_FPGA_SPEED_GRADE(info)	(((info) >> 8) & 0xff)
+#define ADI_AXI_INFO_FPGA_DEV_PACKAGE(info)	((info) & 0xff)
+
+/**
+ * FPGA Technology definitions
+ */
+#define ADI_AXI_FPGA_TECH_XILINX_UNKNOWN		0
+#define ADI_AXI_FPGA_TECH_XILINS_SERIES7		1
+#define ADI_AXI_FPGA_TECH_XILINX_ULTRASCALE		2
+#define ADI_AXI_FPGA_TECH_XILINX_ULTRASCALE_PLUS	3
+
+#define ADI_AXI_FPGA_TECH_INTEL_UNKNOWN			100
+#define ADI_AXI_FPGA_TECH_INTEL_CYCLONE_5		101
+#define ADI_AXI_FPGA_TECH_INTEL_CYCLONE_10		102
+#define ADI_AXI_FPGA_TECH_INTEL_ARRIA_10		103
+#define ADI_AXI_FPGA_TECH_INTEL_STRATIX_10		104
+
+/**
+ * FPGA Family definitions
+ */
+#define ADI_AXI_FPGA_FAMILY_UNKNOWN			0
+
+#define ADI_AXI_FPGA_FAMILY_XILINX_ARTIX		1
+#define ADI_AXI_FPGA_FAMILY_XILINX_KINTEX		2
+#define ADI_AXI_FPGA_FAMILY_XILINX_VIRTEX		3
+#define ADI_AXI_FPGA_FAMILY_XILINX_ZYNQ			4
+
+#define ADI_AXI_FPGA_FAMILY_INTEL_SX			1
+#define ADI_AXI_FPGA_FAMILY_INTEL_GX			2
+#define ADI_AXI_FPGA_FAMILY_INTEL_GT			3
+#define ADI_AXI_FPGA_FAMILY_INTEL_GZ			4
+
+/**
+ * FPGA Speed-grade definitions
+ */
+#define ADI_AXI_FPGA_SPEED_GRADE_UNKNOWN		0
+
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_1		10
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_1L		11
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_1H		12
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_1HV		13
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_1LV		14
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_2		20
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_2L		21
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_2LV		22
+#define ADI_AXI_FPGA_SPEED_GRADE_XILINX_3		30
+
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_1		1
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_2		2
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_3		3
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_4		4
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_5		5
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_6		6
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_7		7
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_8		8
+#define ADI_AXI_FPGA_SPEED_GRADE_INTEL_9		9
+
+/**
+ * FPGA Device Package definitions
+ */
+#define ADI_AXI_FPGA_DEV_PACKAGE_UNKNOWN		0
+
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_RF		1
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FL		2
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FF		3
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FB		4
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_HC		5
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FH		6
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_CS		7
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_CP		8
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FT		9
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FG		10
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_SB		11
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_RB		12
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_RS		13
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_CL		14
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_SF		15
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_BA		16
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FA		17
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FS		18
+#define ADI_AXI_FPGA_DEV_PACKAGE_XILINX_FI		19
+
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_BGA		1
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_PGA		2
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_FBGA		3
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_HBGA		4
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_PDIP		5
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_EQFP		6
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_PLCC		7
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_PQFP		8
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_RQFP		9
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_TQFP		10
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_UBGA		11
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_UFBGA		12
+#define ADI_AXI_FPGA_DEV_PACKAGE_INTEL_MBGA		13
+
 #endif /* ADI_AXI_COMMON_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info
  2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
                   ` (4 preceding siblings ...)
  2020-09-24  6:50 ` [PATCH v3 5/6] include: fpga: adi-axi-common.h: add definitions for supported FPGAs Alexandru Ardelean
@ 2020-09-24  6:50 ` Alexandru Ardelean
  2020-09-24 14:21   ` Moritz Fischer
  5 siblings, 1 reply; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24  6:50 UTC (permalink / raw)
  To: linux-clk, linux-fpga, linux-kernel
  Cc: mturquette, sboyd, mdf, ardeleanalex, mircea.caprioru,
	alexandru.ardelean

From: Mircea Caprioru <mircea.caprioru@analog.com>

This patch adds support for vco maximum and minimum ranges in accordance
with fpga speed grade, voltage, device package, technology and family. This
new information is extracted from two new registers implemented in the ip
core: ADI_REG_FPGA_INFO and ADI_REG_FPGA_VOLTAGE, which are stored in the
'include/linux/fpga/adi-axi-common.h' file as they are common to all ADI
FPGA cores.

Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/clk/clk-axi-clkgen.c | 67 +++++++++++++++++++++++++++++++-----
 1 file changed, 59 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 6ffc19e9d850..b03ea28270cb 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -8,6 +8,7 @@
 
 #include <linux/platform_device.h>
 #include <linux/clk-provider.h>
+#include <linux/fpga/adi-axi-common.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/of.h>
@@ -49,6 +50,7 @@
 struct axi_clkgen {
 	void __iomem *base;
 	struct clk_hw clk_hw;
+	unsigned int pcore_version;
 };
 
 static uint32_t axi_clkgen_lookup_filter(unsigned int m)
@@ -101,15 +103,15 @@ static uint32_t axi_clkgen_lookup_lock(unsigned int m)
 }
 
 #ifdef ARCH_ZYNQMP
-static const unsigned int fpfd_min = 10000;
-static const unsigned int fpfd_max = 450000;
-static const unsigned int fvco_min = 800000;
-static const unsigned int fvco_max = 1600000;
+static unsigned int fpfd_min = 10000;
+static unsigned int fpfd_max = 450000;
+static unsigned int fvco_min = 800000;
+static unsigned int fvco_max = 1600000;
 #else
-static const unsigned int fpfd_min = 10000;
-static const unsigned int fpfd_max = 300000;
-static const unsigned int fvco_min = 600000;
-static const unsigned int fvco_max = 1200000;
+static unsigned int fpfd_min = 10000;
+static unsigned int fpfd_max = 300000;
+static unsigned int fvco_min = 600000;
+static unsigned int fvco_max = 1200000;
 #endif
 
 static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
@@ -229,6 +231,49 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
 	*val = readl(axi_clkgen->base + reg);
 }
 
+static void axi_clkgen_setup_ranges(struct axi_clkgen *axi_clkgen)
+{
+	unsigned int reg_value;
+	unsigned int tech, family, speed_grade, voltage;
+
+	axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_INFO, &reg_value);
+	tech = ADI_AXI_INFO_FPGA_TECH(reg_value);
+	family = ADI_AXI_INFO_FPGA_FAMILY(reg_value);
+	speed_grade = ADI_AXI_INFO_FPGA_SPEED_GRADE(reg_value);
+
+	axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_VOLTAGE, &reg_value);
+	voltage = ADI_AXI_INFO_FPGA_VOLTAGE(reg_value);
+
+	switch (speed_grade) {
+	case ADI_AXI_FPGA_SPEED_GRADE_XILINX_1 ... ADI_AXI_FPGA_SPEED_GRADE_XILINX_1LV:
+		fvco_max = 1200000;
+		fpfd_max = 450000;
+		break;
+	case ADI_AXI_FPGA_SPEED_GRADE_XILINX_2 ... ADI_AXI_FPGA_SPEED_GRADE_XILINX_2LV:
+		fvco_max = 1440000;
+		fpfd_max = 500000;
+		if ((family == ADI_AXI_FPGA_FAMILY_XILINX_KINTEX) |
+		    (family == ADI_AXI_FPGA_FAMILY_XILINX_ARTIX)) {
+			if (voltage < 950) {
+				fvco_max = 1200000;
+				fpfd_max = 450000;
+			}
+		}
+		break;
+	case ADI_AXI_FPGA_SPEED_GRADE_XILINX_3:
+		fvco_max = 1600000;
+		fpfd_max = 550000;
+		break;
+	default:
+		break;
+	};
+
+	if (tech == ADI_AXI_FPGA_TECH_XILINX_ULTRASCALE_PLUS) {
+		fvco_max = 1600000;
+		fvco_min = 800000;
+	}
+}
+
 static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
 {
 	unsigned int timeout = 10000;
@@ -524,6 +569,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
 	if (IS_ERR(axi_clkgen->base))
 		return PTR_ERR(axi_clkgen->base);
 
+	axi_clkgen_read(axi_clkgen, ADI_AXI_REG_VERSION,
+			&axi_clkgen->pcore_version);
+
+	if (ADI_AXI_PCORE_VER_MAJOR(axi_clkgen->pcore_version) > 0x04)
+		axi_clkgen_setup_ranges(axi_clkgen);
+
 	init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
 	if (init.num_parents < 1 || init.num_parents > 2)
 		return -EINVAL;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info
  2020-09-24  6:50 ` [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info Alexandru Ardelean
@ 2020-09-24 14:21   ` Moritz Fischer
  2020-09-24 14:53     ` Alexandru Ardelean
  0 siblings, 1 reply; 9+ messages in thread
From: Moritz Fischer @ 2020-09-24 14:21 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-clk, linux-fpga, linux-kernel, mturquette, sboyd, mdf,
	ardeleanalex, mircea.caprioru

On Thu, Sep 24, 2020 at 09:50:12AM +0300, Alexandru Ardelean wrote:
> From: Mircea Caprioru <mircea.caprioru@analog.com>
> 
> This patch adds support for vco maximum and minimum ranges in accordance
> with fpga speed grade, voltage, device package, technology and family. This
> new information is extracted from two new registers implemented in the ip
> core: ADI_REG_FPGA_INFO and ADI_REG_FPGA_VOLTAGE, which are stored in the
> 'include/linux/fpga/adi-axi-common.h' file as they are common to all ADI
> FPGA cores.
> 
> Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> ---
>  drivers/clk/clk-axi-clkgen.c | 67 +++++++++++++++++++++++++++++++-----
>  1 file changed, 59 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
> index 6ffc19e9d850..b03ea28270cb 100644
> --- a/drivers/clk/clk-axi-clkgen.c
> +++ b/drivers/clk/clk-axi-clkgen.c
> @@ -8,6 +8,7 @@
>  
>  #include <linux/platform_device.h>
>  #include <linux/clk-provider.h>
> +#include <linux/fpga/adi-axi-common.h>
>  #include <linux/slab.h>
>  #include <linux/io.h>
>  #include <linux/of.h>
> @@ -49,6 +50,7 @@
>  struct axi_clkgen {
>  	void __iomem *base;
>  	struct clk_hw clk_hw;
> +	unsigned int pcore_version;
>  };
>  
>  static uint32_t axi_clkgen_lookup_filter(unsigned int m)
> @@ -101,15 +103,15 @@ static uint32_t axi_clkgen_lookup_lock(unsigned int m)
>  }
>  
>  #ifdef ARCH_ZYNQMP
> -static const unsigned int fpfd_min = 10000;
> -static const unsigned int fpfd_max = 450000;
> -static const unsigned int fvco_min = 800000;
> -static const unsigned int fvco_max = 1600000;
> +static unsigned int fpfd_min = 10000;
> +static unsigned int fpfd_max = 450000;
> +static unsigned int fvco_min = 800000;
> +static unsigned int fvco_max = 1600000;
>  #else
> -static const unsigned int fpfd_min = 10000;
> -static const unsigned int fpfd_max = 300000;
> -static const unsigned int fvco_min = 600000;
> -static const unsigned int fvco_max = 1200000;
> +static unsigned int fpfd_min = 10000;
> +static unsigned int fpfd_max = 300000;
> +static unsigned int fvco_min = 600000;
> +static unsigned int fvco_max = 1200000;
>  #endif
Instead of modifying those wouldn't you want those to be part of your
struct axi_clkgen? I understand that they're technically properties of the
fabric you're implementing this IP block in, but are you sure you'll
never have a system with more than one of those in two different FPGAs,
are you never gonna use this beyond ARCH_ZYNQ/ZYNQMP/MICROBLAZE?

What about a PCIe plugin card for a ZynqMP system with a Zynq on it?
>  
>  static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
> @@ -229,6 +231,49 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
>  	*val = readl(axi_clkgen->base + reg);
>  }
>  
> +static void axi_clkgen_setup_ranges(struct axi_clkgen *axi_clkgen)
> +{
> +	unsigned int reg_value;
> +	unsigned int tech, family, speed_grade, voltage;
> +
> +	axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_INFO, &reg_value);
> +	tech = ADI_AXI_INFO_FPGA_TECH(reg_value);
> +	family = ADI_AXI_INFO_FPGA_FAMILY(reg_value);
> +	speed_grade = ADI_AXI_INFO_FPGA_SPEED_GRADE(reg_value);
> +
> +	axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_VOLTAGE, &reg_value);
> +	voltage = ADI_AXI_INFO_FPGA_VOLTAGE(reg_value);
> +
> +	switch (speed_grade) {
> +	case ADI_AXI_FPGA_SPEED_GRADE_XILINX_1 ... ADI_AXI_FPGA_SPEED_GRADE_XILINX_1LV:
> +		fvco_max = 1200000;
> +		fpfd_max = 450000;
> +		break;
> +	case ADI_AXI_FPGA_SPEED_GRADE_XILINX_2 ... ADI_AXI_FPGA_SPEED_GRADE_XILINX_2LV:
> +		fvco_max = 1440000;
> +		fpfd_max = 500000;
> +		if ((family == ADI_AXI_FPGA_FAMILY_XILINX_KINTEX) |
> +		    (family == ADI_AXI_FPGA_FAMILY_XILINX_ARTIX)) {
> +			if (voltage < 950) {
> +				fvco_max = 1200000;
> +				fpfd_max = 450000;
> +			}
> +		}
> +		break;
> +	case ADI_AXI_FPGA_SPEED_GRADE_XILINX_3:
> +		fvco_max = 1600000;
> +		fpfd_max = 550000;
> +		break;
> +	default:
> +		break;
> +	};
> +
> +	if (tech == ADI_AXI_FPGA_TECH_XILINX_ULTRASCALE_PLUS) {
> +		fvco_max = 1600000;
> +		fvco_min = 800000;
> +	}
> +}
> +
>  static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
>  {
>  	unsigned int timeout = 10000;
> @@ -524,6 +569,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
>  	if (IS_ERR(axi_clkgen->base))
>  		return PTR_ERR(axi_clkgen->base);
>  
> +	axi_clkgen_read(axi_clkgen, ADI_AXI_REG_VERSION,
> +			&axi_clkgen->pcore_version);
> +
> +	if (ADI_AXI_PCORE_VER_MAJOR(axi_clkgen->pcore_version) > 0x04)
> +		axi_clkgen_setup_ranges(axi_clkgen);
> +
>  	init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
>  	if (init.num_parents < 1 || init.num_parents > 2)
>  		return -EINVAL;
> -- 
> 2.25.1
> 
Thanks,
Moritz

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info
  2020-09-24 14:21   ` Moritz Fischer
@ 2020-09-24 14:53     ` Alexandru Ardelean
  0 siblings, 0 replies; 9+ messages in thread
From: Alexandru Ardelean @ 2020-09-24 14:53 UTC (permalink / raw)
  To: Moritz Fischer
  Cc: Alexandru Ardelean, linux-clk, linux-fpga, LKML,
	Michael Turquette, Stephen Boyd, Mircea Caprioru

On Thu, Sep 24, 2020 at 5:21 PM Moritz Fischer <mdf@kernel.org> wrote:
>
> On Thu, Sep 24, 2020 at 09:50:12AM +0300, Alexandru Ardelean wrote:
> > From: Mircea Caprioru <mircea.caprioru@analog.com>
> >
> > This patch adds support for vco maximum and minimum ranges in accordance
> > with fpga speed grade, voltage, device package, technology and family. This
> > new information is extracted from two new registers implemented in the ip
> > core: ADI_REG_FPGA_INFO and ADI_REG_FPGA_VOLTAGE, which are stored in the
> > 'include/linux/fpga/adi-axi-common.h' file as they are common to all ADI
> > FPGA cores.
> >
> > Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > ---
> >  drivers/clk/clk-axi-clkgen.c | 67 +++++++++++++++++++++++++++++++-----
> >  1 file changed, 59 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
> > index 6ffc19e9d850..b03ea28270cb 100644
> > --- a/drivers/clk/clk-axi-clkgen.c
> > +++ b/drivers/clk/clk-axi-clkgen.c
> > @@ -8,6 +8,7 @@
> >
> >  #include <linux/platform_device.h>
> >  #include <linux/clk-provider.h>
> > +#include <linux/fpga/adi-axi-common.h>
> >  #include <linux/slab.h>
> >  #include <linux/io.h>
> >  #include <linux/of.h>
> > @@ -49,6 +50,7 @@
> >  struct axi_clkgen {
> >       void __iomem *base;
> >       struct clk_hw clk_hw;
> > +     unsigned int pcore_version;
> >  };
> >
> >  static uint32_t axi_clkgen_lookup_filter(unsigned int m)
> > @@ -101,15 +103,15 @@ static uint32_t axi_clkgen_lookup_lock(unsigned int m)
> >  }
> >
> >  #ifdef ARCH_ZYNQMP
> > -static const unsigned int fpfd_min = 10000;
> > -static const unsigned int fpfd_max = 450000;
> > -static const unsigned int fvco_min = 800000;
> > -static const unsigned int fvco_max = 1600000;
> > +static unsigned int fpfd_min = 10000;
> > +static unsigned int fpfd_max = 450000;
> > +static unsigned int fvco_min = 800000;
> > +static unsigned int fvco_max = 1600000;
> >  #else
> > -static const unsigned int fpfd_min = 10000;
> > -static const unsigned int fpfd_max = 300000;
> > -static const unsigned int fvco_min = 600000;
> > -static const unsigned int fvco_max = 1200000;
> > +static unsigned int fpfd_min = 10000;
> > +static unsigned int fpfd_max = 300000;
> > +static unsigned int fvco_min = 600000;
> > +static unsigned int fvco_max = 1200000;
> >  #endif
> Instead of modifying those wouldn't you want those to be part of your
> struct axi_clkgen? I understand that they're technically properties of the
> fabric you're implementing this IP block in, but are you sure you'll
> never have a system with more than one of those in two different FPGAs,
> are you never gonna use this beyond ARCH_ZYNQ/ZYNQMP/MICROBLAZE?
>
> What about a PCIe plugin card for a ZynqMP system with a Zynq on it?

That is a good point.
Will think about this, and re-implement it per axi_clkgen.
These FPGA can be combined in complex [sometimes weird] ways.

> >
> >  static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
> > @@ -229,6 +231,49 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
> >       *val = readl(axi_clkgen->base + reg);
> >  }
> >
> > +static void axi_clkgen_setup_ranges(struct axi_clkgen *axi_clkgen)
> > +{
> > +     unsigned int reg_value;
> > +     unsigned int tech, family, speed_grade, voltage;
> > +
> > +     axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_INFO, &reg_value);
> > +     tech = ADI_AXI_INFO_FPGA_TECH(reg_value);
> > +     family = ADI_AXI_INFO_FPGA_FAMILY(reg_value);
> > +     speed_grade = ADI_AXI_INFO_FPGA_SPEED_GRADE(reg_value);
> > +
> > +     axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_VOLTAGE, &reg_value);
> > +     voltage = ADI_AXI_INFO_FPGA_VOLTAGE(reg_value);
> > +
> > +     switch (speed_grade) {
> > +     case ADI_AXI_FPGA_SPEED_GRADE_XILINX_1 ... ADI_AXI_FPGA_SPEED_GRADE_XILINX_1LV:
> > +             fvco_max = 1200000;
> > +             fpfd_max = 450000;
> > +             break;
> > +     case ADI_AXI_FPGA_SPEED_GRADE_XILINX_2 ... ADI_AXI_FPGA_SPEED_GRADE_XILINX_2LV:
> > +             fvco_max = 1440000;
> > +             fpfd_max = 500000;
> > +             if ((family == ADI_AXI_FPGA_FAMILY_XILINX_KINTEX) |
> > +                 (family == ADI_AXI_FPGA_FAMILY_XILINX_ARTIX)) {
> > +                     if (voltage < 950) {
> > +                             fvco_max = 1200000;
> > +                             fpfd_max = 450000;
> > +                     }
> > +             }
> > +             break;
> > +     case ADI_AXI_FPGA_SPEED_GRADE_XILINX_3:
> > +             fvco_max = 1600000;
> > +             fpfd_max = 550000;
> > +             break;
> > +     default:
> > +             break;
> > +     };
> > +
> > +     if (tech == ADI_AXI_FPGA_TECH_XILINX_ULTRASCALE_PLUS) {
> > +             fvco_max = 1600000;
> > +             fvco_min = 800000;
> > +     }
> > +}
> > +
> >  static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
> >  {
> >       unsigned int timeout = 10000;
> > @@ -524,6 +569,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
> >       if (IS_ERR(axi_clkgen->base))
> >               return PTR_ERR(axi_clkgen->base);
> >
> > +     axi_clkgen_read(axi_clkgen, ADI_AXI_REG_VERSION,
> > +                     &axi_clkgen->pcore_version);
> > +
> > +     if (ADI_AXI_PCORE_VER_MAJOR(axi_clkgen->pcore_version) > 0x04)
> > +             axi_clkgen_setup_ranges(axi_clkgen);
> > +
> >       init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
> >       if (init.num_parents < 1 || init.num_parents > 2)
> >               return -EINVAL;
> > --
> > 2.25.1
> >
> Thanks,
> Moritz

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-09-24 14:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-24  6:50 [PATCH v3 0/6] clk: axi-clk-gen: misc updates to the driver Alexandru Ardelean
2020-09-24  6:50 ` [PATCH v3 1/6] clk: axi-clkgen: Add support for fractional dividers Alexandru Ardelean
2020-09-24  6:50 ` [PATCH v3 2/6] clk: axi-clkgen: Set power bits for fractional mode Alexandru Ardelean
2020-09-24  6:50 ` [PATCH v3 3/6] clk: axi-clkgen: add support for ZynqMP (UltraScale) Alexandru Ardelean
2020-09-24  6:50 ` [PATCH v3 4/6] clk: axi-clkgen: Respect ZYNQMP PFD/VCO frequency limits Alexandru Ardelean
2020-09-24  6:50 ` [PATCH v3 5/6] include: fpga: adi-axi-common.h: add definitions for supported FPGAs Alexandru Ardelean
2020-09-24  6:50 ` [PATCH v3 6/6] clk: axi-clkgen: Add support for FPGA info Alexandru Ardelean
2020-09-24 14:21   ` Moritz Fischer
2020-09-24 14:53     ` Alexandru Ardelean

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).