gpu: nvgpu: Update GM20B GPCPLL bypass operations
Alex Frid [Thu, 4 Sep 2014 04:01:10 +0000 (21:01 -0700)]
- Skipped PLL re-locking if only post-divider is changing under bypass
- Added 1us delay after switch to bypass clock source
- Changed wait for lock under bypass resolution from 2us to 1us

Change-Id: I259581c00c417752263ef3b2ea057200bb78ecbf
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/495473
(cherry picked from commit d90a19b8bf59c608a2a3a891b34ca714dfe990e9)
Reviewed-on: http://git-master/r/499192
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>

drivers/gpu/nvgpu/gm20b/clk_gm20b.c

index 86aa6e8..a1614a7 100644 (file)
@@ -302,6 +302,33 @@ static int clk_slide_gpc_pll(struct gk20a *g, struct pll *gpll)
        return 0;
 }
 
+static int clk_change_pldiv_under_bypass(struct gk20a *g, struct pll *gpll)
+{
+       u32 data, coeff;
+
+       /* put PLL in bypass before programming it */
+       data = gk20a_readl(g, trim_sys_sel_vco_r());
+       data = set_field(data, trim_sys_sel_vco_gpc2clk_out_m(),
+               trim_sys_sel_vco_gpc2clk_out_bypass_f());
+       gk20a_writel(g, trim_sys_sel_vco_r(), data);
+
+       /* change PLDIV */
+       coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
+       udelay(1);
+       coeff = set_field(coeff, trim_sys_gpcpll_coeff_pldiv_m(),
+                         trim_sys_gpcpll_coeff_pldiv_f(gpll->PL));
+       gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff);
+
+       /* put PLL back on vco */
+       data = gk20a_readl(g, trim_sys_sel_vco_r());
+       udelay(1);
+       data = set_field(data, trim_sys_sel_vco_gpc2clk_out_m(),
+               trim_sys_sel_vco_gpc2clk_out_vco_f());
+       gk20a_writel(g, trim_sys_sel_vco_r(), data);
+
+       return 0;
+}
+
 static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, struct pll *gpll)
 {
        u32 data, cfg, coeff, timeout;
@@ -313,6 +340,7 @@ static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, struct pll *gpll)
        gk20a_writel(g, trim_sys_sel_vco_r(), data);
 
        cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
+       udelay(1);
        if (trim_sys_gpcpll_cfg_iddq_v(cfg)) {
                /* get out from IDDQ (1st power up) */
                cfg = set_field(cfg, trim_sys_gpcpll_cfg_iddq_m(),
@@ -352,15 +380,16 @@ static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, struct pll *gpll)
                cfg = set_field(cfg, trim_sys_gpcpll_cfg_enb_lckdet_m(),
                        trim_sys_gpcpll_cfg_enb_lckdet_power_on_f());
                gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg);
+               cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
        }
 
        /* wait pll lock */
-       timeout = g->clk.pll_delay / 2 + 1;
+       timeout = g->clk.pll_delay + 1;
        do {
+               udelay(1);
                cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r());
                if (cfg & trim_sys_gpcpll_cfg_pll_lock_true_f())
                        goto pll_locked;
-               udelay(2);
        } while (--timeout > 0);
 
        /* PLL is messed up. What can we do here? */
@@ -390,13 +419,11 @@ pll_locked:
 static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
                        int allow_slide)
 {
-#if PLDIV_GLITCHLESS
-       bool skip_bypass;
-#else
+#if !PLDIV_GLITCHLESS
        u32 data;
 #endif
        u32 cfg, coeff;
-       bool can_slide;
+       bool can_slide, pldiv_only;
        struct pll gpll;
 
        gk20a_dbg_fn("");
@@ -427,15 +454,15 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
                if (ret)
                        return ret;
        }
+       pldiv_only = can_slide && (gpll_new->M == gpll.M);
 
 #if PLDIV_GLITCHLESS
        /*
         * Limit either FO-to-FO (path A below) or FO-to-bypass (path B below)
         * jump to min_vco/2 by setting post divider >= 1:2.
         */
-       skip_bypass = can_slide && (gpll_new->M == gpll.M);
        coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
-       if ((skip_bypass && (gpll_new->PL < 2)) || (gpll.PL < 2)) {
+       if ((pldiv_only && (gpll_new->PL < 2)) || (gpll.PL < 2)) {
                if (gpll.PL != 2) {
                        coeff = set_field(coeff,
                                trim_sys_gpcpll_coeff_pldiv_m(),
@@ -446,7 +473,7 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
                }
        }
 
-       if (skip_bypass)
+       if (pldiv_only)
                goto set_pldiv; /* path A: no need to bypass */
 
        /* path B: bypass if either M changes or PLL is disabled */
@@ -473,7 +500,10 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
 #if PLDIV_GLITCHLESS
        gpll.PL = (gpll_new->PL < 2) ? 2 : gpll_new->PL;
 #endif
-       clk_lock_gpc_pll_under_bypass(g, &gpll);
+       if (pldiv_only)
+               clk_change_pldiv_under_bypass(g, &gpll);
+       else
+               clk_lock_gpc_pll_under_bypass(g, &gpll);
 
 #if PLDIV_GLITCHLESS
        coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());