1*73c36010SLad Prabhakar // SPDX-License-Identifier: GPL-2.0
2*73c36010SLad Prabhakar /*
3*73c36010SLad Prabhakar * RZV2H CPG Library. This library provides common functions to calculate
4*73c36010SLad Prabhakar * PLL parameters for the RZV2H SoC.
5*73c36010SLad Prabhakar *
6*73c36010SLad Prabhakar * Copyright (C) 2026 Renesas Electronics Corp.
7*73c36010SLad Prabhakar *
8*73c36010SLad Prabhakar */
9*73c36010SLad Prabhakar
10*73c36010SLad Prabhakar #include <linux/clk/renesas.h>
11*73c36010SLad Prabhakar #include <linux/export.h>
12*73c36010SLad Prabhakar #include <linux/math.h>
13*73c36010SLad Prabhakar #include <linux/types.h>
14*73c36010SLad Prabhakar #include <linux/units.h>
15*73c36010SLad Prabhakar
16*73c36010SLad Prabhakar /**
17*73c36010SLad Prabhakar * rzv2h_cpg_get_pll_pars - Finds the best combination of PLL parameters
18*73c36010SLad Prabhakar * for a given frequency.
19*73c36010SLad Prabhakar *
20*73c36010SLad Prabhakar * @limits: Pointer to the structure containing the limits for the PLL parameters
21*73c36010SLad Prabhakar * @pars: Pointer to the structure where the best calculated PLL parameters values
22*73c36010SLad Prabhakar * will be stored
23*73c36010SLad Prabhakar * @freq_millihz: Target output frequency in millihertz
24*73c36010SLad Prabhakar *
25*73c36010SLad Prabhakar * This function calculates the best set of PLL parameters (M, K, P, S) to achieve
26*73c36010SLad Prabhakar * the desired frequency.
27*73c36010SLad Prabhakar * There is no direct formula to calculate the PLL parameters, as it's an open
28*73c36010SLad Prabhakar * system of equations, therefore this function uses an iterative approach to
29*73c36010SLad Prabhakar * determine the best solution. The best solution is one that minimizes the error
30*73c36010SLad Prabhakar * (desired frequency - actual frequency).
31*73c36010SLad Prabhakar *
32*73c36010SLad Prabhakar * Return: true if a valid set of parameters values is found, false otherwise.
33*73c36010SLad Prabhakar */
rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits * limits,struct rzv2h_pll_pars * pars,u64 freq_millihz)34*73c36010SLad Prabhakar bool rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits *limits,
35*73c36010SLad Prabhakar struct rzv2h_pll_pars *pars, u64 freq_millihz)
36*73c36010SLad Prabhakar {
37*73c36010SLad Prabhakar unsigned long input_fref = limits->input_fref ?: (24 * MEGA);
38*73c36010SLad Prabhakar u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI);
39*73c36010SLad Prabhakar u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI);
40*73c36010SLad Prabhakar struct rzv2h_pll_pars p, best;
41*73c36010SLad Prabhakar
42*73c36010SLad Prabhakar if (freq_millihz > fout_max_millihz ||
43*73c36010SLad Prabhakar freq_millihz < fout_min_millihz)
44*73c36010SLad Prabhakar return false;
45*73c36010SLad Prabhakar
46*73c36010SLad Prabhakar /* Initialize best error to maximum possible value */
47*73c36010SLad Prabhakar best.error_millihz = S64_MAX;
48*73c36010SLad Prabhakar
49*73c36010SLad Prabhakar for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) {
50*73c36010SLad Prabhakar u32 fref = input_fref / p.p;
51*73c36010SLad Prabhakar u16 divider;
52*73c36010SLad Prabhakar
53*73c36010SLad Prabhakar for (divider = 1 << limits->s.min, p.s = limits->s.min;
54*73c36010SLad Prabhakar p.s <= limits->s.max; p.s++, divider <<= 1) {
55*73c36010SLad Prabhakar for (p.m = limits->m.min; p.m <= limits->m.max; p.m++) {
56*73c36010SLad Prabhakar u64 output_m, output_k_range;
57*73c36010SLad Prabhakar s64 pll_k, output_k;
58*73c36010SLad Prabhakar u64 fvco, output;
59*73c36010SLad Prabhakar
60*73c36010SLad Prabhakar /*
61*73c36010SLad Prabhakar * The frequency generated by the PLL + divider
62*73c36010SLad Prabhakar * is calculated as follows:
63*73c36010SLad Prabhakar *
64*73c36010SLad Prabhakar * With:
65*73c36010SLad Prabhakar * Freq = Ffout = Ffvco / 2^(pll_s)
66*73c36010SLad Prabhakar * Ffvco = (pll_m + (pll_k / 65536)) * Ffref
67*73c36010SLad Prabhakar * Ffref = 24MHz / pll_p
68*73c36010SLad Prabhakar *
69*73c36010SLad Prabhakar * Freq can also be rewritten as:
70*73c36010SLad Prabhakar * Freq = Ffvco / 2^(pll_s)
71*73c36010SLad Prabhakar * = ((pll_m + (pll_k / 65536)) * Ffref) / 2^(pll_s)
72*73c36010SLad Prabhakar * = (pll_m * Ffref) / 2^(pll_s) + ((pll_k / 65536) * Ffref) / 2^(pll_s)
73*73c36010SLad Prabhakar * = output_m + output_k
74*73c36010SLad Prabhakar *
75*73c36010SLad Prabhakar * Every parameter has been determined at this
76*73c36010SLad Prabhakar * point, but pll_k.
77*73c36010SLad Prabhakar *
78*73c36010SLad Prabhakar * Considering that:
79*73c36010SLad Prabhakar * limits->k.min <= pll_k <= limits->k.max
80*73c36010SLad Prabhakar * Then:
81*73c36010SLad Prabhakar * -0.5 <= (pll_k / 65536) < 0.5
82*73c36010SLad Prabhakar * Therefore:
83*73c36010SLad Prabhakar * -Ffref / (2 * 2^(pll_s)) <= output_k < Ffref / (2 * 2^(pll_s))
84*73c36010SLad Prabhakar */
85*73c36010SLad Prabhakar
86*73c36010SLad Prabhakar /* Compute output M component (in mHz) */
87*73c36010SLad Prabhakar output_m = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(p.m, fref) * MILLI,
88*73c36010SLad Prabhakar divider);
89*73c36010SLad Prabhakar /* Compute range for output K (in mHz) */
90*73c36010SLad Prabhakar output_k_range = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(fref, MILLI),
91*73c36010SLad Prabhakar 2 * divider);
92*73c36010SLad Prabhakar /*
93*73c36010SLad Prabhakar * No point in continuing if we can't achieve
94*73c36010SLad Prabhakar * the desired frequency
95*73c36010SLad Prabhakar */
96*73c36010SLad Prabhakar if (freq_millihz < (output_m - output_k_range) ||
97*73c36010SLad Prabhakar freq_millihz >= (output_m + output_k_range)) {
98*73c36010SLad Prabhakar continue;
99*73c36010SLad Prabhakar }
100*73c36010SLad Prabhakar
101*73c36010SLad Prabhakar /*
102*73c36010SLad Prabhakar * Compute the K component
103*73c36010SLad Prabhakar *
104*73c36010SLad Prabhakar * Since:
105*73c36010SLad Prabhakar * Freq = output_m + output_k
106*73c36010SLad Prabhakar * Then:
107*73c36010SLad Prabhakar * output_k = Freq - output_m
108*73c36010SLad Prabhakar * = ((pll_k / 65536) * Ffref) / 2^(pll_s)
109*73c36010SLad Prabhakar * Therefore:
110*73c36010SLad Prabhakar * pll_k = (output_k * 65536 * 2^(pll_s)) / Ffref
111*73c36010SLad Prabhakar */
112*73c36010SLad Prabhakar output_k = freq_millihz - output_m;
113*73c36010SLad Prabhakar pll_k = div_s64(output_k * 65536ULL * divider,
114*73c36010SLad Prabhakar fref);
115*73c36010SLad Prabhakar pll_k = DIV_S64_ROUND_CLOSEST(pll_k, MILLI);
116*73c36010SLad Prabhakar
117*73c36010SLad Prabhakar /* Validate K value within allowed limits */
118*73c36010SLad Prabhakar if (pll_k < limits->k.min ||
119*73c36010SLad Prabhakar pll_k > limits->k.max)
120*73c36010SLad Prabhakar continue;
121*73c36010SLad Prabhakar
122*73c36010SLad Prabhakar p.k = pll_k;
123*73c36010SLad Prabhakar
124*73c36010SLad Prabhakar /* Compute (Ffvco * 65536) */
125*73c36010SLad Prabhakar fvco = mul_u32_u32(p.m * 65536 + p.k, fref);
126*73c36010SLad Prabhakar if (fvco < mul_u32_u32(limits->fvco.min, 65536) ||
127*73c36010SLad Prabhakar fvco > mul_u32_u32(limits->fvco.max, 65536))
128*73c36010SLad Prabhakar continue;
129*73c36010SLad Prabhakar
130*73c36010SLad Prabhakar /* PLL_M component of (output * 65536 * PLL_P) */
131*73c36010SLad Prabhakar output = mul_u32_u32(p.m * 65536, input_fref);
132*73c36010SLad Prabhakar /* PLL_K component of (output * 65536 * PLL_P) */
133*73c36010SLad Prabhakar output += p.k * input_fref;
134*73c36010SLad Prabhakar /* Make it in mHz */
135*73c36010SLad Prabhakar output *= MILLI;
136*73c36010SLad Prabhakar output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider);
137*73c36010SLad Prabhakar
138*73c36010SLad Prabhakar /* Check output frequency against limits */
139*73c36010SLad Prabhakar if (output < fout_min_millihz ||
140*73c36010SLad Prabhakar output > fout_max_millihz)
141*73c36010SLad Prabhakar continue;
142*73c36010SLad Prabhakar
143*73c36010SLad Prabhakar p.error_millihz = freq_millihz - output;
144*73c36010SLad Prabhakar p.freq_millihz = output;
145*73c36010SLad Prabhakar
146*73c36010SLad Prabhakar /* If an exact match is found, return immediately */
147*73c36010SLad Prabhakar if (p.error_millihz == 0) {
148*73c36010SLad Prabhakar *pars = p;
149*73c36010SLad Prabhakar return true;
150*73c36010SLad Prabhakar }
151*73c36010SLad Prabhakar
152*73c36010SLad Prabhakar /* Update best match if error is smaller */
153*73c36010SLad Prabhakar if (abs(best.error_millihz) > abs(p.error_millihz))
154*73c36010SLad Prabhakar best = p;
155*73c36010SLad Prabhakar }
156*73c36010SLad Prabhakar }
157*73c36010SLad Prabhakar }
158*73c36010SLad Prabhakar
159*73c36010SLad Prabhakar /* If no valid parameters were found, return false */
160*73c36010SLad Prabhakar if (best.error_millihz == S64_MAX)
161*73c36010SLad Prabhakar return false;
162*73c36010SLad Prabhakar
163*73c36010SLad Prabhakar *pars = best;
164*73c36010SLad Prabhakar return true;
165*73c36010SLad Prabhakar }
166*73c36010SLad Prabhakar EXPORT_SYMBOL_NS_GPL(rzv2h_cpg_get_pll_pars, "RZV2H_CPG");
167*73c36010SLad Prabhakar
168*73c36010SLad Prabhakar /*
169*73c36010SLad Prabhakar * rzv2h_cpg_get_pll_divs_pars - Finds the best combination of PLL parameters
170*73c36010SLad Prabhakar * and divider value for a given frequency.
171*73c36010SLad Prabhakar *
172*73c36010SLad Prabhakar * @limits: Pointer to the structure containing the limits for the PLL parameters
173*73c36010SLad Prabhakar * @pars: Pointer to the structure where the best calculated PLL parameters and
174*73c36010SLad Prabhakar * divider values will be stored
175*73c36010SLad Prabhakar * @table: Pointer to the array of valid divider values
176*73c36010SLad Prabhakar * @table_size: Size of the divider values array
177*73c36010SLad Prabhakar * @freq_millihz: Target output frequency in millihertz
178*73c36010SLad Prabhakar *
179*73c36010SLad Prabhakar * This function calculates the best set of PLL parameters (M, K, P, S) and divider
180*73c36010SLad Prabhakar * value to achieve the desired frequency. See rzv2h_cpg_get_pll_pars() for more
181*73c36010SLad Prabhakar * details on how the PLL parameters are calculated.
182*73c36010SLad Prabhakar *
183*73c36010SLad Prabhakar * freq_millihz is the desired frequency generated by the PLL followed by a
184*73c36010SLad Prabhakar * a gear.
185*73c36010SLad Prabhakar */
rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits * limits,struct rzv2h_pll_div_pars * pars,const u8 * table,u8 table_size,u64 freq_millihz)186*73c36010SLad Prabhakar bool rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits *limits,
187*73c36010SLad Prabhakar struct rzv2h_pll_div_pars *pars,
188*73c36010SLad Prabhakar const u8 *table, u8 table_size, u64 freq_millihz)
189*73c36010SLad Prabhakar {
190*73c36010SLad Prabhakar struct rzv2h_pll_div_pars p, best;
191*73c36010SLad Prabhakar
192*73c36010SLad Prabhakar best.div.error_millihz = S64_MAX;
193*73c36010SLad Prabhakar p.div.error_millihz = S64_MAX;
194*73c36010SLad Prabhakar for (unsigned int i = 0; i < table_size; i++) {
195*73c36010SLad Prabhakar if (!rzv2h_cpg_get_pll_pars(limits, &p.pll, freq_millihz * table[i]))
196*73c36010SLad Prabhakar continue;
197*73c36010SLad Prabhakar
198*73c36010SLad Prabhakar p.div.divider_value = table[i];
199*73c36010SLad Prabhakar p.div.freq_millihz = DIV_U64_ROUND_CLOSEST(p.pll.freq_millihz, table[i]);
200*73c36010SLad Prabhakar p.div.error_millihz = freq_millihz - p.div.freq_millihz;
201*73c36010SLad Prabhakar
202*73c36010SLad Prabhakar if (p.div.error_millihz == 0) {
203*73c36010SLad Prabhakar *pars = p;
204*73c36010SLad Prabhakar return true;
205*73c36010SLad Prabhakar }
206*73c36010SLad Prabhakar
207*73c36010SLad Prabhakar if (abs(best.div.error_millihz) > abs(p.div.error_millihz))
208*73c36010SLad Prabhakar best = p;
209*73c36010SLad Prabhakar }
210*73c36010SLad Prabhakar
211*73c36010SLad Prabhakar if (best.div.error_millihz == S64_MAX)
212*73c36010SLad Prabhakar return false;
213*73c36010SLad Prabhakar
214*73c36010SLad Prabhakar *pars = best;
215*73c36010SLad Prabhakar return true;
216*73c36010SLad Prabhakar }
217*73c36010SLad Prabhakar EXPORT_SYMBOL_NS_GPL(rzv2h_cpg_get_pll_divs_pars, "RZV2H_CPG");
218