diff options
author | Mika Kahola <mika.kahola@intel.com> | 2017-12-07 14:36:34 +0200 |
---|---|---|
committer | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2018-01-08 16:56:18 -0800 |
commit | 834321a5d76a16783000441a02d7e79e72be9cc9 (patch) | |
tree | 713167c0cd69dffa61c0dea318c339ee8911f1ce | |
parent | d5e51a60e5cbb807bcacd2655bd4ffe90a686bbb (diff) |
tools: Cannonlake port clock programming
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>
Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
-rw-r--r-- | tools/Makefile.sources | 1 | ||||
-rw-r--r-- | tools/cnl_compute_wrpll.c | 526 |
2 files changed, 527 insertions, 0 deletions
diff --git a/tools/Makefile.sources b/tools/Makefile.sources index c49ab8f0..abd23a0f 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 00000000..c7b7bd72 --- /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, ¶ms[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, ¶ms[1])) { + fprintf(stderr, "i915 implementation: Couldn't compute divider for %dHz, reference %dHz\n", + clock, params[1].ref_clock*1000); + } + + compare_params(clock, "Reference", ¶ms[0], + "i915 implementation", ¶ms[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; +} |