Blame view

drivers/clk/ux500/clk-prcmu.c 8.22 KB
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
1
2
3
4
5
6
7
8
9
10
  /*
   * PRCMU clock implementation for ux500 platform.
   *
   * Copyright (C) 2012 ST-Ericsson SA
   * Author: Ulf Hansson <ulf.hansson@linaro.org>
   *
   * License terms: GNU General Public License (GPL) version 2
   */
  
  #include <linux/clk-provider.h>
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
11
12
13
14
15
16
17
18
19
20
21
  #include <linux/mfd/dbx500-prcmu.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  #include <linux/err.h>
  #include "clk.h"
  
  #define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
  
  struct clk_prcmu {
  	struct clk_hw hw;
  	u8 cg_sel;
2850985f7   Ulf Hansson   clk: ux500: Suppo...
22
  	int is_prepared;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
23
  	int is_enabled;
2850985f7   Ulf Hansson   clk: ux500: Suppo...
24
  	int opp_requested;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
25
26
27
28
29
30
  };
  
  /* PRCMU clock operations. */
  
  static int clk_prcmu_prepare(struct clk_hw *hw)
  {
2850985f7   Ulf Hansson   clk: ux500: Suppo...
31
  	int ret;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
32
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
33
34
35
36
  
  	ret = prcmu_request_clock(clk->cg_sel, true);
  	if (!ret)
  		clk->is_prepared = 1;
24c039f6a   Sachin Kamat   clk: ux500: Remov...
37
  	return ret;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
38
39
40
41
42
43
44
45
  }
  
  static void clk_prcmu_unprepare(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	if (prcmu_request_clock(clk->cg_sel, false))
  		pr_err("clk_prcmu: %s failed to disable %s.
  ", __func__,
836ee0f7d   Stephen Boyd   clk: Convert __cl...
46
  			clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
47
48
49
50
51
52
53
54
  	else
  		clk->is_prepared = 0;
  }
  
  static int clk_prcmu_is_prepared(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	return clk->is_prepared;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  }
  
  static int clk_prcmu_enable(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	clk->is_enabled = 1;
  	return 0;
  }
  
  static void clk_prcmu_disable(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	clk->is_enabled = 0;
  }
  
  static int clk_prcmu_is_enabled(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	return clk->is_enabled;
  }
  
  static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
  					   unsigned long parent_rate)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	return prcmu_clock_rate(clk->cg_sel);
  }
  
  static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
  				 unsigned long *parent_rate)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	return prcmu_round_clock_rate(clk->cg_sel, rate);
  }
  
  static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
  			      unsigned long parent_rate)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
  	return prcmu_set_clock_rate(clk->cg_sel, rate);
  }
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
96
97
98
99
  static int clk_prcmu_opp_prepare(struct clk_hw *hw)
  {
  	int err;
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
100
101
  	if (!clk->opp_requested) {
  		err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
836ee0f7d   Stephen Boyd   clk: Convert __cl...
102
  						(char *)clk_hw_get_name(hw),
2850985f7   Ulf Hansson   clk: ux500: Suppo...
103
104
105
106
  						100);
  		if (err) {
  			pr_err("clk_prcmu: %s fail req APE OPP for %s.
  ",
836ee0f7d   Stephen Boyd   clk: Convert __cl...
107
  				__func__, clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
108
109
110
  			return err;
  		}
  		clk->opp_requested = 1;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
111
112
113
  	}
  
  	err = prcmu_request_clock(clk->cg_sel, true);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
114
115
  	if (err) {
  		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
836ee0f7d   Stephen Boyd   clk: Convert __cl...
116
  					(char *)clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
117
118
119
  		clk->opp_requested = 0;
  		return err;
  	}
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
120

2850985f7   Ulf Hansson   clk: ux500: Suppo...
121
122
  	clk->is_prepared = 1;
  	return 0;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
123
124
125
126
127
  }
  
  static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
128
129
130
  	if (prcmu_request_clock(clk->cg_sel, false)) {
  		pr_err("clk_prcmu: %s failed to disable %s.
  ", __func__,
836ee0f7d   Stephen Boyd   clk: Convert __cl...
131
  			clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
132
133
134
135
136
  		return;
  	}
  
  	if (clk->opp_requested) {
  		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
836ee0f7d   Stephen Boyd   clk: Convert __cl...
137
  					(char *)clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
138
139
140
141
  		clk->opp_requested = 0;
  	}
  
  	clk->is_prepared = 0;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
142
  }
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
143
144
145
146
  static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
  {
  	int err;
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
147
148
149
150
151
  	if (!clk->opp_requested) {
  		err = prcmu_request_ape_opp_100_voltage(true);
  		if (err) {
  			pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.
  ",
836ee0f7d   Stephen Boyd   clk: Convert __cl...
152
  				__func__, clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
153
154
155
  			return err;
  		}
  		clk->opp_requested = 1;
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
156
157
158
  	}
  
  	err = prcmu_request_clock(clk->cg_sel, true);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
159
  	if (err) {
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
160
  		prcmu_request_ape_opp_100_voltage(false);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
161
162
163
  		clk->opp_requested = 0;
  		return err;
  	}
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
164

2850985f7   Ulf Hansson   clk: ux500: Suppo...
165
166
  	clk->is_prepared = 1;
  	return 0;
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
167
168
169
170
171
  }
  
  static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
  {
  	struct clk_prcmu *clk = to_clk_prcmu(hw);
2850985f7   Ulf Hansson   clk: ux500: Suppo...
172
173
174
  	if (prcmu_request_clock(clk->cg_sel, false)) {
  		pr_err("clk_prcmu: %s failed to disable %s.
  ", __func__,
836ee0f7d   Stephen Boyd   clk: Convert __cl...
175
  			clk_hw_get_name(hw));
2850985f7   Ulf Hansson   clk: ux500: Suppo...
176
177
178
179
180
181
182
183
184
  		return;
  	}
  
  	if (clk->opp_requested) {
  		prcmu_request_ape_opp_100_voltage(false);
  		clk->opp_requested = 0;
  	}
  
  	clk->is_prepared = 0;
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
185
  }
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
186
187
188
  static struct clk_ops clk_prcmu_scalable_ops = {
  	.prepare = clk_prcmu_prepare,
  	.unprepare = clk_prcmu_unprepare,
2850985f7   Ulf Hansson   clk: ux500: Suppo...
189
  	.is_prepared = clk_prcmu_is_prepared,
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
190
191
192
193
194
195
196
197
198
199
200
  	.enable = clk_prcmu_enable,
  	.disable = clk_prcmu_disable,
  	.is_enabled = clk_prcmu_is_enabled,
  	.recalc_rate = clk_prcmu_recalc_rate,
  	.round_rate = clk_prcmu_round_rate,
  	.set_rate = clk_prcmu_set_rate,
  };
  
  static struct clk_ops clk_prcmu_gate_ops = {
  	.prepare = clk_prcmu_prepare,
  	.unprepare = clk_prcmu_unprepare,
2850985f7   Ulf Hansson   clk: ux500: Suppo...
201
  	.is_prepared = clk_prcmu_is_prepared,
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
202
203
204
205
206
  	.enable = clk_prcmu_enable,
  	.disable = clk_prcmu_disable,
  	.is_enabled = clk_prcmu_is_enabled,
  	.recalc_rate = clk_prcmu_recalc_rate,
  };
a816d250e   Ulf Hansson   clk: ux500: Suppo...
207
208
209
210
211
212
  static struct clk_ops clk_prcmu_scalable_rate_ops = {
  	.is_enabled = clk_prcmu_is_enabled,
  	.recalc_rate = clk_prcmu_recalc_rate,
  	.round_rate = clk_prcmu_round_rate,
  	.set_rate = clk_prcmu_set_rate,
  };
70b1fce2e   Ulf Hansson   clk: ux500: Suppo...
213
214
215
216
  static struct clk_ops clk_prcmu_rate_ops = {
  	.is_enabled = clk_prcmu_is_enabled,
  	.recalc_rate = clk_prcmu_recalc_rate,
  };
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
217
218
219
  static struct clk_ops clk_prcmu_opp_gate_ops = {
  	.prepare = clk_prcmu_opp_prepare,
  	.unprepare = clk_prcmu_opp_unprepare,
2850985f7   Ulf Hansson   clk: ux500: Suppo...
220
  	.is_prepared = clk_prcmu_is_prepared,
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
221
222
223
224
225
  	.enable = clk_prcmu_enable,
  	.disable = clk_prcmu_disable,
  	.is_enabled = clk_prcmu_is_enabled,
  	.recalc_rate = clk_prcmu_recalc_rate,
  };
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
226
227
228
  static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
  	.prepare = clk_prcmu_opp_volt_prepare,
  	.unprepare = clk_prcmu_opp_volt_unprepare,
2850985f7   Ulf Hansson   clk: ux500: Suppo...
229
  	.is_prepared = clk_prcmu_is_prepared,
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
230
231
232
233
234
235
236
  	.enable = clk_prcmu_enable,
  	.disable = clk_prcmu_disable,
  	.is_enabled = clk_prcmu_is_enabled,
  	.recalc_rate = clk_prcmu_recalc_rate,
  	.round_rate = clk_prcmu_round_rate,
  	.set_rate = clk_prcmu_set_rate,
  };
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  static struct clk *clk_reg_prcmu(const char *name,
  				 const char *parent_name,
  				 u8 cg_sel,
  				 unsigned long rate,
  				 unsigned long flags,
  				 struct clk_ops *clk_prcmu_ops)
  {
  	struct clk_prcmu *clk;
  	struct clk_init_data clk_prcmu_init;
  	struct clk *clk_reg;
  
  	if (!name) {
  		pr_err("clk_prcmu: %s invalid arguments passed
  ", __func__);
  		return ERR_PTR(-EINVAL);
  	}
  
  	clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL);
  	if (!clk) {
  		pr_err("clk_prcmu: %s could not allocate clk
  ", __func__);
  		return ERR_PTR(-ENOMEM);
  	}
  
  	clk->cg_sel = cg_sel;
2850985f7   Ulf Hansson   clk: ux500: Suppo...
262
  	clk->is_prepared = 1;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
263
  	clk->is_enabled = 1;
2850985f7   Ulf Hansson   clk: ux500: Suppo...
264
  	clk->opp_requested = 0;
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  	/* "rate" can be used for changing the initial frequency */
  	if (rate)
  		prcmu_set_clock_rate(cg_sel, rate);
  
  	clk_prcmu_init.name = name;
  	clk_prcmu_init.ops = clk_prcmu_ops;
  	clk_prcmu_init.flags = flags;
  	clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
  	clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
  	clk->hw.init = &clk_prcmu_init;
  
  	clk_reg = clk_register(NULL, &clk->hw);
  	if (IS_ERR_OR_NULL(clk_reg))
  		goto free_clk;
  
  	return clk_reg;
  
  free_clk:
  	kfree(clk);
  	pr_err("clk_prcmu: %s failed to register clk
  ", __func__);
  	return ERR_PTR(-ENOMEM);
  }
  
  struct clk *clk_reg_prcmu_scalable(const char *name,
  				   const char *parent_name,
  				   u8 cg_sel,
  				   unsigned long rate,
  				   unsigned long flags)
  {
  	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
  			&clk_prcmu_scalable_ops);
  }
  
  struct clk *clk_reg_prcmu_gate(const char *name,
  			       const char *parent_name,
  			       u8 cg_sel,
  			       unsigned long flags)
  {
  	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
  			&clk_prcmu_gate_ops);
  }
a816d250e   Ulf Hansson   clk: ux500: Suppo...
307
308
309
310
311
312
313
314
315
  struct clk *clk_reg_prcmu_scalable_rate(const char *name,
  					const char *parent_name,
  					u8 cg_sel,
  					unsigned long rate,
  					unsigned long flags)
  {
  	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
  			&clk_prcmu_scalable_rate_ops);
  }
70b1fce2e   Ulf Hansson   clk: ux500: Suppo...
316
317
318
319
320
321
322
323
  struct clk *clk_reg_prcmu_rate(const char *name,
  			       const char *parent_name,
  			       u8 cg_sel,
  			       unsigned long flags)
  {
  	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
  			&clk_prcmu_rate_ops);
  }
3b01f87be   Ulf Hansson   clk: ux500: Adapt...
324
325
326
327
328
329
330
331
  struct clk *clk_reg_prcmu_opp_gate(const char *name,
  				   const char *parent_name,
  				   u8 cg_sel,
  				   unsigned long flags)
  {
  	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
  			&clk_prcmu_opp_gate_ops);
  }
b0ea0fc75   Ulf Hansson   clk: ux500: Suppo...
332
333
334
335
336
337
338
339
340
341
  
  struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
  					    const char *parent_name,
  					    u8 cg_sel,
  					    unsigned long rate,
  					    unsigned long flags)
  {
  	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
  			&clk_prcmu_opp_volt_scalable_ops);
  }