1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2016 Maxime Ripard
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 */
6 #include "ccu.h"
7 #include "ccu_gate.h"
8 #include "ccu_mux.h"
9
ccu_mux_get_prediv(struct ccu_common * common,struct ccu_mux_internal * cm,int parent_index)10 static u16 ccu_mux_get_prediv(struct ccu_common *common,
11 struct ccu_mux_internal *cm,
12 int parent_index)
13 {
14 u16 prediv = 1;
15 u32 reg;
16
17 if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
18 (common->features & CCU_FEATURE_VARIABLE_PREDIV) ||
19 (common->features & CCU_FEATURE_ALL_PREDIV)))
20 {
21 return 1;
22 }
23
24 if (common->features & CCU_FEATURE_ALL_PREDIV)
25 {
26 return common->prediv;
27 }
28
29 reg = readl(common->base + common->reg);
30 if (parent_index < 0)
31 {
32 parent_index = reg >> cm->shift;
33 parent_index &= (1 << cm->width) - 1;
34 }
35
36 if (common->features & CCU_FEATURE_FIXED_PREDIV)
37 {
38 int i;
39
40 for (i = 0; i < cm->n_predivs; i++)
41 if (parent_index == cm->fixed_predivs[i].index)
42 {
43 prediv = cm->fixed_predivs[i].div;
44 }
45 }
46
47 if (common->features & CCU_FEATURE_VARIABLE_PREDIV)
48 {
49 int i;
50
51 for (i = 0; i < cm->n_var_predivs; i++)
52 if (parent_index == cm->var_predivs[i].index)
53 {
54 u8 div;
55
56 div = reg >> cm->var_predivs[i].shift;
57 div &= (1 << cm->var_predivs[i].width) - 1;
58 prediv = div + 1;
59 }
60 }
61
62 return prediv;
63 }
64
ccu_mux_helper_apply_prediv(struct ccu_common * common,struct ccu_mux_internal * cm,int parent_index,unsigned long parent_rate)65 unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
66 struct ccu_mux_internal *cm,
67 int parent_index,
68 unsigned long parent_rate)
69 {
70 return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
71 }
72
ccu_mux_helper_unapply_prediv(struct ccu_common * common,struct ccu_mux_internal * cm,int parent_index,unsigned long parent_rate)73 static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
74 struct ccu_mux_internal *cm,
75 int parent_index,
76 unsigned long parent_rate)
77 {
78 return parent_rate * ccu_mux_get_prediv(common, cm, parent_index);
79 }
80
ccu_mux_helper_determine_rate(struct ccu_common * common,struct ccu_mux_internal * cm,struct clk_rate_request * req,unsigned long (* round)(struct ccu_mux_internal *,struct clk_hw *,unsigned long *,unsigned long,void *),void * data)81 int ccu_mux_helper_determine_rate(struct ccu_common *common,
82 struct ccu_mux_internal *cm,
83 struct clk_rate_request *req,
84 unsigned long (*round)(struct ccu_mux_internal *,
85 struct clk_hw *,
86 unsigned long *,
87 unsigned long,
88 void *),
89 void *data)
90 {
91 unsigned long best_parent_rate = 0, best_rate = 0;
92 struct clk_hw *best_parent, *hw = &common->hw;
93 unsigned int i;
94
95 if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)
96 {
97 unsigned long adj_parent_rate;
98
99 best_parent = clk_hw_get_parent(hw);
100 best_parent_rate = clk_hw_get_rate(best_parent);
101 adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1,
102 best_parent_rate);
103
104 best_rate = round(cm, best_parent, &adj_parent_rate,
105 req->rate, data);
106
107 /*
108 * adj_parent_rate might have been modified by our clock.
109 * Unapply the pre-divider if there's one, and give
110 * the actual frequency the parent needs to run at.
111 */
112 best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
113 adj_parent_rate);
114
115 goto out;
116 }
117
118 for (i = 0; i < clk_hw_get_num_parents(hw); i++)
119 {
120 unsigned long tmp_rate, parent_rate;
121 struct clk_hw *parent;
122
123 parent = clk_hw_get_parent_by_index(hw, i);
124 if (!parent)
125 {
126 continue;
127 }
128
129 parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
130 clk_hw_get_rate(parent));
131
132 tmp_rate = round(cm, parent, &parent_rate, req->rate, data);
133
134 /*
135 * parent_rate might have been modified by our clock.
136 * Unapply the pre-divider if there's one, and give
137 * the actual frequency the parent needs to run at.
138 */
139 parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i,
140 parent_rate);
141 if (tmp_rate == req->rate)
142 {
143 best_parent = parent;
144 best_parent_rate = parent_rate;
145 best_rate = tmp_rate;
146 goto out;
147 }
148
149 if ((req->rate - tmp_rate) < (req->rate - best_rate))
150 {
151 best_rate = tmp_rate;
152 best_parent_rate = parent_rate;
153 best_parent = parent;
154 }
155 }
156
157 if (best_rate == 0)
158 {
159 return -1;
160 }
161
162 out:
163 req->best_parent_hw = best_parent;
164 req->best_parent_rate = best_parent_rate;
165 req->rate = best_rate;
166 return 0;
167 }
168
ccu_mux_helper_get_parent(struct ccu_common * common,struct ccu_mux_internal * cm)169 u8 ccu_mux_helper_get_parent(struct ccu_common *common,
170 struct ccu_mux_internal *cm)
171 {
172 u32 reg;
173 u8 parent;
174
175 reg = readl(common->base + common->reg);
176 parent = reg >> cm->shift;
177 parent &= (1 << cm->width) - 1;
178
179 if (cm->table)
180 {
181 int num_parents = clk_hw_get_num_parents(&common->hw);
182 int i;
183
184 for (i = 0; i < num_parents; i++)
185 if (cm->table[i] == parent)
186 {
187 return i;
188 }
189 }
190
191 return parent;
192 }
193
ccu_mux_helper_set_parent(struct ccu_common * common,struct ccu_mux_internal * cm,u8 index)194 int ccu_mux_helper_set_parent(struct ccu_common *common,
195 struct ccu_mux_internal *cm,
196 u8 index)
197 {
198 u32 reg;
199 u32 __cspr;
200
201 if (cm->table)
202 {
203 index = cm->table[index];
204 }
205
206 __cspr = hal_spin_lock_irqsave(&common->lock);
207
208 reg = readl(common->base + common->reg);
209 reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift);
210 writel(reg | (index << cm->shift), common->base + common->reg);
211
212 hal_spin_unlock_irqrestore(&common->lock, __cspr);
213
214 return 0;
215 }
216
ccu_mux_disable(struct clk_hw * hw)217 static void ccu_mux_disable(struct clk_hw *hw)
218 {
219 struct ccu_mux *cm = hw_to_ccu_mux(hw);
220
221 return ccu_gate_helper_disable(&cm->common, cm->enable);
222 }
223
ccu_mux_enable(struct clk_hw * hw)224 static int ccu_mux_enable(struct clk_hw *hw)
225 {
226 struct ccu_mux *cm = hw_to_ccu_mux(hw);
227
228 return ccu_gate_helper_enable(&cm->common, cm->enable);
229 }
230
ccu_mux_is_enabled(struct clk_hw * hw)231 static int ccu_mux_is_enabled(struct clk_hw *hw)
232 {
233 struct ccu_mux *cm = hw_to_ccu_mux(hw);
234
235 return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
236 }
237
ccu_mux_get_parent(struct clk_hw * hw)238 static u8 ccu_mux_get_parent(struct clk_hw *hw)
239 {
240 struct ccu_mux *cm = hw_to_ccu_mux(hw);
241
242 return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
243 }
244
ccu_mux_set_parent(struct clk_hw * hw,u8 index)245 static int ccu_mux_set_parent(struct clk_hw *hw, u8 index)
246 {
247 struct ccu_mux *cm = hw_to_ccu_mux(hw);
248
249 return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
250 }
251
ccu_mux_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)252 static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw,
253 unsigned long parent_rate)
254 {
255 struct ccu_mux *cm = hw_to_ccu_mux(hw);
256
257 return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
258 parent_rate);
259 }
260
261 const struct clk_ops ccu_mux_ops =
262 {
263 .disable = ccu_mux_disable,
264 .enable = ccu_mux_enable,
265 .is_enabled = ccu_mux_is_enabled,
266
267 .get_parent = ccu_mux_get_parent,
268 .set_parent = ccu_mux_set_parent,
269
270 .determine_rate = __clk_mux_determine_rate,
271 .recalc_rate = ccu_mux_recalc_rate,
272 };
273
274