blob: 10f60497973b3a893ce6ca1c4f73829b60352c86 [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
Apurva Nandanb93ab922024-02-24 01:51:44 +053090#ifdef CONFIG_SOC_K3_J784S4
91 {
92 .family = "J784S4",
93 .data = &j784s4_clk_platdata,
94 },
95#endif
Tero Kristo82ceb0d2021-06-11 11:45:14 +030096 { /* sentinel */ }
97};
98
99static int ti_clk_probe(struct udevice *dev)
100{
101 struct ti_clk_data *data = dev_get_priv(dev);
102 struct clk *clk;
103 const char *name;
104 const struct clk_data *ti_clk_data;
105 int i, j;
106 const struct soc_attr *soc_match_data;
107 const struct ti_k3_clk_platdata *pdata;
108
109 debug("%s(dev=%p)\n", __func__, dev);
110
111 soc_match_data = soc_device_match(ti_k3_soc_clk_data);
112 if (!soc_match_data)
113 return -ENODEV;
114
115 pdata = (const struct ti_k3_clk_platdata *)soc_match_data->data;
116
117 data->map = kcalloc(pdata->soc_dev_clk_data_cnt, sizeof(*data->map),
118 GFP_KERNEL);
119 data->size = 0;
120
121 for (i = 0; i < pdata->clk_list_cnt; i++) {
122 ti_clk_data = &pdata->clk_list[i];
123
124 switch (ti_clk_data->type) {
125 case CLK_TYPE_FIXED_RATE:
126 name = ti_clk_data->clk.fixed_rate.name;
127 clk = clk_register_fixed_rate(NULL,
128 name,
129 ti_clk_data->clk.fixed_rate.rate);
130 break;
131 case CLK_TYPE_DIV:
132 name = ti_clk_data->clk.div.name;
133 clk = clk_register_divider(NULL, name,
134 ti_clk_data->clk.div.parent,
135 ti_clk_data->clk.div.flags,
136 map_physmem(ti_clk_data->clk.div.reg, 0, MAP_NOCACHE),
137 ti_clk_data->clk.div.shift,
138 ti_clk_data->clk.div.width,
Suman Annadb4c2dc2021-09-07 17:16:58 -0500139 ti_clk_data->clk.div.div_flags);
Tero Kristo82ceb0d2021-06-11 11:45:14 +0300140 break;
141 case CLK_TYPE_MUX:
142 name = ti_clk_data->clk.mux.name;
143 clk = clk_register_mux(NULL, name,
144 ti_clk_data->clk.mux.parents,
145 ti_clk_data->clk.mux.num_parents,
146 ti_clk_data->clk.mux.flags,
147 map_physmem(ti_clk_data->clk.mux.reg, 0, MAP_NOCACHE),
148 ti_clk_data->clk.mux.shift,
149 ti_clk_data->clk.mux.width,
150 0);
151 break;
152 case CLK_TYPE_PLL:
153 name = ti_clk_data->clk.pll.name;
154 clk = clk_register_ti_pll(name,
155 ti_clk_data->clk.pll.parent,
156 map_physmem(ti_clk_data->clk.pll.reg, 0, MAP_NOCACHE));
157
158 if (!osc_freq)
159 osc_freq = clk_get_rate(clk_get_parent(clk));
160 break;
161 default:
162 name = NULL;
163 clk = NULL;
164 printf("WARNING: %s has encountered unknown clk type %d\n",
165 __func__, ti_clk_data->type);
166 }
167
168 if (clk && ti_clk_data->default_freq)
169 clk_set_rate(clk, ti_clk_data->default_freq);
170
171 if (clk && name) {
172 for (j = 0; j < pdata->soc_dev_clk_data_cnt; j++) {
173 if (!strcmp(name, pdata->soc_dev_clk_data[j].clk_name)) {
174 clk_add_map(data, clk, pdata->soc_dev_clk_data[j].dev_id,
175 pdata->soc_dev_clk_data[j].clk_id);
176 }
177 }
178 }
179 }
180
181 return 0;
182}
183
184static int _clk_cmp(u32 dev_id, u32 clk_id, const struct clk_map *map)
185{
186 if (map->dev_id == dev_id && map->clk_id == clk_id)
187 return 0;
188 if (map->dev_id > dev_id ||
189 (map->dev_id == dev_id && map->clk_id > clk_id))
190 return -1;
191 return 1;
192}
193
194static int bsearch(u32 dev_id, u32 clk_id, struct clk_map *map, int num)
195{
196 int result;
197 int idx;
198
199 for (idx = 0; idx < num; idx++) {
200 result = _clk_cmp(dev_id, clk_id, &map[idx]);
201
202 if (result == 0)
203 return idx;
204 }
205
206 return -ENOENT;
207}
208
209static int ti_clk_of_xlate(struct clk *clk,
210 struct ofnode_phandle_args *args)
211{
212 struct ti_clk_data *data = dev_get_priv(clk->dev);
213 int idx;
214
215 debug("%s(clk=%p, args_count=%d [0]=%d [1]=%d)\n", __func__, clk,
216 args->args_count, args->args[0], args->args[1]);
217
218 if (args->args_count != 2) {
219 debug("Invalid args_count: %d\n", args->args_count);
220 return -EINVAL;
221 }
222
223 if (!data->size)
224 return -EPROBE_DEFER;
225
226 idx = bsearch(args->args[0], args->args[1], data->map, data->size);
227 if (idx < 0)
228 return idx;
229
230 clk->id = idx;
231
232 return 0;
233}
234
235static ulong ti_clk_get_rate(struct clk *clk)
236{
237 struct ti_clk_data *data = dev_get_priv(clk->dev);
238 struct clk *clkp = data->map[clk->id].clk;
239
240 return clk_get_rate(clkp);
241}
242
243static ulong ti_clk_set_rate(struct clk *clk, ulong rate)
244{
245 struct ti_clk_data *data = dev_get_priv(clk->dev);
246 struct clk *clkp = data->map[clk->id].clk;
247 int div = 1;
248 ulong child_rate;
249 const struct clk_ops *ops;
250 ulong new_rate, rem;
251 ulong diff, new_diff;
Udit Kumarc648daa2023-09-21 22:30:38 +0530252 int freq_scale_up = rate >= ti_clk_get_rate(clk) ? 1 : 0;
Tero Kristo82ceb0d2021-06-11 11:45:14 +0300253
Udit Kumarc648daa2023-09-21 22:30:38 +0530254 if (IS_ENABLED(CONFIG_K3_AVS0) && freq_scale_up)
255 k3_avs_notify_freq(data->map[clk->id].dev_id,
256 data->map[clk->id].clk_id, rate);
Tero Kristo82ceb0d2021-06-11 11:45:14 +0300257 /*
258 * We must propagate rate change to parent if current clock type
259 * does not allow setting it.
260 */
261 while (clkp) {
262 ops = clkp->dev->driver->ops;
263 if (ops->set_rate)
264 break;
265
266 /*
267 * Store child rate so we can calculate the clock rate
268 * that must be passed to parent
269 */
270 child_rate = clk_get_rate(clkp);
271 clkp = clk_get_parent(clkp);
272 if (clkp) {
273 debug("%s: propagating rate change to parent %s, rate=%u.\n",
274 __func__, clkp->dev->name, (u32)rate / div);
275 div *= clk_get_rate(clkp) / child_rate;
276 }
277 }
278
279 if (!clkp)
280 return -ENOSYS;
281
282 child_rate = clk_get_rate(clkp);
283
284 new_rate = clk_set_rate(clkp, rate / div);
285
286 diff = abs(new_rate - rate / div);
287
288 debug("%s: clk=%s, div=%d, rate=%u, new_rate=%u, diff=%u\n", __func__,
289 clkp->dev->name, div, (u32)rate, (u32)new_rate, (u32)diff);
290
291 /*
292 * If the new rate differs by 50% of the target,
293 * modify parent. This handles typical cases where we have a hsdiv
294 * following directly a PLL
295 */
296
297 if (diff > rate / div / 2) {
298 ulong pll_tgt;
299 int pll_div = 0;
300
301 clk = clkp;
302
303 debug("%s: propagating rate change to parent, rate=%u.\n",
304 __func__, (u32)rate / div);
305
306 clkp = clk_get_parent(clkp);
307
308 if (rate > osc_freq) {
309 if (rate > PLL_MAX_FREQ / 2 && rate < PLL_MAX_FREQ) {
310 pll_tgt = rate;
311 pll_div = 1;
312 } else {
313 for (pll_div = 2; pll_div < PLL_MAX_DIV; pll_div++) {
314 pll_tgt = rate / div * pll_div;
315 if (pll_tgt >= PLL_MIN_FREQ && pll_tgt <= PLL_MAX_FREQ)
316 break;
317 }
318 }
319 } else {
320 pll_tgt = osc_freq;
321 pll_div = rate / div / osc_freq;
322 }
323
324 debug("%s: pll_tgt=%u, rate=%u, div=%u\n", __func__,
325 (u32)pll_tgt, (u32)rate, pll_div);
326
327 clk_set_rate(clkp, pll_tgt);
328
329 return clk_set_rate(clk, rate / div) * div;
330 }
331
332 /*
333 * If the new rate differs by at least 5% of the target,
334 * we must check for rounding error in a divider, so try
335 * set rate with rate + (parent_freq % rate).
336 */
337
338 if (diff > rate / div / 20) {
339 u64 parent_freq = clk_get_parent_rate(clkp);
340
341 rem = parent_freq % rate;
342 new_rate = clk_set_rate(clkp, (rate / div) + rem);
343 new_diff = abs(new_rate - rate / div);
344
345 if (new_diff > diff) {
346 new_rate = clk_set_rate(clkp, rate / div);
347 } else {
348 debug("%s: Using better rate %lu that gives diff %lu\n",
349 __func__, new_rate, new_diff);
350 }
351 }
352
Udit Kumarc648daa2023-09-21 22:30:38 +0530353 if (IS_ENABLED(CONFIG_K3_AVS0) && !freq_scale_up)
354 k3_avs_notify_freq(data->map[clk->id].dev_id,
355 data->map[clk->id].clk_id, rate);
356
Tero Kristo82ceb0d2021-06-11 11:45:14 +0300357 return new_rate;
358}
359
360static int ti_clk_set_parent(struct clk *clk, struct clk *parent)
361{
362 struct ti_clk_data *data = dev_get_priv(clk->dev);
363 struct clk *clkp = data->map[clk->id].clk;
364 struct clk *parentp = data->map[parent->id].clk;
365
366 return clk_set_parent(clkp, parentp);
367}
368
369static int ti_clk_enable(struct clk *clk)
370{
371 struct ti_clk_data *data = dev_get_priv(clk->dev);
372 struct clk *clkp = data->map[clk->id].clk;
373
374 return clk_enable(clkp);
375}
376
377static int ti_clk_disable(struct clk *clk)
378{
379 struct ti_clk_data *data = dev_get_priv(clk->dev);
380 struct clk *clkp = data->map[clk->id].clk;
381
382 return clk_disable(clkp);
383}
384
385static const struct udevice_id ti_clk_of_match[] = {
386 { .compatible = "ti,k2g-sci-clk" },
387 { /* sentinel */ },
388};
389
390static const struct clk_ops ti_clk_ops = {
391 .of_xlate = ti_clk_of_xlate,
392 .set_rate = ti_clk_set_rate,
393 .get_rate = ti_clk_get_rate,
394 .enable = ti_clk_enable,
395 .disable = ti_clk_disable,
396 .set_parent = ti_clk_set_parent,
397};
398
399U_BOOT_DRIVER(ti_clk) = {
400 .name = "ti-clk",
401 .id = UCLASS_CLK,
402 .of_match = ti_clk_of_match,
403 .probe = ti_clk_probe,
404 .priv_auto = sizeof(struct ti_clk_data),
405 .ops = &ti_clk_ops,
406};