blob: e2d63bc47436d1ee51014a24dc21ebcc1131ca51 [file] [log] [blame]
Ulf Hansson3b01f872012-08-27 15:45:50 +02001/*
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
20struct clk_prcmu {
21 struct clk_hw hw;
22 u8 cg_sel;
Ulf Hansson28509852013-03-12 20:26:05 +010023 int is_prepared;
Ulf Hansson3b01f872012-08-27 15:45:50 +020024 int is_enabled;
Ulf Hansson28509852013-03-12 20:26:05 +010025 int opp_requested;
Ulf Hansson3b01f872012-08-27 15:45:50 +020026};
27
28/* PRCMU clock operations. */
29
30static int clk_prcmu_prepare(struct clk_hw *hw)
31{
Ulf Hansson28509852013-03-12 20:26:05 +010032 int ret;
Ulf Hansson3b01f872012-08-27 15:45:50 +020033 struct clk_prcmu *clk = to_clk_prcmu(hw);
Ulf Hansson28509852013-03-12 20:26:05 +010034
35 ret = prcmu_request_clock(clk->cg_sel, true);
36 if (!ret)
37 clk->is_prepared = 1;
38
Sachin Kamat24c039f2013-10-08 16:47:47 +053039 return ret;
Ulf Hansson3b01f872012-08-27 15:45:50 +020040}
41
42static 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__,
Maxime Coquelinb5489162013-03-26 15:27:15 +010047 __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +010048 else
49 clk->is_prepared = 0;
50}
51
52static int clk_prcmu_is_prepared(struct clk_hw *hw)
53{
54 struct clk_prcmu *clk = to_clk_prcmu(hw);
55 return clk->is_prepared;
Ulf Hansson3b01f872012-08-27 15:45:50 +020056}
57
58static 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
65static 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
71static 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
77static 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
84static 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
91static 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
Ulf Hansson3b01f872012-08-27 15:45:50 +020098static int clk_prcmu_opp_prepare(struct clk_hw *hw)
99{
100 int err;
101 struct clk_prcmu *clk = to_clk_prcmu(hw);
102
Ulf Hansson28509852013-03-12 20:26:05 +0100103 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",
Maxime Coquelinb5489162013-03-26 15:27:15 +0100109 __func__, __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100110 return err;
111 }
112 clk->opp_requested = 1;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200113 }
114
115 err = prcmu_request_clock(clk->cg_sel, true);
Ulf Hansson28509852013-03-12 20:26:05 +0100116 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 }
Ulf Hansson3b01f872012-08-27 15:45:50 +0200122
Ulf Hansson28509852013-03-12 20:26:05 +0100123 clk->is_prepared = 1;
124 return 0;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200125}
126
127static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
128{
129 struct clk_prcmu *clk = to_clk_prcmu(hw);
130
Ulf Hansson28509852013-03-12 20:26:05 +0100131 if (prcmu_request_clock(clk->cg_sel, false)) {
132 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
Maxime Coquelinb5489162013-03-26 15:27:15 +0100133 __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100134 return;
135 }
Ulf Hansson3b01f872012-08-27 15:45:50 +0200136
Ulf Hansson28509852013-03-12 20:26:05 +0100137 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;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200144}
145
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200146static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
147{
148 int err;
149 struct clk_prcmu *clk = to_clk_prcmu(hw);
150
Ulf Hansson28509852013-03-12 20:26:05 +0100151 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",
Maxime Coquelinb5489162013-03-26 15:27:15 +0100155 __func__, __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100156 return err;
157 }
158 clk->opp_requested = 1;
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200159 }
160
161 err = prcmu_request_clock(clk->cg_sel, true);
Ulf Hansson28509852013-03-12 20:26:05 +0100162 if (err) {
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200163 prcmu_request_ape_opp_100_voltage(false);
Ulf Hansson28509852013-03-12 20:26:05 +0100164 clk->opp_requested = 0;
165 return err;
166 }
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200167
Ulf Hansson28509852013-03-12 20:26:05 +0100168 clk->is_prepared = 1;
169 return 0;
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200170}
171
172static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
173{
174 struct clk_prcmu *clk = to_clk_prcmu(hw);
175
Ulf Hansson28509852013-03-12 20:26:05 +0100176 if (prcmu_request_clock(clk->cg_sel, false)) {
177 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
Maxime Coquelinb5489162013-03-26 15:27:15 +0100178 __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100179 return;
180 }
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200181
Ulf Hansson28509852013-03-12 20:26:05 +0100182 if (clk->opp_requested) {
183 prcmu_request_ape_opp_100_voltage(false);
184 clk->opp_requested = 0;
185 }
186
187 clk->is_prepared = 0;
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200188}
189
Ulf Hansson3b01f872012-08-27 15:45:50 +0200190static struct clk_ops clk_prcmu_scalable_ops = {
191 .prepare = clk_prcmu_prepare,
192 .unprepare = clk_prcmu_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100193 .is_prepared = clk_prcmu_is_prepared,
Ulf Hansson3b01f872012-08-27 15:45:50 +0200194 .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
202static struct clk_ops clk_prcmu_gate_ops = {
203 .prepare = clk_prcmu_prepare,
204 .unprepare = clk_prcmu_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100205 .is_prepared = clk_prcmu_is_prepared,
Ulf Hansson3b01f872012-08-27 15:45:50 +0200206 .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
Ulf Hanssona816d252012-10-10 13:42:27 +0200212static 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
Ulf Hansson70b1fce2012-08-31 14:21:29 +0200219static struct clk_ops clk_prcmu_rate_ops = {
220 .is_enabled = clk_prcmu_is_enabled,
221 .recalc_rate = clk_prcmu_recalc_rate,
222};
223
Ulf Hansson3b01f872012-08-27 15:45:50 +0200224static struct clk_ops clk_prcmu_opp_gate_ops = {
225 .prepare = clk_prcmu_opp_prepare,
226 .unprepare = clk_prcmu_opp_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100227 .is_prepared = clk_prcmu_is_prepared,
Ulf Hansson3b01f872012-08-27 15:45:50 +0200228 .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
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200234static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
235 .prepare = clk_prcmu_opp_volt_prepare,
236 .unprepare = clk_prcmu_opp_volt_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100237 .is_prepared = clk_prcmu_is_prepared,
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200238 .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
Ulf Hansson3b01f872012-08-27 15:45:50 +0200246static 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;
Ulf Hansson28509852013-03-12 20:26:05 +0100269 clk->is_prepared = 1;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200270 clk->is_enabled = 1;
Ulf Hansson28509852013-03-12 20:26:05 +0100271 clk->opp_requested = 0;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200272 /* "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
289free_clk:
290 kfree(clk);
291 pr_err("clk_prcmu: %s failed to register clk\n", __func__);
292 return ERR_PTR(-ENOMEM);
293}
294
295struct 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
305struct 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
Ulf Hanssona816d252012-10-10 13:42:27 +0200314struct 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
Ulf Hansson70b1fce2012-08-31 14:21:29 +0200324struct 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
Ulf Hansson3b01f872012-08-27 15:45:50 +0200333struct 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}
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200341
342struct 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}