xref: /rk3399_ARM-atf/drivers/ti/clk/ti_clk.c (revision a28114d66a6d43db4accef5fd5d6dab6c059e584)
1 /*
2  * Copyright (c) 2025-2026 Texas Instruments Incorporated - https://www.ti.com
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /*
8  * TI Clock Management Framework Core
9  *
10  * This is the core clock framework that provides the central clock tree
11  * management, including reference counting, parent-child relationships,
12  * frequency propagation, and clock state management. It implements the
13  * main clock API functions for get/put, set frequency, query frequency,
14  * and state control that are used by device drivers and power management.
15  */
16 
17 #include <assert.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <stddef.h>
21 
22 #include <common/debug.h>
23 
24 #include <ti_clk.h>
25 #include <ti_clk_mux.h>
26 #include <ti_device.h>
27 
ti_clk_value_set_freq(struct ti_clk * clkp,uint32_t target_hz,uint32_t min_hz,uint32_t max_hz,bool * changed)28 uint32_t ti_clk_value_set_freq(struct ti_clk *clkp, uint32_t target_hz,
29 			       uint32_t min_hz,
30 			       uint32_t max_hz,
31 			       bool *changed)
32 {
33 	assert(clkp != NULL);
34 	assert(changed != NULL);
35 
36 	/* Validate that target frequency is within acceptable range */
37 	if (target_hz < min_hz || target_hz > max_hz) {
38 		*changed = false;
39 		return 0;
40 	}
41 
42 	/*
43 	 * Store the frequency value. ti_clk_set_value() validates that
44 	 * freq_idx is within bounds of soc_clock_values[] array.
45 	 */
46 	if (!ti_clk_set_value(clkp->freq_idx, target_hz)) {
47 		*changed = false;
48 		return 0;
49 	}
50 
51 	*changed = true;
52 	return target_hz;
53 }
54 
ti_clk_value_get_freq(struct ti_clk * clkp)55 uint32_t ti_clk_value_get_freq(struct ti_clk *clkp)
56 {
57 	assert(clkp != NULL);
58 
59 	return ti_clk_get_value(clkp->freq_idx);
60 }
61 
ti_clk_get_parent_freq(struct ti_clk * clkp)62 uint32_t ti_clk_get_parent_freq(struct ti_clk *clkp)
63 {
64 	const struct ti_clk_parent *p;
65 	struct ti_clk *parent_clk;
66 
67 	assert(clkp != NULL);
68 
69 	p = ti_clk_mux_get_parent(clkp);
70 
71 	if ((p != NULL) && (p->div != 0U)) {
72 		parent_clk = ti_clk_lookup((ti_clk_idx_t) p->clk);
73 		if (parent_clk != NULL) {
74 			return ti_clk_get_freq(parent_clk) / p->div;
75 		}
76 	}
77 
78 	return 0;
79 }
80 
ti_clk_generic_set_freq_parent(struct ti_clk * clkp,struct ti_clk * parent,uint32_t target_hz,uint32_t min_hz,uint32_t max_hz,bool * changed,uint32_t div)81 uint32_t ti_clk_generic_set_freq_parent(struct ti_clk *clkp, struct ti_clk *parent,
82 					uint32_t target_hz, uint32_t min_hz,
83 					uint32_t max_hz,
84 					bool *changed, uint32_t div)
85 {
86 	uint32_t parent_min_hz, parent_target_hz, parent_max_hz;
87 	uint32_t actual_parent_hz;
88 	uint32_t max_possible_child_hz;
89 	uint32_t max_possible_parent_hz;
90 
91 	assert(parent != NULL);
92 	assert(changed != NULL);
93 	assert(div != 0U);
94 
95 	*changed = false;
96 
97 	(void)clkp;
98 
99 	/*
100 	 * This clock's output frequency is derived from parent via divider:
101 	 *   child_freq = parent_freq / div
102 	 *
103 	 * To achieve desired child frequency, we must configure parent:
104 	 *   required_parent_freq = desired_child_freq * div
105 	 *
106 	 * Since multiplication can overflow uint32_t, we calculate the
107 	 * maximum representable child and parent frequencies.
108 	 */
109 	max_possible_child_hz = UINT32_MAX / div;
110 	max_possible_parent_hz = max_possible_child_hz * div;
111 
112 	/* If child's minimum requirement is too high, cannot satisfy */
113 	if (min_hz > max_possible_child_hz) {
114 		return 0;
115 	}
116 
117 	/* Convert child frequency range to parent frequency range */
118 	parent_min_hz = min_hz * div;
119 
120 	if (target_hz > max_possible_child_hz) {
121 		parent_target_hz = max_possible_parent_hz;
122 	} else {
123 		parent_target_hz = target_hz * div;
124 	}
125 
126 	if (max_hz > max_possible_child_hz) {
127 		parent_max_hz = max_possible_parent_hz;
128 	} else {
129 		parent_max_hz = max_hz * div;
130 	}
131 
132 	/* Request parent clock to operate in the required range */
133 	actual_parent_hz = ti_clk_set_freq(parent, parent_target_hz,
134 					   parent_min_hz, parent_max_hz,
135 					   changed);
136 
137 	/* Convert actual parent frequency back to child frequency */
138 	return actual_parent_hz / div;
139 }
140 
141 /**
142  * ti_clk_generic_set_freq() - Generic clock set-frequency handler.
143  * @clkp: The clock to set the frequency on.
144  * @target_hz: Target frequency in Hz.
145  * @min_hz: Minimum acceptable frequency in Hz.
146  * @max_hz: Maximum acceptable frequency in Hz.
147  * @changed: Set to true if the frequency was changed.
148  *
149  * Return: The actual frequency set, or 0 on failure.
150  */
ti_clk_generic_set_freq(struct ti_clk * clkp,uint32_t target_hz,uint32_t min_hz,uint32_t max_hz,bool * changed)151 static uint32_t ti_clk_generic_set_freq(struct ti_clk *clkp,
152 					uint32_t target_hz,
153 					uint32_t min_hz,
154 					uint32_t max_hz,
155 					bool *changed)
156 {
157 	const struct ti_clk_parent *p;
158 	struct ti_clk *parent;
159 	uint32_t freq;
160 
161 	p = ti_clk_mux_get_parent(clkp);
162 	*changed = false;
163 
164 	if ((p != NULL) && ((clkp->data_flags & TI_CLK_DATA_FLAG_MODIFY_PARENT_FREQ) != 0U)) {
165 		parent = ti_clk_lookup((ti_clk_idx_t) p->clk);
166 
167 		if (parent != NULL) {
168 			return ti_clk_generic_set_freq_parent(clkp, parent,
169 							      target_hz,
170 							      min_hz, max_hz,
171 							      changed,
172 							      p->div);
173 		}
174 	} else {
175 		freq = ti_clk_get_freq(clkp);
176 
177 		if ((freq >= min_hz) && (freq <= max_hz)) {
178 			return freq;
179 		}
180 	}
181 
182 	return 0;
183 }
184 
ti_clk_set_freq(struct ti_clk * clkp,uint32_t target_hz,uint32_t min_hz,uint32_t max_hz,bool * changed)185 uint32_t ti_clk_set_freq(struct ti_clk *clkp, uint32_t target_hz,
186 			 uint32_t min_hz, uint32_t max_hz,
187 			 bool *changed)
188 {
189 	uint32_t ret;
190 
191 	assert(clkp != NULL);
192 	assert(changed != NULL);
193 
194 	*changed = false;
195 
196 	if ((__atomic_load_n(&clkp->flags, __ATOMIC_ACQUIRE) & TI_CLK_FLAG_INITIALIZED) == 0U) {
197 		ret = 0U;
198 	} else if (clkp->drv->set_freq != NULL) {
199 		ret = clkp->drv->set_freq(clkp, target_hz, min_hz,
200 					  max_hz, changed);
201 	} else {
202 		ret = ti_clk_generic_set_freq(clkp, target_hz, min_hz, max_hz,
203 					      changed);
204 	}
205 
206 	if (ret != 0U) {
207 		VERBOSE("CLOCK_SET_RATE: clk_id=%u freq=%u\n", ti_clk_id(clkp), target_hz);
208 	} else {
209 		WARN("CLOCK_SET_RATE failed: clk_id=%u freq=%u\n", ti_clk_id(clkp), target_hz);
210 	}
211 
212 	return ret;
213 }
214 
ti_clk_get_freq(struct ti_clk * clkp)215 uint32_t ti_clk_get_freq(struct ti_clk *clkp)
216 {
217 	uint32_t ret;
218 
219 	assert(clkp != NULL);
220 
221 	if ((__atomic_load_n(&clkp->flags, __ATOMIC_ACQUIRE) & TI_CLK_FLAG_INITIALIZED) == 0U) {
222 		ret = 0U;
223 	} else if (clkp->drv->get_freq != NULL) {
224 		ret = clkp->drv->get_freq(clkp);
225 	} else {
226 		ret = ti_clk_get_parent_freq(clkp);
227 	}
228 
229 	return ret;
230 }
231 
ti_clk_get_state(struct ti_clk * clkp)232 uint32_t ti_clk_get_state(struct ti_clk *clkp)
233 {
234 	uint32_t ret = TI_CLK_HW_STATE_DISABLED;
235 	const struct ti_clk_parent *p;
236 	struct ti_clk *clkp_parent;
237 
238 	assert(clkp != NULL);
239 
240 	if ((__atomic_load_n(&clkp->flags, __ATOMIC_ACQUIRE) & TI_CLK_FLAG_INITIALIZED) == 0U) {
241 		ret = TI_CLK_HW_STATE_DISABLED;
242 	} else if (clkp->drv->get_state != NULL) {
243 		ret = clkp->drv->get_state(clkp);
244 	} else {
245 		p = ti_clk_mux_get_parent(clkp);
246 
247 		if (p != NULL) {
248 			clkp_parent = ti_clk_lookup((ti_clk_idx_t) p->clk);
249 
250 			if (clkp_parent != NULL) {
251 				ret = ti_clk_get_state(clkp_parent);
252 			}
253 		}
254 	}
255 
256 	return ret;
257 }
258 
ti_clk_set_state(struct ti_clk * clkp,bool enable)259 bool ti_clk_set_state(struct ti_clk *clkp, bool enable)
260 {
261 	bool ret = true;
262 
263 	assert(clkp != NULL);
264 
265 	if ((__atomic_load_n(&clkp->flags, __ATOMIC_ACQUIRE) & TI_CLK_FLAG_INITIALIZED) == 0U) {
266 		/* defer action */
267 		return ret;
268 	}
269 
270 	if (clkp->drv->set_state != NULL) {
271 		ret = clkp->drv->set_state(clkp, enable);
272 	}
273 
274 	return ret;
275 }
276 
ti_clk_get(struct ti_clk * clkp)277 bool ti_clk_get(struct ti_clk *clkp)
278 {
279 	bool ret = true;
280 	const struct ti_clk_parent *p;
281 	struct ti_clk *clkp_parent = NULL;
282 
283 	assert(clkp != NULL);
284 
285 	if (__atomic_load_n(&clkp->ref_count, __ATOMIC_ACQUIRE) == 0U) {
286 		p = ti_clk_mux_get_parent(clkp);
287 		if (p != NULL) {
288 			clkp_parent = ti_clk_lookup((ti_clk_idx_t) p->clk);
289 		}
290 
291 		if (clkp_parent != NULL) {
292 			ret = ti_clk_get(clkp_parent);
293 		}
294 
295 		if (ret == true) {
296 			ret = ti_clk_set_state(clkp, true);
297 			if (ret == false) {
298 				WARN("CLOCK_GET failed: clk_id=%u\n", ti_clk_id(clkp));
299 				if (clkp_parent != NULL) {
300 					ti_clk_put(clkp_parent);
301 				}
302 			} else {
303 				VERBOSE("CLOCK_GET: clk_id=%u\n", ti_clk_id(clkp));
304 			}
305 		}
306 	}
307 
308 	if (ret) {
309 		(void)__atomic_fetch_add(&clkp->ref_count, 1U, __ATOMIC_ACQ_REL);
310 	}
311 
312 	return ret;
313 }
314 
ti_clk_put(struct ti_clk * clkp)315 void ti_clk_put(struct ti_clk *clkp)
316 {
317 	uint8_t new_count;
318 	const struct ti_clk_parent *p;
319 	struct ti_clk *clkp_parent;
320 
321 	assert(clkp != NULL);
322 	assert(__atomic_load_n(&clkp->ref_count, __ATOMIC_ACQUIRE) > 0U);
323 
324 	new_count = __atomic_sub_fetch(&clkp->ref_count, 1U, __ATOMIC_ACQ_REL);
325 	if (new_count == 0U) {
326 		p = ti_clk_mux_get_parent(clkp);
327 		ti_clk_set_state(clkp, false);
328 		VERBOSE("CLOCK_PUT: clk_id=%u\n", ti_clk_id(clkp));
329 		if (p != NULL) {
330 			clkp_parent = ti_clk_lookup((ti_clk_idx_t) p->clk);
331 			if (clkp_parent != NULL) {
332 				ti_clk_put(clkp_parent);
333 			}
334 		}
335 	}
336 }
337 
ti_clk_register_clock(struct ti_clk * clkp)338 static int32_t ti_clk_register_clock(struct ti_clk *clkp)
339 {
340 	struct ti_clk *clkp_parent = NULL;
341 	const struct ti_clk_parent *p;
342 	uint8_t parent_flags;
343 	int32_t ret;
344 
345 	p = ti_clk_mux_get_parent(clkp);
346 	if (p != NULL) {
347 		clkp_parent = ti_clk_lookup((ti_clk_idx_t) p->clk);
348 	}
349 	if (clkp_parent != NULL) {
350 		parent_flags = __atomic_load_n(&clkp_parent->flags, __ATOMIC_ACQUIRE);
351 		if ((parent_flags & TI_CLK_FLAG_INITIALIZED) == 0U) {
352 			return -EAGAIN;
353 		}
354 	}
355 
356 	if (clkp->drv->init != NULL) {
357 		ret = clkp->drv->init(clkp);
358 		if (ret != 0) {
359 			return ret;
360 		}
361 	}
362 
363 	(void)__atomic_or_fetch(&clkp->flags, TI_CLK_FLAG_INITIALIZED, __ATOMIC_ACQ_REL);
364 	if (__atomic_load_n(&clkp->ref_count, __ATOMIC_ACQUIRE) != 0U) {
365 		if (!ti_clk_set_state(clkp, true)) {
366 			WARN("CLOCK_ENABLE failed during init: clk_id=%u\n", ti_clk_id(clkp));
367 		}
368 		VERBOSE("CLOCK_ENABLE: clk_id=%u\n", ti_clk_id(clkp));
369 	}
370 	if (clkp->drv->get_state != NULL) {
371 		if (clkp->drv->get_state(clkp) != TI_CLK_HW_STATE_DISABLED) {
372 			(void)__atomic_or_fetch(&clkp->flags,
373 						TI_CLK_FLAG_PWR_UP_EN,
374 						__ATOMIC_ACQ_REL);
375 		}
376 	}
377 
378 	return 0;
379 }
380 
ti_clk_drop_pwr_up_en(void)381 void ti_clk_drop_pwr_up_en(void)
382 {
383 	ti_clk_idx_t i;
384 	uint8_t flags;
385 
386 	for (i = 0U; i < soc_clock_count; i++) {
387 		flags = __atomic_load_n(&soc_clocks[i].flags, __ATOMIC_ACQUIRE);
388 		if ((flags & TI_CLK_FLAG_PWR_UP_EN) != 0U) {
389 			ti_clk_put(&soc_clocks[i]);
390 			(void)__atomic_and_fetch(&soc_clocks[i].flags,
391 						 (uint8_t)~TI_CLK_FLAG_PWR_UP_EN,
392 						 __ATOMIC_ACQ_REL);
393 		}
394 	}
395 }
396 
ti_clk_init(void)397 int32_t ti_clk_init(void)
398 {
399 	bool progress;
400 	bool contents;
401 	int32_t last_error = 0;
402 	uint32_t i;
403 	uint32_t clock_count = soc_clock_count;
404 	struct ti_clk *clkp;
405 	uint8_t clk_flags;
406 	int32_t curr;
407 	devgrp_t devgrp;
408 
409 	/* soc_devgroups[0] is unused; real entries start at index TI_PM_DEVGRP_00 (1U) */
410 	for (i = 1U; i < soc_devgroup_count; i++) {
411 		/* Translate compressed internal representation to bitfield */
412 		devgrp = (devgrp_t) BIT(i - 1U);
413 
414 		/* First disabled devgroup, stop at this clock index */
415 		if (ti_pm_devgroup_is_enabled(devgrp) == false) {
416 			clock_count = soc_devgroups[i].clk_idx;
417 			break;
418 		}
419 	}
420 
421 	contents = false;
422 	progress = false;
423 
424 	/* Loop through all the clocks to initialize them */
425 	for (i = 0U; i < clock_count; i++) {
426 		clkp = &soc_clocks[i];
427 
428 		clk_flags = __atomic_load_n(&clkp->flags, __ATOMIC_ACQUIRE);
429 		if (((clk_flags & TI_CLK_FLAG_INITIALIZED) == 0U) &&
430 		    (clkp->drv != NULL)) {
431 			contents = true;
432 			curr = ti_clk_register_clock(clkp);
433 			if (curr != -EAGAIN) {
434 				progress = true;
435 				if (curr != 0) {
436 					last_error = curr;
437 				}
438 			}
439 		}
440 	}
441 
442 	if (progress) {
443 		for (i = 0U; i < clock_count; i++) {
444 			clk_flags = __atomic_load_n(&soc_clocks[i].flags, __ATOMIC_ACQUIRE);
445 			if ((clk_flags & TI_CLK_FLAG_PWR_UP_EN) != 0U) {
446 				if (!ti_clk_get(&soc_clocks[i])) {
447 					/* ti_clk_get failed for one of the clocks */
448 					return -ENODEV;
449 				}
450 			}
451 		}
452 		return last_error;
453 	}
454 
455 	if (contents) {
456 		/* We processed at least one clock but didn't make progress */
457 		return -EAGAIN;
458 	}
459 
460 	/* No clocks needed processing - this is success */
461 	return 0;
462 }
463