summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2014-09-05 21:29:47 -0400
committerDan Willemsen <dwillemsen@nvidia.com>2015-03-18 15:11:16 -0400
commitefcd608f80028f6c7b2075b07a1112cd944408ac (patch)
tree48f22ac859eb293485e0744f98ea1bdc549820ca /drivers/gpu/nvgpu/gm20b/clk_gm20b.c
parent5bb95a3e41be5ceec13653b7c3040f2bbcfc2076 (diff)
gpu: nvgpu: Change GM20B post-divider in flight
Restored changing GM20B GPCPLL post-divider in flight with the following limitation: post divider transition is glitch-less only if there is common "1" in binary representation of old and new settings. Transitions that may create glitch are implemented in glitch-less steps with minimum possible interim divider value (for example, 1 <=> 2 transition has interim value 3: 1 <=> 3 <=> 2). Steps allowed for glitch-less transitions may not always have frequency jump at/below VCO min/2 (in the example above 1st step jumps 2/3 of VCOmin). Enabled external linear divider at 1:2 during such steps. Used extra write of the same data when changing GM20b linear divider. Bug 1552225 Change-Id: Ie8fba2fbe44afd34ca68f5f355bd302b7426a632 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/496319 (cherry picked from commit bdd21e0003032fe664bd20f163dbab9942fd1d1d) Reviewed-on: http://git-master/r/499193 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/gm20b/clk_gm20b.c')
-rw-r--r--drivers/gpu/nvgpu/gm20b/clk_gm20b.c93
1 files changed, 57 insertions, 36 deletions
diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
index a1614a7a..2e3d96ac 100644
--- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
+++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c
@@ -77,8 +77,26 @@ static inline u32 div_to_pl(u32 div)
77 return div; 77 return div;
78} 78}
79 79
80/* FIXME: remove after on-silicon testing */ 80#define PLDIV_GLITCHLESS 1
81#define PLDIV_GLITCHLESS 0 81
82#if PLDIV_GLITCHLESS
83/*
84 * Post divider tarnsition is glitchless only if there is common "1" in binary
85 * representation of old and new settings.
86 */
87static u32 get_interim_pldiv(u32 old_pl, u32 new_pl)
88{
89 u32 pl;
90
91 if (old_pl & new_pl)
92 return 0;
93
94 pl = old_pl | BIT(ffs(new_pl) - 1); /* pl never 0 */
95 new_pl |= BIT(ffs(old_pl) - 1);
96
97 return min(pl, new_pl);
98}
99#endif
82 100
83/* Calculate and update M/N/PL as well as pll->freq 101/* Calculate and update M/N/PL as well as pll->freq
84 ref_clk_f = clk_in_f; 102 ref_clk_f = clk_in_f;
@@ -419,10 +437,7 @@ pll_locked:
419static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, 437static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
420 int allow_slide) 438 int allow_slide)
421{ 439{
422#if !PLDIV_GLITCHLESS 440 u32 cfg, coeff, data;
423 u32 data;
424#endif
425 u32 cfg, coeff;
426 bool can_slide, pldiv_only; 441 bool can_slide, pldiv_only;
427 struct pll gpll; 442 struct pll gpll;
428 443
@@ -456,50 +471,51 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
456 } 471 }
457 pldiv_only = can_slide && (gpll_new->M == gpll.M); 472 pldiv_only = can_slide && (gpll_new->M == gpll.M);
458 473
459#if PLDIV_GLITCHLESS
460 /* 474 /*
461 * Limit either FO-to-FO (path A below) or FO-to-bypass (path B below) 475 * Split FO-to-bypass jump in halfs by setting out divider 1:2.
462 * jump to min_vco/2 by setting post divider >= 1:2. 476 * (needed even if PLDIV_GLITCHLESS is set, since 1:1 <=> 1:2 direct
477 * transition is not really glitch-less - see get_interim_pldiv
478 * function header).
463 */ 479 */
480 if ((gpll_new->PL < 2) || (gpll.PL < 2)) {
481 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
482 data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(),
483 trim_sys_gpc2clk_out_vcodiv_f(2));
484 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
485 /* Intentional 2nd write to assure linear divider operation */
486 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
487 gk20a_readl(g, trim_sys_gpc2clk_out_r());
488 udelay(2);
489 }
490
491#if PLDIV_GLITCHLESS
464 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); 492 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
465 if ((pldiv_only && (gpll_new->PL < 2)) || (gpll.PL < 2)) { 493 if (pldiv_only) {
466 if (gpll.PL != 2) { 494 /* Insert interim PLDIV state if necessary */
495 u32 interim_pl = get_interim_pldiv(gpll_new->PL, gpll.PL);
496 if (interim_pl) {
467 coeff = set_field(coeff, 497 coeff = set_field(coeff,
468 trim_sys_gpcpll_coeff_pldiv_m(), 498 trim_sys_gpcpll_coeff_pldiv_m(),
469 trim_sys_gpcpll_coeff_pldiv_f(2)); 499 trim_sys_gpcpll_coeff_pldiv_f(interim_pl));
470 gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); 500 gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff);
471 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); 501 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
472 udelay(2);
473 } 502 }
474 }
475
476 if (pldiv_only)
477 goto set_pldiv; /* path A: no need to bypass */ 503 goto set_pldiv; /* path A: no need to bypass */
504 }
478 505
479 /* path B: bypass if either M changes or PLL is disabled */ 506 /* path B: bypass if either M changes or PLL is disabled */
480#else
481 /* split FO-to-bypass jump in halfs by setting out divider 1:2 */
482 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
483 data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(),
484 trim_sys_gpc2clk_out_vcodiv_f(2));
485 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
486 gk20a_readl(g, trim_sys_gpc2clk_out_r());
487 udelay(2);
488#endif 507#endif
489 /* 508 /*
490 * Program and lock pll under bypass. On exit PLL is out of bypass, 509 * Program and lock pll under bypass. On exit PLL is out of bypass,
491 * enabled, and locked. VCO is at vco_min if sliding is allowed. 510 * enabled, and locked. VCO is at vco_min if sliding is allowed.
492 * Otherwise it is at VCO target (and therefore last slide call below 511 * Otherwise it is at VCO target (and therefore last slide call below
493 * is effectively NOP). PL is preserved (not set to target) of post 512 * is effectively NOP). PL is set to target. Output divider is engaged
494 * divider is glitchless. Otherwise it is at PL target. 513 * at 1:2 if either entry, or exit PL setting is 1:1.
495 */ 514 */
496 gpll = *gpll_new; 515 gpll = *gpll_new;
497 if (allow_slide) 516 if (allow_slide)
498 gpll.N = DIV_ROUND_UP(gpll_new->M * gpc_pll_params.min_vco, 517 gpll.N = DIV_ROUND_UP(gpll_new->M * gpc_pll_params.min_vco,
499 gpll_new->clk_in); 518 gpll_new->clk_in);
500#if PLDIV_GLITCHLESS
501 gpll.PL = (gpll_new->PL < 2) ? 2 : gpll_new->PL;
502#endif
503 if (pldiv_only) 519 if (pldiv_only)
504 clk_change_pldiv_under_bypass(g, &gpll); 520 clk_change_pldiv_under_bypass(g, &gpll);
505 else 521 else
@@ -507,7 +523,6 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
507 523
508#if PLDIV_GLITCHLESS 524#if PLDIV_GLITCHLESS
509 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); 525 coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r());
510 udelay(2);
511 526
512set_pldiv: 527set_pldiv:
513 /* coeff must be current from either path A or B */ 528 /* coeff must be current from either path A or B */
@@ -516,14 +531,20 @@ set_pldiv:
516 trim_sys_gpcpll_coeff_pldiv_f(gpll_new->PL)); 531 trim_sys_gpcpll_coeff_pldiv_f(gpll_new->PL));
517 gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); 532 gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff);
518 } 533 }
519#else 534#endif
520 /* restore out divider 1:1 */ 535 /* restore out divider 1:1 */
521 data = gk20a_readl(g, trim_sys_gpc2clk_out_r()); 536 data = gk20a_readl(g, trim_sys_gpc2clk_out_r());
522 data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(), 537 if ((data & trim_sys_gpc2clk_out_vcodiv_m()) !=
523 trim_sys_gpc2clk_out_vcodiv_by1_f()); 538 trim_sys_gpc2clk_out_vcodiv_by1_f()) {
524 udelay(2); 539 data = set_field(data, trim_sys_gpc2clk_out_vcodiv_m(),
525 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data); 540 trim_sys_gpc2clk_out_vcodiv_by1_f());
526#endif 541 udelay(2);
542 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
543 /* Intentional 2nd write to assure linear divider operation */
544 gk20a_writel(g, trim_sys_gpc2clk_out_r(), data);
545 gk20a_readl(g, trim_sys_gpc2clk_out_r());
546 }
547
527 /* slide up to target NDIV */ 548 /* slide up to target NDIV */
528 return clk_slide_gpc_pll(g, gpll_new); 549 return clk_slide_gpc_pll(g, gpll_new);
529} 550}