blob: 916d3080340113b046614ae4ca1d803c09f3afb9 [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 Binacchi6e6eb8b2020-12-30 00:06:34 +010030 struct clk clk_bypass;
31 struct clk clk_ref;
32 u16 last_rounded_mult;
33 u8 last_rounded_div;
34 ulong max_rate;
35};
36
37static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate)
38{
39 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
40 ulong ret, ref_rate, r;
41 int m, d, err_min, err;
42 int mult = INT_MAX, div = INT_MAX;
43
44 if (priv->max_rate && rate > priv->max_rate) {
45 dev_warn(clk->dev, "%ld is to high a rate, lowered to %ld\n",
46 rate, priv->max_rate);
47 rate = priv->max_rate;
48 }
49
50 ret = -EFAULT;
51 err = rate;
52 err_min = rate;
53 ref_rate = clk_get_rate(&priv->clk_ref);
54 for (d = 1; err_min && d <= 128; d++) {
55 for (m = 2; m <= 2047; m++) {
56 r = (ref_rate * m) / d;
57 err = abs(r - rate);
58 if (err < err_min) {
59 err_min = err;
60 ret = r;
61 mult = m;
62 div = d;
63
64 if (err == 0)
65 break;
66 } else if (r > rate) {
67 break;
68 }
69 }
70 }
71
72 priv->last_rounded_mult = mult;
73 priv->last_rounded_div = div;
74 dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, mult=%d, div=%d\n", rate,
75 ret, mult, div);
76 return ret;
77}
78
Dario Binacchieff80eb2021-05-01 17:05:25 +020079static void clk_ti_am3_dpll_clken(struct clk_ti_am3_dpll_priv *priv,
80 u8 clken_bits)
81{
82 u32 v;
83
84 v = clk_ti_readl(&priv->clkmode_reg);
85 v &= ~CM_CLKMODE_DPLL_DPLL_EN_MASK;
86 v |= clken_bits << CM_CLKMODE_DPLL_EN_SHIFT;
87 clk_ti_writel(v, &priv->clkmode_reg);
88}
89
90static int clk_ti_am3_dpll_state(struct clk *clk, u8 state)
91{
92 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
93 u32 i = 0, v;
94
95 do {
96 v = clk_ti_readl(&priv->idlest_reg) & ST_DPLL_CLK_MASK;
97 if (v == state) {
98 dev_dbg(clk->dev, "transition to '%s' in %d loops\n",
99 state ? "locked" : "bypassed", i);
100 return 1;
101 }
102
103 } while (++i < LDELAY);
104
105 dev_err(clk->dev, "failed transition to '%s'\n",
106 state ? "locked" : "bypassed");
107 return 0;
108}
109
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100110static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate)
111{
112 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
113 u32 v;
114 ulong round_rate;
115
116 round_rate = clk_ti_am3_dpll_round_rate(clk, rate);
117 if (IS_ERR_VALUE(round_rate))
118 return round_rate;
119
Dario Binacchieff80eb2021-05-01 17:05:25 +0200120 v = clk_ti_readl(&priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100121
122 /* enter bypass mode */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200123 clk_ti_am3_dpll_clken(priv, DPLL_EN_MN_BYPASS);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100124
125 /* wait for bypass mode */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200126 clk_ti_am3_dpll_state(clk, 0);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100127
128 /* set M & N */
129 v &= ~CM_CLKSEL_DPLL_M_MASK;
130 v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) &
131 CM_CLKSEL_DPLL_M_MASK;
132
133 v &= ~CM_CLKSEL_DPLL_N_MASK;
134 v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) &
135 CM_CLKSEL_DPLL_N_MASK;
136
Dario Binacchieff80eb2021-05-01 17:05:25 +0200137 clk_ti_writel(v, &priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100138
139 /* lock dpll */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200140 clk_ti_am3_dpll_clken(priv, DPLL_EN_LOCK);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100141
142 /* wait till the dpll locks */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200143 if (!clk_ti_am3_dpll_state(clk, ST_DPLL_CLK_MASK))
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100144 hang();
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100145
146 return round_rate;
147}
148
149static ulong clk_ti_am3_dpll_get_rate(struct clk *clk)
150{
151 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
152 u64 rate;
153 u32 m, n, v;
154
155 /* Return bypass rate if DPLL is bypassed */
Dario Binacchieff80eb2021-05-01 17:05:25 +0200156 v = clk_ti_readl(&priv->clkmode_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100157 v &= CM_CLKMODE_DPLL_EN_MASK;
158 v >>= CM_CLKMODE_DPLL_EN_SHIFT;
159
160 switch (v) {
161 case DPLL_EN_MN_BYPASS:
162 case DPLL_EN_LOW_POWER_BYPASS:
163 case DPLL_EN_FAST_RELOCK_BYPASS:
164 rate = clk_get_rate(&priv->clk_bypass);
165 dev_dbg(clk->dev, "rate=%lld\n", rate);
166 return rate;
167 }
168
Dario Binacchieff80eb2021-05-01 17:05:25 +0200169 v = clk_ti_readl(&priv->clksel_reg);
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100170 m = v & CM_CLKSEL_DPLL_M_MASK;
171 m >>= CM_CLKSEL_DPLL_M_SHIFT;
172 n = v & CM_CLKSEL_DPLL_N_MASK;
173 n >>= CM_CLKSEL_DPLL_N_SHIFT;
174
175 rate = clk_get_rate(&priv->clk_ref) * m;
176 do_div(rate, n + 1);
177 dev_dbg(clk->dev, "rate=%lld\n", rate);
178 return rate;
179}
180
181const struct clk_ops clk_ti_am3_dpll_ops = {
182 .round_rate = clk_ti_am3_dpll_round_rate,
183 .get_rate = clk_ti_am3_dpll_get_rate,
184 .set_rate = clk_ti_am3_dpll_set_rate,
185};
186
187static int clk_ti_am3_dpll_remove(struct udevice *dev)
188{
189 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
190 int err;
191
192 err = clk_release_all(&priv->clk_bypass, 1);
193 if (err) {
194 dev_err(dev, "failed to release bypass clock\n");
195 return err;
196 }
197
198 err = clk_release_all(&priv->clk_ref, 1);
199 if (err) {
200 dev_err(dev, "failed to release reference clock\n");
201 return err;
202 }
203
204 return 0;
205}
206
207static int clk_ti_am3_dpll_probe(struct udevice *dev)
208{
209 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
210 int err;
211
212 err = clk_get_by_index(dev, 0, &priv->clk_ref);
213 if (err) {
214 dev_err(dev, "failed to get reference clock\n");
215 return err;
216 }
217
218 err = clk_get_by_index(dev, 1, &priv->clk_bypass);
219 if (err) {
220 dev_err(dev, "failed to get bypass clock\n");
221 return err;
222 }
223
224 return 0;
225}
226
227static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev)
228{
229 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
230 struct clk_ti_am3_dpll_drv_data *data =
231 (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev);
Dario Binacchieff80eb2021-05-01 17:05:25 +0200232 int err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100233
234 priv->max_rate = data->max_rate;
235
Dario Binacchieff80eb2021-05-01 17:05:25 +0200236 err = clk_ti_get_reg_addr(dev, 0, &priv->clkmode_reg);
237 if (err) {
238 dev_err(dev, "failed to get clkmode register address\n");
239 return err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100240 }
241
Dario Binacchieff80eb2021-05-01 17:05:25 +0200242 err = clk_ti_get_reg_addr(dev, 1, &priv->idlest_reg);
243 if (err) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100244 dev_err(dev, "failed to get idlest register\n");
245 return -EINVAL;
246 }
247
Dario Binacchieff80eb2021-05-01 17:05:25 +0200248 err = clk_ti_get_reg_addr(dev, 2, &priv->clksel_reg);
249 if (err) {
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100250 dev_err(dev, "failed to get clksel register\n");
Dario Binacchieff80eb2021-05-01 17:05:25 +0200251 return err;
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100252 }
253
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100254 return 0;
255}
256
257static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
258 .max_rate = 1000000000
259};
260
261static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
262 .max_rate = 2000000000
263};
264
265static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
266 .max_rate = 1000000000
267};
268
269static const struct udevice_id clk_ti_am3_dpll_of_match[] = {
270 {.compatible = "ti,am3-dpll-core-clock",
271 .data = (ulong)&dpll_core_data},
272 {.compatible = "ti,am3-dpll-no-gate-clock",
273 .data = (ulong)&dpll_no_gate_data},
274 {.compatible = "ti,am3-dpll-no-gate-j-type-clock",
275 .data = (ulong)&dpll_no_gate_j_type_data},
276 {}
277};
278
279U_BOOT_DRIVER(clk_ti_am3_dpll) = {
280 .name = "ti_am3_dpll_clock",
281 .id = UCLASS_CLK,
282 .of_match = clk_ti_am3_dpll_of_match,
Dario Binacchif686e4d2021-01-15 09:10:26 +0100283 .of_to_plat = clk_ti_am3_dpll_of_to_plat,
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100284 .probe = clk_ti_am3_dpll_probe,
285 .remove = clk_ti_am3_dpll_remove,
286 .priv_auto = sizeof(struct clk_ti_am3_dpll_priv),
287 .ops = &clk_ti_am3_dpll_ops,
288};