linux/drivers/clk/si5324drv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Si5324 clock driver
   4 *
   5 * Copyright (C) 2017-2018 Xilinx, Inc.
   6 *
   7 * Author:      Venkateshwar Rao G <vgannava.xilinx.com>
   8 *              Leon Woestenberg <leon@sidebranch.com>
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include "si5324drv.h"
  14
  15/**
  16 * si5324_rate_approx - Find closest rational approximation N2_LS/N3 fraction.
  17 *
  18 * @f:          Holds the N2_LS/N3 fraction in 36.28 fixed point notation.
  19 * @md:         Holds the maximum denominator (N3) value allowed.
  20 * @num:        Store the numinator (N2_LS) found.
  21 * @denom:      Store the denominator (N3) found.
  22 *
  23 * This function finds the closest rational approximation.
  24 * It allows only n/1 solution and as a part of the calculation
  25 * multiply fraction until no digits after the decimal point and
  26 * continued fraction and check denominator at each step.
  27 */
  28void si5324_rate_approx(u64 f, u64 md, u32 *num, u32 *denom)
  29{
  30        u64 a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 };
  31        u64 x, d, m, n = 1;
  32        int i = 0;
  33
  34        if (md <= 1) {
  35                *denom = 1;
  36                *num = (u32)(f >> 28);
  37                return;
  38        }
  39
  40        n <<= 28;
  41        for (i = 0; i < 28; i++) {
  42                if ((f & 0x1) == 0) {
  43                        n >>= 1;
  44                        f >>= 1;
  45                } else {
  46                        break;
  47                }
  48        }
  49        d = f;
  50
  51        for (i = 0; i < 64; i++) {
  52                a = n ? (div64_u64(d, n)) : 0;
  53                if (i && !a)
  54                        break;
  55                x = d;
  56                d = n;
  57                div64_u64_rem(x, n, &m);
  58                n = m;
  59                x = a;
  60                if (k[1] * a + k[0] >= md) {
  61                        x = div64_u64((md - k[0]), k[1]);
  62                        if (x * 2 >= a || k[1] >= md)
  63                                i = 65;
  64                        else
  65                                break;
  66                }
  67                h[2] = x * h[1] + h[0];
  68                h[0] = h[1];
  69                h[1] = h[2];
  70                k[2] = x * k[1] + k[0];
  71                k[0] = k[1];
  72                k[1] = k[2];
  73        }
  74
  75        *denom = (u32)k[1];
  76        *num = (u32)h[1];
  77}
  78
  79/**
  80 * si5324_find_n2ls - Search through the possible settings for the N2_LS.
  81 *
  82 * @settings:   Holds the settings up till now.
  83 *
  84 * This function finds the best setting for N2_LS and N3n with the values
  85 * for N1_HS, NCn_LS, and N2_HS.
  86 *
  87 * Return:      1 when the best possible result has been found, 0 on failure.
  88 */
  89static int si5324_find_n2ls(struct si5324_settingst *settings)
  90{
  91        u32 result = 0;
  92        u64 f3_actual;
  93        u64 fosc_actual;
  94        u64 fout_actual;
  95        u64 delta_fout;
  96        u64 n2_ls_div_n3, mult_res;
  97        u32 mult;
  98
  99        n2_ls_div_n3 = div64_u64(div64_u64(div64_u64(settings->fosc,
 100                        (settings->fin >> SI5324_FIN_FOUT_SHIFT)),
 101                        (u64)settings->n2_hs), (u64)2);
 102
 103        si5324_rate_approx(n2_ls_div_n3, settings->n31_max, &settings->n2_ls,
 104                                &settings->n31);
 105        settings->n2_ls *= 2;
 106
 107        if (settings->n2_ls < settings->n2_ls_min) {
 108                mult = div64_u64(settings->n2_ls_min, settings->n2_ls);
 109                div64_u64_rem(settings->n2_ls_min, settings->n2_ls, &mult_res);
 110                mult = mult_res ? mult + 1 : mult;
 111                settings->n2_ls *= mult;
 112                settings->n31 *= mult;
 113        }
 114
 115        if (settings->n31 < settings->n31_min) {
 116                mult = div64_u64(settings->n31_min, settings->n31);
 117                div64_u64_rem(settings->n31_min, settings->n31, &mult_res);
 118                mult = mult_res ? mult + 1 : mult;
 119                settings->n2_ls *= mult;
 120                settings->n31 *= mult;
 121        }
 122        pr_debug("Trying N2_LS = %d N3 = %d.\n", settings->n2_ls,
 123                 settings->n31);
 124
 125        if (settings->n2_ls < settings->n2_ls_min ||
 126            settings->n2_ls > settings->n2_ls_max) {
 127                pr_info("N2_LS out of range.\n");
 128        } else if ((settings->n31 < settings->n31_min) ||
 129                   (settings->n31 > settings->n31_max)) {
 130                pr_info("N3 out of range.\n");
 131        } else {
 132                f3_actual = div64_u64(settings->fin, settings->n31);
 133                fosc_actual = f3_actual * settings->n2_hs * settings->n2_ls;
 134                fout_actual = div64_u64(fosc_actual,
 135                                        (settings->n1_hs * settings->nc1_ls));
 136                delta_fout = fout_actual - settings->fout;
 137
 138                if ((f3_actual < ((u64)SI5324_F3_MIN) <<
 139                        SI5324_FIN_FOUT_SHIFT) ||
 140                        (f3_actual > ((u64)SI5324_F3_MAX) <<
 141                        SI5324_FIN_FOUT_SHIFT)) {
 142                        pr_debug("F3 frequency out of range.\n");
 143                } else if ((fosc_actual < ((u64)SI5324_FOSC_MIN) <<
 144                                SI5324_FIN_FOUT_SHIFT) ||
 145                                (fosc_actual > ((u64)SI5324_FOSC_MAX) <<
 146                                SI5324_FIN_FOUT_SHIFT)) {
 147                        pr_debug("Fosc frequency out of range.\n");
 148                } else if ((fout_actual < ((u64)SI5324_FOUT_MIN) <<
 149                                SI5324_FIN_FOUT_SHIFT) ||
 150                                (fout_actual > ((u64)SI5324_FOUT_MAX) <<
 151                                SI5324_FIN_FOUT_SHIFT)) {
 152                        pr_debug("Fout frequency out of range.\n");
 153                } else {
 154                        pr_debug("Found solution: fout = %dHz delta = %dHz.\n",
 155                                 (u32)(fout_actual >> SI5324_FIN_FOUT_SHIFT),
 156                                 (u32)(delta_fout >> SI5324_FIN_FOUT_SHIFT));
 157                        pr_debug("fosc = %dkHz f3 = %dHz.\n",
 158                                 (u32)((fosc_actual >> SI5324_FIN_FOUT_SHIFT) /
 159                                       1000),
 160                                 (u32)(f3_actual >> SI5324_FIN_FOUT_SHIFT));
 161
 162                        if (((u64)abs(delta_fout)) <
 163                                settings->best_delta_fout) {
 164                                settings->best_n1_hs = settings->n1_hs;
 165                                settings->best_nc1_ls = settings->nc1_ls;
 166                                settings->best_n2_hs = settings->n2_hs;
 167                                settings->best_n2_ls = settings->n2_ls;
 168                                settings->best_n3 = settings->n31;
 169                                settings->best_fout = fout_actual;
 170                                settings->best_delta_fout = abs(delta_fout);
 171                                if (delta_fout == 0)
 172                                        result = 1;
 173                        }
 174                }
 175        }
 176        return result;
 177}
 178
 179/**
 180 * si5324_find_n2 - Find a valid setting for N2_HS and N2_LS.
 181 *
 182 * @settings:   Holds the settings up till now.
 183 *
 184 * This function finds a valid settings for N2_HS and N2_LS. Iterates over
 185 * all possibilities of N2_HS and then performs a binary search over the
 186 * N2_LS values.
 187 *
 188 * Return:      1 when the best possible result has been found.
 189 */
 190static int si5324_find_n2(struct si5324_settingst *settings)
 191{
 192        u32 result = 0;
 193
 194        for (settings->n2_hs = SI5324_N2_HS_MAX; settings->n2_hs >=
 195                SI5324_N2_HS_MIN; settings->n2_hs--) {
 196                pr_debug("Trying N2_HS = %d.\n", settings->n2_hs);
 197                settings->n2_ls_min = (u32)(div64_u64(settings->fosc,
 198                                        ((u64)(SI5324_F3_MAX * settings->n2_hs)
 199                                                << SI5324_FIN_FOUT_SHIFT)));
 200
 201                if (settings->n2_ls_min < SI5324_N2_LS_MIN)
 202                        settings->n2_ls_min = SI5324_N2_LS_MIN;
 203
 204                settings->n2_ls_max = (u32)(div64_u64(settings->fosc,
 205                                        ((u64)(SI5324_F3_MIN *
 206                                        settings->n2_hs) <<
 207                                        SI5324_FIN_FOUT_SHIFT)));
 208                if (settings->n2_ls_max > SI5324_N2_LS_MAX)
 209                        settings->n2_ls_max = SI5324_N2_LS_MAX;
 210
 211                result = si5324_find_n2ls(settings);
 212                if (result)
 213                        break;
 214        }
 215        return result;
 216}
 217
 218/**
 219 * si5324_calc_ncls_limits - Calculates the valid range for NCn_LS.
 220 *
 221 * @settings:   Holds the input and output frequencies and the setting
 222 * for N1_HS.
 223 *
 224 * This function calculates the valid range for NCn_LS with the value
 225 * for the output frequency and N1_HS already set in settings.
 226 *
 227 * Return:      -1 when there are no valid settings, 0 otherwise.
 228 */
 229int si5324_calc_ncls_limits(struct si5324_settingst *settings)
 230{
 231        settings->nc1_ls_min = div64_u64(settings->n1_hs_min,
 232                                         settings->n1_hs);
 233
 234        if (settings->nc1_ls_min < SI5324_NC_LS_MIN)
 235                settings->nc1_ls_min = SI5324_NC_LS_MIN;
 236        if (settings->nc1_ls_min > 1 && (settings->nc1_ls_min & 0x1) == 1)
 237                settings->nc1_ls_min++;
 238        settings->nc1_ls_max = div64_u64(settings->n1_hs_max, settings->n1_hs);
 239
 240        if (settings->nc1_ls_max > SI5324_NC_LS_MAX)
 241                settings->nc1_ls_max = SI5324_NC_LS_MAX;
 242
 243        if ((settings->nc1_ls_max & 0x1) == 1)
 244                settings->nc1_ls_max--;
 245        if ((settings->nc1_ls_max * settings->n1_hs < settings->n1_hs_min) ||
 246            (settings->nc1_ls_min * settings->n1_hs > settings->n1_hs_max))
 247                return -1;
 248
 249        return 0;
 250}
 251
 252/**
 253 * si5324_find_ncls - Find a valid setting for NCn_LS
 254 *
 255 * @settings:   Holds the input and output frequencies, the setting for
 256 * N1_HS, and the limits for NCn_LS.
 257 *
 258 * This function find a valid setting for NCn_LS that can deliver the correct
 259 * output frequency. Assumes that the valid range is relatively small
 260 * so a full search can be done (should be true for video clock frequencies).
 261 *
 262 * Return:      1 when the best possible result has been found.
 263 */
 264static int si5324_find_ncls(struct si5324_settingst *settings)
 265{
 266        u64 fosc_1;
 267        u32 result;
 268
 269        fosc_1 = settings->fout * settings->n1_hs;
 270        for (settings->nc1_ls = settings->nc1_ls_min;
 271                settings->nc1_ls <= settings->nc1_ls_max;) {
 272                settings->fosc = fosc_1 * settings->nc1_ls;
 273                pr_debug("Trying NCn_LS = %d: fosc = %dkHz.\n",
 274                         settings->nc1_ls,
 275                         (u32)(div64_u64((settings->fosc >>
 276                                SI5324_FIN_FOUT_SHIFT), 1000)));
 277
 278                result = si5324_find_n2(settings);
 279                if (result)
 280                        break;
 281                if (settings->nc1_ls == 1)
 282                        settings->nc1_ls++;
 283                else
 284                        settings->nc1_ls += 2;
 285        }
 286        return result;
 287}
 288
 289/**
 290 * si5324_calcfreqsettings - Calculate the frequency settings
 291 *
 292 * @clkinfreq:  Frequency of the input clock.
 293 * @clkoutfreq: Desired output clock frequency.
 294 * @clkactual:  Actual clock frequency.
 295 * @n1_hs:      Set to the value for the N1_HS register.
 296 * @ncn_ls:     Set to the value for the NCn_LS register.
 297 * @n2_hs:      Set to the value for the N2_HS register.
 298 * @n2_ls:      Set to the value for the N2_LS register.
 299 * @n3n:        Set to the value for the N3n register.
 300 * @bwsel:      Set to the value for the BW_SEL register.
 301 *
 302 * This funciton calculates the frequency settings for the desired output
 303 * frequency.
 304 *
 305 * Return:      SI5324_SUCCESS for success, SI5324_ERR_FREQ when the
 306 * requested frequency cannot be generated.
 307 */
 308int si5324_calcfreqsettings(u32 clkinfreq, u32 clkoutfreq, u32 *clkactual,
 309                            u8 *n1_hs, u32 *ncn_ls, u8 *n2_hs, u32 *n2_ls,
 310                            u32 *n3n, u8 *bwsel)
 311{
 312        struct si5324_settingst settings;
 313        int result;
 314
 315        settings.fin = (u64)clkinfreq << SI5324_FIN_FOUT_SHIFT;
 316        settings.fout = (u64)clkoutfreq << SI5324_FIN_FOUT_SHIFT;
 317        settings.best_delta_fout = settings.fout;
 318
 319        settings.n1_hs_min = (int)(div64_u64(SI5324_FOSC_MIN, clkoutfreq));
 320        if (settings.n1_hs_min < SI5324_N1_HS_MIN * SI5324_NC_LS_MIN)
 321                settings.n1_hs_min = SI5324_N1_HS_MIN * SI5324_NC_LS_MIN;
 322
 323        settings.n1_hs_max = (int)(div64_u64(SI5324_FOSC_MAX, clkoutfreq));
 324        if (settings.n1_hs_max > SI5324_N1_HS_MAX * SI5324_NC_LS_MAX)
 325                settings.n1_hs_max = SI5324_N1_HS_MAX * SI5324_NC_LS_MAX;
 326
 327        settings.n31_min = div64_u64(clkinfreq, SI5324_F3_MAX);
 328        if (settings.n31_min < SI5324_N3_MIN)
 329                settings.n31_min = SI5324_N3_MIN;
 330
 331        settings.n31_max = div64_u64(clkinfreq, SI5324_F3_MIN);
 332        if (settings.n31_max > SI5324_N3_MAX)
 333                settings.n31_max = SI5324_N3_MAX;
 334
 335        /* Find a valid oscillator frequency with the highest setting of N1_HS
 336         * possible (reduces power)
 337         */
 338        for (settings.n1_hs = SI5324_N1_HS_MAX;
 339                settings.n1_hs >= SI5324_N1_HS_MIN; settings.n1_hs--) {
 340                pr_debug("Trying N1_HS = %d.\n", settings.n1_hs);
 341
 342                result = si5324_calc_ncls_limits(&settings);
 343                if (result) {
 344                        pr_debug("No valid settings\n");
 345                        continue;
 346                }
 347                result = si5324_find_ncls(&settings);
 348                if (result)
 349                        break;
 350        }
 351
 352                pr_debug("Si5324: settings.best_delta_fout = %llu\n",
 353                         (unsigned long long)settings.best_delta_fout);
 354                pr_debug("Si5324: settings.fout = %llu\n",
 355                         (unsigned long long)settings.fout);
 356
 357        if (settings.best_delta_fout == settings.fout) {
 358                pr_debug("Si5324: No valid settings found.");
 359                return SI5324_ERR_FREQ;
 360        }
 361                pr_debug("Si5324: Found solution: fout = %dHz.\n",
 362                         (u32)(settings.best_fout >> 28));
 363
 364        /* Post processing: convert temporary values to actual registers */
 365        *n1_hs = (u8)settings.best_n1_hs - 4;
 366        *ncn_ls = settings.best_nc1_ls - 1;
 367        *n2_hs = (u8)settings.best_n2_hs - 4;
 368        *n2_ls = settings.best_n2_ls - 1;
 369        *n3n = settings.best_n3 - 1;
 370        /*
 371         * How must the bandwidth selection be determined?
 372         * Not all settings will be valid.
 373         * refclk 2, 0xA2, BWSEL_REG=1010 (?)
 374         * free running 2, 0x42, BWSEL_REG=0100 (?)
 375         */
 376        *bwsel = 6;
 377
 378        if (clkactual)
 379                *clkactual = (settings.best_fout >> SI5324_FIN_FOUT_SHIFT);
 380
 381        return SI5324_SUCCESS;
 382}
 383