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