diff options
author | Alex Frid <afrid@nvidia.com> | 2014-09-04 00:01:10 -0400 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2015-03-18 15:11:16 -0400 |
commit | 5bb95a3e41be5ceec13653b7c3040f2bbcfc2076 (patch) | |
tree | 8b164bfad33e9f5e93b10fdd730107d526bb7d7c /drivers/gpu/nvgpu | |
parent | 59f00a42e47ccf4aacfac01d1e97b66537a01aed (diff) |
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 <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>
Diffstat (limited to 'drivers/gpu/nvgpu')
-rw-r--r-- | drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 50 |
1 files 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) | |||
302 | return 0; | 302 | return 0; |
303 | } | 303 | } |
304 | 304 | ||
305 | static int clk_change_pldiv_under_bypass(struct gk20a *g, struct pll *gpll) | ||
306 | { | ||
307 | u32 data, coeff; | ||
308 | |||
309 | /* put PLL in bypass before programming it */ | ||
310 | data = gk20a_readl(g, trim_sys_sel_vco_r()); | ||
311 | data = set_field(data, trim_sys_sel_vco_gpc2clk_out_m(), | ||
312 | trim_sys_sel_vco_gpc2clk_out_bypass_f()); | ||
313 | gk20a_writel(g, trim_sys_sel_vco_r(), data); | ||
314 | |||
315 | /* change PLDIV */ | ||
316 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); | ||
317 | udelay(1); | ||
318 | coeff = set_field(coeff, trim_sys_gpcpll_coeff_pldiv_m(), | ||
319 | trim_sys_gpcpll_coeff_pldiv_f(gpll->PL)); | ||
320 | gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); | ||
321 | |||
322 | /* put PLL back on vco */ | ||
323 | data = gk20a_readl(g, trim_sys_sel_vco_r()); | ||
324 | udelay(1); | ||
325 | data = set_field(data, trim_sys_sel_vco_gpc2clk_out_m(), | ||
326 | trim_sys_sel_vco_gpc2clk_out_vco_f()); | ||
327 | gk20a_writel(g, trim_sys_sel_vco_r(), data); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
305 | static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, struct pll *gpll) | 332 | static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, struct pll *gpll) |
306 | { | 333 | { |
307 | u32 data, cfg, coeff, timeout; | 334 | u32 data, cfg, coeff, timeout; |
@@ -313,6 +340,7 @@ static int clk_lock_gpc_pll_under_bypass(struct gk20a *g, struct pll *gpll) | |||
313 | gk20a_writel(g, trim_sys_sel_vco_r(), data); | 340 | gk20a_writel(g, trim_sys_sel_vco_r(), data); |
314 | 341 | ||
315 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); | 342 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); |
343 | udelay(1); | ||
316 | if (trim_sys_gpcpll_cfg_iddq_v(cfg)) { | 344 | if (trim_sys_gpcpll_cfg_iddq_v(cfg)) { |
317 | /* get out from IDDQ (1st power up) */ | 345 | /* get out from IDDQ (1st power up) */ |
318 | cfg = set_field(cfg, trim_sys_gpcpll_cfg_iddq_m(), | 346 | 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) | |||
352 | cfg = set_field(cfg, trim_sys_gpcpll_cfg_enb_lckdet_m(), | 380 | cfg = set_field(cfg, trim_sys_gpcpll_cfg_enb_lckdet_m(), |
353 | trim_sys_gpcpll_cfg_enb_lckdet_power_on_f()); | 381 | trim_sys_gpcpll_cfg_enb_lckdet_power_on_f()); |
354 | gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); | 382 | gk20a_writel(g, trim_sys_gpcpll_cfg_r(), cfg); |
383 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); | ||
355 | } | 384 | } |
356 | 385 | ||
357 | /* wait pll lock */ | 386 | /* wait pll lock */ |
358 | timeout = g->clk.pll_delay / 2 + 1; | 387 | timeout = g->clk.pll_delay + 1; |
359 | do { | 388 | do { |
389 | udelay(1); | ||
360 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); | 390 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); |
361 | if (cfg & trim_sys_gpcpll_cfg_pll_lock_true_f()) | 391 | if (cfg & trim_sys_gpcpll_cfg_pll_lock_true_f()) |
362 | goto pll_locked; | 392 | goto pll_locked; |
363 | udelay(2); | ||
364 | } while (--timeout > 0); | 393 | } while (--timeout > 0); |
365 | 394 | ||
366 | /* PLL is messed up. What can we do here? */ | 395 | /* PLL is messed up. What can we do here? */ |
@@ -390,13 +419,11 @@ pll_locked: | |||
390 | static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | 419 | static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, |
391 | int allow_slide) | 420 | int allow_slide) |
392 | { | 421 | { |
393 | #if PLDIV_GLITCHLESS | 422 | #if !PLDIV_GLITCHLESS |
394 | bool skip_bypass; | ||
395 | #else | ||
396 | u32 data; | 423 | u32 data; |
397 | #endif | 424 | #endif |
398 | u32 cfg, coeff; | 425 | u32 cfg, coeff; |
399 | bool can_slide; | 426 | bool can_slide, pldiv_only; |
400 | struct pll gpll; | 427 | struct pll gpll; |
401 | 428 | ||
402 | gk20a_dbg_fn(""); | 429 | gk20a_dbg_fn(""); |
@@ -427,15 +454,15 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | |||
427 | if (ret) | 454 | if (ret) |
428 | return ret; | 455 | return ret; |
429 | } | 456 | } |
457 | pldiv_only = can_slide && (gpll_new->M == gpll.M); | ||
430 | 458 | ||
431 | #if PLDIV_GLITCHLESS | 459 | #if PLDIV_GLITCHLESS |
432 | /* | 460 | /* |
433 | * Limit either FO-to-FO (path A below) or FO-to-bypass (path B below) | 461 | * Limit either FO-to-FO (path A below) or FO-to-bypass (path B below) |
434 | * jump to min_vco/2 by setting post divider >= 1:2. | 462 | * jump to min_vco/2 by setting post divider >= 1:2. |
435 | */ | 463 | */ |
436 | skip_bypass = can_slide && (gpll_new->M == gpll.M); | ||
437 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); | 464 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); |
438 | if ((skip_bypass && (gpll_new->PL < 2)) || (gpll.PL < 2)) { | 465 | if ((pldiv_only && (gpll_new->PL < 2)) || (gpll.PL < 2)) { |
439 | if (gpll.PL != 2) { | 466 | if (gpll.PL != 2) { |
440 | coeff = set_field(coeff, | 467 | coeff = set_field(coeff, |
441 | trim_sys_gpcpll_coeff_pldiv_m(), | 468 | trim_sys_gpcpll_coeff_pldiv_m(), |
@@ -446,7 +473,7 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | |||
446 | } | 473 | } |
447 | } | 474 | } |
448 | 475 | ||
449 | if (skip_bypass) | 476 | if (pldiv_only) |
450 | goto set_pldiv; /* path A: no need to bypass */ | 477 | goto set_pldiv; /* path A: no need to bypass */ |
451 | 478 | ||
452 | /* path B: bypass if either M changes or PLL is disabled */ | 479 | /* 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, | |||
473 | #if PLDIV_GLITCHLESS | 500 | #if PLDIV_GLITCHLESS |
474 | gpll.PL = (gpll_new->PL < 2) ? 2 : gpll_new->PL; | 501 | gpll.PL = (gpll_new->PL < 2) ? 2 : gpll_new->PL; |
475 | #endif | 502 | #endif |
476 | clk_lock_gpc_pll_under_bypass(g, &gpll); | 503 | if (pldiv_only) |
504 | clk_change_pldiv_under_bypass(g, &gpll); | ||
505 | else | ||
506 | clk_lock_gpc_pll_under_bypass(g, &gpll); | ||
477 | 507 | ||
478 | #if PLDIV_GLITCHLESS | 508 | #if PLDIV_GLITCHLESS |
479 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); | 509 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); |