xref: /openbmc/linux/drivers/clk/meson/clk-pll.c (revision 96de2506)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2015 Endless Mobile, Inc.
4  * Author: Carlo Caione <carlo@endlessm.com>
5  *
6  * Copyright (c) 2018 Baylibre, SAS.
7  * Author: Jerome Brunet <jbrunet@baylibre.com>
8  */
9 
10 /*
11  * In the most basic form, a Meson PLL is composed as follows:
12  *
13  *                     PLL
14  *      +------------------------------+
15  *      |                              |
16  * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
17  *      |         ^        ^           |
18  *      +------------------------------+
19  *                |        |
20  *               FREF     VCO
21  *
22  * out = in * (m + frac / frac_max) / (n << sum(ods))
23  */
24 
25 #include <linux/clk-provider.h>
26 #include <linux/delay.h>
27 #include <linux/err.h>
28 #include <linux/io.h>
29 #include <linux/math64.h>
30 #include <linux/module.h>
31 #include <linux/of_address.h>
32 #include <linux/slab.h>
33 #include <linux/string.h>
34 
35 #include "clkc.h"
36 
37 static inline struct meson_clk_pll_data *
38 meson_clk_pll_data(struct clk_regmap *clk)
39 {
40 	return (struct meson_clk_pll_data *)clk->data;
41 }
42 
43 static unsigned long __pll_params_to_rate(unsigned long parent_rate,
44 					  const struct pll_rate_table *pllt,
45 					  u16 frac,
46 					  struct meson_clk_pll_data *pll)
47 {
48 	u64 rate = (u64)parent_rate * pllt->m;
49 	unsigned int od = pllt->od + pllt->od2 + pllt->od3;
50 
51 	if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
52 		u64 frac_rate = (u64)parent_rate * frac;
53 
54 		rate += DIV_ROUND_UP_ULL(frac_rate,
55 					 (1 << pll->frac.width));
56 	}
57 
58 	return DIV_ROUND_UP_ULL(rate, pllt->n << od);
59 }
60 
61 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
62 						unsigned long parent_rate)
63 {
64 	struct clk_regmap *clk = to_clk_regmap(hw);
65 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
66 	struct pll_rate_table pllt;
67 	u16 frac;
68 
69 	pllt.n = meson_parm_read(clk->map, &pll->n);
70 	pllt.m = meson_parm_read(clk->map, &pll->m);
71 	pllt.od = meson_parm_read(clk->map, &pll->od);
72 
73 	pllt.od2 = MESON_PARM_APPLICABLE(&pll->od2) ?
74 		meson_parm_read(clk->map, &pll->od2) :
75 		0;
76 
77 	pllt.od3 = MESON_PARM_APPLICABLE(&pll->od3) ?
78 		meson_parm_read(clk->map, &pll->od3) :
79 		0;
80 
81 	frac = MESON_PARM_APPLICABLE(&pll->frac) ?
82 		meson_parm_read(clk->map, &pll->frac) :
83 		0;
84 
85 	return __pll_params_to_rate(parent_rate, &pllt, frac, pll);
86 }
87 
88 static u16 __pll_params_with_frac(unsigned long rate,
89 				  unsigned long parent_rate,
90 				  const struct pll_rate_table *pllt,
91 				  struct meson_clk_pll_data *pll)
92 {
93 	u16 frac_max = (1 << pll->frac.width);
94 	u64 val = (u64)rate * pllt->n;
95 
96 	val <<= pllt->od + pllt->od2 + pllt->od3;
97 
98 	if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
99 		val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate);
100 	else
101 		val = div_u64(val * frac_max, parent_rate);
102 
103 	val -= pllt->m * frac_max;
104 
105 	return min((u16)val, (u16)(frac_max - 1));
106 }
107 
108 static const struct pll_rate_table *
109 meson_clk_get_pll_settings(unsigned long rate,
110 			   struct meson_clk_pll_data *pll)
111 {
112 	const struct pll_rate_table *table = pll->table;
113 	unsigned int i = 0;
114 
115 	if (!table)
116 		return NULL;
117 
118 	/* Find the first table element exceeding rate */
119 	while (table[i].rate && table[i].rate <= rate)
120 		i++;
121 
122 	if (i != 0) {
123 		if (MESON_PARM_APPLICABLE(&pll->frac) ||
124 		    !(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) ||
125 		    (abs(rate - table[i - 1].rate) <
126 		     abs(rate - table[i].rate)))
127 			i--;
128 	}
129 
130 	return (struct pll_rate_table *)&table[i];
131 }
132 
133 static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
134 				     unsigned long *parent_rate)
135 {
136 	struct clk_regmap *clk = to_clk_regmap(hw);
137 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
138 	const struct pll_rate_table *pllt =
139 		meson_clk_get_pll_settings(rate, pll);
140 	u16 frac;
141 
142 	if (!pllt)
143 		return meson_clk_pll_recalc_rate(hw, *parent_rate);
144 
145 	if (!MESON_PARM_APPLICABLE(&pll->frac)
146 	    || rate == pllt->rate)
147 		return pllt->rate;
148 
149 	/*
150 	 * The rate provided by the setting is not an exact match, let's
151 	 * try to improve the result using the fractional parameter
152 	 */
153 	frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll);
154 
155 	return __pll_params_to_rate(*parent_rate, pllt, frac, pll);
156 }
157 
158 static int meson_clk_pll_wait_lock(struct clk_hw *hw)
159 {
160 	struct clk_regmap *clk = to_clk_regmap(hw);
161 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
162 	int delay = 24000000;
163 
164 	do {
165 		/* Is the clock locked now ? */
166 		if (meson_parm_read(clk->map, &pll->l))
167 			return 0;
168 
169 		delay--;
170 	} while (delay > 0);
171 
172 	return -ETIMEDOUT;
173 }
174 
175 static void meson_clk_pll_init(struct clk_hw *hw)
176 {
177 	struct clk_regmap *clk = to_clk_regmap(hw);
178 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
179 
180 	if (pll->init_count) {
181 		meson_parm_write(clk->map, &pll->rst, 1);
182 		regmap_multi_reg_write(clk->map, pll->init_regs,
183 				       pll->init_count);
184 		meson_parm_write(clk->map, &pll->rst, 0);
185 	}
186 }
187 
188 static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
189 				  unsigned long parent_rate)
190 {
191 	struct clk_regmap *clk = to_clk_regmap(hw);
192 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
193 	const struct pll_rate_table *pllt;
194 	unsigned long old_rate;
195 	u16 frac = 0;
196 
197 	if (parent_rate == 0 || rate == 0)
198 		return -EINVAL;
199 
200 	old_rate = rate;
201 
202 	pllt = meson_clk_get_pll_settings(rate, pll);
203 	if (!pllt)
204 		return -EINVAL;
205 
206 	/* Put the pll in reset to write the params */
207 	meson_parm_write(clk->map, &pll->rst, 1);
208 
209 	meson_parm_write(clk->map, &pll->n, pllt->n);
210 	meson_parm_write(clk->map, &pll->m, pllt->m);
211 	meson_parm_write(clk->map, &pll->od, pllt->od);
212 
213 	if (MESON_PARM_APPLICABLE(&pll->od2))
214 		meson_parm_write(clk->map, &pll->od2, pllt->od2);
215 
216 	if (MESON_PARM_APPLICABLE(&pll->od3))
217 		meson_parm_write(clk->map, &pll->od3, pllt->od3);
218 
219 	if (MESON_PARM_APPLICABLE(&pll->frac)) {
220 		frac = __pll_params_with_frac(rate, parent_rate, pllt, pll);
221 		meson_parm_write(clk->map, &pll->frac, frac);
222 	}
223 
224 	/* make sure the reset is cleared at this point */
225 	meson_parm_write(clk->map, &pll->rst, 0);
226 
227 	if (meson_clk_pll_wait_lock(hw)) {
228 		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
229 			__func__, old_rate);
230 		/*
231 		 * FIXME: Do we really need/want this HACK ?
232 		 * It looks unsafe. what happens if the clock gets into a
233 		 * broken state and we can't lock back on the old_rate ? Looks
234 		 * like an infinite recursion is possible
235 		 */
236 		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
237 	}
238 
239 	return 0;
240 }
241 
242 const struct clk_ops meson_clk_pll_ops = {
243 	.init		= meson_clk_pll_init,
244 	.recalc_rate	= meson_clk_pll_recalc_rate,
245 	.round_rate	= meson_clk_pll_round_rate,
246 	.set_rate	= meson_clk_pll_set_rate,
247 };
248 
249 const struct clk_ops meson_clk_pll_ro_ops = {
250 	.recalc_rate	= meson_clk_pll_recalc_rate,
251 };
252