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 <[email protected]>

Acked-by: Rodrigo Vivi <[email protected]>

> ---
>  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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to