1 /*
2 * Copyright (c) 2017 Rockchip Electronics Co. Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #include "clk-regmap.h"
16
17 #define PLLCON_OFFSET(x) (x * 4)
18
19 #define PLL_BYPASS(x) HIWORD_UPDATE(x, 15, 15)
20 #define PLL_BYPASS_MASK BIT(15)
21 #define PLL_BYPASS_SHIFT 15
22 #define PLL_POSTDIV1(x) HIWORD_UPDATE(x, 14, 12)
23 #define PLL_POSTDIV1_MASK GENMASK(14, 12)
24 #define PLL_POSTDIV1_SHIFT 12
25 #define PLL_FBDIV(x) HIWORD_UPDATE(x, 11, 0)
26 #define PLL_FBDIV_MASK GENMASK(11, 0)
27 #define PLL_FBDIV_SHIFT 0
28
29 #define PLL_POSTDIV2(x) HIWORD_UPDATE(x, 8, 6)
30 #define PLL_POSTDIV2_MASK GENMASK(8, 6)
31 #define PLL_POSTDIV2_SHIFT 6
32 #define PLL_REFDIV(x) HIWORD_UPDATE(x, 5, 0)
33 #define PLL_REFDIV_MASK GENMASK(5, 0)
34 #define PLL_REFDIV_SHIFT 0
35
36 #define PLL_FOUT_4PHASE_CLK_POWER_DOWN BIT(27)
37 #define PLL_FOUT_VCO_CLK_POWER_DOWN BIT(26)
38 #define PLL_FOUT_POST_DIV_POWER_DOWN BIT(25)
39 #define PLL_DAC_POWER_DOWN BIT(24)
40 #define PLL_FRAC(x) UPDATE(x, 23, 0)
41 #define PLL_FRAC_MASK GENMASK(23, 0)
42 #define PLL_FRAC_SHIFT 0
43
44 #define MIN_FREF_RATE 10000000UL
45 #define MAX_FREF_RATE 800000000UL
46 #define MIN_FREFDIV_RATE 1000000UL
47 #define MAX_FREFDIV_RATE 40000000UL
48 #define MIN_FVCO_RATE 400000000UL
49 #define MAX_FVCO_RATE 1600000000UL
50 #define MIN_FOUTPOSTDIV_RATE 8000000UL
51 #define MAX_FOUTPOSTDIV_RATE 1600000000UL
52
53 struct clk_regmap_pll {
54 struct clk_hw hw;
55 struct device *dev;
56 struct regmap *regmap;
57 unsigned int reg;
58 u8 pd_shift;
59 u8 dsmpd_shift;
60 u8 lock_shift;
61 };
62
63 #define to_clk_regmap_pll(_hw) container_of(_hw, struct clk_regmap_pll, hw)
64
65 static unsigned long
clk_regmap_pll_recalc_rate(struct clk_hw * hw,unsigned long prate)66 clk_regmap_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
67 {
68 struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
69 unsigned int postdiv1, fbdiv, dsmpd, postdiv2, refdiv, frac, bypass;
70 unsigned int con0, con1, con2;
71 u64 foutvco, foutpostdiv;
72
73 regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(0), &con0);
74 regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(1), &con1);
75 regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(2), &con2);
76
77 bypass = (con0 & PLL_BYPASS_MASK) >> PLL_BYPASS_SHIFT;
78 postdiv1 = (con0 & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
79 fbdiv = (con0 & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
80 dsmpd = (con1 & BIT(pll->dsmpd_shift)) >> pll->dsmpd_shift;
81 postdiv2 = (con1 & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
82 refdiv = (con1 & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
83 frac = (con2 & PLL_FRAC_MASK) >> PLL_FRAC_SHIFT;
84
85 if (bypass)
86 return prate;
87
88 foutvco = prate * fbdiv;
89 do_div(foutvco, refdiv);
90
91 if (!dsmpd) {
92 u64 frac_rate = (u64)prate * frac;
93
94 do_div(frac_rate, refdiv);
95 foutvco += frac_rate >> 24;
96 }
97
98 foutpostdiv = foutvco;
99 do_div(foutpostdiv, postdiv1);
100 do_div(foutpostdiv, postdiv2);
101
102 return foutpostdiv;
103 }
104
clk_pll_round_rate(unsigned long fin,unsigned long fout,u8 * refdiv,u16 * fbdiv,u8 * postdiv1,u8 * postdiv2,u32 * frac,u8 * dsmpd,u8 * bypass)105 static long clk_pll_round_rate(unsigned long fin, unsigned long fout,
106 u8 *refdiv, u16 *fbdiv,
107 u8 *postdiv1, u8 *postdiv2,
108 u32 *frac, u8 *dsmpd, u8 *bypass)
109 {
110 u8 min_refdiv, max_refdiv, postdiv;
111 u8 _dsmpd = 1, _postdiv1 = 0, _postdiv2 = 0, _refdiv = 0;
112 u16 _fbdiv = 0;
113 u32 _frac = 0;
114 u64 foutvco, foutpostdiv;
115
116 /*
117 * FREF : 10MHz ~ 800MHz
118 * FREFDIV : 1MHz ~ 40MHz
119 * FOUTVCO : 400MHz ~ 1.6GHz
120 * FOUTPOSTDIV : 8MHz ~ 1.6GHz
121 */
122 if (fin < MIN_FREF_RATE || fin > MAX_FREF_RATE)
123 return -EINVAL;
124
125 if (fout < MIN_FOUTPOSTDIV_RATE || fout > MAX_FOUTPOSTDIV_RATE)
126 return -EINVAL;
127
128 if (fin == fout) {
129 if (bypass)
130 *bypass = true;
131 return fin;
132 }
133
134 min_refdiv = DIV_ROUND_UP(fin, MAX_FREFDIV_RATE);
135 max_refdiv = fin / MIN_FREFDIV_RATE;
136 if (max_refdiv > 64)
137 max_refdiv = 64;
138
139 if (fout < MIN_FVCO_RATE) {
140 postdiv = DIV_ROUND_UP_ULL(MIN_FVCO_RATE, fout);
141
142 for (_postdiv2 = 1; _postdiv2 < 8; _postdiv2++) {
143 if (postdiv % _postdiv2)
144 continue;
145
146 _postdiv1 = postdiv / _postdiv2;
147
148 if (_postdiv1 > 0 && _postdiv1 < 8)
149 break;
150 }
151
152 if (_postdiv2 > 7)
153 return -EINVAL;
154
155 fout *= _postdiv1 * _postdiv2;
156 } else {
157 _postdiv1 = 1;
158 _postdiv2 = 1;
159 }
160
161 for (_refdiv = min_refdiv; _refdiv <= max_refdiv; _refdiv++) {
162 u64 tmp, frac_rate;
163
164 if (fin % _refdiv)
165 continue;
166
167 tmp = (u64)fout * _refdiv;
168 do_div(tmp, fin);
169 _fbdiv = tmp;
170 if (_fbdiv < 10 || _fbdiv > 1600)
171 continue;
172
173 tmp = (u64)_fbdiv * fin;
174 do_div(tmp, _refdiv);
175 if (fout < MIN_FVCO_RATE || fout > MAX_FVCO_RATE)
176 continue;
177
178 frac_rate = fout - tmp;
179
180 if (frac_rate) {
181 tmp = (u64)frac_rate * _refdiv;
182 tmp <<= 24;
183 do_div(tmp, fin);
184 _frac = tmp;
185 _dsmpd = 0;
186 }
187
188 break;
189 }
190
191 /*
192 * If DSMPD = 1 (DSM is disabled, "integer mode")
193 * FOUTVCO = FREF / REFDIV * FBDIV
194 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
195 *
196 * If DSMPD = 0 (DSM is enabled, "fractional mode")
197 * FOUTVCO = FREF / REFDIV * (FBDIV + FRAC / 2^24)
198 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
199 */
200 foutvco = fin * _fbdiv;
201 do_div(foutvco, _refdiv);
202
203 if (!_dsmpd) {
204 u64 frac_rate = (u64)fin * _frac;
205
206 do_div(frac_rate, _refdiv);
207 foutvco += frac_rate >> 24;
208 }
209
210 foutpostdiv = foutvco;
211 do_div(foutpostdiv, _postdiv1);
212 do_div(foutpostdiv, _postdiv2);
213
214 if (refdiv)
215 *refdiv = _refdiv;
216 if (fbdiv)
217 *fbdiv = _fbdiv;
218 if (postdiv1)
219 *postdiv1 = _postdiv1;
220 if (postdiv2)
221 *postdiv2 = _postdiv2;
222 if (frac)
223 *frac = _frac;
224 if (dsmpd)
225 *dsmpd = _dsmpd;
226 if (bypass)
227 *bypass = false;
228
229 return (unsigned long)foutpostdiv;
230 }
231
232 static long
clk_regmap_pll_round_rate(struct clk_hw * hw,unsigned long drate,unsigned long * prate)233 clk_regmap_pll_round_rate(struct clk_hw *hw, unsigned long drate,
234 unsigned long *prate)
235 {
236 struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
237 long rate;
238
239 rate = clk_pll_round_rate(*prate, drate, NULL, NULL, NULL, NULL, NULL,
240 NULL, NULL);
241
242 dev_dbg(pll->dev, "%s: prate=%ld, drate=%ld, rate=%ld\n",
243 clk_hw_get_name(hw), *prate, drate, rate);
244
245 return rate;
246 }
247
248 static int
clk_regmap_pll_set_rate(struct clk_hw * hw,unsigned long drate,unsigned long prate)249 clk_regmap_pll_set_rate(struct clk_hw *hw, unsigned long drate,
250 unsigned long prate)
251 {
252 struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
253 u8 refdiv, postdiv1, postdiv2, dsmpd, bypass;
254 u16 fbdiv;
255 u32 frac;
256 long rate;
257
258 rate = clk_pll_round_rate(prate, drate, &refdiv, &fbdiv, &postdiv1,
259 &postdiv2, &frac, &dsmpd, &bypass);
260 if (rate < 0)
261 return rate;
262
263 dev_dbg(pll->dev, "%s: rate=%ld, bypass=%d\n",
264 clk_hw_get_name(hw), drate, bypass);
265
266 if (bypass) {
267 regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(0),
268 PLL_BYPASS(1));
269 } else {
270 regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(0),
271 PLL_BYPASS(0) | PLL_POSTDIV1(postdiv1) |
272 PLL_FBDIV(fbdiv));
273 regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1),
274 HIWORD_UPDATE(dsmpd, pll->dsmpd_shift, pll->dsmpd_shift) |
275 PLL_POSTDIV2(postdiv2) | PLL_REFDIV(refdiv));
276 regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(2),
277 PLL_FRAC(frac));
278
279 dev_dbg(pll->dev, "refdiv=%d, fbdiv=%d, frac=%d\n",
280 refdiv, fbdiv, frac);
281 dev_dbg(pll->dev, "postdiv1=%d, postdiv2=%d\n",
282 postdiv1, postdiv2);
283 }
284
285 return 0;
286 }
287
clk_regmap_pll_prepare(struct clk_hw * hw)288 static int clk_regmap_pll_prepare(struct clk_hw *hw)
289 {
290 struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
291 u32 v;
292 int ret;
293
294 regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1),
295 HIWORD_UPDATE(0, pll->pd_shift, pll->pd_shift));
296
297 ret = regmap_read_poll_timeout(pll->regmap,
298 pll->reg + PLLCON_OFFSET(1),
299 v, v & BIT(pll->lock_shift), 50, 50000);
300 if (ret)
301 dev_err(pll->dev, "%s is not lock\n", clk_hw_get_name(hw));
302
303 return 0;
304 }
305
clk_regmap_pll_unprepare(struct clk_hw * hw)306 static void clk_regmap_pll_unprepare(struct clk_hw *hw)
307 {
308 struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
309
310 regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1),
311 HIWORD_UPDATE(1, pll->pd_shift, pll->pd_shift));
312 }
313
clk_regmap_pll_is_prepared(struct clk_hw * hw)314 static int clk_regmap_pll_is_prepared(struct clk_hw *hw)
315 {
316 struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
317 unsigned int con1;
318
319 regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(1), &con1);
320
321 return !(con1 & BIT(pll->pd_shift));
322 }
323
324 static const struct clk_ops clk_regmap_pll_ops = {
325 .recalc_rate = clk_regmap_pll_recalc_rate,
326 .round_rate = clk_regmap_pll_round_rate,
327 .set_rate = clk_regmap_pll_set_rate,
328 .prepare = clk_regmap_pll_prepare,
329 .unprepare = clk_regmap_pll_unprepare,
330 .is_prepared = clk_regmap_pll_is_prepared,
331 };
332
333 struct clk *
devm_clk_regmap_register_pll(struct device * dev,const char * name,const char * parent_name,struct regmap * regmap,u32 reg,u8 pd_shift,u8 dsmpd_shift,u8 lock_shift,unsigned long flags)334 devm_clk_regmap_register_pll(struct device *dev, const char *name,
335 const char *parent_name,
336 struct regmap *regmap, u32 reg, u8 pd_shift,
337 u8 dsmpd_shift, u8 lock_shift,
338 unsigned long flags)
339 {
340 struct clk_regmap_pll *pll;
341 struct clk_init_data init = {};
342
343 pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
344 if (!pll)
345 return ERR_PTR(-ENOMEM);
346
347 init.name = name;
348 init.ops = &clk_regmap_pll_ops;
349 init.flags = flags;
350 init.parent_names = (parent_name ? &parent_name : NULL);
351 init.num_parents = (parent_name ? 1 : 0);
352
353 pll->dev = dev;
354 pll->regmap = regmap;
355 pll->reg = reg;
356 pll->pd_shift = pd_shift;
357 pll->dsmpd_shift = dsmpd_shift;
358 pll->lock_shift = lock_shift;
359 pll->hw.init = &init;
360
361 return devm_clk_register(dev, &pll->hw);
362 }
363 EXPORT_SYMBOL_GPL(devm_clk_regmap_register_pll);
364