clk: qcom: Properly change rates for ahbix clock
authorStephen Boyd <sboyd@codeaurora.org>
Fri, 6 Mar 2015 23:41:53 +0000 (15:41 -0800)
committerStephen Boyd <sboyd@codeaurora.org>
Thu, 12 Mar 2015 19:20:30 +0000 (12:20 -0700)
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>
drivers/clk/qcom/clk-rcg.c
drivers/clk/qcom/clk-rcg.h
drivers/clk/qcom/lcc-ipq806x.c

index 0039bd7d3965370108bba619abdf40009495e693..466f30ca65c29420370be358ab0337b8f1fd3bec 100644 (file)
@@ -495,6 +495,57 @@ static int clk_rcg_bypass_set_rate(struct clk_hw *hw, unsigned long rate,
        return __clk_rcg_set_rate(rcg, rcg->freq_tbl);
 }
 
+/*
+ * This type of clock has a glitch-free mux that switches between the output of
+ * the M/N counter and an always on clock source (XO). When clk_set_rate() is
+ * called we need to make sure that we don't switch to the M/N counter if it
+ * isn't clocking because the mux will get stuck and the clock will stop
+ * outputting a clock. This can happen if the framework isn't aware that this
+ * clock is on and so clk_set_rate() doesn't turn on the new parent. To fix
+ * this we switch the mux in the enable/disable ops and reprogram the M/N
+ * counter in the set_rate op. We also make sure to switch away from the M/N
+ * counter in set_rate if software thinks the clock is off.
+ */
+static int clk_rcg_lcc_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       struct clk_rcg *rcg = to_clk_rcg(hw);
+       const struct freq_tbl *f;
+       int ret;
+       u32 gfm = BIT(10);
+
+       f = qcom_find_freq(rcg->freq_tbl, rate);
+       if (!f)
+               return -EINVAL;
+
+       /* Switch to XO to avoid glitches */
+       regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, 0);
+       ret = __clk_rcg_set_rate(rcg, f);
+       /* Switch back to M/N if it's clocking */
+       if (__clk_is_enabled(hw->clk))
+               regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, gfm);
+
+       return ret;
+}
+
+static int clk_rcg_lcc_enable(struct clk_hw *hw)
+{
+       struct clk_rcg *rcg = to_clk_rcg(hw);
+       u32 gfm = BIT(10);
+
+       /* Use M/N */
+       return regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, gfm);
+}
+
+static void clk_rcg_lcc_disable(struct clk_hw *hw)
+{
+       struct clk_rcg *rcg = to_clk_rcg(hw);
+       u32 gfm = BIT(10);
+
+       /* Use XO */
+       regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, 0);
+}
+
 static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
 {
        struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
@@ -543,6 +594,17 @@ const struct clk_ops clk_rcg_bypass_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops);
 
+const struct clk_ops clk_rcg_lcc_ops = {
+       .enable = clk_rcg_lcc_enable,
+       .disable = clk_rcg_lcc_disable,
+       .get_parent = clk_rcg_get_parent,
+       .set_parent = clk_rcg_set_parent,
+       .recalc_rate = clk_rcg_recalc_rate,
+       .determine_rate = clk_rcg_determine_rate,
+       .set_rate = clk_rcg_lcc_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_rcg_lcc_ops);
+
 const struct clk_ops clk_dyn_rcg_ops = {
        .enable = clk_enable_regmap,
        .is_enabled = clk_is_enabled_regmap,
index 687e41f91d7c9f90d99eb80acbd98086d237e4e5..d09d06ba278e9af505b5d382d0274624d1379e41 100644 (file)
@@ -96,6 +96,7 @@ struct clk_rcg {
 
 extern const struct clk_ops clk_rcg_ops;
 extern const struct clk_ops clk_rcg_bypass_ops;
+extern const struct clk_ops clk_rcg_lcc_ops;
 
 #define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)
 
index 121ffde25dc3d6ec95d839699bc1f8829b0c003c..5e4a3dafcf634a85a5c3b4b4d3b9c306a93e5dd8 100644 (file)
@@ -386,13 +386,12 @@ static struct clk_rcg ahbix_clk = {
        .freq_tbl = clk_tbl_ahbix,
        .clkr = {
                .enable_reg = 0x38,
-               .enable_mask = BIT(10), /* toggle the gfmux to select mn/pxo */
+               .enable_mask = BIT(11),
                .hw.init = &(struct clk_init_data){
                        .name = "ahbix",
                        .parent_names = lcc_pxo_pll4,
                        .num_parents = 2,
-                       .ops = &clk_rcg_ops,
-                       .flags = CLK_SET_RATE_GATE,
+                       .ops = &clk_rcg_lcc_ops,
                },
        },
 };