All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mika Kahola <mika.kahola@intel.com>
To: intel-gfx@lists.freedesktop.org
Cc: rodrigo.vivi@intel.com
Subject: [PATCH i-g-t] tools: Cannonlake port clock programming
Date: Thu,  7 Dec 2017 14:36:34 +0200	[thread overview]
Message-ID: <1512650194-8441-1-git-send-email-mika.kahola@intel.com> (raw)

Cannonlake port clock programming tests and verifies DPLL legal dividers
P, Q, and K. This tests adds two reference clocks 19.2MHz and 24MHz to
test algorithm's capability to find P, Q, and K dividers as well as DCO
frequency for different symbol clock rates.

The test compares two algorithms, the reference with double precision and
i915 implementation with fixed point precision. In case of a difference in
computation the difference on dividers is printed out to the screen.

Signed-off-by: Mika Kahola <mika.kahola@intel.com>
---
 tools/Makefile.sources    |   1 +
 tools/cnl_compute_wrpll.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 527 insertions(+)
 create mode 100644 tools/cnl_compute_wrpll.c

diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index c49ab8f..abd23a0 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -2,6 +2,7 @@ noinst_PROGRAMS =		\
 	hsw_compute_wrpll	\
 	skl_compute_wrpll	\
 	skl_ddb_allocation	\
+	cnl_compute_wrpll 	\
 	$(NULL)
 
 tools_prog_lists =		\
diff --git a/tools/cnl_compute_wrpll.c b/tools/cnl_compute_wrpll.c
new file mode 100644
index 0000000..c7b7bd7
--- /dev/null
+++ b/tools/cnl_compute_wrpll.c
@@ -0,0 +1,526 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define U32_MAX         ((uint32_t)~0ULL)
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
+
+static inline uint64_t div_u64(uint64_t dividend, uint32_t divisor)
+{
+	return dividend / divisor;
+}
+
+struct skl_wrpll_params {
+	uint32_t dco_fraction;
+	uint32_t dco_integer;
+	uint32_t qdiv_ratio;
+	uint32_t qdiv_mode;
+	uint32_t kdiv;
+	uint32_t pdiv;
+
+	/* for this test code only */
+	unsigned int ref_clock;
+} __attribute__((packed));
+
+static void dump_params(const char *name, struct skl_wrpll_params *params)
+{
+	printf("%s:\n", name);
+	printf("Pdiv: %d\n", params->pdiv);
+	printf("Qdiv: %d\n", params->qdiv_ratio);
+	printf("Kdiv: %d\n", params->kdiv);
+	printf("qdiv mode: %d\n", params->qdiv_mode);
+	printf("dco integer: %d\n", params->dco_integer);
+	printf("dco fraction: %d\n", params->dco_fraction);
+}
+
+static void compare_params(unsigned int clock,
+			   const char *name1, struct skl_wrpll_params *p1,
+			   const char *name2, struct skl_wrpll_params *p2)
+{
+	if (memcmp(p1, p2, sizeof(struct skl_wrpll_params)) == 0)
+		return;
+
+	printf("=======================================\n");
+	printf("Difference with clock: %10.6f MHz\n", clock/1000000.0);
+	printf("Reference clock:       %10.6f MHz\n\n", p1->ref_clock/1000.0);
+	dump_params(name1, p1);
+	printf("\n");
+	dump_params(name2, p2);
+	printf("=======================================\n");
+}
+
+static void cnl_wrpll_params_populate(struct skl_wrpll_params *params,
+				      uint32_t dco_freq, uint32_t ref_freq,
+				      uint32_t pdiv, uint32_t qdiv,
+				      uint32_t kdiv)
+{
+	uint32_t dco;
+
+	params->qdiv_ratio = qdiv;
+	params->qdiv_mode = (qdiv == 1) ? 0 : 1;
+	params->pdiv = pdiv;
+	params->kdiv = kdiv;
+
+	if (kdiv != 2 && qdiv != 1)
+		printf("kdiv != 2 and qdiv != 1\n");
+
+	dco = div_u64((uint64_t)dco_freq << 15, ref_freq);
+
+	params->dco_integer = dco >> 15;
+	params->dco_fraction = dco & 0x7fff;
+}
+
+static void cnl_wrpll_get_multipliers(int bestdiv,
+				      int *pdiv,
+				      int *qdiv,
+				      int *kdiv)
+{
+	/* even dividers */
+	if (bestdiv % 2 == 0) {
+		if (bestdiv == 2) {
+			*pdiv = 2;
+			*qdiv = 1;
+			*kdiv = 1;
+		} else if (bestdiv % 4 == 0) {
+			*pdiv = 2;
+			*qdiv = bestdiv / 4;
+			*kdiv = 2;
+		} else if (bestdiv % 6 == 0) {
+			*pdiv = 3;
+			*qdiv = bestdiv / 6;
+			*kdiv = 2;
+		} else if (bestdiv % 5 == 0) {
+			*pdiv = 5;
+			*qdiv = bestdiv / 10;
+			*kdiv = 2;
+		} else if (bestdiv % 14 == 0) {
+			*pdiv = 7;
+			*qdiv = bestdiv / 14;
+			*kdiv = 2;
+		}
+	} else {
+		if (bestdiv == 3 || bestdiv == 5 || bestdiv == 7) {
+			*pdiv = bestdiv;
+			*qdiv = 1;
+			*kdiv = 1;
+		} else { /* 9, 15, 21 */
+			*pdiv = bestdiv / 3;
+			*qdiv = 1;
+			*kdiv = 3;
+		}
+	}
+}
+
+static bool
+cnl_ddi_calculate_wrpll1(int clock /* in Hz */,
+			 struct skl_wrpll_params *params)
+{
+	double afe_clock = (clock/1000000.0) * 5; /* clocks in MHz */
+	double dco_min = 7998;
+	double dco_max = 10000;
+	double dco_mid = (dco_min + dco_max) / 2;
+	static const int dividers[] = {  2,  4,  6,  8, 10, 12,  14,  16,
+					 18, 20, 24, 28, 30, 32,  36,  40,
+					 42, 44, 48, 50, 52, 54,  56,  60,
+					 64, 66, 68, 70, 72, 76,  78,  80,
+					 84, 88, 90, 92, 96, 98, 100, 102,
+					 3,  5,  7,  9, 15, 21 };
+	double dco, dco_centrality = 0;
+	double best_dco_centrality = 999999;
+	int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0;
+	double ref_clock = params->ref_clock/1000.0; /* MHz */
+	uint32_t dco_int, dco_frac;
+
+	for (d = 0; d < ARRAY_SIZE(dividers); d++) {
+		dco = afe_clock * dividers[d];
+
+		if ((dco <= dco_max) && (dco >= dco_min)) {
+			dco_centrality = fabs(dco - dco_mid);
+
+			if (dco_centrality < best_dco_centrality) {
+				best_dco_centrality = dco_centrality;
+				best_div = dividers[d];
+				dco_int = (uint32_t)(dco/ref_clock);
+				dco_frac = round((dco/ref_clock - dco_int) * (1<<15));
+			}
+		}
+	}
+
+	if (best_div != 0) {
+		cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv);
+
+		params->qdiv_ratio = qdiv;
+		params->qdiv_mode = (qdiv == 1) ? 0 : 1;
+		params->pdiv = pdiv;
+		params->kdiv = kdiv;
+		params->dco_integer = dco_int;
+		params->dco_fraction = dco_frac;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+static bool
+cnl_ddi_calculate_wrpll2(int clock,
+			 struct skl_wrpll_params *params)
+{
+	uint32_t afe_clock = clock * 5 / 1000; /* clock in kHz */
+	uint32_t dco_min = 7998000;
+	uint32_t dco_max = 10000000;
+	uint32_t dco_mid = (dco_min + dco_max) / 2;
+	static const int dividers[] = {  2,  4,  6,  8, 10, 12,  14,  16,
+					 18, 20, 24, 28, 30, 32,  36,  40,
+					 42, 44, 48, 50, 52, 54,  56,  60,
+					 64, 66, 68, 70, 72, 76,  78,  80,
+					 84, 88, 90, 92, 96, 98, 100, 102,
+					  3,  5,  7,  9, 15, 21 };
+	uint32_t dco, best_dco = 0, dco_centrality = 0;
+	uint32_t best_dco_centrality = U32_MAX; /* Spec meaning of 999999 MHz */
+	int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0;
+	uint32_t ref_clock = params->ref_clock;
+
+	for (d = 0; d < ARRAY_SIZE(dividers); d++) {
+		dco = afe_clock * dividers[d];
+
+		if ((dco <= dco_max) && (dco >= dco_min)) {
+			dco_centrality = abs(dco - dco_mid);
+
+			if (dco_centrality < best_dco_centrality) {
+				best_dco_centrality = dco_centrality;
+				best_div = dividers[d];
+				best_dco = dco;
+			}
+		}
+	}
+
+	if (best_div == 0)
+		return false;
+
+	cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv);
+
+	cnl_wrpll_params_populate(params, best_dco, ref_clock,
+				  pdiv, qdiv, kdiv);
+
+	return true;
+}
+
+static void test_multipliers(unsigned int clock)
+{
+	uint64_t afe_clock = clock * 5 / 1000; /* clocks in kHz */
+	unsigned int dco_min = 7998000;
+	unsigned int dco_max = 10000000;
+	unsigned int dco_mid = (dco_min + dco_max) / 2;
+
+	static const int dividerlist[] = {  2,  4,  6,  8, 10, 12,  14,  16,
+					   18, 20, 24, 28, 30, 32,  36,  40,
+					   42, 44, 48, 50, 52, 54,  56,  60,
+					   64, 66, 68, 70, 72, 76,  78,  80,
+					   84, 88, 90, 92, 96, 98, 100, 102,
+					    3,  5,  7,  9, 15, 21 };
+	unsigned int dco, dco_centrality = 0;
+	unsigned int best_dco_centrality = U32_MAX;
+	int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0;
+
+	for (d = 0; d < ARRAY_SIZE(dividerlist); d++) {
+		dco = afe_clock * dividerlist[d];
+
+		if ((dco <= dco_max) && (dco >= dco_min)) {
+			dco_centrality = abs(dco - dco_mid);
+
+			if (dco_centrality < best_dco_centrality) {
+				best_dco_centrality = dco_centrality;
+				best_div = dividerlist[d];
+			}
+		}
+
+		if (best_div != 0) {
+			cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv);
+
+			if ((kdiv != 2) && (qdiv == 1))
+				continue;
+			else
+				break;
+		}
+	}
+
+	assert(pdiv);
+	assert(qdiv);
+	assert(kdiv);
+
+	if (kdiv != 2)
+		assert(qdiv == 1);
+}
+
+static const struct {
+	uint32_t clock; /* in Hz */
+} modes[] = {
+	{19750000},
+	{23500000},
+	{23750000},
+	{25175000},
+	{25200000},
+	{26000000},
+	{27000000},
+	{27027000},
+	{27500000},
+	{28750000},
+	{29750000},
+	{30750000},
+	{31500000},
+	{35000000},
+	{35500000},
+	{36750000},
+	{37000000},
+	{37088000},
+	{37125000},
+	{37762500},
+	{37800000},
+	{38250000},
+	{40500000},
+	{40541000},
+	{40750000},
+	{41000000},
+	{41500000},
+	{42500000},
+	{45250000},
+	{46360000},
+	{46406000},
+	{46750000},
+	{49000000},
+	{50500000},
+	{52000000},
+	{54000000},
+	{54054000},
+	{54500000},
+	{55632000},
+	{55688000},
+	{56000000},
+	{56750000},
+	{58250000},
+	{58750000},
+	{59341000},
+	{59400000},
+	{60500000},
+	{62250000},
+	{63500000},
+	{64000000},
+	{65250000},
+	{65500000},
+	{66750000},
+	{67750000},
+	{68250000},
+	{69000000},
+	{72000000},
+	{74176000},
+	{74250000},
+	{74500000},
+	{75250000},
+	{76000000},
+	{79500000},
+	{81000000},
+	{81081000},
+	{82000000},
+	{83000000},
+	{84750000},
+	{85250000},
+	{85750000},
+	{88500000},
+	{89012000},
+	{89100000},
+	{91000000},
+	{92719800},
+	{92812500},
+	{94500000},
+	{95750000},
+	{97750000},
+	{99000000},
+	{99750000},
+	{100000000},
+	{100500000},
+	{101000000},
+	{101250000},
+	{102250000},
+	{107892000},
+	{108000000},
+	{108108000},
+	{109000000},
+	{110250000},
+	{110500000},
+	{111264000},
+	{111375000},
+	{112500000},
+	{117500000},
+	{119000000},
+	{119500000},
+	{121250000},
+	{121750000},
+	{125250000},
+	{125750000},
+	{127250000},
+	{130000000},
+	{130250000},
+	{131000000},
+	{131500000},
+	{132750000},
+	{135250000},
+	{138500000},
+	{138750000},
+	{141500000},
+	{146250000},
+	{148250000},
+	{148352000},
+	{148500000},
+	{154000000},
+	{155250000},
+	{155750000},
+	{156000000},
+	{158250000},
+	{159500000},
+	{161000000},
+	{162000000},
+	{162162000},
+	{162500000},
+	{169500000},
+	{172750000},
+	{173000000},
+	{175000000},
+	{178500000},
+	{179500000},
+	{184750000},
+	{185440000},
+	{185625000},
+	{187000000},
+	{192250000},
+	{193250000},
+	{197750000},
+	{198500000},
+	{204750000},
+	{207500000},
+	{209250000},
+	{213750000},
+	{214750000},
+	{216000000},
+	{218750000},
+	{219000000},
+	{220750000},
+	{222525000},
+	{222750000},
+	{227000000},
+	{230250000},
+	{233500000},
+	{235000000},
+	{238000000},
+	{241500000},
+	{243000000},
+	{245250000},
+	{247750000},
+	{253250000},
+	{256250000},
+	{262500000},
+	{267250000},
+	{268500000},
+	{270000000},
+	{272500000},
+	{273750000},
+	{280750000},
+	{281250000},
+	{286000000},
+	{291750000},
+	{296703000},
+	{297000000},
+	{298000000},
+	{303750000},
+	{322250000},
+	{324000000},
+	{337750000},
+	{370878750},
+	{371250000},
+	{373250000},
+	{414500000},
+	{432000000},
+	{445054500},
+	{445500000},
+	{497750000},
+	{533250000},
+	{540000000},
+	{592500000},
+	{594000000},
+	{648000000},
+	{810000000},
+};
+
+static void test_run(unsigned int ref_clock)
+{
+	unsigned int m;
+	struct skl_wrpll_params params[2];
+
+	for (m = 0; m < ARRAY_SIZE(modes); m++) {
+		int clock = modes[m].clock;
+		bool skip = false;
+
+		params[0].ref_clock = params[1].ref_clock = ref_clock;
+
+		if (!cnl_ddi_calculate_wrpll1(clock, &params[0])) {
+			fprintf(stderr, "Reference: Couldn't compute divider for %dHz, reference %dHz\n",
+				clock, params[0].ref_clock*1000);
+			skip = true;
+		}
+
+		if (!skip) {
+			if (!cnl_ddi_calculate_wrpll2(clock, &params[1])) {
+				fprintf(stderr, "i915 implementation: Couldn't compute divider for %dHz, reference %dHz\n",
+					clock, params[1].ref_clock*1000);
+			}
+
+			compare_params(clock, "Reference", &params[0],
+				       "i915 implementation", &params[1]);
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	unsigned int m;
+	unsigned int f;
+	unsigned int ref_clocks[] = {19200, 24000}; /* in kHz */
+
+	for (m = 0; m < ARRAY_SIZE(modes); m++)
+		test_multipliers(modes[m].clock);
+
+	for (f = 0; f < ARRAY_SIZE(ref_clocks); f++) {
+		printf("=== Testing with ref clock %d kHz\n", ref_clocks[f]);
+		test_run(ref_clocks[f]);
+	}
+
+	return 0;
+}
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

             reply	other threads:[~2017-12-07 12:36 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-07 12:36 Mika Kahola [this message]
2017-12-07 17:45 ` ✓ Fi.CI.BAT: success for tools: Cannonlake port clock programming Patchwork
2017-12-07 22:08 ` ✗ Fi.CI.IGT: failure " Patchwork
2017-12-20 17:27 ` [PATCH i-g-t] " Rodrigo Vivi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1512650194-8441-1-git-send-email-mika.kahola@intel.com \
    --to=mika.kahola@intel.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=rodrigo.vivi@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.