blob: 398a011a5cea9c82c2d38c823a2bc498c870e6c9 [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
10#include <common.h>
11#include <clk.h>
12#include <clk-uclass.h>
13#include <div64.h>
14#include <dm.h>
15#include <dm/device_compat.h>
16#include <hang.h>
17#include <asm/arch/clock.h>
18#include <asm/arch/sys_proto.h>
19#include <asm/io.h>
Dario Binacchieff80eb2021-05-01 17:05:25 +020020#include "clk.h"
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010021
22struct clk_ti_am3_dpll_drv_data {
23 ulong max_rate;
24};
25
26struct clk_ti_am3_dpll_priv {
Dario Binacchieff80eb2021-05-01 17:05:25 +020027 struct clk_ti_reg clkmode_reg;
28 struct clk_ti_reg idlest_reg;
29 struct clk_ti_reg clksel_reg;
Dario Binacchid4411752021-09-26 11:58:58 +020030 struct clk_ti_reg ssc_deltam_reg;
31 struct clk_ti_reg ssc_modfreq_reg;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010032 struct clk clk_bypass;
33 struct clk clk_ref;
34 u16 last_rounded_mult;
35 u8 last_rounded_div;
Dario Binacchid4411752021-09-26 11:58:58 +020036 u8 min_div;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010037 ulong max_rate;
Dario Binacchid4411752021-09-26 11:58:58 +020038 u32 ssc_modfreq;
39 u32 ssc_deltam;
40 bool ssc_downspread;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010041};
42
43static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate)
44{
45 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
46 ulong ret, ref_rate, r;
47 int m, d, err_min, err;
48 int mult = INT_MAX, div = INT_MAX;
49
50 if (priv->max_rate && rate > priv->max_rate) {
51 dev_warn(clk->dev, "%ld is to high a rate, lowered to %ld\n",
52 rate, priv->max_rate);
53 rate = priv->max_rate;
54 }
55
56 ret = -EFAULT;
57 err = rate;
58 err_min = rate;
59 ref_rate = clk_get_rate(&priv->clk_ref);
Dario Binacchid4411752021-09-26 11:58:58 +020060 for (d = priv->min_div; err_min && d <= 128; d++) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010061 for (m = 2; m <= 2047; m++) {
62 r = (ref_rate * m) / d;
63 err = abs(r - rate);
64 if (err < err_min) {
65 err_min = err;
66 ret = r;
67 mult = m;
68 div = d;
69
70 if (err == 0)
71 break;
72 } else if (r > rate) {
73 break;
74 }
75 }
76 }
77
78 priv->last_rounded_mult = mult;
79 priv->last_rounded_div = div;
Dario Binacchid4411752021-09-26 11:58:58 +020080 dev_dbg(clk->dev, "rate=%ld, min-div: %d, best_rate=%ld, mult=%d, div=%d\n",
81 rate, priv->min_div, ret, mult, div);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +010082 return ret;
83}
84
Dario Binacchieff80eb2021-05-01 17:05:25 +020085static void clk_ti_am3_dpll_clken(struct clk_ti_am3_dpll_priv *priv,
86 u8 clken_bits)
87{
88 u32 v;
89
90 v = clk_ti_readl(&priv->clkmode_reg);
91 v &= ~CM_CLKMODE_DPLL_DPLL_EN_MASK;
92 v |= clken_bits << CM_CLKMODE_DPLL_EN_SHIFT;
93 clk_ti_writel(v, &priv->clkmode_reg);
94}
95
96static int clk_ti_am3_dpll_state(struct clk *clk, u8 state)
97{
98 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
99 u32 i = 0, v;
100
101 do {
102 v = clk_ti_readl(&priv->idlest_reg) & ST_DPLL_CLK_MASK;
103 if (v == state) {
104 dev_dbg(clk->dev, "transition to '%s' in %d loops\n",
105 state ? "locked" : "bypassed", i);
106 return 1;
107 }
108
109 } while (++i < LDELAY);
110
111 dev_err(clk->dev, "failed transition to '%s'\n",
112 state ? "locked" : "bypassed");
113 return 0;
114}
115
Dario Binacchid4411752021-09-26 11:58:58 +0200116/**
117 * clk_ti_am3_dpll_ssc_program - set spread-spectrum clocking registers
118 * @clk: struct clk * of DPLL to set
119 *
120 * Enable the DPLL spread spectrum clocking if frequency modulation and
121 * frequency spreading have been set, otherwise disable it.
122 */
123static void clk_ti_am3_dpll_ssc_program(struct clk *clk)
124{
125 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
126 unsigned long ref_rate;
127 u32 v, ctrl, mod_freq_divider, exponent, mantissa;
128 u32 deltam_step, deltam_ceil;
129
130 ctrl = clk_ti_readl(&priv->clkmode_reg);
131
132 if (priv->ssc_modfreq && priv->ssc_deltam) {
133 ctrl |= CM_CLKMODE_DPLL_SSC_EN_MASK;
134
135 if (priv->ssc_downspread)
136 ctrl |= CM_CLKMODE_DPLL_SSC_DOWNSPREAD_MASK;
137 else
138 ctrl &= ~CM_CLKMODE_DPLL_SSC_DOWNSPREAD_MASK;
139
140 ref_rate = clk_get_rate(&priv->clk_ref);
141 mod_freq_divider =
142 (ref_rate / priv->last_rounded_div) / (4 * priv->ssc_modfreq);
143 if (priv->ssc_modfreq > (ref_rate / 70))
144 dev_warn(clk->dev,
145 "clock: SSC modulation frequency of DPLL %s greater than %ld\n",
146 clk->dev->name, ref_rate / 70);
147
148 exponent = 0;
149 mantissa = mod_freq_divider;
150 while ((mantissa > 127) && (exponent < 7)) {
151 exponent++;
152 mantissa /= 2;
153 }
154 if (mantissa > 127)
155 mantissa = 127;
156
157 v = clk_ti_readl(&priv->ssc_modfreq_reg);
158 v &= ~(CM_SSC_MODFREQ_DPLL_MANT_MASK | CM_SSC_MODFREQ_DPLL_EXP_MASK);
159 v |= mantissa << __ffs(CM_SSC_MODFREQ_DPLL_MANT_MASK);
160 v |= exponent << __ffs(CM_SSC_MODFREQ_DPLL_EXP_MASK);
161 clk_ti_writel(v, &priv->ssc_modfreq_reg);
162 dev_dbg(clk->dev,
163 "mod_freq_divider: %u, exponent: %u, mantissa: %u, modfreq_reg: 0x%x\n",
164 mod_freq_divider, exponent, mantissa, v);
165
166 deltam_step = priv->last_rounded_mult * priv->ssc_deltam;
167 deltam_step /= 10;
168 if (priv->ssc_downspread)
169 deltam_step /= 2;
170
171 deltam_step <<= __ffs(CM_SSC_DELTAM_DPLL_INT_MASK);
172 deltam_step /= 100;
173 deltam_step /= mod_freq_divider;
174 if (deltam_step > 0xFFFFF)
175 deltam_step = 0xFFFFF;
176
177 deltam_ceil = (deltam_step & CM_SSC_DELTAM_DPLL_INT_MASK) >>
178 __ffs(CM_SSC_DELTAM_DPLL_INT_MASK);
179 if (deltam_step & CM_SSC_DELTAM_DPLL_FRAC_MASK)
180 deltam_ceil++;
181
182 if ((priv->ssc_downspread &&
183 ((priv->last_rounded_mult - (2 * deltam_ceil)) < 20 ||
184 priv->last_rounded_mult > 2045)) ||
185 ((priv->last_rounded_mult - deltam_ceil) < 20 ||
186 (priv->last_rounded_mult + deltam_ceil) > 2045))
187 dev_warn(clk->dev,
188 "clock: SSC multiplier of DPLL %s is out of range\n",
189 clk->dev->name);
190
191 v = clk_ti_readl(&priv->ssc_deltam_reg);
192 v &= ~(CM_SSC_DELTAM_DPLL_INT_MASK | CM_SSC_DELTAM_DPLL_FRAC_MASK);
193 v |= deltam_step << __ffs(CM_SSC_DELTAM_DPLL_INT_MASK |
194 CM_SSC_DELTAM_DPLL_FRAC_MASK);
195 clk_ti_writel(v, &priv->ssc_deltam_reg);
196 dev_dbg(clk->dev,
197 "deltam_step: %u, deltam_ceil: %u, deltam_reg: 0x%x\n",
198 deltam_step, deltam_ceil, v);
199 } else {
200 ctrl &= ~CM_CLKMODE_DPLL_SSC_EN_MASK;
201 }
202
203 clk_ti_writel(ctrl, &priv->clkmode_reg);
204}
205
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100206static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate)
207{
208 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
209 u32 v;
210 ulong round_rate;
211
212 round_rate = clk_ti_am3_dpll_round_rate(clk, rate);
213 if (IS_ERR_VALUE(round_rate))
214 return round_rate;
215
Dario Binacchieff80eb2021-05-01 17:05:25 +0200216 v = clk_ti_readl(&priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100217
218 /* enter bypass mode */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200219 clk_ti_am3_dpll_clken(priv, DPLL_EN_MN_BYPASS);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100220
221 /* wait for bypass mode */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200222 clk_ti_am3_dpll_state(clk, 0);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100223
224 /* set M & N */
225 v &= ~CM_CLKSEL_DPLL_M_MASK;
226 v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) &
227 CM_CLKSEL_DPLL_M_MASK;
228
229 v &= ~CM_CLKSEL_DPLL_N_MASK;
230 v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) &
231 CM_CLKSEL_DPLL_N_MASK;
232
Dario Binacchieff80eb2021-05-01 17:05:25 +0200233 clk_ti_writel(v, &priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100234
Dario Binacchid4411752021-09-26 11:58:58 +0200235 clk_ti_am3_dpll_ssc_program(clk);
236
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100237 /* lock dpll */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200238 clk_ti_am3_dpll_clken(priv, DPLL_EN_LOCK);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100239
240 /* wait till the dpll locks */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200241 if (!clk_ti_am3_dpll_state(clk, ST_DPLL_CLK_MASK))
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100242 hang();
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100243
244 return round_rate;
245}
246
247static ulong clk_ti_am3_dpll_get_rate(struct clk *clk)
248{
249 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
250 u64 rate;
251 u32 m, n, v;
252
253 /* Return bypass rate if DPLL is bypassed */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200254 v = clk_ti_readl(&priv->clkmode_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100255 v &= CM_CLKMODE_DPLL_EN_MASK;
256 v >>= CM_CLKMODE_DPLL_EN_SHIFT;
257
258 switch (v) {
259 case DPLL_EN_MN_BYPASS:
260 case DPLL_EN_LOW_POWER_BYPASS:
261 case DPLL_EN_FAST_RELOCK_BYPASS:
262 rate = clk_get_rate(&priv->clk_bypass);
263 dev_dbg(clk->dev, "rate=%lld\n", rate);
264 return rate;
265 }
266
Dario Binacchieff80eb2021-05-01 17:05:25 +0200267 v = clk_ti_readl(&priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100268 m = v & CM_CLKSEL_DPLL_M_MASK;
269 m >>= CM_CLKSEL_DPLL_M_SHIFT;
270 n = v & CM_CLKSEL_DPLL_N_MASK;
271 n >>= CM_CLKSEL_DPLL_N_SHIFT;
272
273 rate = clk_get_rate(&priv->clk_ref) * m;
274 do_div(rate, n + 1);
275 dev_dbg(clk->dev, "rate=%lld\n", rate);
276 return rate;
277}
278
279const struct clk_ops clk_ti_am3_dpll_ops = {
280 .round_rate = clk_ti_am3_dpll_round_rate,
281 .get_rate = clk_ti_am3_dpll_get_rate,
282 .set_rate = clk_ti_am3_dpll_set_rate,
283};
284
285static int clk_ti_am3_dpll_remove(struct udevice *dev)
286{
287 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
288 int err;
289
290 err = clk_release_all(&priv->clk_bypass, 1);
291 if (err) {
292 dev_err(dev, "failed to release bypass clock\n");
293 return err;
294 }
295
296 err = clk_release_all(&priv->clk_ref, 1);
297 if (err) {
298 dev_err(dev, "failed to release reference clock\n");
299 return err;
300 }
301
302 return 0;
303}
304
305static int clk_ti_am3_dpll_probe(struct udevice *dev)
306{
307 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
308 int err;
309
310 err = clk_get_by_index(dev, 0, &priv->clk_ref);
311 if (err) {
312 dev_err(dev, "failed to get reference clock\n");
313 return err;
314 }
315
316 err = clk_get_by_index(dev, 1, &priv->clk_bypass);
317 if (err) {
318 dev_err(dev, "failed to get bypass clock\n");
319 return err;
320 }
321
322 return 0;
323}
324
325static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev)
326{
327 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
328 struct clk_ti_am3_dpll_drv_data *data =
329 (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev);
Dario Binacchid4411752021-09-26 11:58:58 +0200330 u32 min_div;
Dario Binacchieff80eb2021-05-01 17:05:25 +0200331 int err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100332
333 priv->max_rate = data->max_rate;
334
Dario Binacchieff80eb2021-05-01 17:05:25 +0200335 err = clk_ti_get_reg_addr(dev, 0, &priv->clkmode_reg);
336 if (err) {
337 dev_err(dev, "failed to get clkmode register address\n");
338 return err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100339 }
340
Dario Binacchieff80eb2021-05-01 17:05:25 +0200341 err = clk_ti_get_reg_addr(dev, 1, &priv->idlest_reg);
342 if (err) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100343 dev_err(dev, "failed to get idlest register\n");
344 return -EINVAL;
345 }
346
Dario Binacchieff80eb2021-05-01 17:05:25 +0200347 err = clk_ti_get_reg_addr(dev, 2, &priv->clksel_reg);
348 if (err) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100349 dev_err(dev, "failed to get clksel register\n");
Dario Binacchieff80eb2021-05-01 17:05:25 +0200350 return err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100351 }
352
Dario Binacchid4411752021-09-26 11:58:58 +0200353 err = clk_ti_get_reg_addr(dev, 3, &priv->ssc_deltam_reg);
354 if (err) {
355 dev_err(dev, "failed to get SSC deltam register\n");
356 return err;
357 }
358
359 err = clk_ti_get_reg_addr(dev, 4, &priv->ssc_modfreq_reg);
360 if (err) {
361 dev_err(dev, "failed to get SSC modfreq register\n");
362 return err;
363 }
364
365 if (dev_read_u32(dev, "ti,ssc-modfreq-hz", &priv->ssc_modfreq))
366 priv->ssc_modfreq = 0;
367
368 if (dev_read_u32(dev, "ti,ssc-deltam", &priv->ssc_deltam))
369 priv->ssc_deltam = 0;
370
371 priv->ssc_downspread = dev_read_bool(dev, "ti,ssc-downspread");
372
373 if (dev_read_u32(dev, "ti,min-div", &min_div) || min_div == 0 ||
374 min_div > 128)
375 priv->min_div = 1;
376 else
377 priv->min_div = min_div;
378
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100379 return 0;
380}
381
382static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
383 .max_rate = 1000000000
384};
385
386static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
387 .max_rate = 2000000000
388};
389
390static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
391 .max_rate = 1000000000
392};
393
394static const struct udevice_id clk_ti_am3_dpll_of_match[] = {
395 {.compatible = "ti,am3-dpll-core-clock",
396 .data = (ulong)&dpll_core_data},
397 {.compatible = "ti,am3-dpll-no-gate-clock",
398 .data = (ulong)&dpll_no_gate_data},
399 {.compatible = "ti,am3-dpll-no-gate-j-type-clock",
400 .data = (ulong)&dpll_no_gate_j_type_data},
401 {}
402};
403
404U_BOOT_DRIVER(clk_ti_am3_dpll) = {
405 .name = "ti_am3_dpll_clock",
406 .id = UCLASS_CLK,
407 .of_match = clk_ti_am3_dpll_of_match,
Dario Binacchif686e4d2021-01-15 09:10:26 +0100408 .of_to_plat = clk_ti_am3_dpll_of_to_plat,
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100409 .probe = clk_ti_am3_dpll_probe,
410 .remove = clk_ti_am3_dpll_remove,
411 .priv_auto = sizeof(struct clk_ti_am3_dpll_priv),
412 .ops = &clk_ti_am3_dpll_ops,
413};