Re: [PATCH i-g-t] tools: Cannonlake port clock programming

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Dec 07, 2017 at 12:36:34PM +0000, Mika Kahola wrote:
> 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@xxxxxxxxx>

Acked-by: Rodrigo Vivi <rodrigo.vivi@xxxxxxxxx>

> ---
>  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@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/intel-gfx




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux