blob: 74beb4d8ebda13815788d427f0f05c3c028125b8 [file] [log] [blame]
Tero Kristo82ceb0d2021-06-11 11:45:14 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Texas Instruments K3 clock driver
4 *
Suman Annadb4c2dc2021-09-07 17:16:58 -05005 * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/
Tero Kristo82ceb0d2021-06-11 11:45:14 +03006 * Tero Kristo <t-kristo@ti.com>
7 */
8
9#include <common.h>
10#include <dm.h>
11#include <errno.h>
12#include <soc.h>
13#include <clk-uclass.h>
14#include "k3-clk.h"
15
16#define PLL_MIN_FREQ 800000000
17#define PLL_MAX_FREQ 3200000000UL
18#define PLL_MAX_DIV 127
19
20/**
21 * struct clk_map - mapping from dev/clk id tuples towards physical clocks
22 * @dev_id: device ID for the clock
23 * @clk_id: clock ID for the clock
24 * @clk: pointer to the registered clock entry for the mapping
25 */
26struct clk_map {
27 u16 dev_id;
28 u32 clk_id;
29 struct clk *clk;
30};
31
32/**
33 * struct ti_clk_data - clock controller information structure
34 * @map: mapping from dev/clk id tuples to physical clock entries
35 * @size: number of entries in the map
36 */
37struct ti_clk_data {
38 struct clk_map *map;
39 int size;
40};
41
42static ulong osc_freq;
43
44static void clk_add_map(struct ti_clk_data *data, struct clk *clk,
45 u32 dev_id, u32 clk_id)
46{
47 struct clk_map *map;
48
49 debug("%s: added clk=%p, data=%p, dev=%d, clk=%d\n", __func__,
50 clk, data, dev_id, clk_id);
51 if (!clk)
52 return;
53
54 map = data->map + data->size++;
55
56 map->dev_id = dev_id;
57 map->clk_id = clk_id;
58 map->clk = clk;
59}
60
61static const struct soc_attr ti_k3_soc_clk_data[] = {
62#if IS_ENABLED(CONFIG_SOC_K3_J721E)
63 {
64 .family = "J721E",
65 .data = &j721e_clk_platdata,
66 },
67 {
68 .family = "J7200",
69 .data = &j7200_clk_platdata,
70 },
David Huange04854b2022-01-25 20:56:33 +053071#elif CONFIG_SOC_K3_J721S2
72 {
73 .family = "J721S2",
74 .data = &j721s2_clk_platdata,
75 },
Tero Kristo82ceb0d2021-06-11 11:45:14 +030076#endif
77 { /* sentinel */ }
78};
79
80static int ti_clk_probe(struct udevice *dev)
81{
82 struct ti_clk_data *data = dev_get_priv(dev);
83 struct clk *clk;
84 const char *name;
85 const struct clk_data *ti_clk_data;
86 int i, j;
87 const struct soc_attr *soc_match_data;
88 const struct ti_k3_clk_platdata *pdata;
89
90 debug("%s(dev=%p)\n", __func__, dev);
91
92 soc_match_data = soc_device_match(ti_k3_soc_clk_data);
93 if (!soc_match_data)
94 return -ENODEV;
95
96 pdata = (const struct ti_k3_clk_platdata *)soc_match_data->data;
97
98 data->map = kcalloc(pdata->soc_dev_clk_data_cnt, sizeof(*data->map),
99 GFP_KERNEL);
100 data->size = 0;
101
102 for (i = 0; i < pdata->clk_list_cnt; i++) {
103 ti_clk_data = &pdata->clk_list[i];
104
105 switch (ti_clk_data->type) {
106 case CLK_TYPE_FIXED_RATE:
107 name = ti_clk_data->clk.fixed_rate.name;
108 clk = clk_register_fixed_rate(NULL,
109 name,
110 ti_clk_data->clk.fixed_rate.rate);
111 break;
112 case CLK_TYPE_DIV:
113 name = ti_clk_data->clk.div.name;
114 clk = clk_register_divider(NULL, name,
115 ti_clk_data->clk.div.parent,
116 ti_clk_data->clk.div.flags,
117 map_physmem(ti_clk_data->clk.div.reg, 0, MAP_NOCACHE),
118 ti_clk_data->clk.div.shift,
119 ti_clk_data->clk.div.width,
Suman Annadb4c2dc2021-09-07 17:16:58 -0500120 ti_clk_data->clk.div.div_flags);
Tero Kristo82ceb0d2021-06-11 11:45:14 +0300121 break;
122 case CLK_TYPE_MUX:
123 name = ti_clk_data->clk.mux.name;
124 clk = clk_register_mux(NULL, name,
125 ti_clk_data->clk.mux.parents,
126 ti_clk_data->clk.mux.num_parents,
127 ti_clk_data->clk.mux.flags,
128 map_physmem(ti_clk_data->clk.mux.reg, 0, MAP_NOCACHE),
129 ti_clk_data->clk.mux.shift,
130 ti_clk_data->clk.mux.width,
131 0);
132 break;
133 case CLK_TYPE_PLL:
134 name = ti_clk_data->clk.pll.name;
135 clk = clk_register_ti_pll(name,
136 ti_clk_data->clk.pll.parent,
137 map_physmem(ti_clk_data->clk.pll.reg, 0, MAP_NOCACHE));
138
139 if (!osc_freq)
140 osc_freq = clk_get_rate(clk_get_parent(clk));
141 break;
142 default:
143 name = NULL;
144 clk = NULL;
145 printf("WARNING: %s has encountered unknown clk type %d\n",
146 __func__, ti_clk_data->type);
147 }
148
149 if (clk && ti_clk_data->default_freq)
150 clk_set_rate(clk, ti_clk_data->default_freq);
151
152 if (clk && name) {
153 for (j = 0; j < pdata->soc_dev_clk_data_cnt; j++) {
154 if (!strcmp(name, pdata->soc_dev_clk_data[j].clk_name)) {
155 clk_add_map(data, clk, pdata->soc_dev_clk_data[j].dev_id,
156 pdata->soc_dev_clk_data[j].clk_id);
157 }
158 }
159 }
160 }
161
162 return 0;
163}
164
165static int _clk_cmp(u32 dev_id, u32 clk_id, const struct clk_map *map)
166{
167 if (map->dev_id == dev_id && map->clk_id == clk_id)
168 return 0;
169 if (map->dev_id > dev_id ||
170 (map->dev_id == dev_id && map->clk_id > clk_id))
171 return -1;
172 return 1;
173}
174
175static int bsearch(u32 dev_id, u32 clk_id, struct clk_map *map, int num)
176{
177 int result;
178 int idx;
179
180 for (idx = 0; idx < num; idx++) {
181 result = _clk_cmp(dev_id, clk_id, &map[idx]);
182
183 if (result == 0)
184 return idx;
185 }
186
187 return -ENOENT;
188}
189
190static int ti_clk_of_xlate(struct clk *clk,
191 struct ofnode_phandle_args *args)
192{
193 struct ti_clk_data *data = dev_get_priv(clk->dev);
194 int idx;
195
196 debug("%s(clk=%p, args_count=%d [0]=%d [1]=%d)\n", __func__, clk,
197 args->args_count, args->args[0], args->args[1]);
198
199 if (args->args_count != 2) {
200 debug("Invalid args_count: %d\n", args->args_count);
201 return -EINVAL;
202 }
203
204 if (!data->size)
205 return -EPROBE_DEFER;
206
207 idx = bsearch(args->args[0], args->args[1], data->map, data->size);
208 if (idx < 0)
209 return idx;
210
211 clk->id = idx;
212
213 return 0;
214}
215
216static ulong ti_clk_get_rate(struct clk *clk)
217{
218 struct ti_clk_data *data = dev_get_priv(clk->dev);
219 struct clk *clkp = data->map[clk->id].clk;
220
221 return clk_get_rate(clkp);
222}
223
224static ulong ti_clk_set_rate(struct clk *clk, ulong rate)
225{
226 struct ti_clk_data *data = dev_get_priv(clk->dev);
227 struct clk *clkp = data->map[clk->id].clk;
228 int div = 1;
229 ulong child_rate;
230 const struct clk_ops *ops;
231 ulong new_rate, rem;
232 ulong diff, new_diff;
233
234 /*
235 * We must propagate rate change to parent if current clock type
236 * does not allow setting it.
237 */
238 while (clkp) {
239 ops = clkp->dev->driver->ops;
240 if (ops->set_rate)
241 break;
242
243 /*
244 * Store child rate so we can calculate the clock rate
245 * that must be passed to parent
246 */
247 child_rate = clk_get_rate(clkp);
248 clkp = clk_get_parent(clkp);
249 if (clkp) {
250 debug("%s: propagating rate change to parent %s, rate=%u.\n",
251 __func__, clkp->dev->name, (u32)rate / div);
252 div *= clk_get_rate(clkp) / child_rate;
253 }
254 }
255
256 if (!clkp)
257 return -ENOSYS;
258
259 child_rate = clk_get_rate(clkp);
260
261 new_rate = clk_set_rate(clkp, rate / div);
262
263 diff = abs(new_rate - rate / div);
264
265 debug("%s: clk=%s, div=%d, rate=%u, new_rate=%u, diff=%u\n", __func__,
266 clkp->dev->name, div, (u32)rate, (u32)new_rate, (u32)diff);
267
268 /*
269 * If the new rate differs by 50% of the target,
270 * modify parent. This handles typical cases where we have a hsdiv
271 * following directly a PLL
272 */
273
274 if (diff > rate / div / 2) {
275 ulong pll_tgt;
276 int pll_div = 0;
277
278 clk = clkp;
279
280 debug("%s: propagating rate change to parent, rate=%u.\n",
281 __func__, (u32)rate / div);
282
283 clkp = clk_get_parent(clkp);
284
285 if (rate > osc_freq) {
286 if (rate > PLL_MAX_FREQ / 2 && rate < PLL_MAX_FREQ) {
287 pll_tgt = rate;
288 pll_div = 1;
289 } else {
290 for (pll_div = 2; pll_div < PLL_MAX_DIV; pll_div++) {
291 pll_tgt = rate / div * pll_div;
292 if (pll_tgt >= PLL_MIN_FREQ && pll_tgt <= PLL_MAX_FREQ)
293 break;
294 }
295 }
296 } else {
297 pll_tgt = osc_freq;
298 pll_div = rate / div / osc_freq;
299 }
300
301 debug("%s: pll_tgt=%u, rate=%u, div=%u\n", __func__,
302 (u32)pll_tgt, (u32)rate, pll_div);
303
304 clk_set_rate(clkp, pll_tgt);
305
306 return clk_set_rate(clk, rate / div) * div;
307 }
308
309 /*
310 * If the new rate differs by at least 5% of the target,
311 * we must check for rounding error in a divider, so try
312 * set rate with rate + (parent_freq % rate).
313 */
314
315 if (diff > rate / div / 20) {
316 u64 parent_freq = clk_get_parent_rate(clkp);
317
318 rem = parent_freq % rate;
319 new_rate = clk_set_rate(clkp, (rate / div) + rem);
320 new_diff = abs(new_rate - rate / div);
321
322 if (new_diff > diff) {
323 new_rate = clk_set_rate(clkp, rate / div);
324 } else {
325 debug("%s: Using better rate %lu that gives diff %lu\n",
326 __func__, new_rate, new_diff);
327 }
328 }
329
330 return new_rate;
331}
332
333static int ti_clk_set_parent(struct clk *clk, struct clk *parent)
334{
335 struct ti_clk_data *data = dev_get_priv(clk->dev);
336 struct clk *clkp = data->map[clk->id].clk;
337 struct clk *parentp = data->map[parent->id].clk;
338
339 return clk_set_parent(clkp, parentp);
340}
341
342static int ti_clk_enable(struct clk *clk)
343{
344 struct ti_clk_data *data = dev_get_priv(clk->dev);
345 struct clk *clkp = data->map[clk->id].clk;
346
347 return clk_enable(clkp);
348}
349
350static int ti_clk_disable(struct clk *clk)
351{
352 struct ti_clk_data *data = dev_get_priv(clk->dev);
353 struct clk *clkp = data->map[clk->id].clk;
354
355 return clk_disable(clkp);
356}
357
358static const struct udevice_id ti_clk_of_match[] = {
359 { .compatible = "ti,k2g-sci-clk" },
360 { /* sentinel */ },
361};
362
363static const struct clk_ops ti_clk_ops = {
364 .of_xlate = ti_clk_of_xlate,
365 .set_rate = ti_clk_set_rate,
366 .get_rate = ti_clk_get_rate,
367 .enable = ti_clk_enable,
368 .disable = ti_clk_disable,
369 .set_parent = ti_clk_set_parent,
370};
371
372U_BOOT_DRIVER(ti_clk) = {
373 .name = "ti-clk",
374 .id = UCLASS_CLK,
375 .of_match = ti_clk_of_match,
376 .probe = ti_clk_probe,
377 .priv_auto = sizeof(struct ti_clk_data),
378 .ops = &ti_clk_ops,
379};