Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: analogbits: add Wide-Range PLL library

Add common library code for the Analog Bits Wide-Range PLL (WRPLL) IP
block, as implemented in TSMC CLN28HPC.

There is no bus interface or register target associated with this PLL.
This library is intended to be used by drivers for IP blocks that
expose registers connected to the PLL configuration and status
signals.

Based on code originally written by Wesley Terpstra
<wesley@sifive.com>:
https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60

This version incorporates several changes requested by Stephen
Boyd <sboyd@kernel.org>.

Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Wesley Terpstra <wesley@sifive.com>
Cc: Palmer Dabbelt <palmer@sifive.com>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@kernel.org>
Cc: Megan Wachs <megan@sifive.com>
Cc: linux-clk@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
[sboyd@kernel.org: Fix some const issues]
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

Paul Walmsley and committed by
Stephen Boyd
7b9487a9 a6c6cb2e

+457
+6
MAINTAINERS
··· 960 960 X: drivers/iio/*/adjd* 961 961 F: drivers/staging/iio/*/ad* 962 962 963 + ANALOGBITS PLL LIBRARIES 964 + M: Paul Walmsley <paul.walmsley@sifive.com> 965 + S: Supported 966 + F: drivers/clk/analogbits/* 967 + F: include/linux/clk/analogbits* 968 + 963 969 ANDES ARCHITECTURE 964 970 M: Greentime Hu <green.hu@gmail.com> 965 971 M: Vincent Chen <deanbo422@gmail.com>
+2
drivers/clk/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0 1 2 2 3 config CLKDEV_LOOKUP 3 4 bool ··· 298 297 Support for Memory Mapped IO Fixed clocks 299 298 300 299 source "drivers/clk/actions/Kconfig" 300 + source "drivers/clk/analogbits/Kconfig" 301 301 source "drivers/clk/bcm/Kconfig" 302 302 source "drivers/clk/hisilicon/Kconfig" 303 303 source "drivers/clk/imgtec/Kconfig"
+1
drivers/clk/Makefile
··· 64 64 65 65 # please keep this section sorted lexicographically by directory path name 66 66 obj-y += actions/ 67 + obj-y += analogbits/ 67 68 obj-$(CONFIG_COMMON_CLK_AT91) += at91/ 68 69 obj-$(CONFIG_ARCH_ARTPEC) += axis/ 69 70 obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
+2
drivers/clk/analogbits/Kconfig
··· 1 + config CLK_ANALOGBITS_WRPLL_CLN28HPC 2 + bool
+3
drivers/clk/analogbits/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + obj-$(CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC) += wrpll-cln28hpc.o
+364
drivers/clk/analogbits/wrpll-cln28hpc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2018-2019 SiFive, Inc. 4 + * Wesley Terpstra 5 + * Paul Walmsley 6 + * 7 + * This library supports configuration parsing and reprogramming of 8 + * the CLN28HPC variant of the Analog Bits Wide Range PLL. The 9 + * intention is for this library to be reusable for any device that 10 + * integrates this PLL; thus the register structure and programming 11 + * details are expected to be provided by a separate IP block driver. 12 + * 13 + * The bulk of this code is primarily useful for clock configurations 14 + * that must operate at arbitrary rates, as opposed to clock configurations 15 + * that are restricted by software or manufacturer guidance to a small, 16 + * pre-determined set of performance points. 17 + * 18 + * References: 19 + * - Analog Bits "Wide Range PLL Datasheet", version 2015.10.01 20 + * - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset" 21 + * https://static.dev.sifive.com/FU540-C000-v1.0.pdf 22 + */ 23 + 24 + #include <linux/bug.h> 25 + #include <linux/err.h> 26 + #include <linux/log2.h> 27 + #include <linux/math64.h> 28 + #include <linux/clk/analogbits-wrpll-cln28hpc.h> 29 + 30 + /* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */ 31 + #define MIN_INPUT_FREQ 7000000 32 + 33 + /* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */ 34 + #define MAX_INPUT_FREQ 600000000 35 + 36 + /* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */ 37 + #define MIN_POST_DIVR_FREQ 7000000 38 + 39 + /* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */ 40 + #define MAX_POST_DIVR_FREQ 200000000 41 + 42 + /* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */ 43 + #define MIN_VCO_FREQ 2400000000UL 44 + 45 + /* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */ 46 + #define MAX_VCO_FREQ 4800000000ULL 47 + 48 + /* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */ 49 + #define MAX_DIVQ_DIVISOR 64 50 + 51 + /* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */ 52 + #define MAX_DIVR_DIVISOR 64 53 + 54 + /* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */ 55 + #define MAX_LOCK_US 70 56 + 57 + /* 58 + * ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding 59 + * algorithm 60 + */ 61 + #define ROUND_SHIFT 20 62 + 63 + /* 64 + * Private functions 65 + */ 66 + 67 + /** 68 + * __wrpll_calc_filter_range() - determine PLL loop filter bandwidth 69 + * @post_divr_freq: input clock rate after the R divider 70 + * 71 + * Select the value to be presented to the PLL RANGE input signals, based 72 + * on the input clock frequency after the post-R-divider @post_divr_freq. 73 + * This code follows the recommendations in the PLL datasheet for filter 74 + * range selection. 75 + * 76 + * Return: The RANGE value to be presented to the PLL configuration inputs, 77 + * or a negative return code upon error. 78 + */ 79 + static int __wrpll_calc_filter_range(unsigned long post_divr_freq) 80 + { 81 + if (post_divr_freq < MIN_POST_DIVR_FREQ || 82 + post_divr_freq > MAX_POST_DIVR_FREQ) { 83 + WARN(1, "%s: post-divider reference freq out of range: %lu", 84 + __func__, post_divr_freq); 85 + return -ERANGE; 86 + } 87 + 88 + switch (post_divr_freq) { 89 + case 0 ... 10999999: 90 + return 1; 91 + case 11000000 ... 17999999: 92 + return 2; 93 + case 18000000 ... 29999999: 94 + return 3; 95 + case 30000000 ... 49999999: 96 + return 4; 97 + case 50000000 ... 79999999: 98 + return 5; 99 + case 80000000 ... 129999999: 100 + return 6; 101 + } 102 + 103 + return 7; 104 + } 105 + 106 + /** 107 + * __wrpll_calc_fbdiv() - return feedback fixed divide value 108 + * @c: ptr to a struct wrpll_cfg record to read from 109 + * 110 + * The internal feedback path includes a fixed by-two divider; the 111 + * external feedback path does not. Return the appropriate divider 112 + * value (2 or 1) depending on whether internal or external feedback 113 + * is enabled. This code doesn't test for invalid configurations 114 + * (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies 115 + * on the caller to do so. 116 + * 117 + * Context: Any context. Caller must protect the memory pointed to by 118 + * @c from simultaneous modification. 119 + * 120 + * Return: 2 if internal feedback is enabled or 1 if external feedback 121 + * is enabled. 122 + */ 123 + static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c) 124 + { 125 + return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1; 126 + } 127 + 128 + /** 129 + * __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate 130 + * @target_rate: target PLL output clock rate 131 + * @vco_rate: pointer to a u64 to store the computed VCO rate into 132 + * 133 + * Determine a reasonable value for the PLL Q post-divider, based on the 134 + * target output rate @target_rate for the PLL. Along with returning the 135 + * computed Q divider value as the return value, this function stores the 136 + * desired target VCO rate into the variable pointed to by @vco_rate. 137 + * 138 + * Context: Any context. Caller must protect the memory pointed to by 139 + * @vco_rate from simultaneous access or modification. 140 + * 141 + * Return: a positive integer DIVQ value to be programmed into the hardware 142 + * upon success, or 0 upon error (since 0 is an invalid DIVQ value) 143 + */ 144 + static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate) 145 + { 146 + u64 s; 147 + u8 divq = 0; 148 + 149 + if (!vco_rate) { 150 + WARN_ON(1); 151 + goto wcd_out; 152 + } 153 + 154 + s = div_u64(MAX_VCO_FREQ, target_rate); 155 + if (s <= 1) { 156 + divq = 1; 157 + *vco_rate = MAX_VCO_FREQ; 158 + } else if (s > MAX_DIVQ_DIVISOR) { 159 + divq = ilog2(MAX_DIVQ_DIVISOR); 160 + *vco_rate = MIN_VCO_FREQ; 161 + } else { 162 + divq = ilog2(s); 163 + *vco_rate = (u64)target_rate << divq; 164 + } 165 + 166 + wcd_out: 167 + return divq; 168 + } 169 + 170 + /** 171 + * __wrpll_update_parent_rate() - update PLL data when parent rate changes 172 + * @c: ptr to a struct wrpll_cfg record to write PLL data to 173 + * @parent_rate: PLL input refclk rate (pre-R-divider) 174 + * 175 + * Pre-compute some data used by the PLL configuration algorithm when 176 + * the PLL's reference clock rate changes. The intention is to avoid 177 + * computation when the parent rate remains constant - expected to be 178 + * the common case. 179 + * 180 + * Returns: 0 upon success or -ERANGE if the reference clock rate is 181 + * out of range. 182 + */ 183 + static int __wrpll_update_parent_rate(struct wrpll_cfg *c, 184 + unsigned long parent_rate) 185 + { 186 + u8 max_r_for_parent; 187 + 188 + if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ) 189 + return -ERANGE; 190 + 191 + c->parent_rate = parent_rate; 192 + max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ); 193 + c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent); 194 + 195 + c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ); 196 + 197 + return 0; 198 + } 199 + 200 + /** 201 + * wrpll_configure() - compute PLL configuration for a target rate 202 + * @c: ptr to a struct wrpll_cfg record to write into 203 + * @target_rate: target PLL output clock rate (post-Q-divider) 204 + * @parent_rate: PLL input refclk rate (pre-R-divider) 205 + * 206 + * Compute the appropriate PLL signal configuration values and store 207 + * in PLL context @c. PLL reprogramming is not glitchless, so the 208 + * caller should switch any downstream logic to a different clock 209 + * source or clock-gate it before presenting these values to the PLL 210 + * configuration signals. 211 + * 212 + * The caller must pass this function a pre-initialized struct 213 + * wrpll_cfg record: either initialized to zero (with the 214 + * exception of the .name and .flags fields) or read from the PLL. 215 + * 216 + * Context: Any context. Caller must protect the memory pointed to by @c 217 + * from simultaneous access or modification. 218 + * 219 + * Return: 0 upon success; anything else upon failure. 220 + */ 221 + int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate, 222 + unsigned long parent_rate) 223 + { 224 + unsigned long ratio; 225 + u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre; 226 + u32 best_f, f, post_divr_freq; 227 + u8 fbdiv, divq, best_r, r; 228 + int range; 229 + 230 + if (c->flags == 0) { 231 + WARN(1, "%s called with uninitialized PLL config", __func__); 232 + return -EINVAL; 233 + } 234 + 235 + /* Initialize rounding data if it hasn't been initialized already */ 236 + if (parent_rate != c->parent_rate) { 237 + if (__wrpll_update_parent_rate(c, parent_rate)) { 238 + pr_err("%s: PLL input rate is out of range\n", 239 + __func__); 240 + return -ERANGE; 241 + } 242 + } 243 + 244 + c->flags &= ~WRPLL_FLAGS_RESET_MASK; 245 + 246 + /* Put the PLL into bypass if the user requests the parent clock rate */ 247 + if (target_rate == parent_rate) { 248 + c->flags |= WRPLL_FLAGS_BYPASS_MASK; 249 + return 0; 250 + } 251 + 252 + c->flags &= ~WRPLL_FLAGS_BYPASS_MASK; 253 + 254 + /* Calculate the Q shift and target VCO rate */ 255 + divq = __wrpll_calc_divq(target_rate, &target_vco_rate); 256 + if (!divq) 257 + return -1; 258 + c->divq = divq; 259 + 260 + /* Precalculate the pre-Q divider target ratio */ 261 + ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate); 262 + 263 + fbdiv = __wrpll_calc_fbdiv(c); 264 + best_r = 0; 265 + best_f = 0; 266 + best_delta = MAX_VCO_FREQ; 267 + 268 + /* 269 + * Consider all values for R which land within 270 + * [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R 271 + */ 272 + for (r = c->init_r; r <= c->max_r; ++r) { 273 + f_pre_div = ratio * r; 274 + f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT; 275 + f >>= (fbdiv - 1); 276 + 277 + post_divr_freq = div_u64(parent_rate, r); 278 + vco_pre = fbdiv * post_divr_freq; 279 + vco = vco_pre * f; 280 + 281 + /* Ensure rounding didn't take us out of range */ 282 + if (vco > target_vco_rate) { 283 + --f; 284 + vco = vco_pre * f; 285 + } else if (vco < MIN_VCO_FREQ) { 286 + ++f; 287 + vco = vco_pre * f; 288 + } 289 + 290 + delta = abs(target_rate - vco); 291 + if (delta < best_delta) { 292 + best_delta = delta; 293 + best_r = r; 294 + best_f = f; 295 + } 296 + } 297 + 298 + c->divr = best_r - 1; 299 + c->divf = best_f - 1; 300 + 301 + post_divr_freq = div_u64(parent_rate, best_r); 302 + 303 + /* Pick the best PLL jitter filter */ 304 + range = __wrpll_calc_filter_range(post_divr_freq); 305 + if (range < 0) 306 + return range; 307 + c->range = range; 308 + 309 + return 0; 310 + } 311 + 312 + /** 313 + * wrpll_calc_output_rate() - calculate the PLL's target output rate 314 + * @c: ptr to a struct wrpll_cfg record to read from 315 + * @parent_rate: PLL refclk rate 316 + * 317 + * Given a pointer to the PLL's current input configuration @c and the 318 + * PLL's input reference clock rate @parent_rate (before the R 319 + * pre-divider), calculate the PLL's output clock rate (after the Q 320 + * post-divider). 321 + * 322 + * Context: Any context. Caller must protect the memory pointed to by @c 323 + * from simultaneous modification. 324 + * 325 + * Return: the PLL's output clock rate, in Hz. The return value from 326 + * this function is intended to be convenient to pass directly 327 + * to the Linux clock framework; thus there is no explicit 328 + * error return value. 329 + */ 330 + unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c, 331 + unsigned long parent_rate) 332 + { 333 + u8 fbdiv; 334 + u64 n; 335 + 336 + if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) { 337 + WARN(1, "external feedback mode not yet supported"); 338 + return ULONG_MAX; 339 + } 340 + 341 + fbdiv = __wrpll_calc_fbdiv(c); 342 + n = parent_rate * fbdiv * (c->divf + 1); 343 + n = div_u64(n, c->divr + 1); 344 + n >>= c->divq; 345 + 346 + return n; 347 + } 348 + 349 + /** 350 + * wrpll_calc_max_lock_us() - return the time for the PLL to lock 351 + * @c: ptr to a struct wrpll_cfg record to read from 352 + * 353 + * Return the minimum amount of time (in microseconds) that the caller 354 + * must wait after reprogramming the PLL to ensure that it is locked 355 + * to the input frequency and stable. This is likely to depend on the DIVR 356 + * value; this is under discussion with the manufacturer. 357 + * 358 + * Return: the minimum amount of time the caller must wait for the PLL 359 + * to lock (in microseconds) 360 + */ 361 + unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c) 362 + { 363 + return MAX_LOCK_US; 364 + }
+79
include/linux/clk/analogbits-wrpll-cln28hpc.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2018-2019 SiFive, Inc. 4 + * Wesley Terpstra 5 + * Paul Walmsley 6 + */ 7 + 8 + #ifndef __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H 9 + #define __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H 10 + 11 + #include <linux/types.h> 12 + 13 + /* DIVQ_VALUES: number of valid DIVQ values */ 14 + #define DIVQ_VALUES 6 15 + 16 + /* 17 + * Bit definitions for struct wrpll_cfg.flags 18 + * 19 + * WRPLL_FLAGS_BYPASS_FLAG: if set, the PLL is either in bypass, or should be 20 + * programmed to enter bypass 21 + * WRPLL_FLAGS_RESET_FLAG: if set, the PLL is in reset 22 + * WRPLL_FLAGS_INT_FEEDBACK_FLAG: if set, the PLL is configured for internal 23 + * feedback mode 24 + * WRPLL_FLAGS_EXT_FEEDBACK_FLAG: if set, the PLL is configured for external 25 + * feedback mode (not yet supported by this driver) 26 + */ 27 + #define WRPLL_FLAGS_BYPASS_SHIFT 0 28 + #define WRPLL_FLAGS_BYPASS_MASK BIT(WRPLL_FLAGS_BYPASS_SHIFT) 29 + #define WRPLL_FLAGS_RESET_SHIFT 1 30 + #define WRPLL_FLAGS_RESET_MASK BIT(WRPLL_FLAGS_RESET_SHIFT) 31 + #define WRPLL_FLAGS_INT_FEEDBACK_SHIFT 2 32 + #define WRPLL_FLAGS_INT_FEEDBACK_MASK BIT(WRPLL_FLAGS_INT_FEEDBACK_SHIFT) 33 + #define WRPLL_FLAGS_EXT_FEEDBACK_SHIFT 3 34 + #define WRPLL_FLAGS_EXT_FEEDBACK_MASK BIT(WRPLL_FLAGS_EXT_FEEDBACK_SHIFT) 35 + 36 + /** 37 + * struct wrpll_cfg - WRPLL configuration values 38 + * @divr: reference divider value (6 bits), as presented to the PLL signals 39 + * @divf: feedback divider value (9 bits), as presented to the PLL signals 40 + * @divq: output divider value (3 bits), as presented to the PLL signals 41 + * @flags: PLL configuration flags. See above for more information 42 + * @range: PLL loop filter range. See below for more information 43 + * @output_rate_cache: cached output rates, swept across DIVQ 44 + * @parent_rate: PLL refclk rate for which values are valid 45 + * @max_r: maximum possible R divider value, given @parent_rate 46 + * @init_r: initial R divider value to start the search from 47 + * 48 + * @divr, @divq, @divq, @range represent what the PLL expects to see 49 + * on its input signals. Thus @divr and @divf are the actual divisors 50 + * minus one. @divq is a power-of-two divider; for example, 1 = 51 + * divide-by-2 and 6 = divide-by-64. 0 is an invalid @divq value. 52 + * 53 + * When initially passing a struct wrpll_cfg record, the 54 + * record should be zero-initialized with the exception of the @flags 55 + * field. The only flag bits that need to be set are either 56 + * WRPLL_FLAGS_INT_FEEDBACK or WRPLL_FLAGS_EXT_FEEDBACK. 57 + */ 58 + struct wrpll_cfg { 59 + u8 divr; 60 + u8 divq; 61 + u8 range; 62 + u8 flags; 63 + u16 divf; 64 + /* private: */ 65 + u32 output_rate_cache[DIVQ_VALUES]; 66 + unsigned long parent_rate; 67 + u8 max_r; 68 + u8 init_r; 69 + }; 70 + 71 + int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate, 72 + unsigned long parent_rate); 73 + 74 + unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c); 75 + 76 + unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c, 77 + unsigned long parent_rate); 78 + 79 + #endif /* __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H */