1 // 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 
21 struct clk_ti_am3_dpll_drv_data {
22 	ulong max_rate;
23 };
24 
25 struct 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 
clk_ti_am3_dpll_round_rate(struct clk * clk,ulong rate)36 static 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 
clk_ti_am3_dpll_set_rate(struct clk * clk,ulong rate)78 static 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 
clk_ti_am3_dpll_get_rate(struct clk * clk)124 static 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 
156 const 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 
clk_ti_am3_dpll_remove(struct udevice * dev)162 static 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 
clk_ti_am3_dpll_probe(struct udevice * dev)182 static 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 
clk_ti_am3_dpll_of_to_plat(struct udevice * dev)202 static 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 
237 static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
238 	.max_rate = 1000000000
239 };
240 
241 static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
242 	.max_rate = 2000000000
243 };
244 
245 static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
246 	.max_rate = 1000000000
247 };
248 
249 static 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 
259 U_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,
263 	.of_to_plat = clk_ti_am3_dpll_of_to_plat,
264 	.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 };
269