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 Multiplexer Driver
9 *
10 * This driver implements clock multiplexer (mux) functionality, allowing
11 * selection between multiple parent clock sources. It handles parent
12 * selection via register writes, validates parent choices, manages clock
13 * dividers associated with each parent, and properly handles reference
14 * counting when switching between parents.
15 */
16
17 #include <assert.h>
18
19 #include <common/debug.h>
20 #include <lib/mmio.h>
21
22 #include <ti_clk_mux.h>
23 #include <ti_container_of.h>
24
ti_clk_mux_get_parent_value(struct ti_clk * clkp)25 static uint32_t ti_clk_mux_get_parent_value(struct ti_clk *clkp)
26 {
27 const struct ti_clk_data_mux *mux;
28 const struct ti_clk_data_mux_reg *reg;
29 uint32_t reg_val;
30 uint32_t n_minus_one;
31
32 assert(clkp != NULL);
33
34 mux = ti_container_of(clkp->data, const struct ti_clk_data_mux, data);
35 reg = ti_container_of(mux, const struct ti_clk_data_mux_reg, data_mux);
36
37 /*
38 * Muxes without register assignments (reg == 0) have a fixed
39 * parent at index 0.
40 */
41 if (reg->reg == 0U) {
42 reg_val = 0U;
43 } else {
44 reg_val = mmio_read_32(reg->reg);
45 reg_val >>= (uint32_t) reg->bit;
46
47 n_minus_one = mux->num_parents - 1U;
48 reg_val &= TI_MASK_COVER_FOR_NUMBER(n_minus_one);
49 }
50
51 return reg_val;
52 }
53
ti_clk_mux_get_parent_internal(struct ti_clk * clkp)54 static const struct ti_clk_parent *ti_clk_mux_get_parent_internal(struct ti_clk *clkp)
55 {
56 const struct ti_clk_data_mux *mux;
57 uint32_t val;
58 bool valid_parent;
59 bool valid_div;
60
61 assert(clkp != NULL);
62
63 mux = ti_container_of(clkp->data, const struct ti_clk_data_mux, data);
64 val = ti_clk_mux_get_parent_value(clkp);
65
66 valid_parent = (val < mux->num_parents);
67 valid_div = (mux->parents[val].div != 0U);
68
69 return (valid_parent && valid_div) ? &mux->parents[val] : NULL;
70 }
71
ti_clk_mux_set_parent_internal(struct ti_clk * clkp,uint8_t new_parent)72 static bool ti_clk_mux_set_parent_internal(struct ti_clk *clkp, uint8_t new_parent)
73 {
74 const struct ti_clk_data_mux *mux;
75 const struct ti_clk_data_mux_reg *reg;
76 uint32_t mask;
77 uint32_t val;
78
79 assert(clkp != NULL);
80
81 mux = ti_container_of(clkp->data, const struct ti_clk_data_mux, data);
82 reg = ti_container_of(mux, const struct ti_clk_data_mux_reg, data_mux);
83
84 if (reg->reg == 0U) {
85 /*
86 * Muxes without register assignments are fixed and cannot
87 * change parent selection.
88 */
89 } else {
90 mask = TI_MASK_COVER_FOR_NUMBER(mux->num_parents - 1U)
91 << (uint32_t)reg->bit;
92 val = (uint32_t)new_parent << (uint32_t)reg->bit;
93
94 mmio_clrsetbits_32((uintptr_t)reg->reg, mask, val);
95
96 VERBOSE("CLOCK_SET_PARENT: clk_id=%d new_parent=%d\n",
97 ti_clk_id(clkp), new_parent);
98 }
99
100 return true;
101 }
102
103 const struct ti_clk_drv_mux ti_clk_drv_mux_reg_ro = {
104 .get_parent = ti_clk_mux_get_parent_internal,
105 };
106
107 const struct ti_clk_drv_mux ti_clk_drv_mux_reg = {
108 .set_parent = ti_clk_mux_set_parent_internal,
109 .get_parent = ti_clk_mux_get_parent_internal,
110 };
111
ti_clk_mux_get_parent(struct ti_clk * clkp)112 const struct ti_clk_parent *ti_clk_mux_get_parent(struct ti_clk *clkp)
113 {
114 const struct ti_clk_parent *ret = NULL;
115 const struct ti_clk_drv_mux *mux;
116 bool is_mux_type;
117 bool valid_div;
118
119 assert(clkp != NULL);
120
121 is_mux_type = (clkp->type == TI_CLK_TYPE_MUX);
122
123 if (is_mux_type) {
124 mux = ti_container_of(clkp->drv, const struct ti_clk_drv_mux, drv);
125 ret = mux->get_parent(clkp);
126 } else {
127 valid_div = (clkp->parent.div > 0U);
128 ret = valid_div ? &clkp->parent : NULL;
129 }
130
131 return ret;
132 }
133
ti_clk_mux_set_parent(struct ti_clk * clkp,uint8_t new_parent)134 bool ti_clk_mux_set_parent(struct ti_clk *clkp, uint8_t new_parent)
135 {
136 const struct ti_clk_drv_mux *mux_drv = NULL;
137 const struct ti_clk_data_mux *mux_data = NULL;
138 const struct ti_clk_parent *op = NULL;
139 struct ti_clk *parent = NULL;
140 struct ti_clk *op_parent = NULL;
141
142 assert(clkp != NULL);
143
144 if (clkp->type != TI_CLK_TYPE_MUX) {
145 return false;
146 }
147
148 mux_data = ti_container_of(clkp->data,
149 const struct ti_clk_data_mux, data);
150 if (new_parent >= mux_data->num_parents) {
151 return false;
152 }
153
154 if (mux_data->parents[new_parent].div == 0U) {
155 return false;
156 }
157
158 mux_drv = ti_container_of(clkp->drv,
159 const struct ti_clk_drv_mux, drv);
160 if (mux_drv->set_parent == NULL) {
161 return false;
162 }
163
164 op = mux_drv->get_parent(clkp);
165 if ((op != NULL) && (op->clk == mux_data->parents[new_parent].clk) &&
166 (op->div == mux_data->parents[new_parent].div)) {
167 return true;
168 }
169
170 parent = ti_clk_lookup((ti_clk_idx_t) mux_data->parents[new_parent].clk);
171 if (parent == NULL) {
172 return false;
173 }
174
175 if ((clkp->flags & TI_CLK_FLAG_INITIALIZED) == 0U) {
176 return false;
177 }
178
179 if (clkp->ref_count != 0U) {
180 if (!ti_clk_get(parent)) {
181 return false;
182 }
183 }
184
185 if (!mux_drv->set_parent(clkp, new_parent)) {
186 if (clkp->ref_count != 0U) {
187 ti_clk_put(parent);
188 }
189 return false;
190 }
191
192 if ((op != NULL) && (clkp->ref_count != 0U)) {
193 op_parent = ti_clk_lookup((ti_clk_idx_t) op->clk);
194 if (op_parent != NULL) {
195 ti_clk_put(op_parent);
196 }
197 }
198
199 return true;
200 }
201