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