| /* |
| * 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; |
| } |