Path: blob/master/drivers/clk/analogbits/wrpll-cln28hpc.c
26282 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (C) 2018-2019 SiFive, Inc.3* Wesley Terpstra4* Paul Walmsley5*6* This library supports configuration parsing and reprogramming of7* the CLN28HPC variant of the Analog Bits Wide Range PLL. The8* intention is for this library to be reusable for any device that9* integrates this PLL; thus the register structure and programming10* details are expected to be provided by a separate IP block driver.11*12* The bulk of this code is primarily useful for clock configurations13* that must operate at arbitrary rates, as opposed to clock configurations14* that are restricted by software or manufacturer guidance to a small,15* pre-determined set of performance points.16*17* References:18* - Analog Bits "Wide Range PLL Datasheet", version 2015.10.0119* - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset"20* https://static.dev.sifive.com/FU540-C000-v1.0.pdf21*/2223#include <linux/bug.h>24#include <linux/err.h>25#include <linux/limits.h>26#include <linux/log2.h>27#include <linux/math64.h>28#include <linux/math.h>29#include <linux/minmax.h>30#include <linux/module.h>3132#include <linux/clk/analogbits-wrpll-cln28hpc.h>3334/* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */35#define MIN_INPUT_FREQ 70000003637/* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */38#define MAX_INPUT_FREQ 6000000003940/* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */41#define MIN_POST_DIVR_FREQ 70000004243/* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */44#define MAX_POST_DIVR_FREQ 2000000004546/* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */47#define MIN_VCO_FREQ 2400000000UL4849/* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */50#define MAX_VCO_FREQ 4800000000ULL5152/* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */53#define MAX_DIVQ_DIVISOR 645455/* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */56#define MAX_DIVR_DIVISOR 645758/* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */59#define MAX_LOCK_US 706061/*62* ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding63* algorithm64*/65#define ROUND_SHIFT 206667/*68* Private functions69*/7071/**72* __wrpll_calc_filter_range() - determine PLL loop filter bandwidth73* @post_divr_freq: input clock rate after the R divider74*75* Select the value to be presented to the PLL RANGE input signals, based76* on the input clock frequency after the post-R-divider @post_divr_freq.77* This code follows the recommendations in the PLL datasheet for filter78* range selection.79*80* Return: The RANGE value to be presented to the PLL configuration inputs,81* or a negative return code upon error.82*/83static int __wrpll_calc_filter_range(unsigned long post_divr_freq)84{85if (post_divr_freq < MIN_POST_DIVR_FREQ ||86post_divr_freq > MAX_POST_DIVR_FREQ) {87WARN(1, "%s: post-divider reference freq out of range: %lu",88__func__, post_divr_freq);89return -ERANGE;90}9192switch (post_divr_freq) {93case 0 ... 10999999:94return 1;95case 11000000 ... 17999999:96return 2;97case 18000000 ... 29999999:98return 3;99case 30000000 ... 49999999:100return 4;101case 50000000 ... 79999999:102return 5;103case 80000000 ... 129999999:104return 6;105}106107return 7;108}109110/**111* __wrpll_calc_fbdiv() - return feedback fixed divide value112* @c: ptr to a struct wrpll_cfg record to read from113*114* The internal feedback path includes a fixed by-two divider; the115* external feedback path does not. Return the appropriate divider116* value (2 or 1) depending on whether internal or external feedback117* is enabled. This code doesn't test for invalid configurations118* (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies119* on the caller to do so.120*121* Context: Any context. Caller must protect the memory pointed to by122* @c from simultaneous modification.123*124* Return: 2 if internal feedback is enabled or 1 if external feedback125* is enabled.126*/127static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c)128{129return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1;130}131132/**133* __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate134* @target_rate: target PLL output clock rate135* @vco_rate: pointer to a u64 to store the computed VCO rate into136*137* Determine a reasonable value for the PLL Q post-divider, based on the138* target output rate @target_rate for the PLL. Along with returning the139* computed Q divider value as the return value, this function stores the140* desired target VCO rate into the variable pointed to by @vco_rate.141*142* Context: Any context. Caller must protect the memory pointed to by143* @vco_rate from simultaneous access or modification.144*145* Return: a positive integer DIVQ value to be programmed into the hardware146* upon success, or 0 upon error (since 0 is an invalid DIVQ value)147*/148static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate)149{150u64 s;151u8 divq = 0;152153if (!vco_rate) {154WARN_ON(1);155goto wcd_out;156}157158s = div_u64(MAX_VCO_FREQ, target_rate);159if (s <= 1) {160divq = 1;161*vco_rate = MAX_VCO_FREQ;162} else if (s > MAX_DIVQ_DIVISOR) {163divq = ilog2(MAX_DIVQ_DIVISOR);164*vco_rate = MIN_VCO_FREQ;165} else {166divq = ilog2(s);167*vco_rate = (u64)target_rate << divq;168}169170wcd_out:171return divq;172}173174/**175* __wrpll_update_parent_rate() - update PLL data when parent rate changes176* @c: ptr to a struct wrpll_cfg record to write PLL data to177* @parent_rate: PLL input refclk rate (pre-R-divider)178*179* Pre-compute some data used by the PLL configuration algorithm when180* the PLL's reference clock rate changes. The intention is to avoid181* computation when the parent rate remains constant - expected to be182* the common case.183*184* Returns: 0 upon success or -ERANGE if the reference clock rate is185* out of range.186*/187static int __wrpll_update_parent_rate(struct wrpll_cfg *c,188unsigned long parent_rate)189{190u8 max_r_for_parent;191192if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ)193return -ERANGE;194195c->parent_rate = parent_rate;196max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ);197c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent);198199c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ);200201return 0;202}203204/**205* wrpll_configure_for_rate() - compute PLL configuration for a target rate206* @c: ptr to a struct wrpll_cfg record to write into207* @target_rate: target PLL output clock rate (post-Q-divider)208* @parent_rate: PLL input refclk rate (pre-R-divider)209*210* Compute the appropriate PLL signal configuration values and store211* in PLL context @c. PLL reprogramming is not glitchless, so the212* caller should switch any downstream logic to a different clock213* source or clock-gate it before presenting these values to the PLL214* configuration signals.215*216* The caller must pass this function a pre-initialized struct217* wrpll_cfg record: either initialized to zero (with the218* exception of the .name and .flags fields) or read from the PLL.219*220* Context: Any context. Caller must protect the memory pointed to by @c221* from simultaneous access or modification.222*223* Return: 0 upon success; anything else upon failure.224*/225int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,226unsigned long parent_rate)227{228unsigned long ratio;229u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre;230u32 best_f, f, post_divr_freq;231u8 fbdiv, divq, best_r, r;232int range;233234if (c->flags == 0) {235WARN(1, "%s called with uninitialized PLL config", __func__);236return -EINVAL;237}238239/* Initialize rounding data if it hasn't been initialized already */240if (parent_rate != c->parent_rate) {241if (__wrpll_update_parent_rate(c, parent_rate)) {242pr_err("%s: PLL input rate is out of range\n",243__func__);244return -ERANGE;245}246}247248c->flags &= ~WRPLL_FLAGS_RESET_MASK;249250/* Put the PLL into bypass if the user requests the parent clock rate */251if (target_rate == parent_rate) {252c->flags |= WRPLL_FLAGS_BYPASS_MASK;253return 0;254}255256c->flags &= ~WRPLL_FLAGS_BYPASS_MASK;257258/* Calculate the Q shift and target VCO rate */259divq = __wrpll_calc_divq(target_rate, &target_vco_rate);260if (!divq)261return -1;262c->divq = divq;263264/* Precalculate the pre-Q divider target ratio */265ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate);266267fbdiv = __wrpll_calc_fbdiv(c);268best_r = 0;269best_f = 0;270best_delta = MAX_VCO_FREQ;271272/*273* Consider all values for R which land within274* [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R275*/276for (r = c->init_r; r <= c->max_r; ++r) {277f_pre_div = ratio * r;278f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT;279f >>= (fbdiv - 1);280281post_divr_freq = div_u64(parent_rate, r);282vco_pre = fbdiv * post_divr_freq;283vco = vco_pre * f;284285/* Ensure rounding didn't take us out of range */286if (vco > target_vco_rate) {287--f;288vco = vco_pre * f;289} else if (vco < MIN_VCO_FREQ) {290++f;291vco = vco_pre * f;292}293294delta = abs(target_vco_rate - vco);295if (delta < best_delta) {296best_delta = delta;297best_r = r;298best_f = f;299}300}301302c->divr = best_r - 1;303c->divf = best_f - 1;304305post_divr_freq = div_u64(parent_rate, best_r);306307/* Pick the best PLL jitter filter */308range = __wrpll_calc_filter_range(post_divr_freq);309if (range < 0)310return range;311c->range = range;312313return 0;314}315EXPORT_SYMBOL_GPL(wrpll_configure_for_rate);316317/**318* wrpll_calc_output_rate() - calculate the PLL's target output rate319* @c: ptr to a struct wrpll_cfg record to read from320* @parent_rate: PLL refclk rate321*322* Given a pointer to the PLL's current input configuration @c and the323* PLL's input reference clock rate @parent_rate (before the R324* pre-divider), calculate the PLL's output clock rate (after the Q325* post-divider).326*327* Context: Any context. Caller must protect the memory pointed to by @c328* from simultaneous modification.329*330* Return: the PLL's output clock rate, in Hz. The return value from331* this function is intended to be convenient to pass directly332* to the Linux clock framework; thus there is no explicit333* error return value.334*/335unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,336unsigned long parent_rate)337{338u8 fbdiv;339u64 n;340341if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) {342WARN(1, "external feedback mode not yet supported");343return ULONG_MAX;344}345346fbdiv = __wrpll_calc_fbdiv(c);347n = parent_rate * fbdiv * (c->divf + 1);348n = div_u64(n, c->divr + 1);349n >>= c->divq;350351return n;352}353EXPORT_SYMBOL_GPL(wrpll_calc_output_rate);354355/**356* wrpll_calc_max_lock_us() - return the time for the PLL to lock357* @c: ptr to a struct wrpll_cfg record to read from358*359* Return the minimum amount of time (in microseconds) that the caller360* must wait after reprogramming the PLL to ensure that it is locked361* to the input frequency and stable. This is likely to depend on the DIVR362* value; this is under discussion with the manufacturer.363*364* Return: the minimum amount of time the caller must wait for the PLL365* to lock (in microseconds)366*/367unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c)368{369return MAX_LOCK_US;370}371EXPORT_SYMBOL_GPL(wrpll_calc_max_lock_us);372373MODULE_AUTHOR("Paul Walmsley <[email protected]>");374MODULE_DESCRIPTION("Analog Bits Wide-Range PLL library");375MODULE_LICENSE("GPL");376377378