clk: qcom: support for dynamic updating the PLL
authorAbhishek Sahu <absahu@codeaurora.org>
Thu, 28 Sep 2017 17:50:45 +0000 (23:20 +0530)
committerStephen Boyd <sboyd@codeaurora.org>
Thu, 14 Dec 2017 00:54:08 +0000 (16:54 -0800)
Some of the Alpha PLLs support dynamic update in which the
frequency can be changed dynamically without turning off the PLL.

This dynamic update requires the following sequence:

 1. Write the desired values to L_VAL and ALPHA_VAL registers
 2. Toggle pll_latch_input from low to high
 3. Wait for pll_ack_latch to transition from low to high
    The new L and alpha values have been latched. It may
    take some time for the PLL to fully settle with these
    new values.
 4. Pull pll_latch_input low

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Taniya Das <tdas@codeaurora.org>
Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/qcom/clk-alpha-pll.c
drivers/clk/qcom/clk-alpha-pll.h

index 38ed1b8..2a4b0c9 100644 (file)
 # define PLL_VOTE_FSM_ENA      BIT(20)
 # define PLL_FSM_ENA           BIT(20)
 # define PLL_VOTE_FSM_RESET    BIT(21)
+# define PLL_UPDATE            BIT(22)
+# define PLL_UPDATE_BYPASS     BIT(23)
 # define PLL_OFFLINE_ACK       BIT(28)
+# define ALPHA_PLL_ACK_LATCH   BIT(29)
 # define PLL_ACTIVE_FLAG       BIT(30)
 # define PLL_LOCK_DET          BIT(31)
 
@@ -130,6 +133,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
 #define wait_for_pll_offline(pll) \
        wait_for_pll(pll, PLL_OFFLINE_ACK, 0, "offline")
 
+#define wait_for_pll_update(pll) \
+       wait_for_pll(pll, PLL_UPDATE, 1, "update")
+
+#define wait_for_pll_update_ack_set(pll) \
+       wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "update_ack_set")
+
+#define wait_for_pll_update_ack_clear(pll) \
+       wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear")
+
 void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
                             const struct alpha_pll_config *config)
 {
@@ -402,8 +414,57 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
        return alpha_pll_calc_rate(prate, l, a, alpha_width);
 }
 
-static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
-                                 unsigned long prate)
+static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll,
+                                     int (*is_enabled)(struct clk_hw *))
+{
+       int ret;
+       u32 mode;
+
+       if (!is_enabled(&pll->clkr.hw) ||
+           !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
+               return 0;
+
+       regmap_read(pll->clkr.regmap, PLL_MODE(pll), &mode);
+
+       /* Latch the input to the PLL */
+       regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE,
+                          PLL_UPDATE);
+
+       /* Wait for 2 reference cycle before checking ACK bit */
+       udelay(1);
+
+       /*
+        * PLL will latch the new L, Alpha and freq control word.
+        * PLL will respond by raising PLL_ACK_LATCH output when new programming
+        * has been latched in and PLL is being updated. When
+        * UPDATE_LOGIC_BYPASS bit is not set, PLL_UPDATE will be cleared
+        * automatically by hardware when PLL_ACK_LATCH is asserted by PLL.
+        */
+       if (mode & PLL_UPDATE_BYPASS) {
+               ret = wait_for_pll_update_ack_set(pll);
+               if (ret)
+                       return ret;
+
+               regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, 0);
+       } else {
+               ret = wait_for_pll_update(pll);
+               if (ret)
+                       return ret;
+       }
+
+       ret = wait_for_pll_update_ack_clear(pll);
+       if (ret)
+               return ret;
+
+       /* Wait for PLL output to stabilize */
+       udelay(10);
+
+       return 0;
+}
+
+static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+                                   unsigned long prate,
+                                   int (*is_enabled)(struct clk_hw *))
 {
        struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
        const struct pll_vco *vco;
@@ -434,7 +495,21 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
        regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
                           PLL_ALPHA_EN, PLL_ALPHA_EN);
 
-       return 0;
+       return clk_alpha_pll_update_latch(pll, is_enabled);
+}
+
+static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long prate)
+{
+       return __clk_alpha_pll_set_rate(hw, rate, prate,
+                                       clk_alpha_pll_is_enabled);
+}
+
+static int clk_alpha_pll_hwfsm_set_rate(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long prate)
+{
+       return __clk_alpha_pll_set_rate(hw, rate, prate,
+                                       clk_alpha_pll_hwfsm_is_enabled);
 }
 
 static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -471,7 +546,7 @@ const struct clk_ops clk_alpha_pll_hwfsm_ops = {
        .is_enabled = clk_alpha_pll_hwfsm_is_enabled,
        .recalc_rate = clk_alpha_pll_recalc_rate,
        .round_rate = clk_alpha_pll_round_rate,
-       .set_rate = clk_alpha_pll_set_rate,
+       .set_rate = clk_alpha_pll_hwfsm_set_rate,
 };
 EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);
 
index c9c1f2f..82fef8f 100644 (file)
@@ -60,6 +60,7 @@ struct clk_alpha_pll {
        size_t num_vco;
 #define SUPPORTS_OFFLINE_REQ   BIT(0)
 #define SUPPORTS_FSM_MODE      BIT(2)
+#define SUPPORTS_DYNAMIC_UPDATE        BIT(3)
        u8 flags;
 
        struct clk_regmap clkr;