blob: 21ec01f8dd9aa06d9938f06439fbff54b7265c19 [file] [log] [blame]
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * TI DPLL clock support
4 *
5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
6 *
7 * Loosely based on Linux kernel drivers/clk/ti/dpll.c
8 */
9
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010010#include <clk.h>
11#include <clk-uclass.h>
12#include <div64.h>
13#include <dm.h>
14#include <dm/device_compat.h>
15#include <hang.h>
16#include <asm/arch/clock.h>
17#include <asm/arch/sys_proto.h>
18#include <asm/io.h>
Dario Binacchieff80eb2021-05-01 17:05:25 +020019#include "clk.h"
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010020
21struct clk_ti_am3_dpll_drv_data {
22 ulong max_rate;
23};
24
25struct clk_ti_am3_dpll_priv {
Dario Binacchieff80eb2021-05-01 17:05:25 +020026 struct clk_ti_reg clkmode_reg;
27 struct clk_ti_reg idlest_reg;
28 struct clk_ti_reg clksel_reg;
Dario Binacchid4411752021-09-26 11:58:58 +020029 struct clk_ti_reg ssc_deltam_reg;
30 struct clk_ti_reg ssc_modfreq_reg;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010031 struct clk clk_bypass;
32 struct clk clk_ref;
33 u16 last_rounded_mult;
34 u8 last_rounded_div;
Dario Binacchid4411752021-09-26 11:58:58 +020035 u8 min_div;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010036 ulong max_rate;
Dario Binacchid4411752021-09-26 11:58:58 +020037 u32 ssc_modfreq;
38 u32 ssc_deltam;
39 bool ssc_downspread;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010040};
41
42static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate)
43{
44 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
45 ulong ret, ref_rate, r;
46 int m, d, err_min, err;
47 int mult = INT_MAX, div = INT_MAX;
48
49 if (priv->max_rate && rate > priv->max_rate) {
50 dev_warn(clk->dev, "%ld is to high a rate, lowered to %ld\n",
51 rate, priv->max_rate);
52 rate = priv->max_rate;
53 }
54
55 ret = -EFAULT;
56 err = rate;
57 err_min = rate;
58 ref_rate = clk_get_rate(&priv->clk_ref);
Dario Binacchid4411752021-09-26 11:58:58 +020059 for (d = priv->min_div; err_min && d <= 128; d++) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010060 for (m = 2; m <= 2047; m++) {
61 r = (ref_rate * m) / d;
62 err = abs(r - rate);
63 if (err < err_min) {
64 err_min = err;
65 ret = r;
66 mult = m;
67 div = d;
68
69 if (err == 0)
70 break;
71 } else if (r > rate) {
72 break;
73 }
74 }
75 }
76
77 priv->last_rounded_mult = mult;
78 priv->last_rounded_div = div;
Dario Binacchid4411752021-09-26 11:58:58 +020079 dev_dbg(clk->dev, "rate=%ld, min-div: %d, best_rate=%ld, mult=%d, div=%d\n",
80 rate, priv->min_div, ret, mult, div);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010081 return ret;
82}
83
Dario Binacchieff80eb2021-05-01 17:05:25 +020084static void clk_ti_am3_dpll_clken(struct clk_ti_am3_dpll_priv *priv,
85 u8 clken_bits)
86{
87 u32 v;
88
89 v = clk_ti_readl(&priv->clkmode_reg);
90 v &= ~CM_CLKMODE_DPLL_DPLL_EN_MASK;
91 v |= clken_bits << CM_CLKMODE_DPLL_EN_SHIFT;
92 clk_ti_writel(v, &priv->clkmode_reg);
93}
94
95static int clk_ti_am3_dpll_state(struct clk *clk, u8 state)
96{
97 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
98 u32 i = 0, v;
99
100 do {
101 v = clk_ti_readl(&priv->idlest_reg) & ST_DPLL_CLK_MASK;
102 if (v == state) {
103 dev_dbg(clk->dev, "transition to '%s' in %d loops\n",
104 state ? "locked" : "bypassed", i);
105 return 1;
106 }
107
108 } while (++i < LDELAY);
109
110 dev_err(clk->dev, "failed transition to '%s'\n",
111 state ? "locked" : "bypassed");
112 return 0;
113}
114
Dario Binacchid4411752021-09-26 11:58:58 +0200115/**
116 * clk_ti_am3_dpll_ssc_program - set spread-spectrum clocking registers
117 * @clk: struct clk * of DPLL to set
118 *
119 * Enable the DPLL spread spectrum clocking if frequency modulation and
120 * frequency spreading have been set, otherwise disable it.
121 */
122static void clk_ti_am3_dpll_ssc_program(struct clk *clk)
123{
124 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
125 unsigned long ref_rate;
126 u32 v, ctrl, mod_freq_divider, exponent, mantissa;
127 u32 deltam_step, deltam_ceil;
128
129 ctrl = clk_ti_readl(&priv->clkmode_reg);
130
131 if (priv->ssc_modfreq && priv->ssc_deltam) {
132 ctrl |= CM_CLKMODE_DPLL_SSC_EN_MASK;
133
134 if (priv->ssc_downspread)
135 ctrl |= CM_CLKMODE_DPLL_SSC_DOWNSPREAD_MASK;
136 else
137 ctrl &= ~CM_CLKMODE_DPLL_SSC_DOWNSPREAD_MASK;
138
139 ref_rate = clk_get_rate(&priv->clk_ref);
140 mod_freq_divider =
141 (ref_rate / priv->last_rounded_div) / (4 * priv->ssc_modfreq);
142 if (priv->ssc_modfreq > (ref_rate / 70))
143 dev_warn(clk->dev,
144 "clock: SSC modulation frequency of DPLL %s greater than %ld\n",
145 clk->dev->name, ref_rate / 70);
146
147 exponent = 0;
148 mantissa = mod_freq_divider;
149 while ((mantissa > 127) && (exponent < 7)) {
150 exponent++;
151 mantissa /= 2;
152 }
153 if (mantissa > 127)
154 mantissa = 127;
155
156 v = clk_ti_readl(&priv->ssc_modfreq_reg);
157 v &= ~(CM_SSC_MODFREQ_DPLL_MANT_MASK | CM_SSC_MODFREQ_DPLL_EXP_MASK);
158 v |= mantissa << __ffs(CM_SSC_MODFREQ_DPLL_MANT_MASK);
159 v |= exponent << __ffs(CM_SSC_MODFREQ_DPLL_EXP_MASK);
160 clk_ti_writel(v, &priv->ssc_modfreq_reg);
161 dev_dbg(clk->dev,
162 "mod_freq_divider: %u, exponent: %u, mantissa: %u, modfreq_reg: 0x%x\n",
163 mod_freq_divider, exponent, mantissa, v);
164
165 deltam_step = priv->last_rounded_mult * priv->ssc_deltam;
166 deltam_step /= 10;
167 if (priv->ssc_downspread)
168 deltam_step /= 2;
169
170 deltam_step <<= __ffs(CM_SSC_DELTAM_DPLL_INT_MASK);
171 deltam_step /= 100;
172 deltam_step /= mod_freq_divider;
173 if (deltam_step > 0xFFFFF)
174 deltam_step = 0xFFFFF;
175
176 deltam_ceil = (deltam_step & CM_SSC_DELTAM_DPLL_INT_MASK) >>
177 __ffs(CM_SSC_DELTAM_DPLL_INT_MASK);
178 if (deltam_step & CM_SSC_DELTAM_DPLL_FRAC_MASK)
179 deltam_ceil++;
180
181 if ((priv->ssc_downspread &&
182 ((priv->last_rounded_mult - (2 * deltam_ceil)) < 20 ||
183 priv->last_rounded_mult > 2045)) ||
184 ((priv->last_rounded_mult - deltam_ceil) < 20 ||
185 (priv->last_rounded_mult + deltam_ceil) > 2045))
186 dev_warn(clk->dev,
187 "clock: SSC multiplier of DPLL %s is out of range\n",
188 clk->dev->name);
189
190 v = clk_ti_readl(&priv->ssc_deltam_reg);
191 v &= ~(CM_SSC_DELTAM_DPLL_INT_MASK | CM_SSC_DELTAM_DPLL_FRAC_MASK);
192 v |= deltam_step << __ffs(CM_SSC_DELTAM_DPLL_INT_MASK |
193 CM_SSC_DELTAM_DPLL_FRAC_MASK);
194 clk_ti_writel(v, &priv->ssc_deltam_reg);
195 dev_dbg(clk->dev,
196 "deltam_step: %u, deltam_ceil: %u, deltam_reg: 0x%x\n",
197 deltam_step, deltam_ceil, v);
198 } else {
199 ctrl &= ~CM_CLKMODE_DPLL_SSC_EN_MASK;
200 }
201
202 clk_ti_writel(ctrl, &priv->clkmode_reg);
203}
204
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100205static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate)
206{
207 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
208 u32 v;
209 ulong round_rate;
210
211 round_rate = clk_ti_am3_dpll_round_rate(clk, rate);
212 if (IS_ERR_VALUE(round_rate))
213 return round_rate;
214
Dario Binacchieff80eb2021-05-01 17:05:25 +0200215 v = clk_ti_readl(&priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100216
217 /* enter bypass mode */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200218 clk_ti_am3_dpll_clken(priv, DPLL_EN_MN_BYPASS);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100219
220 /* wait for bypass mode */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200221 clk_ti_am3_dpll_state(clk, 0);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100222
223 /* set M & N */
224 v &= ~CM_CLKSEL_DPLL_M_MASK;
225 v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) &
226 CM_CLKSEL_DPLL_M_MASK;
227
228 v &= ~CM_CLKSEL_DPLL_N_MASK;
229 v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) &
230 CM_CLKSEL_DPLL_N_MASK;
231
Dario Binacchieff80eb2021-05-01 17:05:25 +0200232 clk_ti_writel(v, &priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100233
Dario Binacchid4411752021-09-26 11:58:58 +0200234 clk_ti_am3_dpll_ssc_program(clk);
235
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100236 /* lock dpll */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200237 clk_ti_am3_dpll_clken(priv, DPLL_EN_LOCK);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100238
239 /* wait till the dpll locks */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200240 if (!clk_ti_am3_dpll_state(clk, ST_DPLL_CLK_MASK))
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100241 hang();
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100242
243 return round_rate;
244}
245
246static ulong clk_ti_am3_dpll_get_rate(struct clk *clk)
247{
248 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
249 u64 rate;
250 u32 m, n, v;
251
252 /* Return bypass rate if DPLL is bypassed */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200253 v = clk_ti_readl(&priv->clkmode_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100254 v &= CM_CLKMODE_DPLL_EN_MASK;
255 v >>= CM_CLKMODE_DPLL_EN_SHIFT;
256
257 switch (v) {
258 case DPLL_EN_MN_BYPASS:
259 case DPLL_EN_LOW_POWER_BYPASS:
260 case DPLL_EN_FAST_RELOCK_BYPASS:
261 rate = clk_get_rate(&priv->clk_bypass);
262 dev_dbg(clk->dev, "rate=%lld\n", rate);
263 return rate;
264 }
265
Dario Binacchieff80eb2021-05-01 17:05:25 +0200266 v = clk_ti_readl(&priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100267 m = v & CM_CLKSEL_DPLL_M_MASK;
268 m >>= CM_CLKSEL_DPLL_M_SHIFT;
269 n = v & CM_CLKSEL_DPLL_N_MASK;
270 n >>= CM_CLKSEL_DPLL_N_SHIFT;
271
272 rate = clk_get_rate(&priv->clk_ref) * m;
273 do_div(rate, n + 1);
274 dev_dbg(clk->dev, "rate=%lld\n", rate);
275 return rate;
276}
277
278const struct clk_ops clk_ti_am3_dpll_ops = {
279 .round_rate = clk_ti_am3_dpll_round_rate,
280 .get_rate = clk_ti_am3_dpll_get_rate,
281 .set_rate = clk_ti_am3_dpll_set_rate,
282};
283
284static int clk_ti_am3_dpll_remove(struct udevice *dev)
285{
286 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
287 int err;
288
289 err = clk_release_all(&priv->clk_bypass, 1);
290 if (err) {
291 dev_err(dev, "failed to release bypass clock\n");
292 return err;
293 }
294
295 err = clk_release_all(&priv->clk_ref, 1);
296 if (err) {
297 dev_err(dev, "failed to release reference clock\n");
298 return err;
299 }
300
301 return 0;
302}
303
304static int clk_ti_am3_dpll_probe(struct udevice *dev)
305{
306 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
307 int err;
308
309 err = clk_get_by_index(dev, 0, &priv->clk_ref);
310 if (err) {
311 dev_err(dev, "failed to get reference clock\n");
312 return err;
313 }
314
315 err = clk_get_by_index(dev, 1, &priv->clk_bypass);
316 if (err) {
317 dev_err(dev, "failed to get bypass clock\n");
318 return err;
319 }
320
321 return 0;
322}
323
324static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev)
325{
326 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
327 struct clk_ti_am3_dpll_drv_data *data =
328 (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev);
Dario Binacchid4411752021-09-26 11:58:58 +0200329 u32 min_div;
Dario Binacchieff80eb2021-05-01 17:05:25 +0200330 int err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100331
332 priv->max_rate = data->max_rate;
333
Dario Binacchieff80eb2021-05-01 17:05:25 +0200334 err = clk_ti_get_reg_addr(dev, 0, &priv->clkmode_reg);
335 if (err) {
336 dev_err(dev, "failed to get clkmode register address\n");
337 return err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100338 }
339
Dario Binacchieff80eb2021-05-01 17:05:25 +0200340 err = clk_ti_get_reg_addr(dev, 1, &priv->idlest_reg);
341 if (err) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100342 dev_err(dev, "failed to get idlest register\n");
343 return -EINVAL;
344 }
345
Dario Binacchieff80eb2021-05-01 17:05:25 +0200346 err = clk_ti_get_reg_addr(dev, 2, &priv->clksel_reg);
347 if (err) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100348 dev_err(dev, "failed to get clksel register\n");
Dario Binacchieff80eb2021-05-01 17:05:25 +0200349 return err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100350 }
351
Dario Binacchid4411752021-09-26 11:58:58 +0200352 err = clk_ti_get_reg_addr(dev, 3, &priv->ssc_deltam_reg);
353 if (err) {
354 dev_err(dev, "failed to get SSC deltam register\n");
355 return err;
356 }
357
358 err = clk_ti_get_reg_addr(dev, 4, &priv->ssc_modfreq_reg);
359 if (err) {
360 dev_err(dev, "failed to get SSC modfreq register\n");
361 return err;
362 }
363
364 if (dev_read_u32(dev, "ti,ssc-modfreq-hz", &priv->ssc_modfreq))
365 priv->ssc_modfreq = 0;
366
367 if (dev_read_u32(dev, "ti,ssc-deltam", &priv->ssc_deltam))
368 priv->ssc_deltam = 0;
369
370 priv->ssc_downspread = dev_read_bool(dev, "ti,ssc-downspread");
371
372 if (dev_read_u32(dev, "ti,min-div", &min_div) || min_div == 0 ||
373 min_div > 128)
374 priv->min_div = 1;
375 else
376 priv->min_div = min_div;
377
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100378 return 0;
379}
380
381static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
382 .max_rate = 1000000000
383};
384
385static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
386 .max_rate = 2000000000
387};
388
389static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
390 .max_rate = 1000000000
391};
392
393static const struct udevice_id clk_ti_am3_dpll_of_match[] = {
394 {.compatible = "ti,am3-dpll-core-clock",
395 .data = (ulong)&dpll_core_data},
396 {.compatible = "ti,am3-dpll-no-gate-clock",
397 .data = (ulong)&dpll_no_gate_data},
398 {.compatible = "ti,am3-dpll-no-gate-j-type-clock",
399 .data = (ulong)&dpll_no_gate_j_type_data},
400 {}
401};
402
403U_BOOT_DRIVER(clk_ti_am3_dpll) = {
404 .name = "ti_am3_dpll_clock",
405 .id = UCLASS_CLK,
406 .of_match = clk_ti_am3_dpll_of_match,
Dario Binacchif686e4d2021-01-15 09:10:26 +0100407 .of_to_plat = clk_ti_am3_dpll_of_to_plat,
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100408 .probe = clk_ti_am3_dpll_probe,
409 .remove = clk_ti_am3_dpll_remove,
410 .priv_auto = sizeof(struct clk_ti_am3_dpll_priv),
411 .ops = &clk_ti_am3_dpll_ops,
412};