]> Git Repo - J-u-boot.git/blob - drivers/clk/ti/clk-am3-dpll.c
sunxi: Enable DM_I2C for all sunxi boards
[J-u-boot.git] / drivers / clk / ti / clk-am3-dpll.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * TI DPLL clock support
4  *
5  * Copyright (C) 2020 Dario Binacchi <[email protected]>
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>
20 #include "clk.h"
21
22 struct clk_ti_am3_dpll_drv_data {
23         ulong max_rate;
24 };
25
26 struct clk_ti_am3_dpll_priv {
27         struct clk_ti_reg clkmode_reg;
28         struct clk_ti_reg idlest_reg;
29         struct clk_ti_reg clksel_reg;
30         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
37 static 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
79 static 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
90 static 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
110 static 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
120         v = clk_ti_readl(&priv->clksel_reg);
121
122         /* enter bypass mode */
123         clk_ti_am3_dpll_clken(priv, DPLL_EN_MN_BYPASS);
124
125         /* wait for bypass mode */
126         clk_ti_am3_dpll_state(clk, 0);
127
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
137         clk_ti_writel(v, &priv->clksel_reg);
138
139         /* lock dpll */
140         clk_ti_am3_dpll_clken(priv, DPLL_EN_LOCK);
141
142         /* wait till the dpll locks */
143         if (!clk_ti_am3_dpll_state(clk, ST_DPLL_CLK_MASK))
144                 hang();
145
146         return round_rate;
147 }
148
149 static 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 */
156         v = clk_ti_readl(&priv->clkmode_reg);
157         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
169         v = clk_ti_readl(&priv->clksel_reg);
170         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
181 const 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
187 static 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
207 static 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
227 static 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);
232         int err;
233
234         priv->max_rate = data->max_rate;
235
236         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;
240         }
241
242         err = clk_ti_get_reg_addr(dev, 1, &priv->idlest_reg);
243         if (err) {
244                 dev_err(dev, "failed to get idlest register\n");
245                 return -EINVAL;
246         }
247
248         err = clk_ti_get_reg_addr(dev, 2, &priv->clksel_reg);
249         if (err) {
250                 dev_err(dev, "failed to get clksel register\n");
251                 return err;
252         }
253
254         return 0;
255 }
256
257 static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
258         .max_rate = 1000000000
259 };
260
261 static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
262         .max_rate = 2000000000
263 };
264
265 static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
266         .max_rate = 1000000000
267 };
268
269 static 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
279 U_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,
283         .of_to_plat = clk_ti_am3_dpll_of_to_plat,
284         .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 };
This page took 0.045459 seconds and 4 git commands to generate.