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