From 5bb95a3e41be5ceec13653b7c3040f2bbcfc2076 Mon Sep 17 00:00:00 2001 From: Alex Frid Date: Wed, 3 Sep 2014 21:01:10 -0700 Subject: gpu: nvgpu: Update GM20B GPCPLL bypass operations - 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 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 --- drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 50 +++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c index 86aa6e8b..a1614a7a 100644 --- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c +++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c @@ -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()); -- cgit v1.2.2