1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PRCMU clock implementation for ux500 platform. |
4 | * |
5 | * Copyright (C) 2012 ST-Ericsson SA |
6 | * Author: Ulf Hansson <ulf.hansson@linaro.org> |
7 | */ |
8 | |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/mfd/dbx500-prcmu.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/io.h> |
13 | #include <linux/err.h> |
14 | #include "clk.h" |
15 | |
16 | #define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw) |
17 | #define to_clk_prcmu_clkout(_hw) container_of(_hw, struct clk_prcmu_clkout, hw) |
18 | |
19 | struct clk_prcmu { |
20 | struct clk_hw hw; |
21 | u8 cg_sel; |
22 | int opp_requested; |
23 | }; |
24 | |
25 | struct clk_prcmu_clkout { |
26 | struct clk_hw hw; |
27 | u8 clkout_id; |
28 | u8 source; |
29 | u8 divider; |
30 | }; |
31 | |
32 | /* PRCMU clock operations. */ |
33 | |
34 | static int clk_prcmu_prepare(struct clk_hw *hw) |
35 | { |
36 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
37 | |
38 | return prcmu_request_clock(clock: clk->cg_sel, enable: true); |
39 | } |
40 | |
41 | static void clk_prcmu_unprepare(struct clk_hw *hw) |
42 | { |
43 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
44 | if (prcmu_request_clock(clock: clk->cg_sel, enable: false)) |
45 | pr_err("clk_prcmu: %s failed to disable %s.\n" , __func__, |
46 | clk_hw_get_name(hw)); |
47 | } |
48 | |
49 | static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw, |
50 | unsigned long parent_rate) |
51 | { |
52 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
53 | return prcmu_clock_rate(clock: clk->cg_sel); |
54 | } |
55 | |
56 | static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate, |
57 | unsigned long *parent_rate) |
58 | { |
59 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
60 | return prcmu_round_clock_rate(clock: clk->cg_sel, rate); |
61 | } |
62 | |
63 | static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, |
64 | unsigned long parent_rate) |
65 | { |
66 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
67 | return prcmu_set_clock_rate(clock: clk->cg_sel, rate); |
68 | } |
69 | |
70 | static int clk_prcmu_opp_prepare(struct clk_hw *hw) |
71 | { |
72 | int err; |
73 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
74 | |
75 | if (!clk->opp_requested) { |
76 | err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, |
77 | name: (char *)clk_hw_get_name(hw), |
78 | value: 100); |
79 | if (err) { |
80 | pr_err("clk_prcmu: %s fail req APE OPP for %s.\n" , |
81 | __func__, clk_hw_get_name(hw)); |
82 | return err; |
83 | } |
84 | clk->opp_requested = 1; |
85 | } |
86 | |
87 | err = prcmu_request_clock(clock: clk->cg_sel, enable: true); |
88 | if (err) { |
89 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, |
90 | name: (char *)clk_hw_get_name(hw)); |
91 | clk->opp_requested = 0; |
92 | return err; |
93 | } |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static void clk_prcmu_opp_unprepare(struct clk_hw *hw) |
99 | { |
100 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
101 | |
102 | if (prcmu_request_clock(clock: clk->cg_sel, enable: false)) { |
103 | pr_err("clk_prcmu: %s failed to disable %s.\n" , __func__, |
104 | clk_hw_get_name(hw)); |
105 | return; |
106 | } |
107 | |
108 | if (clk->opp_requested) { |
109 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, |
110 | name: (char *)clk_hw_get_name(hw)); |
111 | clk->opp_requested = 0; |
112 | } |
113 | } |
114 | |
115 | static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) |
116 | { |
117 | int err; |
118 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
119 | |
120 | if (!clk->opp_requested) { |
121 | err = prcmu_request_ape_opp_100_voltage(enable: true); |
122 | if (err) { |
123 | pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n" , |
124 | __func__, clk_hw_get_name(hw)); |
125 | return err; |
126 | } |
127 | clk->opp_requested = 1; |
128 | } |
129 | |
130 | err = prcmu_request_clock(clock: clk->cg_sel, enable: true); |
131 | if (err) { |
132 | prcmu_request_ape_opp_100_voltage(enable: false); |
133 | clk->opp_requested = 0; |
134 | return err; |
135 | } |
136 | |
137 | return 0; |
138 | } |
139 | |
140 | static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) |
141 | { |
142 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
143 | |
144 | if (prcmu_request_clock(clock: clk->cg_sel, enable: false)) { |
145 | pr_err("clk_prcmu: %s failed to disable %s.\n" , __func__, |
146 | clk_hw_get_name(hw)); |
147 | return; |
148 | } |
149 | |
150 | if (clk->opp_requested) { |
151 | prcmu_request_ape_opp_100_voltage(enable: false); |
152 | clk->opp_requested = 0; |
153 | } |
154 | } |
155 | |
156 | static const struct clk_ops clk_prcmu_scalable_ops = { |
157 | .prepare = clk_prcmu_prepare, |
158 | .unprepare = clk_prcmu_unprepare, |
159 | .recalc_rate = clk_prcmu_recalc_rate, |
160 | .round_rate = clk_prcmu_round_rate, |
161 | .set_rate = clk_prcmu_set_rate, |
162 | }; |
163 | |
164 | static const struct clk_ops clk_prcmu_gate_ops = { |
165 | .prepare = clk_prcmu_prepare, |
166 | .unprepare = clk_prcmu_unprepare, |
167 | .recalc_rate = clk_prcmu_recalc_rate, |
168 | }; |
169 | |
170 | static const struct clk_ops clk_prcmu_scalable_rate_ops = { |
171 | .recalc_rate = clk_prcmu_recalc_rate, |
172 | .round_rate = clk_prcmu_round_rate, |
173 | .set_rate = clk_prcmu_set_rate, |
174 | }; |
175 | |
176 | static const struct clk_ops clk_prcmu_rate_ops = { |
177 | .recalc_rate = clk_prcmu_recalc_rate, |
178 | }; |
179 | |
180 | static const struct clk_ops clk_prcmu_opp_gate_ops = { |
181 | .prepare = clk_prcmu_opp_prepare, |
182 | .unprepare = clk_prcmu_opp_unprepare, |
183 | .recalc_rate = clk_prcmu_recalc_rate, |
184 | }; |
185 | |
186 | static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = { |
187 | .prepare = clk_prcmu_opp_volt_prepare, |
188 | .unprepare = clk_prcmu_opp_volt_unprepare, |
189 | .recalc_rate = clk_prcmu_recalc_rate, |
190 | .round_rate = clk_prcmu_round_rate, |
191 | .set_rate = clk_prcmu_set_rate, |
192 | }; |
193 | |
194 | static struct clk_hw *clk_reg_prcmu(const char *name, |
195 | const char *parent_name, |
196 | u8 cg_sel, |
197 | unsigned long rate, |
198 | unsigned long flags, |
199 | const struct clk_ops *clk_prcmu_ops) |
200 | { |
201 | struct clk_prcmu *clk; |
202 | struct clk_init_data clk_prcmu_init; |
203 | int ret; |
204 | |
205 | if (!name) { |
206 | pr_err("clk_prcmu: %s invalid arguments passed\n" , __func__); |
207 | return ERR_PTR(error: -EINVAL); |
208 | } |
209 | |
210 | clk = kzalloc(size: sizeof(*clk), GFP_KERNEL); |
211 | if (!clk) |
212 | return ERR_PTR(error: -ENOMEM); |
213 | |
214 | clk->cg_sel = cg_sel; |
215 | clk->opp_requested = 0; |
216 | /* "rate" can be used for changing the initial frequency */ |
217 | if (rate) |
218 | prcmu_set_clock_rate(clock: cg_sel, rate); |
219 | |
220 | clk_prcmu_init.name = name; |
221 | clk_prcmu_init.ops = clk_prcmu_ops; |
222 | clk_prcmu_init.flags = flags; |
223 | clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL); |
224 | clk_prcmu_init.num_parents = (parent_name ? 1 : 0); |
225 | clk->hw.init = &clk_prcmu_init; |
226 | |
227 | ret = clk_hw_register(NULL, hw: &clk->hw); |
228 | if (ret) |
229 | goto free_clk; |
230 | |
231 | return &clk->hw; |
232 | |
233 | free_clk: |
234 | kfree(objp: clk); |
235 | pr_err("clk_prcmu: %s failed to register clk\n" , __func__); |
236 | return ERR_PTR(error: -ENOMEM); |
237 | } |
238 | |
239 | struct clk_hw *clk_reg_prcmu_scalable(const char *name, |
240 | const char *parent_name, |
241 | u8 cg_sel, |
242 | unsigned long rate, |
243 | unsigned long flags) |
244 | { |
245 | return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, |
246 | clk_prcmu_ops: &clk_prcmu_scalable_ops); |
247 | } |
248 | |
249 | struct clk_hw *clk_reg_prcmu_gate(const char *name, |
250 | const char *parent_name, |
251 | u8 cg_sel, |
252 | unsigned long flags) |
253 | { |
254 | return clk_reg_prcmu(name, parent_name, cg_sel, rate: 0, flags, |
255 | clk_prcmu_ops: &clk_prcmu_gate_ops); |
256 | } |
257 | |
258 | struct clk_hw *clk_reg_prcmu_scalable_rate(const char *name, |
259 | const char *parent_name, |
260 | u8 cg_sel, |
261 | unsigned long rate, |
262 | unsigned long flags) |
263 | { |
264 | return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, |
265 | clk_prcmu_ops: &clk_prcmu_scalable_rate_ops); |
266 | } |
267 | |
268 | struct clk_hw *clk_reg_prcmu_rate(const char *name, |
269 | const char *parent_name, |
270 | u8 cg_sel, |
271 | unsigned long flags) |
272 | { |
273 | return clk_reg_prcmu(name, parent_name, cg_sel, rate: 0, flags, |
274 | clk_prcmu_ops: &clk_prcmu_rate_ops); |
275 | } |
276 | |
277 | struct clk_hw *clk_reg_prcmu_opp_gate(const char *name, |
278 | const char *parent_name, |
279 | u8 cg_sel, |
280 | unsigned long flags) |
281 | { |
282 | return clk_reg_prcmu(name, parent_name, cg_sel, rate: 0, flags, |
283 | clk_prcmu_ops: &clk_prcmu_opp_gate_ops); |
284 | } |
285 | |
286 | struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name, |
287 | const char *parent_name, |
288 | u8 cg_sel, |
289 | unsigned long rate, |
290 | unsigned long flags) |
291 | { |
292 | return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, |
293 | clk_prcmu_ops: &clk_prcmu_opp_volt_scalable_ops); |
294 | } |
295 | |
296 | /* The clkout (external) clock is special and need special ops */ |
297 | |
298 | static int clk_prcmu_clkout_prepare(struct clk_hw *hw) |
299 | { |
300 | struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); |
301 | |
302 | return prcmu_config_clkout(clk->clkout_id, clk->source, clk->divider); |
303 | } |
304 | |
305 | static void clk_prcmu_clkout_unprepare(struct clk_hw *hw) |
306 | { |
307 | struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); |
308 | int ret; |
309 | |
310 | /* The clkout clock is disabled by dividing by 0 */ |
311 | ret = prcmu_config_clkout(clk->clkout_id, clk->source, 0); |
312 | if (ret) |
313 | pr_err("clk_prcmu: %s failed to disable %s\n" , __func__, |
314 | clk_hw_get_name(hw)); |
315 | } |
316 | |
317 | static unsigned long clk_prcmu_clkout_recalc_rate(struct clk_hw *hw, |
318 | unsigned long parent_rate) |
319 | { |
320 | struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); |
321 | |
322 | return (parent_rate / clk->divider); |
323 | } |
324 | |
325 | static u8 clk_prcmu_clkout_get_parent(struct clk_hw *hw) |
326 | { |
327 | struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); |
328 | |
329 | return clk->source; |
330 | } |
331 | |
332 | static int clk_prcmu_clkout_set_parent(struct clk_hw *hw, u8 index) |
333 | { |
334 | struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); |
335 | |
336 | clk->source = index; |
337 | /* Make sure the change reaches the hardware immediately */ |
338 | if (clk_hw_is_prepared(hw)) |
339 | return clk_prcmu_clkout_prepare(hw); |
340 | return 0; |
341 | } |
342 | |
343 | static const struct clk_ops clk_prcmu_clkout_ops = { |
344 | .prepare = clk_prcmu_clkout_prepare, |
345 | .unprepare = clk_prcmu_clkout_unprepare, |
346 | .recalc_rate = clk_prcmu_clkout_recalc_rate, |
347 | .determine_rate = clk_hw_determine_rate_no_reparent, |
348 | .get_parent = clk_prcmu_clkout_get_parent, |
349 | .set_parent = clk_prcmu_clkout_set_parent, |
350 | }; |
351 | |
352 | struct clk_hw *clk_reg_prcmu_clkout(const char *name, |
353 | const char * const *parent_names, |
354 | int num_parents, |
355 | u8 source, u8 divider) |
356 | |
357 | { |
358 | struct clk_prcmu_clkout *clk; |
359 | struct clk_init_data clk_prcmu_clkout_init; |
360 | u8 clkout_id; |
361 | int ret; |
362 | |
363 | if (!name) { |
364 | pr_err("clk_prcmu_clkout: %s invalid arguments passed\n" , __func__); |
365 | return ERR_PTR(error: -EINVAL); |
366 | } |
367 | |
368 | if (!strcmp(name, "clkout1" )) |
369 | clkout_id = 0; |
370 | else if (!strcmp(name, "clkout2" )) |
371 | clkout_id = 1; |
372 | else { |
373 | pr_err("clk_prcmu_clkout: %s bad clock name\n" , __func__); |
374 | return ERR_PTR(error: -EINVAL); |
375 | } |
376 | |
377 | clk = kzalloc(size: sizeof(*clk), GFP_KERNEL); |
378 | if (!clk) |
379 | return ERR_PTR(error: -ENOMEM); |
380 | |
381 | clk->clkout_id = clkout_id; |
382 | clk->source = source; |
383 | clk->divider = divider; |
384 | |
385 | clk_prcmu_clkout_init.name = name; |
386 | clk_prcmu_clkout_init.ops = &clk_prcmu_clkout_ops; |
387 | clk_prcmu_clkout_init.flags = CLK_GET_RATE_NOCACHE; |
388 | clk_prcmu_clkout_init.parent_names = parent_names; |
389 | clk_prcmu_clkout_init.num_parents = num_parents; |
390 | clk->hw.init = &clk_prcmu_clkout_init; |
391 | |
392 | ret = clk_hw_register(NULL, hw: &clk->hw); |
393 | if (ret) |
394 | goto free_clkout; |
395 | |
396 | return &clk->hw; |
397 | free_clkout: |
398 | kfree(objp: clk); |
399 | pr_err("clk_prcmu_clkout: %s failed to register clk\n" , __func__); |
400 | return ERR_PTR(error: -ENOMEM); |
401 | } |
402 | |