aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorgi Djakov <georgi.djakov@linaro.org>2015-09-17 12:39:27 -0400
committerStephen Boyd <sboyd@codeaurora.org>2015-09-17 15:35:59 -0400
commitd042877aa7a36e7a5e0bb8c60dcd86e939f205c9 (patch)
treeffebe6f237d2fd9fffda7fcae6bce50fd7a3cfaa
parent93e71695daa654918fbe5b768cd8c5bca677df0c (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.h4
-rw-r--r--drivers/clk/qcom/clk-rcg2.c79
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 */
160struct clk_rcg2 { 160struct 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
171extern const struct clk_ops clk_rcg2_ops; 172extern const struct clk_ops clk_rcg2_ops;
173extern const struct clk_ops clk_rcg2_shared_ops;
172extern const struct clk_ops clk_edp_pixel_ops; 174extern const struct clk_ops clk_edp_pixel_ops;
173extern const struct clk_ops clk_byte_ops; 175extern const struct clk_ops clk_byte_ops;
174extern const struct clk_ops clk_byte2_ops; 176extern 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};
301EXPORT_SYMBOL_GPL(clk_rcg2_ops); 301EXPORT_SYMBOL_GPL(clk_rcg2_ops);
302 302
303static 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
335static 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
349static unsigned long
350clk_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
357static 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
364static 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
372const 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};
380EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops);
381
303struct frac_entry { 382struct frac_entry {
304 int num; 383 int num;
305 int den; 384 int den;