blob: 90b4cc69efa5d67e579f584f2a7702fe718a6f50 [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>
20
21struct clk_ti_am3_dpll_drv_data {
22 ulong max_rate;
23};
24
25struct clk_ti_am3_dpll_priv {
26 fdt_addr_t clkmode_reg;
27 fdt_addr_t idlest_reg;
28 fdt_addr_t clksel_reg;
29 struct clk clk_bypass;
30 struct clk clk_ref;
31 u16 last_rounded_mult;
32 u8 last_rounded_div;
33 ulong max_rate;
34};
35
36static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate)
37{
38 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
39 ulong ret, ref_rate, r;
40 int m, d, err_min, err;
41 int mult = INT_MAX, div = INT_MAX;
42
43 if (priv->max_rate && rate > priv->max_rate) {
44 dev_warn(clk->dev, "%ld is to high a rate, lowered to %ld\n",
45 rate, priv->max_rate);
46 rate = priv->max_rate;
47 }
48
49 ret = -EFAULT;
50 err = rate;
51 err_min = rate;
52 ref_rate = clk_get_rate(&priv->clk_ref);
53 for (d = 1; err_min && d <= 128; d++) {
54 for (m = 2; m <= 2047; m++) {
55 r = (ref_rate * m) / d;
56 err = abs(r - rate);
57 if (err < err_min) {
58 err_min = err;
59 ret = r;
60 mult = m;
61 div = d;
62
63 if (err == 0)
64 break;
65 } else if (r > rate) {
66 break;
67 }
68 }
69 }
70
71 priv->last_rounded_mult = mult;
72 priv->last_rounded_div = div;
73 dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, mult=%d, div=%d\n", rate,
74 ret, mult, div);
75 return ret;
76}
77
78static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate)
79{
80 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
81 u32 v;
82 ulong round_rate;
83
84 round_rate = clk_ti_am3_dpll_round_rate(clk, rate);
85 if (IS_ERR_VALUE(round_rate))
86 return round_rate;
87
88 v = readl(priv->clksel_reg);
89
90 /* enter bypass mode */
91 clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK,
92 DPLL_EN_MN_BYPASS << CM_CLKMODE_DPLL_EN_SHIFT);
93
94 /* wait for bypass mode */
95 if (!wait_on_value(ST_DPLL_CLK_MASK, 0,
96 (void *)priv->idlest_reg, LDELAY))
97 dev_err(clk->dev, "failed bypassing dpll\n");
98
99 /* set M & N */
100 v &= ~CM_CLKSEL_DPLL_M_MASK;
101 v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) &
102 CM_CLKSEL_DPLL_M_MASK;
103
104 v &= ~CM_CLKSEL_DPLL_N_MASK;
105 v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) &
106 CM_CLKSEL_DPLL_N_MASK;
107
108 writel(v, priv->clksel_reg);
109
110 /* lock dpll */
111 clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK,
112 DPLL_EN_LOCK << CM_CLKMODE_DPLL_EN_SHIFT);
113
114 /* wait till the dpll locks */
115 if (!wait_on_value(ST_DPLL_CLK_MASK, ST_DPLL_CLK_MASK,
116 (void *)priv->idlest_reg, LDELAY)) {
117 dev_err(clk->dev, "failed locking dpll\n");
118 hang();
119 }
120
121 return round_rate;
122}
123
124static ulong clk_ti_am3_dpll_get_rate(struct clk *clk)
125{
126 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
127 u64 rate;
128 u32 m, n, v;
129
130 /* Return bypass rate if DPLL is bypassed */
131 v = readl(priv->clkmode_reg);
132 v &= CM_CLKMODE_DPLL_EN_MASK;
133 v >>= CM_CLKMODE_DPLL_EN_SHIFT;
134
135 switch (v) {
136 case DPLL_EN_MN_BYPASS:
137 case DPLL_EN_LOW_POWER_BYPASS:
138 case DPLL_EN_FAST_RELOCK_BYPASS:
139 rate = clk_get_rate(&priv->clk_bypass);
140 dev_dbg(clk->dev, "rate=%lld\n", rate);
141 return rate;
142 }
143
144 v = readl(priv->clksel_reg);
145 m = v & CM_CLKSEL_DPLL_M_MASK;
146 m >>= CM_CLKSEL_DPLL_M_SHIFT;
147 n = v & CM_CLKSEL_DPLL_N_MASK;
148 n >>= CM_CLKSEL_DPLL_N_SHIFT;
149
150 rate = clk_get_rate(&priv->clk_ref) * m;
151 do_div(rate, n + 1);
152 dev_dbg(clk->dev, "rate=%lld\n", rate);
153 return rate;
154}
155
156const struct clk_ops clk_ti_am3_dpll_ops = {
157 .round_rate = clk_ti_am3_dpll_round_rate,
158 .get_rate = clk_ti_am3_dpll_get_rate,
159 .set_rate = clk_ti_am3_dpll_set_rate,
160};
161
162static int clk_ti_am3_dpll_remove(struct udevice *dev)
163{
164 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
165 int err;
166
167 err = clk_release_all(&priv->clk_bypass, 1);
168 if (err) {
169 dev_err(dev, "failed to release bypass clock\n");
170 return err;
171 }
172
173 err = clk_release_all(&priv->clk_ref, 1);
174 if (err) {
175 dev_err(dev, "failed to release reference clock\n");
176 return err;
177 }
178
179 return 0;
180}
181
182static int clk_ti_am3_dpll_probe(struct udevice *dev)
183{
184 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
185 int err;
186
187 err = clk_get_by_index(dev, 0, &priv->clk_ref);
188 if (err) {
189 dev_err(dev, "failed to get reference clock\n");
190 return err;
191 }
192
193 err = clk_get_by_index(dev, 1, &priv->clk_bypass);
194 if (err) {
195 dev_err(dev, "failed to get bypass clock\n");
196 return err;
197 }
198
199 return 0;
200}
201
202static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev)
203{
204 struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
205 struct clk_ti_am3_dpll_drv_data *data =
206 (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev);
207
208 priv->max_rate = data->max_rate;
209
210 priv->clkmode_reg = dev_read_addr_index(dev, 0);
211 if (priv->clkmode_reg == FDT_ADDR_T_NONE) {
212 dev_err(dev, "failed to get clkmode register\n");
213 return -EINVAL;
214 }
215
216 dev_dbg(dev, "clkmode_reg=0x%08lx\n", priv->clkmode_reg);
217
218 priv->idlest_reg = dev_read_addr_index(dev, 1);
219 if (priv->idlest_reg == FDT_ADDR_T_NONE) {
220 dev_err(dev, "failed to get idlest register\n");
221 return -EINVAL;
222 }
223
224 dev_dbg(dev, "idlest_reg=0x%08lx\n", priv->idlest_reg);
225
226 priv->clksel_reg = dev_read_addr_index(dev, 2);
227 if (priv->clksel_reg == FDT_ADDR_T_NONE) {
228 dev_err(dev, "failed to get clksel register\n");
229 return -EINVAL;
230 }
231
232 dev_dbg(dev, "clksel_reg=0x%08lx\n", priv->clksel_reg);
233
234 return 0;
235}
236
237static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
238 .max_rate = 1000000000
239};
240
241static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
242 .max_rate = 2000000000
243};
244
245static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
246 .max_rate = 1000000000
247};
248
249static const struct udevice_id clk_ti_am3_dpll_of_match[] = {
250 {.compatible = "ti,am3-dpll-core-clock",
251 .data = (ulong)&dpll_core_data},
252 {.compatible = "ti,am3-dpll-no-gate-clock",
253 .data = (ulong)&dpll_no_gate_data},
254 {.compatible = "ti,am3-dpll-no-gate-j-type-clock",
255 .data = (ulong)&dpll_no_gate_j_type_data},
256 {}
257};
258
259U_BOOT_DRIVER(clk_ti_am3_dpll) = {
260 .name = "ti_am3_dpll_clock",
261 .id = UCLASS_CLK,
262 .of_match = clk_ti_am3_dpll_of_match,
Dario Binacchif686e4d2021-01-15 09:10:26 +0100263 .of_to_plat = clk_ti_am3_dpll_of_to_plat,
Dario Binacchi6e6eb8b2020-12-30 00:06:34 +0100264 .probe = clk_ti_am3_dpll_probe,
265 .remove = clk_ti_am3_dpll_remove,
266 .priv_auto = sizeof(struct clk_ti_am3_dpll_priv),
267 .ops = &clk_ti_am3_dpll_ops,
268};