/** * struct clk_si544_muldiv - Multiplier/divider settings * @fb_div_frac: integer part of feedback divider (32 bits) * @fb_div_int: fractional part of feedback divider (11 bits) * @hs_div: 1st divider, 5..2046, must be even when >33 * @ls_div_bits: 2nd divider, as 2^x, range 0..5 * If ls_div_bits is non-zero, hs_div must be even * @delta_m: Frequency shift for small -950..+950 ppm changes, 24 bit
*/ struct clk_si544_muldiv {
u32 fb_div_frac;
u16 fb_div_int;
u16 hs_div;
u8 ls_div_bits;
s32 delta_m;
};
/* * Writing to SI544_REG_FBDIV40 triggers the clock change, so that * must be written last
*/ return regmap_bulk_write(data->regmap, SI544_REG_FBDIV0, reg, 6);
}
/* Calculate divider settings for a given frequency */ staticint si544_calc_muldiv(struct clk_si544_muldiv *settings, unsignedlong frequency)
{
u64 vco;
u32 ls_freq;
u32 tmp;
u8 res;
/* Determine the minimum value of LS_DIV and resulting target freq. */
ls_freq = frequency;
settings->ls_div_bits = 0;
if (frequency >= MIN_HSDIV_FREQ) {
settings->ls_div_bits = 0;
} else {
res = 1;
tmp = 2 * HS_DIV_MAX; while (tmp <= (HS_DIV_MAX * 32)) { if (((u64)frequency * tmp) >= FVCO_MIN) break;
++res;
tmp <<= 1;
}
settings->ls_div_bits = res;
ls_freq = frequency << res;
}
/* Determine minimum HS_DIV by rounding up */
vco = FVCO_MIN + ls_freq - 1;
do_div(vco, ls_freq);
settings->hs_div = vco;
/* round up to even number when required */ if ((settings->hs_div & 1) &&
(settings->hs_div > HS_DIV_MAX_ODD || settings->ls_div_bits))
++settings->hs_div;
/* Calculate VCO frequency (in 10..12GHz range) */
vco = (u64)ls_freq * settings->hs_div;
/* Calculate the integer part of the feedback divider */
tmp = do_div(vco, FXO);
settings->fb_div_int = vco;
/* And the fractional bits using the remainder */
vco = (u64)tmp << 32;
vco += FXO / 2; /* Round to nearest multiple */
do_div(vco, FXO);
settings->fb_div_frac = vco;
/* Reset the frequency adjustment */
settings->delta_m = 0;
return 0;
}
/* Calculate resulting frequency given the register settings */ staticunsignedlong si544_calc_center_rate( conststruct clk_si544_muldiv *settings)
{
u32 d = settings->hs_div * BIT(settings->ls_div_bits);
u64 vco;
/* Calculate VCO from the fractional part */
vco = (u64)settings->fb_div_frac * FXO;
vco += (FXO / 2);
vco >>= 32;
/* Add the integer part of the VCO frequency */
vco += (u64)settings->fb_div_int * FXO;
/* Apply divider to obtain the generated frequency */
do_div(vco, d);
/* * The clock adjustment is much smaller than 1 Hz, round to the * nearest multiple. Apparently div64_s64 rounds towards zero, hence * check the sign and adjust into the proper direction.
*/ if (settings->delta_m < 0)
delta -= ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2; else
delta += ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2;
delta = div64_s64(delta, ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.