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