xref: /rk3399_ARM-atf/drivers/ti/clk/ti_clk_mux.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 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