Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2018-2019 SiFive, Inc.
0004  * Wesley Terpstra
0005  * Paul Walmsley
0006  *
0007  * This library supports configuration parsing and reprogramming of
0008  * the CLN28HPC variant of the Analog Bits Wide Range PLL.  The
0009  * intention is for this library to be reusable for any device that
0010  * integrates this PLL; thus the register structure and programming
0011  * details are expected to be provided by a separate IP block driver.
0012  *
0013  * The bulk of this code is primarily useful for clock configurations
0014  * that must operate at arbitrary rates, as opposed to clock configurations
0015  * that are restricted by software or manufacturer guidance to a small,
0016  * pre-determined set of performance points.
0017  *
0018  * References:
0019  * - Analog Bits "Wide Range PLL Datasheet", version 2015.10.01
0020  * - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset"
0021  *   https://static.dev.sifive.com/FU540-C000-v1.0.pdf
0022  */
0023 
0024 #include <linux/bug.h>
0025 #include <linux/err.h>
0026 #include <linux/limits.h>
0027 #include <linux/log2.h>
0028 #include <linux/math64.h>
0029 #include <linux/math.h>
0030 #include <linux/minmax.h>
0031 
0032 #include <linux/clk/analogbits-wrpll-cln28hpc.h>
0033 
0034 /* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */
0035 #define MIN_INPUT_FREQ          7000000
0036 
0037 /* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */
0038 #define MAX_INPUT_FREQ          600000000
0039 
0040 /* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */
0041 #define MIN_POST_DIVR_FREQ      7000000
0042 
0043 /* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */
0044 #define MAX_POST_DIVR_FREQ      200000000
0045 
0046 /* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */
0047 #define MIN_VCO_FREQ            2400000000UL
0048 
0049 /* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */
0050 #define MAX_VCO_FREQ            4800000000ULL
0051 
0052 /* MAX_DIVQ_DIVISOR: maximum output divisor.  Selected by DIVQ = 6 */
0053 #define MAX_DIVQ_DIVISOR        64
0054 
0055 /* MAX_DIVR_DIVISOR: maximum reference divisor.  Selected by DIVR = 63 */
0056 #define MAX_DIVR_DIVISOR        64
0057 
0058 /* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */
0059 #define MAX_LOCK_US         70
0060 
0061 /*
0062  * ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding
0063  *              algorithm
0064  */
0065 #define ROUND_SHIFT         20
0066 
0067 /*
0068  * Private functions
0069  */
0070 
0071 /**
0072  * __wrpll_calc_filter_range() - determine PLL loop filter bandwidth
0073  * @post_divr_freq: input clock rate after the R divider
0074  *
0075  * Select the value to be presented to the PLL RANGE input signals, based
0076  * on the input clock frequency after the post-R-divider @post_divr_freq.
0077  * This code follows the recommendations in the PLL datasheet for filter
0078  * range selection.
0079  *
0080  * Return: The RANGE value to be presented to the PLL configuration inputs,
0081  *         or a negative return code upon error.
0082  */
0083 static int __wrpll_calc_filter_range(unsigned long post_divr_freq)
0084 {
0085     if (post_divr_freq < MIN_POST_DIVR_FREQ ||
0086         post_divr_freq > MAX_POST_DIVR_FREQ) {
0087         WARN(1, "%s: post-divider reference freq out of range: %lu",
0088              __func__, post_divr_freq);
0089         return -ERANGE;
0090     }
0091 
0092     switch (post_divr_freq) {
0093     case 0 ... 10999999:
0094         return 1;
0095     case 11000000 ... 17999999:
0096         return 2;
0097     case 18000000 ... 29999999:
0098         return 3;
0099     case 30000000 ... 49999999:
0100         return 4;
0101     case 50000000 ... 79999999:
0102         return 5;
0103     case 80000000 ... 129999999:
0104         return 6;
0105     }
0106 
0107     return 7;
0108 }
0109 
0110 /**
0111  * __wrpll_calc_fbdiv() - return feedback fixed divide value
0112  * @c: ptr to a struct wrpll_cfg record to read from
0113  *
0114  * The internal feedback path includes a fixed by-two divider; the
0115  * external feedback path does not.  Return the appropriate divider
0116  * value (2 or 1) depending on whether internal or external feedback
0117  * is enabled.  This code doesn't test for invalid configurations
0118  * (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies
0119  * on the caller to do so.
0120  *
0121  * Context: Any context.  Caller must protect the memory pointed to by
0122  *          @c from simultaneous modification.
0123  *
0124  * Return: 2 if internal feedback is enabled or 1 if external feedback
0125  *         is enabled.
0126  */
0127 static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c)
0128 {
0129     return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1;
0130 }
0131 
0132 /**
0133  * __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate
0134  * @target_rate: target PLL output clock rate
0135  * @vco_rate: pointer to a u64 to store the computed VCO rate into
0136  *
0137  * Determine a reasonable value for the PLL Q post-divider, based on the
0138  * target output rate @target_rate for the PLL.  Along with returning the
0139  * computed Q divider value as the return value, this function stores the
0140  * desired target VCO rate into the variable pointed to by @vco_rate.
0141  *
0142  * Context: Any context.  Caller must protect the memory pointed to by
0143  *          @vco_rate from simultaneous access or modification.
0144  *
0145  * Return: a positive integer DIVQ value to be programmed into the hardware
0146  *         upon success, or 0 upon error (since 0 is an invalid DIVQ value)
0147  */
0148 static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate)
0149 {
0150     u64 s;
0151     u8 divq = 0;
0152 
0153     if (!vco_rate) {
0154         WARN_ON(1);
0155         goto wcd_out;
0156     }
0157 
0158     s = div_u64(MAX_VCO_FREQ, target_rate);
0159     if (s <= 1) {
0160         divq = 1;
0161         *vco_rate = MAX_VCO_FREQ;
0162     } else if (s > MAX_DIVQ_DIVISOR) {
0163         divq = ilog2(MAX_DIVQ_DIVISOR);
0164         *vco_rate = MIN_VCO_FREQ;
0165     } else {
0166         divq = ilog2(s);
0167         *vco_rate = (u64)target_rate << divq;
0168     }
0169 
0170 wcd_out:
0171     return divq;
0172 }
0173 
0174 /**
0175  * __wrpll_update_parent_rate() - update PLL data when parent rate changes
0176  * @c: ptr to a struct wrpll_cfg record to write PLL data to
0177  * @parent_rate: PLL input refclk rate (pre-R-divider)
0178  *
0179  * Pre-compute some data used by the PLL configuration algorithm when
0180  * the PLL's reference clock rate changes.  The intention is to avoid
0181  * computation when the parent rate remains constant - expected to be
0182  * the common case.
0183  *
0184  * Returns: 0 upon success or -ERANGE if the reference clock rate is
0185  * out of range.
0186  */
0187 static int __wrpll_update_parent_rate(struct wrpll_cfg *c,
0188                       unsigned long parent_rate)
0189 {
0190     u8 max_r_for_parent;
0191 
0192     if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ)
0193         return -ERANGE;
0194 
0195     c->parent_rate = parent_rate;
0196     max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ);
0197     c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent);
0198 
0199     c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ);
0200 
0201     return 0;
0202 }
0203 
0204 /**
0205  * wrpll_configure_for_rate() - compute PLL configuration for a target rate
0206  * @c: ptr to a struct wrpll_cfg record to write into
0207  * @target_rate: target PLL output clock rate (post-Q-divider)
0208  * @parent_rate: PLL input refclk rate (pre-R-divider)
0209  *
0210  * Compute the appropriate PLL signal configuration values and store
0211  * in PLL context @c.  PLL reprogramming is not glitchless, so the
0212  * caller should switch any downstream logic to a different clock
0213  * source or clock-gate it before presenting these values to the PLL
0214  * configuration signals.
0215  *
0216  * The caller must pass this function a pre-initialized struct
0217  * wrpll_cfg record: either initialized to zero (with the
0218  * exception of the .name and .flags fields) or read from the PLL.
0219  *
0220  * Context: Any context.  Caller must protect the memory pointed to by @c
0221  *          from simultaneous access or modification.
0222  *
0223  * Return: 0 upon success; anything else upon failure.
0224  */
0225 int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
0226                  unsigned long parent_rate)
0227 {
0228     unsigned long ratio;
0229     u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre;
0230     u32 best_f, f, post_divr_freq;
0231     u8 fbdiv, divq, best_r, r;
0232     int range;
0233 
0234     if (c->flags == 0) {
0235         WARN(1, "%s called with uninitialized PLL config", __func__);
0236         return -EINVAL;
0237     }
0238 
0239     /* Initialize rounding data if it hasn't been initialized already */
0240     if (parent_rate != c->parent_rate) {
0241         if (__wrpll_update_parent_rate(c, parent_rate)) {
0242             pr_err("%s: PLL input rate is out of range\n",
0243                    __func__);
0244             return -ERANGE;
0245         }
0246     }
0247 
0248     c->flags &= ~WRPLL_FLAGS_RESET_MASK;
0249 
0250     /* Put the PLL into bypass if the user requests the parent clock rate */
0251     if (target_rate == parent_rate) {
0252         c->flags |= WRPLL_FLAGS_BYPASS_MASK;
0253         return 0;
0254     }
0255 
0256     c->flags &= ~WRPLL_FLAGS_BYPASS_MASK;
0257 
0258     /* Calculate the Q shift and target VCO rate */
0259     divq = __wrpll_calc_divq(target_rate, &target_vco_rate);
0260     if (!divq)
0261         return -1;
0262     c->divq = divq;
0263 
0264     /* Precalculate the pre-Q divider target ratio */
0265     ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate);
0266 
0267     fbdiv = __wrpll_calc_fbdiv(c);
0268     best_r = 0;
0269     best_f = 0;
0270     best_delta = MAX_VCO_FREQ;
0271 
0272     /*
0273      * Consider all values for R which land within
0274      * [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R
0275      */
0276     for (r = c->init_r; r <= c->max_r; ++r) {
0277         f_pre_div = ratio * r;
0278         f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT;
0279         f >>= (fbdiv - 1);
0280 
0281         post_divr_freq = div_u64(parent_rate, r);
0282         vco_pre = fbdiv * post_divr_freq;
0283         vco = vco_pre * f;
0284 
0285         /* Ensure rounding didn't take us out of range */
0286         if (vco > target_vco_rate) {
0287             --f;
0288             vco = vco_pre * f;
0289         } else if (vco < MIN_VCO_FREQ) {
0290             ++f;
0291             vco = vco_pre * f;
0292         }
0293 
0294         delta = abs(target_rate - vco);
0295         if (delta < best_delta) {
0296             best_delta = delta;
0297             best_r = r;
0298             best_f = f;
0299         }
0300     }
0301 
0302     c->divr = best_r - 1;
0303     c->divf = best_f - 1;
0304 
0305     post_divr_freq = div_u64(parent_rate, best_r);
0306 
0307     /* Pick the best PLL jitter filter */
0308     range = __wrpll_calc_filter_range(post_divr_freq);
0309     if (range < 0)
0310         return range;
0311     c->range = range;
0312 
0313     return 0;
0314 }
0315 
0316 /**
0317  * wrpll_calc_output_rate() - calculate the PLL's target output rate
0318  * @c: ptr to a struct wrpll_cfg record to read from
0319  * @parent_rate: PLL refclk rate
0320  *
0321  * Given a pointer to the PLL's current input configuration @c and the
0322  * PLL's input reference clock rate @parent_rate (before the R
0323  * pre-divider), calculate the PLL's output clock rate (after the Q
0324  * post-divider).
0325  *
0326  * Context: Any context.  Caller must protect the memory pointed to by @c
0327  *          from simultaneous modification.
0328  *
0329  * Return: the PLL's output clock rate, in Hz.  The return value from
0330  *         this function is intended to be convenient to pass directly
0331  *         to the Linux clock framework; thus there is no explicit
0332  *         error return value.
0333  */
0334 unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,
0335                      unsigned long parent_rate)
0336 {
0337     u8 fbdiv;
0338     u64 n;
0339 
0340     if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) {
0341         WARN(1, "external feedback mode not yet supported");
0342         return ULONG_MAX;
0343     }
0344 
0345     fbdiv = __wrpll_calc_fbdiv(c);
0346     n = parent_rate * fbdiv * (c->divf + 1);
0347     n = div_u64(n, c->divr + 1);
0348     n >>= c->divq;
0349 
0350     return n;
0351 }
0352 
0353 /**
0354  * wrpll_calc_max_lock_us() - return the time for the PLL to lock
0355  * @c: ptr to a struct wrpll_cfg record to read from
0356  *
0357  * Return the minimum amount of time (in microseconds) that the caller
0358  * must wait after reprogramming the PLL to ensure that it is locked
0359  * to the input frequency and stable.  This is likely to depend on the DIVR
0360  * value; this is under discussion with the manufacturer.
0361  *
0362  * Return: the minimum amount of time the caller must wait for the PLL
0363  *         to lock (in microseconds)
0364  */
0365 unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c)
0366 {
0367     return MAX_LOCK_US;
0368 }