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/limits.h> |
27 | #include <linux/log2.h> |
28 | #include <linux/math64.h> |
29 | #include <linux/math.h> |
30 | #include <linux/minmax.h> |
31 | #include <linux/module.h> |
32 | |
33 | #include <linux/clk/analogbits-wrpll-cln28hpc.h> |
34 | |
35 | /* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */ |
36 | #define MIN_INPUT_FREQ 7000000 |
37 | |
38 | /* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */ |
39 | #define MAX_INPUT_FREQ 600000000 |
40 | |
41 | /* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */ |
42 | #define MIN_POST_DIVR_FREQ 7000000 |
43 | |
44 | /* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */ |
45 | #define MAX_POST_DIVR_FREQ 200000000 |
46 | |
47 | /* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */ |
48 | #define MIN_VCO_FREQ 2400000000UL |
49 | |
50 | /* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */ |
51 | #define MAX_VCO_FREQ 4800000000ULL |
52 | |
53 | /* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */ |
54 | #define MAX_DIVQ_DIVISOR 64 |
55 | |
56 | /* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */ |
57 | #define MAX_DIVR_DIVISOR 64 |
58 | |
59 | /* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */ |
60 | #define MAX_LOCK_US 70 |
61 | |
62 | /* |
63 | * ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding |
64 | * algorithm |
65 | */ |
66 | #define ROUND_SHIFT 20 |
67 | |
68 | /* |
69 | * Private functions |
70 | */ |
71 | |
72 | /** |
73 | * __wrpll_calc_filter_range() - determine PLL loop filter bandwidth |
74 | * @post_divr_freq: input clock rate after the R divider |
75 | * |
76 | * Select the value to be presented to the PLL RANGE input signals, based |
77 | * on the input clock frequency after the post-R-divider @post_divr_freq. |
78 | * This code follows the recommendations in the PLL datasheet for filter |
79 | * range selection. |
80 | * |
81 | * Return: The RANGE value to be presented to the PLL configuration inputs, |
82 | * or a negative return code upon error. |
83 | */ |
84 | static int __wrpll_calc_filter_range(unsigned long post_divr_freq) |
85 | { |
86 | if (post_divr_freq < MIN_POST_DIVR_FREQ || |
87 | post_divr_freq > MAX_POST_DIVR_FREQ) { |
88 | WARN(1, "%s: post-divider reference freq out of range: %lu" , |
89 | __func__, post_divr_freq); |
90 | return -ERANGE; |
91 | } |
92 | |
93 | switch (post_divr_freq) { |
94 | case 0 ... 10999999: |
95 | return 1; |
96 | case 11000000 ... 17999999: |
97 | return 2; |
98 | case 18000000 ... 29999999: |
99 | return 3; |
100 | case 30000000 ... 49999999: |
101 | return 4; |
102 | case 50000000 ... 79999999: |
103 | return 5; |
104 | case 80000000 ... 129999999: |
105 | return 6; |
106 | } |
107 | |
108 | return 7; |
109 | } |
110 | |
111 | /** |
112 | * __wrpll_calc_fbdiv() - return feedback fixed divide value |
113 | * @c: ptr to a struct wrpll_cfg record to read from |
114 | * |
115 | * The internal feedback path includes a fixed by-two divider; the |
116 | * external feedback path does not. Return the appropriate divider |
117 | * value (2 or 1) depending on whether internal or external feedback |
118 | * is enabled. This code doesn't test for invalid configurations |
119 | * (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies |
120 | * on the caller to do so. |
121 | * |
122 | * Context: Any context. Caller must protect the memory pointed to by |
123 | * @c from simultaneous modification. |
124 | * |
125 | * Return: 2 if internal feedback is enabled or 1 if external feedback |
126 | * is enabled. |
127 | */ |
128 | static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c) |
129 | { |
130 | return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1; |
131 | } |
132 | |
133 | /** |
134 | * __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate |
135 | * @target_rate: target PLL output clock rate |
136 | * @vco_rate: pointer to a u64 to store the computed VCO rate into |
137 | * |
138 | * Determine a reasonable value for the PLL Q post-divider, based on the |
139 | * target output rate @target_rate for the PLL. Along with returning the |
140 | * computed Q divider value as the return value, this function stores the |
141 | * desired target VCO rate into the variable pointed to by @vco_rate. |
142 | * |
143 | * Context: Any context. Caller must protect the memory pointed to by |
144 | * @vco_rate from simultaneous access or modification. |
145 | * |
146 | * Return: a positive integer DIVQ value to be programmed into the hardware |
147 | * upon success, or 0 upon error (since 0 is an invalid DIVQ value) |
148 | */ |
149 | static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate) |
150 | { |
151 | u64 s; |
152 | u8 divq = 0; |
153 | |
154 | if (!vco_rate) { |
155 | WARN_ON(1); |
156 | goto wcd_out; |
157 | } |
158 | |
159 | s = div_u64(MAX_VCO_FREQ, divisor: target_rate); |
160 | if (s <= 1) { |
161 | divq = 1; |
162 | *vco_rate = MAX_VCO_FREQ; |
163 | } else if (s > MAX_DIVQ_DIVISOR) { |
164 | divq = ilog2(MAX_DIVQ_DIVISOR); |
165 | *vco_rate = MIN_VCO_FREQ; |
166 | } else { |
167 | divq = ilog2(s); |
168 | *vco_rate = (u64)target_rate << divq; |
169 | } |
170 | |
171 | wcd_out: |
172 | return divq; |
173 | } |
174 | |
175 | /** |
176 | * __wrpll_update_parent_rate() - update PLL data when parent rate changes |
177 | * @c: ptr to a struct wrpll_cfg record to write PLL data to |
178 | * @parent_rate: PLL input refclk rate (pre-R-divider) |
179 | * |
180 | * Pre-compute some data used by the PLL configuration algorithm when |
181 | * the PLL's reference clock rate changes. The intention is to avoid |
182 | * computation when the parent rate remains constant - expected to be |
183 | * the common case. |
184 | * |
185 | * Returns: 0 upon success or -ERANGE if the reference clock rate is |
186 | * out of range. |
187 | */ |
188 | static int __wrpll_update_parent_rate(struct wrpll_cfg *c, |
189 | unsigned long parent_rate) |
190 | { |
191 | u8 max_r_for_parent; |
192 | |
193 | if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ) |
194 | return -ERANGE; |
195 | |
196 | c->parent_rate = parent_rate; |
197 | max_r_for_parent = div_u64(dividend: parent_rate, MIN_POST_DIVR_FREQ); |
198 | c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent); |
199 | |
200 | c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ); |
201 | |
202 | return 0; |
203 | } |
204 | |
205 | /** |
206 | * wrpll_configure_for_rate() - compute PLL configuration for a target rate |
207 | * @c: ptr to a struct wrpll_cfg record to write into |
208 | * @target_rate: target PLL output clock rate (post-Q-divider) |
209 | * @parent_rate: PLL input refclk rate (pre-R-divider) |
210 | * |
211 | * Compute the appropriate PLL signal configuration values and store |
212 | * in PLL context @c. PLL reprogramming is not glitchless, so the |
213 | * caller should switch any downstream logic to a different clock |
214 | * source or clock-gate it before presenting these values to the PLL |
215 | * configuration signals. |
216 | * |
217 | * The caller must pass this function a pre-initialized struct |
218 | * wrpll_cfg record: either initialized to zero (with the |
219 | * exception of the .name and .flags fields) or read from the PLL. |
220 | * |
221 | * Context: Any context. Caller must protect the memory pointed to by @c |
222 | * from simultaneous access or modification. |
223 | * |
224 | * Return: 0 upon success; anything else upon failure. |
225 | */ |
226 | int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate, |
227 | unsigned long parent_rate) |
228 | { |
229 | unsigned long ratio; |
230 | u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre; |
231 | u32 best_f, f, post_divr_freq; |
232 | u8 fbdiv, divq, best_r, r; |
233 | int range; |
234 | |
235 | if (c->flags == 0) { |
236 | WARN(1, "%s called with uninitialized PLL config" , __func__); |
237 | return -EINVAL; |
238 | } |
239 | |
240 | /* Initialize rounding data if it hasn't been initialized already */ |
241 | if (parent_rate != c->parent_rate) { |
242 | if (__wrpll_update_parent_rate(c, parent_rate)) { |
243 | pr_err("%s: PLL input rate is out of range\n" , |
244 | __func__); |
245 | return -ERANGE; |
246 | } |
247 | } |
248 | |
249 | c->flags &= ~WRPLL_FLAGS_RESET_MASK; |
250 | |
251 | /* Put the PLL into bypass if the user requests the parent clock rate */ |
252 | if (target_rate == parent_rate) { |
253 | c->flags |= WRPLL_FLAGS_BYPASS_MASK; |
254 | return 0; |
255 | } |
256 | |
257 | c->flags &= ~WRPLL_FLAGS_BYPASS_MASK; |
258 | |
259 | /* Calculate the Q shift and target VCO rate */ |
260 | divq = __wrpll_calc_divq(target_rate, vco_rate: &target_vco_rate); |
261 | if (!divq) |
262 | return -1; |
263 | c->divq = divq; |
264 | |
265 | /* Precalculate the pre-Q divider target ratio */ |
266 | ratio = div64_u64(dividend: (target_vco_rate << ROUND_SHIFT), divisor: parent_rate); |
267 | |
268 | fbdiv = __wrpll_calc_fbdiv(c); |
269 | best_r = 0; |
270 | best_f = 0; |
271 | best_delta = MAX_VCO_FREQ; |
272 | |
273 | /* |
274 | * Consider all values for R which land within |
275 | * [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R |
276 | */ |
277 | for (r = c->init_r; r <= c->max_r; ++r) { |
278 | f_pre_div = ratio * r; |
279 | f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT; |
280 | f >>= (fbdiv - 1); |
281 | |
282 | post_divr_freq = div_u64(dividend: parent_rate, divisor: r); |
283 | vco_pre = fbdiv * post_divr_freq; |
284 | vco = vco_pre * f; |
285 | |
286 | /* Ensure rounding didn't take us out of range */ |
287 | if (vco > target_vco_rate) { |
288 | --f; |
289 | vco = vco_pre * f; |
290 | } else if (vco < MIN_VCO_FREQ) { |
291 | ++f; |
292 | vco = vco_pre * f; |
293 | } |
294 | |
295 | delta = abs(target_rate - vco); |
296 | if (delta < best_delta) { |
297 | best_delta = delta; |
298 | best_r = r; |
299 | best_f = f; |
300 | } |
301 | } |
302 | |
303 | c->divr = best_r - 1; |
304 | c->divf = best_f - 1; |
305 | |
306 | post_divr_freq = div_u64(dividend: parent_rate, divisor: best_r); |
307 | |
308 | /* Pick the best PLL jitter filter */ |
309 | range = __wrpll_calc_filter_range(post_divr_freq); |
310 | if (range < 0) |
311 | return range; |
312 | c->range = range; |
313 | |
314 | return 0; |
315 | } |
316 | EXPORT_SYMBOL_GPL(wrpll_configure_for_rate); |
317 | |
318 | /** |
319 | * wrpll_calc_output_rate() - calculate the PLL's target output rate |
320 | * @c: ptr to a struct wrpll_cfg record to read from |
321 | * @parent_rate: PLL refclk rate |
322 | * |
323 | * Given a pointer to the PLL's current input configuration @c and the |
324 | * PLL's input reference clock rate @parent_rate (before the R |
325 | * pre-divider), calculate the PLL's output clock rate (after the Q |
326 | * post-divider). |
327 | * |
328 | * Context: Any context. Caller must protect the memory pointed to by @c |
329 | * from simultaneous modification. |
330 | * |
331 | * Return: the PLL's output clock rate, in Hz. The return value from |
332 | * this function is intended to be convenient to pass directly |
333 | * to the Linux clock framework; thus there is no explicit |
334 | * error return value. |
335 | */ |
336 | unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c, |
337 | unsigned long parent_rate) |
338 | { |
339 | u8 fbdiv; |
340 | u64 n; |
341 | |
342 | if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) { |
343 | WARN(1, "external feedback mode not yet supported" ); |
344 | return ULONG_MAX; |
345 | } |
346 | |
347 | fbdiv = __wrpll_calc_fbdiv(c); |
348 | n = parent_rate * fbdiv * (c->divf + 1); |
349 | n = div_u64(dividend: n, divisor: c->divr + 1); |
350 | n >>= c->divq; |
351 | |
352 | return n; |
353 | } |
354 | EXPORT_SYMBOL_GPL(wrpll_calc_output_rate); |
355 | |
356 | /** |
357 | * wrpll_calc_max_lock_us() - return the time for the PLL to lock |
358 | * @c: ptr to a struct wrpll_cfg record to read from |
359 | * |
360 | * Return the minimum amount of time (in microseconds) that the caller |
361 | * must wait after reprogramming the PLL to ensure that it is locked |
362 | * to the input frequency and stable. This is likely to depend on the DIVR |
363 | * value; this is under discussion with the manufacturer. |
364 | * |
365 | * Return: the minimum amount of time the caller must wait for the PLL |
366 | * to lock (in microseconds) |
367 | */ |
368 | unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c) |
369 | { |
370 | return MAX_LOCK_US; |
371 | } |
372 | EXPORT_SYMBOL_GPL(wrpll_calc_max_lock_us); |
373 | |
374 | MODULE_AUTHOR("Paul Walmsley <paul.walmsley@sifive.com>" ); |
375 | MODULE_DESCRIPTION("Analog Bits Wide-Range PLL library" ); |
376 | MODULE_LICENSE("GPL" ); |
377 | |