blob: 04577ca6a3081d63a88fd78f68484db91a1bd011 [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;
23 int is_enabled;
24};
25
26/* PRCMU clock operations. */
27
28static int clk_prcmu_prepare(struct clk_hw *hw)
29{
30 struct clk_prcmu *clk = to_clk_prcmu(hw);
31 return prcmu_request_clock(clk->cg_sel, true);
32}
33
34static void clk_prcmu_unprepare(struct clk_hw *hw)
35{
36 struct clk_prcmu *clk = to_clk_prcmu(hw);
37 if (prcmu_request_clock(clk->cg_sel, false))
38 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
39 hw->init->name);
40}
41
42static int clk_prcmu_enable(struct clk_hw *hw)
43{
44 struct clk_prcmu *clk = to_clk_prcmu(hw);
45 clk->is_enabled = 1;
46 return 0;
47}
48
49static void clk_prcmu_disable(struct clk_hw *hw)
50{
51 struct clk_prcmu *clk = to_clk_prcmu(hw);
52 clk->is_enabled = 0;
53}
54
55static int clk_prcmu_is_enabled(struct clk_hw *hw)
56{
57 struct clk_prcmu *clk = to_clk_prcmu(hw);
58 return clk->is_enabled;
59}
60
61static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
62 unsigned long parent_rate)
63{
64 struct clk_prcmu *clk = to_clk_prcmu(hw);
65 return prcmu_clock_rate(clk->cg_sel);
66}
67
68static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
69 unsigned long *parent_rate)
70{
71 struct clk_prcmu *clk = to_clk_prcmu(hw);
72 return prcmu_round_clock_rate(clk->cg_sel, rate);
73}
74
75static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
76 unsigned long parent_rate)
77{
78 struct clk_prcmu *clk = to_clk_prcmu(hw);
79 return prcmu_set_clock_rate(clk->cg_sel, rate);
80}
81
82static int request_ape_opp100(bool enable)
83{
84 static int reqs;
85 int err = 0;
86
87 if (enable) {
88 if (!reqs)
89 err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
90 "clock", 100);
91 if (!err)
92 reqs++;
93 } else {
94 reqs--;
95 if (!reqs)
96 prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
97 "clock");
98 }
99 return err;
100}
101
102static int clk_prcmu_opp_prepare(struct clk_hw *hw)
103{
104 int err;
105 struct clk_prcmu *clk = to_clk_prcmu(hw);
106
107 err = request_ape_opp100(true);
108 if (err) {
109 pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n",
110 __func__, hw->init->name);
111 return err;
112 }
113
114 err = prcmu_request_clock(clk->cg_sel, true);
115 if (err)
116 request_ape_opp100(false);
117
118 return err;
119}
120
121static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
122{
123 struct clk_prcmu *clk = to_clk_prcmu(hw);
124
125 if (prcmu_request_clock(clk->cg_sel, false))
126 goto out_error;
127 if (request_ape_opp100(false))
128 goto out_error;
129 return;
130
131out_error:
132 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
133 hw->init->name);
134}
135
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200136static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
137{
138 int err;
139 struct clk_prcmu *clk = to_clk_prcmu(hw);
140
141 err = prcmu_request_ape_opp_100_voltage(true);
142 if (err) {
143 pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n",
144 __func__, hw->init->name);
145 return err;
146 }
147
148 err = prcmu_request_clock(clk->cg_sel, true);
149 if (err)
150 prcmu_request_ape_opp_100_voltage(false);
151
152 return err;
153}
154
155static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
156{
157 struct clk_prcmu *clk = to_clk_prcmu(hw);
158
159 if (prcmu_request_clock(clk->cg_sel, false))
160 goto out_error;
161 if (prcmu_request_ape_opp_100_voltage(false))
162 goto out_error;
163 return;
164
165out_error:
166 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
167 hw->init->name);
168}
169
Ulf Hansson3b01f872012-08-27 15:45:50 +0200170static struct clk_ops clk_prcmu_scalable_ops = {
171 .prepare = clk_prcmu_prepare,
172 .unprepare = clk_prcmu_unprepare,
173 .enable = clk_prcmu_enable,
174 .disable = clk_prcmu_disable,
175 .is_enabled = clk_prcmu_is_enabled,
176 .recalc_rate = clk_prcmu_recalc_rate,
177 .round_rate = clk_prcmu_round_rate,
178 .set_rate = clk_prcmu_set_rate,
179};
180
181static struct clk_ops clk_prcmu_gate_ops = {
182 .prepare = clk_prcmu_prepare,
183 .unprepare = clk_prcmu_unprepare,
184 .enable = clk_prcmu_enable,
185 .disable = clk_prcmu_disable,
186 .is_enabled = clk_prcmu_is_enabled,
187 .recalc_rate = clk_prcmu_recalc_rate,
188};
189
Ulf Hansson70b1fce2012-08-31 14:21:29 +0200190static struct clk_ops clk_prcmu_rate_ops = {
191 .is_enabled = clk_prcmu_is_enabled,
192 .recalc_rate = clk_prcmu_recalc_rate,
193};
194
Ulf Hansson3b01f872012-08-27 15:45:50 +0200195static struct clk_ops clk_prcmu_opp_gate_ops = {
196 .prepare = clk_prcmu_opp_prepare,
197 .unprepare = clk_prcmu_opp_unprepare,
198 .enable = clk_prcmu_enable,
199 .disable = clk_prcmu_disable,
200 .is_enabled = clk_prcmu_is_enabled,
201 .recalc_rate = clk_prcmu_recalc_rate,
202};
203
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200204static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
205 .prepare = clk_prcmu_opp_volt_prepare,
206 .unprepare = clk_prcmu_opp_volt_unprepare,
207 .enable = clk_prcmu_enable,
208 .disable = clk_prcmu_disable,
209 .is_enabled = clk_prcmu_is_enabled,
210 .recalc_rate = clk_prcmu_recalc_rate,
211 .round_rate = clk_prcmu_round_rate,
212 .set_rate = clk_prcmu_set_rate,
213};
214
Ulf Hansson3b01f872012-08-27 15:45:50 +0200215static struct clk *clk_reg_prcmu(const char *name,
216 const char *parent_name,
217 u8 cg_sel,
218 unsigned long rate,
219 unsigned long flags,
220 struct clk_ops *clk_prcmu_ops)
221{
222 struct clk_prcmu *clk;
223 struct clk_init_data clk_prcmu_init;
224 struct clk *clk_reg;
225
226 if (!name) {
227 pr_err("clk_prcmu: %s invalid arguments passed\n", __func__);
228 return ERR_PTR(-EINVAL);
229 }
230
231 clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL);
232 if (!clk) {
233 pr_err("clk_prcmu: %s could not allocate clk\n", __func__);
234 return ERR_PTR(-ENOMEM);
235 }
236
237 clk->cg_sel = cg_sel;
238 clk->is_enabled = 1;
239 /* "rate" can be used for changing the initial frequency */
240 if (rate)
241 prcmu_set_clock_rate(cg_sel, rate);
242
243 clk_prcmu_init.name = name;
244 clk_prcmu_init.ops = clk_prcmu_ops;
245 clk_prcmu_init.flags = flags;
246 clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
247 clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
248 clk->hw.init = &clk_prcmu_init;
249
250 clk_reg = clk_register(NULL, &clk->hw);
251 if (IS_ERR_OR_NULL(clk_reg))
252 goto free_clk;
253
254 return clk_reg;
255
256free_clk:
257 kfree(clk);
258 pr_err("clk_prcmu: %s failed to register clk\n", __func__);
259 return ERR_PTR(-ENOMEM);
260}
261
262struct clk *clk_reg_prcmu_scalable(const char *name,
263 const char *parent_name,
264 u8 cg_sel,
265 unsigned long rate,
266 unsigned long flags)
267{
268 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
269 &clk_prcmu_scalable_ops);
270}
271
272struct clk *clk_reg_prcmu_gate(const char *name,
273 const char *parent_name,
274 u8 cg_sel,
275 unsigned long flags)
276{
277 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
278 &clk_prcmu_gate_ops);
279}
280
Ulf Hansson70b1fce2012-08-31 14:21:29 +0200281struct clk *clk_reg_prcmu_rate(const char *name,
282 const char *parent_name,
283 u8 cg_sel,
284 unsigned long flags)
285{
286 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
287 &clk_prcmu_rate_ops);
288}
289
Ulf Hansson3b01f872012-08-27 15:45:50 +0200290struct clk *clk_reg_prcmu_opp_gate(const char *name,
291 const char *parent_name,
292 u8 cg_sel,
293 unsigned long flags)
294{
295 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
296 &clk_prcmu_opp_gate_ops);
297}
Ulf Hanssonb0ea0fc2012-09-24 16:43:18 +0200298
299struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
300 const char *parent_name,
301 u8 cg_sel,
302 unsigned long rate,
303 unsigned long flags)
304{
305 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
306 &clk_prcmu_opp_volt_scalable_ops);
307}