aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@codeaurora.org>2015-03-06 18:41:53 -0500
committerStephen Boyd <sboyd@codeaurora.org>2015-03-12 15:20:30 -0400
commit9d3745d44a7faa7d24db7facb1949a1378162f3e (patch)
treed98f818f3c4c2011299f803e8b80aeda2f56c648
parent65bd20046f15c9c465df7aa9b86491e56e9c0d25 (diff)
clk: qcom: Properly change rates for ahbix clock
The ahbix clock can never be turned off in practice. To change the rates we need to switch the mux off the M/N counter to an always on source (XO), reprogram the M/N counter to get the rate we want and finally switch back to the M/N counter. Add a new ops structure for this type of clock so that we can set the rate properly. Fixes: c99e515a92e9 "clk: qcom: Add IPQ806X LPASS clock controller (LCC) driver" Tested-by: Kenneth Westfield <kwestfie@codeaurora.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r--drivers/clk/qcom/clk-rcg.c62
-rw-r--r--drivers/clk/qcom/clk-rcg.h1
-rw-r--r--drivers/clk/qcom/lcc-ipq806x.c5
3 files changed, 65 insertions, 3 deletions
diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c
index 0039bd7d3965..466f30ca65c2 100644
--- a/drivers/clk/qcom/clk-rcg.c
+++ b/drivers/clk/qcom/clk-rcg.c
@@ -495,6 +495,57 @@ static int clk_rcg_bypass_set_rate(struct clk_hw *hw, unsigned long rate,
495 return __clk_rcg_set_rate(rcg, rcg->freq_tbl); 495 return __clk_rcg_set_rate(rcg, rcg->freq_tbl);
496} 496}
497 497
498/*
499 * This type of clock has a glitch-free mux that switches between the output of
500 * the M/N counter and an always on clock source (XO). When clk_set_rate() is
501 * called we need to make sure that we don't switch to the M/N counter if it
502 * isn't clocking because the mux will get stuck and the clock will stop
503 * outputting a clock. This can happen if the framework isn't aware that this
504 * clock is on and so clk_set_rate() doesn't turn on the new parent. To fix
505 * this we switch the mux in the enable/disable ops and reprogram the M/N
506 * counter in the set_rate op. We also make sure to switch away from the M/N
507 * counter in set_rate if software thinks the clock is off.
508 */
509static int clk_rcg_lcc_set_rate(struct clk_hw *hw, unsigned long rate,
510 unsigned long parent_rate)
511{
512 struct clk_rcg *rcg = to_clk_rcg(hw);
513 const struct freq_tbl *f;
514 int ret;
515 u32 gfm = BIT(10);
516
517 f = qcom_find_freq(rcg->freq_tbl, rate);
518 if (!f)
519 return -EINVAL;
520
521 /* Switch to XO to avoid glitches */
522 regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, 0);
523 ret = __clk_rcg_set_rate(rcg, f);
524 /* Switch back to M/N if it's clocking */
525 if (__clk_is_enabled(hw->clk))
526 regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, gfm);
527
528 return ret;
529}
530
531static int clk_rcg_lcc_enable(struct clk_hw *hw)
532{
533 struct clk_rcg *rcg = to_clk_rcg(hw);
534 u32 gfm = BIT(10);
535
536 /* Use M/N */
537 return regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, gfm);
538}
539
540static void clk_rcg_lcc_disable(struct clk_hw *hw)
541{
542 struct clk_rcg *rcg = to_clk_rcg(hw);
543 u32 gfm = BIT(10);
544
545 /* Use XO */
546 regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, 0);
547}
548
498static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate) 549static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
499{ 550{
500 struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); 551 struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
@@ -543,6 +594,17 @@ const struct clk_ops clk_rcg_bypass_ops = {
543}; 594};
544EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops); 595EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops);
545 596
597const struct clk_ops clk_rcg_lcc_ops = {
598 .enable = clk_rcg_lcc_enable,
599 .disable = clk_rcg_lcc_disable,
600 .get_parent = clk_rcg_get_parent,
601 .set_parent = clk_rcg_set_parent,
602 .recalc_rate = clk_rcg_recalc_rate,
603 .determine_rate = clk_rcg_determine_rate,
604 .set_rate = clk_rcg_lcc_set_rate,
605};
606EXPORT_SYMBOL_GPL(clk_rcg_lcc_ops);
607
546const struct clk_ops clk_dyn_rcg_ops = { 608const struct clk_ops clk_dyn_rcg_ops = {
547 .enable = clk_enable_regmap, 609 .enable = clk_enable_regmap,
548 .is_enabled = clk_is_enabled_regmap, 610 .is_enabled = clk_is_enabled_regmap,
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 687e41f91d7c..d09d06ba278e 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -96,6 +96,7 @@ struct clk_rcg {
96 96
97extern const struct clk_ops clk_rcg_ops; 97extern const struct clk_ops clk_rcg_ops;
98extern const struct clk_ops clk_rcg_bypass_ops; 98extern const struct clk_ops clk_rcg_bypass_ops;
99extern const struct clk_ops clk_rcg_lcc_ops;
99 100
100#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr) 101#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)
101 102
diff --git a/drivers/clk/qcom/lcc-ipq806x.c b/drivers/clk/qcom/lcc-ipq806x.c
index 121ffde25dc3..5e4a3dafcf63 100644
--- a/drivers/clk/qcom/lcc-ipq806x.c
+++ b/drivers/clk/qcom/lcc-ipq806x.c
@@ -386,13 +386,12 @@ static struct clk_rcg ahbix_clk = {
386 .freq_tbl = clk_tbl_ahbix, 386 .freq_tbl = clk_tbl_ahbix,
387 .clkr = { 387 .clkr = {
388 .enable_reg = 0x38, 388 .enable_reg = 0x38,
389 .enable_mask = BIT(10), /* toggle the gfmux to select mn/pxo */ 389 .enable_mask = BIT(11),
390 .hw.init = &(struct clk_init_data){ 390 .hw.init = &(struct clk_init_data){
391 .name = "ahbix", 391 .name = "ahbix",
392 .parent_names = lcc_pxo_pll4, 392 .parent_names = lcc_pxo_pll4,
393 .num_parents = 2, 393 .num_parents = 2,
394 .ops = &clk_rcg_ops, 394 .ops = &clk_rcg_lcc_ops,
395 .flags = CLK_SET_RATE_GATE,
396 }, 395 },
397 }, 396 },
398}; 397};