From 8c802fc6aec619d2ab9a104b92f1ac7c4f0d963f Mon Sep 17 00:00:00 2001 From: Alex Frid Date: Fri, 25 Jul 2014 17:46:57 -0700 Subject: gpu: nvgpu: Update GM20b GPCPLL locking under bypass Moved GPCPLL locking under bypass procedure into separate function. Added SYNC_MODE control during locking. Bug 1450787 Change-Id: I8dbf9427fbdaf55ea20b6876750b518eb738de1b Signed-off-by: Alex Frid --- drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 144 ++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 56 deletions(-) (limited to 'drivers/gpu/nvgpu/gm20b/clk_gm20b.c') diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c index 7fc4b8fb..6211a2cc 100644 --- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c +++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c @@ -249,76 +249,42 @@ static int clk_slide_gpc_pll(struct gk20a *g, u32 n) return 0; } -static int clk_program_gpc_pll(struct gk20a *g, struct clk_gk20a *clk, - int allow_slide) +static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, u32 m, u32 n, u32 pl) { u32 data, cfg, coeff, timeout; - u32 m, n, pl; - u32 nlo; - - gk20a_dbg_fn(""); - - if (!tegra_platform_is_silicon()) - return 0; - - /* get old coefficients */ - coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); - m = trim_sys_gpcpll_coeff_mdiv_v(coeff); - n = trim_sys_gpcpll_coeff_ndiv_v(coeff); - pl = trim_sys_gpcpll_coeff_pldiv_v(coeff); - - /* do NDIV slide if there is no change in M and PL */ - cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); - if (allow_slide && clk->gpc_pll.M == m && clk->gpc_pll.PL == pl - && trim_sys_gpcpll_cfg_enable_v(cfg)) { - return clk_slide_gpc_pll(g, clk->gpc_pll.N); - } - - /* slide down to NDIV_LO */ - nlo = DIV_ROUND_UP(m * gpc_pll_params.min_vco, clk->gpc_pll.clk_in); - if (allow_slide && trim_sys_gpcpll_cfg_enable_v(cfg)) { - int ret = clk_slide_gpc_pll(g, nlo); - if (ret) - return ret; - } - - /* split FO-to-bypass jump in halfs by setting out divider 1:2 */ - data = gk20a_readl(g, trim_sys_gpc2clk_out_r()); - data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(), - trim_sys_gpc2clk_out_vcodiv_f(2)); - gk20a_writel(g, trim_sys_gpc2clk_out_r(), data); /* 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()); - udelay(2); gk20a_writel(g, trim_sys_sel_vco_r(), data); - /* get out from IDDQ */ cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); 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(), trim_sys_gpcpll_cfg_iddq_power_on_v()); gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); gk20a_readl(g, trim_sys_gpcpll_cfg_r()); - udelay(2); - } + udelay(5); + } else { + /* clear SYNC_MODE before disabling PLL */ + cfg = set_field(cfg, trim_sys_gpcpll_cfg_sync_mode_m(), + trim_sys_gpcpll_cfg_sync_mode_disable_f()); + gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); + gk20a_readl(g, trim_sys_gpcpll_cfg_r()); - /* disable PLL before changing coefficients */ - cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); - cfg = set_field(cfg, trim_sys_gpcpll_cfg_enable_m(), - trim_sys_gpcpll_cfg_enable_no_f()); - gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); - gk20a_readl(g, trim_sys_gpcpll_cfg_r()); + /* disable running PLL before changing coefficients */ + cfg = set_field(cfg, trim_sys_gpcpll_cfg_enable_m(), + trim_sys_gpcpll_cfg_enable_no_f()); + gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); + gk20a_readl(g, trim_sys_gpcpll_cfg_r()); + } /* change coefficients */ - nlo = DIV_ROUND_UP(clk->gpc_pll.M * gpc_pll_params.min_vco, - clk->gpc_pll.clk_in); - coeff = trim_sys_gpcpll_coeff_mdiv_f(clk->gpc_pll.M) | - trim_sys_gpcpll_coeff_ndiv_f(allow_slide ? - nlo : clk->gpc_pll.N) | - trim_sys_gpcpll_coeff_pldiv_f(clk->gpc_pll.PL); + coeff = trim_sys_gpcpll_coeff_mdiv_f(m) | + trim_sys_gpcpll_coeff_ndiv_f(n) | + trim_sys_gpcpll_coeff_pldiv_f(pl); gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); /* enable PLL after changing coefficients */ @@ -336,7 +302,7 @@ static int clk_program_gpc_pll(struct gk20a *g, struct clk_gk20a *clk, } /* wait pll lock */ - timeout = clk->pll_delay / 2 + 1; + timeout = g->clk.pll_delay / 2 + 1; do { cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); if (cfg & trim_sys_gpcpll_cfg_pll_lock_true_f()) @@ -349,11 +315,76 @@ static int clk_program_gpc_pll(struct gk20a *g, struct clk_gk20a *clk, return -EBUSY; pll_locked: + gk20a_dbg_clk("locked config_pll under bypass r=0x%x v=0x%x", + trim_sys_gpcpll_cfg_r(), cfg); + + /* set SYNC_MODE for glitchless switch out of bypass */ + cfg = set_field(cfg, trim_sys_gpcpll_cfg_sync_mode_m(), + trim_sys_gpcpll_cfg_sync_mode_enable_f()); + gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); + gk20a_readl(g, trim_sys_gpcpll_cfg_r()); + /* put PLL back on vco */ 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_vco_f()); gk20a_writel(g, trim_sys_sel_vco_r(), data); + + return 0; +} + +static int clk_program_gpc_pll(struct gk20a *g, struct clk_gk20a *clk, + int allow_slide) +{ + u32 data, cfg, coeff; + u32 m, n, pl; + u32 nlo; + + gk20a_dbg_fn(""); + + if (!tegra_platform_is_silicon()) + return 0; + + /* get old coefficients */ + coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); + m = trim_sys_gpcpll_coeff_mdiv_v(coeff); + n = trim_sys_gpcpll_coeff_ndiv_v(coeff); + pl = trim_sys_gpcpll_coeff_pldiv_v(coeff); + + /* do NDIV slide if there is no change in M and PL */ + cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); + if (allow_slide && clk->gpc_pll.M == m && clk->gpc_pll.PL == pl + && trim_sys_gpcpll_cfg_enable_v(cfg)) { + return clk_slide_gpc_pll(g, clk->gpc_pll.N); + } + + /* slide down to NDIV_LO */ + nlo = DIV_ROUND_UP(m * gpc_pll_params.min_vco, clk->gpc_pll.clk_in); + if (allow_slide && trim_sys_gpcpll_cfg_enable_v(cfg)) { + int ret = clk_slide_gpc_pll(g, nlo); + if (ret) + return ret; + } + + /* split FO-to-bypass jump in halfs by setting out divider 1:2 */ + data = gk20a_readl(g, trim_sys_gpc2clk_out_r()); + data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(), + trim_sys_gpc2clk_out_vcodiv_f(2)); + gk20a_writel(g, trim_sys_gpc2clk_out_r(), data); + gk20a_readl(g, trim_sys_gpc2clk_out_r()); + udelay(2); + + /* + * Program and lock pll under bypass. On exit PLL is out of bypass, + * enabled, and locked. VCO is at vco_min if sliding is allowed. + * Otherwise it is at VCO target (and therefore last slide call below + * is effectively NOP). + */ + m = clk->gpc_pll.M; + nlo = DIV_ROUND_UP(m * gpc_pll_params.min_vco, clk->gpc_pll.clk_in); + n = allow_slide ? nlo : clk->gpc_pll.N; + pl = clk->gpc_pll.PL; + clk_lock_gpc_pll_under_bypass(g, m, n, pl); clk->gpc_pll.enabled = true; /* restore out divider 1:1 */ @@ -731,9 +762,10 @@ static int pll_reg_show(struct seq_file *s, void *data) seq_printf(s, "sel_vco = %s, ", reg ? "vco" : "bypass"); reg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); - seq_printf(s, "cfg = 0x%x : %s : %s\n", reg, - trim_sys_gpcpll_cfg_enable_v(reg) ? "enabled" : "disabled", - trim_sys_gpcpll_cfg_pll_lock_v(reg) ? "locked" : "unlocked"); + seq_printf(s, "cfg = 0x%x : %s : %s : %s\n", reg, + trim_sys_gpcpll_cfg_enable_v(reg) ? "enabled" : "disabled", + trim_sys_gpcpll_cfg_pll_lock_v(reg) ? "locked" : "unlocked", + trim_sys_gpcpll_cfg_sync_mode_v(reg) ? "sync_on" : "sync_off"); reg = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); m = trim_sys_gpcpll_coeff_mdiv_v(reg); -- cgit v1.2.2