diff options
author | Alex Frid <afrid@nvidia.com> | 2014-09-05 21:29:47 -0400 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2015-03-18 15:11:16 -0400 |
commit | efcd608f80028f6c7b2075b07a1112cd944408ac (patch) | |
tree | 48f22ac859eb293485e0744f98ea1bdc549820ca /drivers/gpu/nvgpu/gm20b | |
parent | 5bb95a3e41be5ceec13653b7c3040f2bbcfc2076 (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')
-rw-r--r-- | drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 93 |
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 | */ | ||
87 | static 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: | |||
419 | static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | 437 | static 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 | ||
512 | set_pldiv: | 527 | set_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 | } |