clk: qcom: clk-alpha-pll: Add support for Fabia PLL calibration
authorTaniya Das <tdas@codeaurora.org>
Fri, 15 Nov 2019 10:04:58 +0000 (15:34 +0530)
committerStephen Boyd <sboyd@kernel.org>
Tue, 24 Dec 2019 06:30:09 +0000 (22:30 -0800)
In the cases where the PLL is not calibrated the PLL could fail to lock.
Add support for prepare ops which would take care of the same.

Fabia PLL user/test control registers might required to be configured, so
add support for configuring them.

Signed-off-by: Taniya Das <tdas@codeaurora.org>
Link: https://lkml.kernel.org/r/1573812304-24074-3-git-send-email-tdas@codeaurora.org
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/qcom/clk-alpha-pll.c
drivers/clk/qcom/clk-alpha-pll.h

index 46c9a3d..7c2936d 100644 (file)
@@ -1032,6 +1032,25 @@ void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
                regmap_write(regmap, PLL_CONFIG_CTL(pll),
                                                config->config_ctl_val);
 
+       if (config->config_ctl_hi_val)
+               regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
+                                               config->config_ctl_hi_val);
+
+       if (config->user_ctl_val)
+               regmap_write(regmap, PLL_USER_CTL(pll), config->user_ctl_val);
+
+       if (config->user_ctl_hi_val)
+               regmap_write(regmap, PLL_USER_CTL_U(pll),
+                                               config->user_ctl_hi_val);
+
+       if (config->test_ctl_val)
+               regmap_write(regmap, PLL_TEST_CTL(pll),
+                                               config->test_ctl_val);
+
+       if (config->test_ctl_hi_val)
+               regmap_write(regmap, PLL_TEST_CTL_U(pll),
+                                               config->test_ctl_hi_val);
+
        if (config->post_div_mask) {
                mask = config->post_div_mask;
                val = config->post_div_val;
@@ -1170,7 +1189,64 @@ static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate,
        return __clk_alpha_pll_update_latch(pll);
 }
 
+static int alpha_pll_fabia_prepare(struct clk_hw *hw)
+{
+       struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+       const struct pll_vco *vco;
+       struct clk_hw *parent_hw;
+       unsigned long cal_freq, rrate;
+       u32 cal_l, val, alpha_width = pll_alpha_width(pll);
+       u64 a;
+       int ret;
+
+       /* Check if calibration needs to be done i.e. PLL is in reset */
+       ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);
+       if (ret)
+               return ret;
+
+       /* Return early if calibration is not needed. */
+       if (val & PLL_RESET_N)
+               return 0;
+
+       vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw));
+       if (!vco) {
+               pr_err("alpha pll: not in a valid vco range\n");
+               return -EINVAL;
+       }
+
+       cal_freq = DIV_ROUND_CLOSEST((pll->vco_table[0].min_freq +
+                               pll->vco_table[0].max_freq) * 54, 100);
+
+       parent_hw = clk_hw_get_parent(hw);
+       if (!parent_hw)
+               return -EINVAL;
+
+       rrate = alpha_pll_round_rate(cal_freq, clk_hw_get_rate(parent_hw),
+                                       &cal_l, &a, alpha_width);
+       /*
+        * Due to a limited number of bits for fractional rate programming, the
+        * rounded up rate could be marginally higher than the requested rate.
+        */
+       if (rrate > (cal_freq + FABIA_PLL_RATE_MARGIN) || rrate < cal_freq)
+               return -EINVAL;
+
+       /* Setup PLL for calibration frequency */
+       regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), cal_l);
+
+       /* Bringup the PLL at calibration frequency */
+       ret = clk_alpha_pll_enable(hw);
+       if (ret) {
+               pr_err("alpha pll calibration failed\n");
+               return ret;
+       }
+
+       clk_alpha_pll_disable(hw);
+
+       return 0;
+}
+
 const struct clk_ops clk_alpha_pll_fabia_ops = {
+       .prepare = alpha_pll_fabia_prepare,
        .enable = alpha_pll_fabia_enable,
        .disable = alpha_pll_fabia_disable,
        .is_enabled = clk_alpha_pll_is_enabled,
index c28eb1a..fbc1f67 100644 (file)
@@ -94,6 +94,10 @@ struct alpha_pll_config {
        u32 alpha_hi;
        u32 config_ctl_val;
        u32 config_ctl_hi_val;
+       u32 user_ctl_val;
+       u32 user_ctl_hi_val;
+       u32 test_ctl_val;
+       u32 test_ctl_hi_val;
        u32 main_output_mask;
        u32 aux_output_mask;
        u32 aux2_output_mask;