Commit | Line | Data |
---|---|---|
3b01f87b UH |
1 | /* |
2 | * PRCMU clock implementation for ux500 platform. | |
3 | * | |
4 | * Copyright (C) 2012 ST-Ericsson SA | |
5 | * Author: Ulf Hansson <ulf.hansson@linaro.org> | |
6 | * | |
7 | * License terms: GNU General Public License (GPL) version 2 | |
8 | */ | |
9 | ||
10 | #include <linux/clk-provider.h> | |
11 | #include <linux/clk-private.h> | |
12 | #include <linux/mfd/dbx500-prcmu.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/io.h> | |
15 | #include <linux/err.h> | |
16 | #include "clk.h" | |
17 | ||
18 | #define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw) | |
19 | ||
20 | struct clk_prcmu { | |
21 | struct clk_hw hw; | |
22 | u8 cg_sel; | |
2850985f | 23 | int is_prepared; |
3b01f87b | 24 | int is_enabled; |
2850985f | 25 | int opp_requested; |
3b01f87b UH |
26 | }; |
27 | ||
28 | /* PRCMU clock operations. */ | |
29 | ||
30 | static int clk_prcmu_prepare(struct clk_hw *hw) | |
31 | { | |
2850985f | 32 | int ret; |
3b01f87b | 33 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
2850985f UH |
34 | |
35 | ret = prcmu_request_clock(clk->cg_sel, true); | |
36 | if (!ret) | |
37 | clk->is_prepared = 1; | |
38 | ||
39 | return ret;; | |
3b01f87b UH |
40 | } |
41 | ||
42 | static void clk_prcmu_unprepare(struct clk_hw *hw) | |
43 | { | |
44 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
45 | if (prcmu_request_clock(clk->cg_sel, false)) | |
46 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, | |
b5489168 | 47 | __clk_get_name(hw->clk)); |
2850985f UH |
48 | else |
49 | clk->is_prepared = 0; | |
50 | } | |
51 | ||
52 | static int clk_prcmu_is_prepared(struct clk_hw *hw) | |
53 | { | |
54 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
55 | return clk->is_prepared; | |
3b01f87b UH |
56 | } |
57 | ||
58 | static int clk_prcmu_enable(struct clk_hw *hw) | |
59 | { | |
60 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
61 | clk->is_enabled = 1; | |
62 | return 0; | |
63 | } | |
64 | ||
65 | static void clk_prcmu_disable(struct clk_hw *hw) | |
66 | { | |
67 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
68 | clk->is_enabled = 0; | |
69 | } | |
70 | ||
71 | static int clk_prcmu_is_enabled(struct clk_hw *hw) | |
72 | { | |
73 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
74 | return clk->is_enabled; | |
75 | } | |
76 | ||
77 | static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw, | |
78 | unsigned long parent_rate) | |
79 | { | |
80 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
81 | return prcmu_clock_rate(clk->cg_sel); | |
82 | } | |
83 | ||
84 | static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate, | |
85 | unsigned long *parent_rate) | |
86 | { | |
87 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
88 | return prcmu_round_clock_rate(clk->cg_sel, rate); | |
89 | } | |
90 | ||
91 | static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, | |
92 | unsigned long parent_rate) | |
93 | { | |
94 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
95 | return prcmu_set_clock_rate(clk->cg_sel, rate); | |
96 | } | |
97 | ||
3b01f87b UH |
98 | static int clk_prcmu_opp_prepare(struct clk_hw *hw) |
99 | { | |
100 | int err; | |
101 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
102 | ||
2850985f UH |
103 | if (!clk->opp_requested) { |
104 | err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, | |
105 | (char *)__clk_get_name(hw->clk), | |
106 | 100); | |
107 | if (err) { | |
108 | pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", | |
b5489168 | 109 | __func__, __clk_get_name(hw->clk)); |
2850985f UH |
110 | return err; |
111 | } | |
112 | clk->opp_requested = 1; | |
3b01f87b UH |
113 | } |
114 | ||
115 | err = prcmu_request_clock(clk->cg_sel, true); | |
2850985f UH |
116 | if (err) { |
117 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, | |
118 | (char *)__clk_get_name(hw->clk)); | |
119 | clk->opp_requested = 0; | |
120 | return err; | |
121 | } | |
3b01f87b | 122 | |
2850985f UH |
123 | clk->is_prepared = 1; |
124 | return 0; | |
3b01f87b UH |
125 | } |
126 | ||
127 | static void clk_prcmu_opp_unprepare(struct clk_hw *hw) | |
128 | { | |
129 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
130 | ||
2850985f UH |
131 | if (prcmu_request_clock(clk->cg_sel, false)) { |
132 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, | |
b5489168 | 133 | __clk_get_name(hw->clk)); |
2850985f UH |
134 | return; |
135 | } | |
136 | ||
137 | if (clk->opp_requested) { | |
138 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, | |
139 | (char *)__clk_get_name(hw->clk)); | |
140 | clk->opp_requested = 0; | |
141 | } | |
142 | ||
143 | clk->is_prepared = 0; | |
3b01f87b UH |
144 | } |
145 | ||
b0ea0fc7 UH |
146 | static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) |
147 | { | |
148 | int err; | |
149 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
150 | ||
2850985f UH |
151 | if (!clk->opp_requested) { |
152 | err = prcmu_request_ape_opp_100_voltage(true); | |
153 | if (err) { | |
154 | pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", | |
b5489168 | 155 | __func__, __clk_get_name(hw->clk)); |
2850985f UH |
156 | return err; |
157 | } | |
158 | clk->opp_requested = 1; | |
b0ea0fc7 UH |
159 | } |
160 | ||
161 | err = prcmu_request_clock(clk->cg_sel, true); | |
2850985f | 162 | if (err) { |
b0ea0fc7 | 163 | prcmu_request_ape_opp_100_voltage(false); |
2850985f UH |
164 | clk->opp_requested = 0; |
165 | return err; | |
166 | } | |
b0ea0fc7 | 167 | |
2850985f UH |
168 | clk->is_prepared = 1; |
169 | return 0; | |
b0ea0fc7 UH |
170 | } |
171 | ||
172 | static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) | |
173 | { | |
174 | struct clk_prcmu *clk = to_clk_prcmu(hw); | |
175 | ||
2850985f UH |
176 | if (prcmu_request_clock(clk->cg_sel, false)) { |
177 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, | |
b5489168 | 178 | __clk_get_name(hw->clk)); |
2850985f UH |
179 | return; |
180 | } | |
181 | ||
182 | if (clk->opp_requested) { | |
183 | prcmu_request_ape_opp_100_voltage(false); | |
184 | clk->opp_requested = 0; | |
185 | } | |
186 | ||
187 | clk->is_prepared = 0; | |
b0ea0fc7 UH |
188 | } |
189 | ||
3b01f87b UH |
190 | static struct clk_ops clk_prcmu_scalable_ops = { |
191 | .prepare = clk_prcmu_prepare, | |
192 | .unprepare = clk_prcmu_unprepare, | |
2850985f | 193 | .is_prepared = clk_prcmu_is_prepared, |
3b01f87b UH |
194 | .enable = clk_prcmu_enable, |
195 | .disable = clk_prcmu_disable, | |
196 | .is_enabled = clk_prcmu_is_enabled, | |
197 | .recalc_rate = clk_prcmu_recalc_rate, | |
198 | .round_rate = clk_prcmu_round_rate, | |
199 | .set_rate = clk_prcmu_set_rate, | |
200 | }; | |
201 | ||
202 | static struct clk_ops clk_prcmu_gate_ops = { | |
203 | .prepare = clk_prcmu_prepare, | |
204 | .unprepare = clk_prcmu_unprepare, | |
2850985f | 205 | .is_prepared = clk_prcmu_is_prepared, |
3b01f87b UH |
206 | .enable = clk_prcmu_enable, |
207 | .disable = clk_prcmu_disable, | |
208 | .is_enabled = clk_prcmu_is_enabled, | |
209 | .recalc_rate = clk_prcmu_recalc_rate, | |
210 | }; | |
211 | ||
a816d250 UH |
212 | static struct clk_ops clk_prcmu_scalable_rate_ops = { |
213 | .is_enabled = clk_prcmu_is_enabled, | |
214 | .recalc_rate = clk_prcmu_recalc_rate, | |
215 | .round_rate = clk_prcmu_round_rate, | |
216 | .set_rate = clk_prcmu_set_rate, | |
217 | }; | |
218 | ||
70b1fce2 UH |
219 | static struct clk_ops clk_prcmu_rate_ops = { |
220 | .is_enabled = clk_prcmu_is_enabled, | |
221 | .recalc_rate = clk_prcmu_recalc_rate, | |
222 | }; | |
223 | ||
3b01f87b UH |
224 | static struct clk_ops clk_prcmu_opp_gate_ops = { |
225 | .prepare = clk_prcmu_opp_prepare, | |
226 | .unprepare = clk_prcmu_opp_unprepare, | |
2850985f | 227 | .is_prepared = clk_prcmu_is_prepared, |
3b01f87b UH |
228 | .enable = clk_prcmu_enable, |
229 | .disable = clk_prcmu_disable, | |
230 | .is_enabled = clk_prcmu_is_enabled, | |
231 | .recalc_rate = clk_prcmu_recalc_rate, | |
232 | }; | |
233 | ||
b0ea0fc7 UH |
234 | static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { |
235 | .prepare = clk_prcmu_opp_volt_prepare, | |
236 | .unprepare = clk_prcmu_opp_volt_unprepare, | |
2850985f | 237 | .is_prepared = clk_prcmu_is_prepared, |
b0ea0fc7 UH |
238 | .enable = clk_prcmu_enable, |
239 | .disable = clk_prcmu_disable, | |
240 | .is_enabled = clk_prcmu_is_enabled, | |
241 | .recalc_rate = clk_prcmu_recalc_rate, | |
242 | .round_rate = clk_prcmu_round_rate, | |
243 | .set_rate = clk_prcmu_set_rate, | |
244 | }; | |
245 | ||
3b01f87b UH |
246 | static struct clk *clk_reg_prcmu(const char *name, |
247 | const char *parent_name, | |
248 | u8 cg_sel, | |
249 | unsigned long rate, | |
250 | unsigned long flags, | |
251 | struct clk_ops *clk_prcmu_ops) | |
252 | { | |
253 | struct clk_prcmu *clk; | |
254 | struct clk_init_data clk_prcmu_init; | |
255 | struct clk *clk_reg; | |
256 | ||
257 | if (!name) { | |
258 | pr_err("clk_prcmu: %s invalid arguments passed\n", __func__); | |
259 | return ERR_PTR(-EINVAL); | |
260 | } | |
261 | ||
262 | clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL); | |
263 | if (!clk) { | |
264 | pr_err("clk_prcmu: %s could not allocate clk\n", __func__); | |
265 | return ERR_PTR(-ENOMEM); | |
266 | } | |
267 | ||
268 | clk->cg_sel = cg_sel; | |
2850985f | 269 | clk->is_prepared = 1; |
3b01f87b | 270 | clk->is_enabled = 1; |
2850985f | 271 | clk->opp_requested = 0; |
3b01f87b UH |
272 | /* "rate" can be used for changing the initial frequency */ |
273 | if (rate) | |
274 | prcmu_set_clock_rate(cg_sel, rate); | |
275 | ||
276 | clk_prcmu_init.name = name; | |
277 | clk_prcmu_init.ops = clk_prcmu_ops; | |
278 | clk_prcmu_init.flags = flags; | |
279 | clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL); | |
280 | clk_prcmu_init.num_parents = (parent_name ? 1 : 0); | |
281 | clk->hw.init = &clk_prcmu_init; | |
282 | ||
283 | clk_reg = clk_register(NULL, &clk->hw); | |
284 | if (IS_ERR_OR_NULL(clk_reg)) | |
285 | goto free_clk; | |
286 | ||
287 | return clk_reg; | |
288 | ||
289 | free_clk: | |
290 | kfree(clk); | |
291 | pr_err("clk_prcmu: %s failed to register clk\n", __func__); | |
292 | return ERR_PTR(-ENOMEM); | |
293 | } | |
294 | ||
295 | struct clk *clk_reg_prcmu_scalable(const char *name, | |
296 | const char *parent_name, | |
297 | u8 cg_sel, | |
298 | unsigned long rate, | |
299 | unsigned long flags) | |
300 | { | |
301 | return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, | |
302 | &clk_prcmu_scalable_ops); | |
303 | } | |
304 | ||
305 | struct clk *clk_reg_prcmu_gate(const char *name, | |
306 | const char *parent_name, | |
307 | u8 cg_sel, | |
308 | unsigned long flags) | |
309 | { | |
310 | return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, | |
311 | &clk_prcmu_gate_ops); | |
312 | } | |
313 | ||
a816d250 UH |
314 | struct clk *clk_reg_prcmu_scalable_rate(const char *name, |
315 | const char *parent_name, | |
316 | u8 cg_sel, | |
317 | unsigned long rate, | |
318 | unsigned long flags) | |
319 | { | |
320 | return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, | |
321 | &clk_prcmu_scalable_rate_ops); | |
322 | } | |
323 | ||
70b1fce2 UH |
324 | struct clk *clk_reg_prcmu_rate(const char *name, |
325 | const char *parent_name, | |
326 | u8 cg_sel, | |
327 | unsigned long flags) | |
328 | { | |
329 | return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, | |
330 | &clk_prcmu_rate_ops); | |
331 | } | |
332 | ||
3b01f87b UH |
333 | struct clk *clk_reg_prcmu_opp_gate(const char *name, |
334 | const char *parent_name, | |
335 | u8 cg_sel, | |
336 | unsigned long flags) | |
337 | { | |
338 | return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, | |
339 | &clk_prcmu_opp_gate_ops); | |
340 | } | |
b0ea0fc7 UH |
341 | |
342 | struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name, | |
343 | const char *parent_name, | |
344 | u8 cg_sel, | |
345 | unsigned long rate, | |
346 | unsigned long flags) | |
347 | { | |
348 | return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, | |
349 | &clk_prcmu_opp_volt_scalable_ops); | |
350 | } |