blob: bf63c96acb1a2947ce7e274f2869b51ab8e95550 [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>
Ulf Hansson3b01f872012-08-27 15:45:50 +020011#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
19struct clk_prcmu {
20 struct clk_hw hw;
21 u8 cg_sel;
Ulf Hansson28509852013-03-12 20:26:05 +010022 int is_prepared;
Ulf Hansson3b01f872012-08-27 15:45:50 +020023 int is_enabled;
Ulf Hansson28509852013-03-12 20:26:05 +010024 int opp_requested;
Ulf Hansson3b01f872012-08-27 15:45:50 +020025};
26
27/* PRCMU clock operations. */
28
29static int clk_prcmu_prepare(struct clk_hw *hw)
30{
Ulf Hansson28509852013-03-12 20:26:05 +010031 int ret;
Ulf Hansson3b01f872012-08-27 15:45:50 +020032 struct clk_prcmu *clk = to_clk_prcmu(hw);
Ulf Hansson28509852013-03-12 20:26:05 +010033
34 ret = prcmu_request_clock(clk->cg_sel, true);
35 if (!ret)
36 clk->is_prepared = 1;
37
Sachin Kamat24c039f2013-10-08 16:47:47 +053038 return ret;
Ulf Hansson3b01f872012-08-27 15:45:50 +020039}
40
41static 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__,
Maxime Coquelinb5489162013-03-26 15:27:15 +010046 __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +010047 else
48 clk->is_prepared = 0;
49}
50
51static int clk_prcmu_is_prepared(struct clk_hw *hw)
52{
53 struct clk_prcmu *clk = to_clk_prcmu(hw);
54 return clk->is_prepared;
Ulf Hansson3b01f872012-08-27 15:45:50 +020055}
56
57static 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
64static 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
70static 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
76static 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
83static 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
90static 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
Ulf Hansson3b01f872012-08-27 15:45:50 +020097static int clk_prcmu_opp_prepare(struct clk_hw *hw)
98{
99 int err;
100 struct clk_prcmu *clk = to_clk_prcmu(hw);
101
Ulf Hansson28509852013-03-12 20:26:05 +0100102 if (!clk->opp_requested) {
103 err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
104 (char *)__clk_get_name(hw->clk),
105 100);
106 if (err) {
107 pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
Maxime Coquelinb5489162013-03-26 15:27:15 +0100108 __func__, __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100109 return err;
110 }
111 clk->opp_requested = 1;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200112 }
113
114 err = prcmu_request_clock(clk->cg_sel, true);
Ulf Hansson28509852013-03-12 20:26:05 +0100115 if (err) {
116 prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
117 (char *)__clk_get_name(hw->clk));
118 clk->opp_requested = 0;
119 return err;
120 }
Ulf Hansson3b01f872012-08-27 15:45:50 +0200121
Ulf Hansson28509852013-03-12 20:26:05 +0100122 clk->is_prepared = 1;
123 return 0;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200124}
125
126static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
127{
128 struct clk_prcmu *clk = to_clk_prcmu(hw);
129
Ulf Hansson28509852013-03-12 20:26:05 +0100130 if (prcmu_request_clock(clk->cg_sel, false)) {
131 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
Maxime Coquelinb5489162013-03-26 15:27:15 +0100132 __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100133 return;
134 }
Ulf Hansson3b01f872012-08-27 15:45:50 +0200135
Ulf Hansson28509852013-03-12 20:26:05 +0100136 if (clk->opp_requested) {
137 prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
138 (char *)__clk_get_name(hw->clk));
139 clk->opp_requested = 0;
140 }
141
142 clk->is_prepared = 0;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200143}
144
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200145static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
146{
147 int err;
148 struct clk_prcmu *clk = to_clk_prcmu(hw);
149
Ulf Hansson28509852013-03-12 20:26:05 +0100150 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",
Maxime Coquelinb5489162013-03-26 15:27:15 +0100154 __func__, __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100155 return err;
156 }
157 clk->opp_requested = 1;
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200158 }
159
160 err = prcmu_request_clock(clk->cg_sel, true);
Ulf Hansson28509852013-03-12 20:26:05 +0100161 if (err) {
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200162 prcmu_request_ape_opp_100_voltage(false);
Ulf Hansson28509852013-03-12 20:26:05 +0100163 clk->opp_requested = 0;
164 return err;
165 }
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200166
Ulf Hansson28509852013-03-12 20:26:05 +0100167 clk->is_prepared = 1;
168 return 0;
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200169}
170
171static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
172{
173 struct clk_prcmu *clk = to_clk_prcmu(hw);
174
Ulf Hansson28509852013-03-12 20:26:05 +0100175 if (prcmu_request_clock(clk->cg_sel, false)) {
176 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
Maxime Coquelinb5489162013-03-26 15:27:15 +0100177 __clk_get_name(hw->clk));
Ulf Hansson28509852013-03-12 20:26:05 +0100178 return;
179 }
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200180
Ulf Hansson28509852013-03-12 20:26:05 +0100181 if (clk->opp_requested) {
182 prcmu_request_ape_opp_100_voltage(false);
183 clk->opp_requested = 0;
184 }
185
186 clk->is_prepared = 0;
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200187}
188
Ulf Hansson3b01f872012-08-27 15:45:50 +0200189static struct clk_ops clk_prcmu_scalable_ops = {
190 .prepare = clk_prcmu_prepare,
191 .unprepare = clk_prcmu_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100192 .is_prepared = clk_prcmu_is_prepared,
Ulf Hansson3b01f872012-08-27 15:45:50 +0200193 .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
201static struct clk_ops clk_prcmu_gate_ops = {
202 .prepare = clk_prcmu_prepare,
203 .unprepare = clk_prcmu_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100204 .is_prepared = clk_prcmu_is_prepared,
Ulf Hansson3b01f872012-08-27 15:45:50 +0200205 .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
Ulf Hanssona816d252012-10-10 13:42:27 +0200211static struct clk_ops clk_prcmu_scalable_rate_ops = {
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
Ulf Hansson70b1fce2012-08-31 14:21:29 +0200218static struct clk_ops clk_prcmu_rate_ops = {
219 .is_enabled = clk_prcmu_is_enabled,
220 .recalc_rate = clk_prcmu_recalc_rate,
221};
222
Ulf Hansson3b01f872012-08-27 15:45:50 +0200223static struct clk_ops clk_prcmu_opp_gate_ops = {
224 .prepare = clk_prcmu_opp_prepare,
225 .unprepare = clk_prcmu_opp_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100226 .is_prepared = clk_prcmu_is_prepared,
Ulf Hansson3b01f872012-08-27 15:45:50 +0200227 .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
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200233static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
234 .prepare = clk_prcmu_opp_volt_prepare,
235 .unprepare = clk_prcmu_opp_volt_unprepare,
Ulf Hansson28509852013-03-12 20:26:05 +0100236 .is_prepared = clk_prcmu_is_prepared,
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200237 .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
Ulf Hansson3b01f872012-08-27 15:45:50 +0200245static 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,
250 struct clk_ops *clk_prcmu_ops)
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
261 clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL);
262 if (!clk) {
263 pr_err("clk_prcmu: %s could not allocate clk\n", __func__);
264 return ERR_PTR(-ENOMEM);
265 }
266
267 clk->cg_sel = cg_sel;
Ulf Hansson28509852013-03-12 20:26:05 +0100268 clk->is_prepared = 1;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200269 clk->is_enabled = 1;
Ulf Hansson28509852013-03-12 20:26:05 +0100270 clk->opp_requested = 0;
Ulf Hansson3b01f872012-08-27 15:45:50 +0200271 /* "rate" can be used for changing the initial frequency */
272 if (rate)
273 prcmu_set_clock_rate(cg_sel, rate);
274
275 clk_prcmu_init.name = name;
276 clk_prcmu_init.ops = clk_prcmu_ops;
277 clk_prcmu_init.flags = flags;
278 clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
279 clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
280 clk->hw.init = &clk_prcmu_init;
281
282 clk_reg = clk_register(NULL, &clk->hw);
283 if (IS_ERR_OR_NULL(clk_reg))
284 goto free_clk;
285
286 return clk_reg;
287
288free_clk:
289 kfree(clk);
290 pr_err("clk_prcmu: %s failed to register clk\n", __func__);
291 return ERR_PTR(-ENOMEM);
292}
293
294struct clk *clk_reg_prcmu_scalable(const char *name,
295 const char *parent_name,
296 u8 cg_sel,
297 unsigned long rate,
298 unsigned long flags)
299{
300 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
301 &clk_prcmu_scalable_ops);
302}
303
304struct clk *clk_reg_prcmu_gate(const char *name,
305 const char *parent_name,
306 u8 cg_sel,
307 unsigned long flags)
308{
309 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
310 &clk_prcmu_gate_ops);
311}
312
Ulf Hanssona816d252012-10-10 13:42:27 +0200313struct clk *clk_reg_prcmu_scalable_rate(const char *name,
314 const char *parent_name,
315 u8 cg_sel,
316 unsigned long rate,
317 unsigned long flags)
318{
319 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
320 &clk_prcmu_scalable_rate_ops);
321}
322
Ulf Hansson70b1fce2012-08-31 14:21:29 +0200323struct clk *clk_reg_prcmu_rate(const char *name,
324 const char *parent_name,
325 u8 cg_sel,
326 unsigned long flags)
327{
328 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
329 &clk_prcmu_rate_ops);
330}
331
Ulf Hansson3b01f872012-08-27 15:45:50 +0200332struct clk *clk_reg_prcmu_opp_gate(const char *name,
333 const char *parent_name,
334 u8 cg_sel,
335 unsigned long flags)
336{
337 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
338 &clk_prcmu_opp_gate_ops);
339}
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200340
341struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
342 const char *parent_name,
343 u8 cg_sel,
344 unsigned long rate,
345 unsigned long flags)
346{
347 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
348 &clk_prcmu_opp_volt_scalable_ops);
349}