diff options
author | Georgi Djakov <georgi.djakov@linaro.org> | 2015-09-17 12:39:27 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2015-09-17 15:35:59 -0400 |
commit | d042877aa7a36e7a5e0bb8c60dcd86e939f205c9 (patch) | |
tree | ffebe6f237d2fd9fffda7fcae6bce50fd7a3cfaa | |
parent | 93e71695daa654918fbe5b768cd8c5bca677df0c (diff) |
clk: qcom: Add support for RCGs with shared branches
Some root clock generators may have child branches that are controlled
by different CPUs. These RCGs require some special operations:
- some enable bits have to be toggled when we set the rate;
- if RCG is disabled we only cache the rate and set it later when enabled;
- when the RCG is disabled, the mux is set to the safe source;
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
[sboyd@codeaurora.org: Simplify recalc_rate implementation]
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r-- | drivers/clk/qcom/clk-rcg.h | 4 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-rcg2.c | 79 |
2 files changed, 82 insertions, 1 deletions
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 31f92d70e8e0..5012e5b90cfb 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h | |||
@@ -153,8 +153,8 @@ extern const struct clk_ops clk_dyn_rcg_ops; | |||
153 | * @hid_width: number of bits in half integer divider | 153 | * @hid_width: number of bits in half integer divider |
154 | * @parent_map: map from software's parent index to hardware's src_sel field | 154 | * @parent_map: map from software's parent index to hardware's src_sel field |
155 | * @freq_tbl: frequency table | 155 | * @freq_tbl: frequency table |
156 | * @current_freq: last cached frequency when using branches with shared RCGs | ||
156 | * @clkr: regmap clock handle | 157 | * @clkr: regmap clock handle |
157 | * @lock: register lock | ||
158 | * | 158 | * |
159 | */ | 159 | */ |
160 | struct clk_rcg2 { | 160 | struct clk_rcg2 { |
@@ -163,12 +163,14 @@ struct clk_rcg2 { | |||
163 | u8 hid_width; | 163 | u8 hid_width; |
164 | const struct parent_map *parent_map; | 164 | const struct parent_map *parent_map; |
165 | const struct freq_tbl *freq_tbl; | 165 | const struct freq_tbl *freq_tbl; |
166 | unsigned long current_freq; | ||
166 | struct clk_regmap clkr; | 167 | struct clk_regmap clkr; |
167 | }; | 168 | }; |
168 | 169 | ||
169 | #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) | 170 | #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) |
170 | 171 | ||
171 | extern const struct clk_ops clk_rcg2_ops; | 172 | extern const struct clk_ops clk_rcg2_ops; |
173 | extern const struct clk_ops clk_rcg2_shared_ops; | ||
172 | extern const struct clk_ops clk_edp_pixel_ops; | 174 | extern const struct clk_ops clk_edp_pixel_ops; |
173 | extern const struct clk_ops clk_byte_ops; | 175 | extern const struct clk_ops clk_byte_ops; |
174 | extern const struct clk_ops clk_byte2_ops; | 176 | extern const struct clk_ops clk_byte2_ops; |
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index d941dea6f7c7..b544bb302f79 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c | |||
@@ -300,6 +300,85 @@ const struct clk_ops clk_rcg2_ops = { | |||
300 | }; | 300 | }; |
301 | EXPORT_SYMBOL_GPL(clk_rcg2_ops); | 301 | EXPORT_SYMBOL_GPL(clk_rcg2_ops); |
302 | 302 | ||
303 | static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) | ||
304 | { | ||
305 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
306 | const char *name = clk_hw_get_name(hw); | ||
307 | int ret, count; | ||
308 | |||
309 | /* force enable RCG */ | ||
310 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, | ||
311 | CMD_ROOT_EN, CMD_ROOT_EN); | ||
312 | if (ret) | ||
313 | return ret; | ||
314 | |||
315 | /* wait for RCG to turn ON */ | ||
316 | for (count = 500; count > 0; count--) { | ||
317 | ret = clk_rcg2_is_enabled(hw); | ||
318 | if (ret) | ||
319 | break; | ||
320 | udelay(1); | ||
321 | } | ||
322 | if (!count) | ||
323 | pr_err("%s: RCG did not turn on\n", name); | ||
324 | |||
325 | /* set clock rate */ | ||
326 | ret = __clk_rcg2_set_rate(hw, rate); | ||
327 | if (ret) | ||
328 | return ret; | ||
329 | |||
330 | /* clear force enable RCG */ | ||
331 | return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, | ||
332 | CMD_ROOT_EN, 0); | ||
333 | } | ||
334 | |||
335 | static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate, | ||
336 | unsigned long parent_rate) | ||
337 | { | ||
338 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
339 | |||
340 | /* cache the rate */ | ||
341 | rcg->current_freq = rate; | ||
342 | |||
343 | if (!__clk_is_enabled(hw->clk)) | ||
344 | return 0; | ||
345 | |||
346 | return clk_rcg2_shared_force_enable(hw, rcg->current_freq); | ||
347 | } | ||
348 | |||
349 | static unsigned long | ||
350 | clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | ||
351 | { | ||
352 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
353 | |||
354 | return rcg->current_freq = clk_rcg2_recalc_rate(hw, parent_rate); | ||
355 | } | ||
356 | |||
357 | static int clk_rcg2_shared_enable(struct clk_hw *hw) | ||
358 | { | ||
359 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
360 | |||
361 | return clk_rcg2_shared_force_enable(hw, rcg->current_freq); | ||
362 | } | ||
363 | |||
364 | static void clk_rcg2_shared_disable(struct clk_hw *hw) | ||
365 | { | ||
366 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
367 | |||
368 | /* switch to XO, which is the lowest entry in the freq table */ | ||
369 | clk_rcg2_shared_set_rate(hw, rcg->freq_tbl[0].freq, 0); | ||
370 | } | ||
371 | |||
372 | const struct clk_ops clk_rcg2_shared_ops = { | ||
373 | .enable = clk_rcg2_shared_enable, | ||
374 | .disable = clk_rcg2_shared_disable, | ||
375 | .get_parent = clk_rcg2_get_parent, | ||
376 | .recalc_rate = clk_rcg2_shared_recalc_rate, | ||
377 | .determine_rate = clk_rcg2_determine_rate, | ||
378 | .set_rate = clk_rcg2_shared_set_rate, | ||
379 | }; | ||
380 | EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); | ||
381 | |||
303 | struct frac_entry { | 382 | struct frac_entry { |
304 | int num; | 383 | int num; |
305 | int den; | 384 | int den; |