diff options
Diffstat (limited to 'drivers/gpu/nvgpu')
-rw-r--r-- | drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 75 |
1 files changed, 64 insertions, 11 deletions
diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c index 6f7d04dd..adf5136a 100644 --- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c +++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c | |||
@@ -56,6 +56,7 @@ static struct pll_parms gpc_pll_params = { | |||
56 | #ifdef CONFIG_DEBUG_FS | 56 | #ifdef CONFIG_DEBUG_FS |
57 | static int clk_gm20b_debugfs_init(struct gk20a *g); | 57 | static int clk_gm20b_debugfs_init(struct gk20a *g); |
58 | #endif | 58 | #endif |
59 | static void clk_setup_slide(struct gk20a *g, u32 clk_u); | ||
59 | 60 | ||
60 | #define DUMP_REG(addr_func) \ | 61 | #define DUMP_REG(addr_func) \ |
61 | do { \ | 62 | do { \ |
@@ -433,6 +434,12 @@ static int clk_enbale_pll_dvfs(struct gk20a *g) | |||
433 | gk20a_readl(g, trim_sys_gpcpll_cfg_r()); | 434 | gk20a_readl(g, trim_sys_gpcpll_cfg_r()); |
434 | udelay(delay); | 435 | udelay(delay); |
435 | 436 | ||
437 | /* | ||
438 | * Dynamic ramp setup based on update rate, which in DVFS mode on GM20b | ||
439 | * is always 38.4 MHz, the same as reference clock rate. | ||
440 | */ | ||
441 | clk_setup_slide(g, g->clk.gpc_pll.clk_in); | ||
442 | |||
436 | if (calibrated) | 443 | if (calibrated) |
437 | return 0; | 444 | return 0; |
438 | 445 | ||
@@ -506,7 +513,7 @@ static void clk_setup_slide(struct gk20a *g, u32 clk_u) | |||
506 | static int clk_slide_gpc_pll(struct gk20a *g, struct pll *gpll) | 513 | static int clk_slide_gpc_pll(struct gk20a *g, struct pll *gpll) |
507 | { | 514 | { |
508 | u32 data, coeff; | 515 | u32 data, coeff; |
509 | u32 nold; | 516 | u32 nold, sdm_old; |
510 | int ramp_timeout = 500; | 517 | int ramp_timeout = 500; |
511 | 518 | ||
512 | /* get old coefficients */ | 519 | /* get old coefficients */ |
@@ -514,11 +521,20 @@ static int clk_slide_gpc_pll(struct gk20a *g, struct pll *gpll) | |||
514 | nold = trim_sys_gpcpll_coeff_ndiv_v(coeff); | 521 | nold = trim_sys_gpcpll_coeff_ndiv_v(coeff); |
515 | 522 | ||
516 | /* do nothing if NDIV is same */ | 523 | /* do nothing if NDIV is same */ |
517 | if (gpll->N == nold) | 524 | if (gpll->mode == GPC_PLL_MODE_DVFS) { |
518 | return 0; | 525 | /* in DVFS mode check both integer and fraction */ |
526 | coeff = gk20a_readl(g, trim_sys_gpcpll_cfg2_r()); | ||
527 | sdm_old = trim_sys_gpcpll_cfg2_sdm_din_v(coeff); | ||
528 | if ((gpll->dvfs.n_int == nold) && | ||
529 | (gpll->dvfs.sdm_din == sdm_old)) | ||
530 | return 0; | ||
531 | } else { | ||
532 | if (gpll->N == nold) | ||
533 | return 0; | ||
519 | 534 | ||
520 | /* dynamic ramp setup based on update rate */ | 535 | /* dynamic ramp setup based on update rate */ |
521 | clk_setup_slide(g, gpll->clk_in / gpll->M); | 536 | clk_setup_slide(g, gpll->clk_in / gpll->M); |
537 | } | ||
522 | 538 | ||
523 | /* pll slowdown mode */ | 539 | /* pll slowdown mode */ |
524 | data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r()); | 540 | data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r()); |
@@ -528,11 +544,25 @@ static int clk_slide_gpc_pll(struct gk20a *g, struct pll *gpll) | |||
528 | gk20a_writel(g, trim_sys_gpcpll_ndiv_slowdown_r(), data); | 544 | gk20a_writel(g, trim_sys_gpcpll_ndiv_slowdown_r(), data); |
529 | 545 | ||
530 | /* new ndiv ready for ramp */ | 546 | /* new ndiv ready for ramp */ |
531 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); | 547 | if (gpll->mode == GPC_PLL_MODE_DVFS) { |
532 | coeff = set_field(coeff, trim_sys_gpcpll_coeff_ndiv_m(), | 548 | /* in DVFS mode SDM is updated via "new" field */ |
533 | trim_sys_gpcpll_coeff_ndiv_f(gpll->N)); | 549 | coeff = gk20a_readl(g, trim_sys_gpcpll_cfg2_r()); |
534 | udelay(1); | 550 | coeff = set_field(coeff, trim_sys_gpcpll_cfg2_sdm_din_new_m(), |
535 | gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); | 551 | trim_sys_gpcpll_cfg2_sdm_din_new_f(gpll->dvfs.sdm_din)); |
552 | gk20a_writel(g, trim_sys_gpcpll_cfg2_r(), coeff); | ||
553 | |||
554 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); | ||
555 | coeff = set_field(coeff, trim_sys_gpcpll_coeff_ndiv_m(), | ||
556 | trim_sys_gpcpll_coeff_ndiv_f(gpll->dvfs.n_int)); | ||
557 | udelay(1); | ||
558 | gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); | ||
559 | } else { | ||
560 | coeff = gk20a_readl(g, trim_sys_gpcpll_coeff_r()); | ||
561 | coeff = set_field(coeff, trim_sys_gpcpll_coeff_ndiv_m(), | ||
562 | trim_sys_gpcpll_coeff_ndiv_f(gpll->N)); | ||
563 | udelay(1); | ||
564 | gk20a_writel(g, trim_sys_gpcpll_coeff_r(), coeff); | ||
565 | } | ||
536 | 566 | ||
537 | /* dynamic ramp to new ndiv */ | 567 | /* dynamic ramp to new ndiv */ |
538 | data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r()); | 568 | data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r()); |
@@ -551,6 +581,14 @@ static int clk_slide_gpc_pll(struct gk20a *g, struct pll *gpll) | |||
551 | break; | 581 | break; |
552 | } while (ramp_timeout > 0); | 582 | } while (ramp_timeout > 0); |
553 | 583 | ||
584 | if ((gpll->mode == GPC_PLL_MODE_DVFS) && (ramp_timeout > 0)) { | ||
585 | /* in DVFS mode complete SDM update */ | ||
586 | coeff = gk20a_readl(g, trim_sys_gpcpll_cfg2_r()); | ||
587 | coeff = set_field(coeff, trim_sys_gpcpll_cfg2_sdm_din_m(), | ||
588 | trim_sys_gpcpll_cfg2_sdm_din_f(gpll->dvfs.sdm_din)); | ||
589 | gk20a_writel(g, trim_sys_gpcpll_cfg2_r(), coeff); | ||
590 | } | ||
591 | |||
554 | /* exit slowdown mode */ | 592 | /* exit slowdown mode */ |
555 | data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r()); | 593 | data = gk20a_readl(g, trim_sys_gpcpll_ndiv_slowdown_r()); |
556 | data = set_field(data, | 594 | data = set_field(data, |
@@ -725,6 +763,10 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | |||
725 | gpll.PL = trim_sys_gpcpll_coeff_pldiv_v(coeff); | 763 | gpll.PL = trim_sys_gpcpll_coeff_pldiv_v(coeff); |
726 | gpll.clk_in = gpll_new->clk_in; | 764 | gpll.clk_in = gpll_new->clk_in; |
727 | 765 | ||
766 | /* combine target dvfs with old coefficients */ | ||
767 | gpll.dvfs = gpll_new->dvfs; | ||
768 | gpll.mode = gpll_new->mode; | ||
769 | |||
728 | /* do NDIV slide if there is no change in M and PL */ | 770 | /* do NDIV slide if there is no change in M and PL */ |
729 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); | 771 | cfg = gk20a_readl(g, trim_sys_gpcpll_cfg_r()); |
730 | can_slide = allow_slide && trim_sys_gpcpll_cfg_enable_v(cfg); | 772 | can_slide = allow_slide && trim_sys_gpcpll_cfg_enable_v(cfg); |
@@ -737,6 +779,8 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | |||
737 | int ret; | 779 | int ret; |
738 | gpll.N = DIV_ROUND_UP(gpll.M * gpc_pll_params.min_vco, | 780 | gpll.N = DIV_ROUND_UP(gpll.M * gpc_pll_params.min_vco, |
739 | gpll.clk_in); | 781 | gpll.clk_in); |
782 | if (gpll.mode == GPC_PLL_MODE_DVFS) | ||
783 | clk_config_dvfs_ndiv(gpll.dvfs.mv, gpll.N, &gpll.dvfs); | ||
740 | ret = clk_slide_gpc_pll(g, &gpll); | 784 | ret = clk_slide_gpc_pll(g, &gpll); |
741 | if (ret) | 785 | if (ret) |
742 | return ret; | 786 | return ret; |
@@ -785,9 +829,12 @@ static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new, | |||
785 | * at 1:2 if either entry, or exit PL setting is 1:1. | 829 | * at 1:2 if either entry, or exit PL setting is 1:1. |
786 | */ | 830 | */ |
787 | gpll = *gpll_new; | 831 | gpll = *gpll_new; |
788 | if (allow_slide) | 832 | if (allow_slide) { |
789 | gpll.N = DIV_ROUND_UP(gpll_new->M * gpc_pll_params.min_vco, | 833 | gpll.N = DIV_ROUND_UP(gpll_new->M * gpc_pll_params.min_vco, |
790 | gpll_new->clk_in); | 834 | gpll_new->clk_in); |
835 | if (gpll.mode == GPC_PLL_MODE_DVFS) | ||
836 | clk_config_dvfs_ndiv(gpll.dvfs.mv, gpll.N, &gpll.dvfs); | ||
837 | } | ||
791 | if (pldiv_only) | 838 | if (pldiv_only) |
792 | clk_change_pldiv_under_bypass(g, &gpll); | 839 | clk_change_pldiv_under_bypass(g, &gpll); |
793 | else | 840 | else |
@@ -827,6 +874,9 @@ static int clk_program_na_gpc_pll(struct gk20a *g, struct pll *gpll_new, | |||
827 | { | 874 | { |
828 | clk_config_dvfs(g, gpll_new); | 875 | clk_config_dvfs(g, gpll_new); |
829 | 876 | ||
877 | if (!gpll_new->enabled) | ||
878 | return clk_program_gpc_pll(g, gpll_new, allow_slide); | ||
879 | |||
830 | /* always under bypass, for now */ | 880 | /* always under bypass, for now */ |
831 | return clk_program_gpc_pll(g, gpll_new, 0); | 881 | return clk_program_gpc_pll(g, gpll_new, 0); |
832 | } | 882 | } |
@@ -844,6 +894,8 @@ static int clk_disable_gpcpll(struct gk20a *g, int allow_slide) | |||
844 | gpll.M = trim_sys_gpcpll_coeff_mdiv_v(coeff); | 894 | gpll.M = trim_sys_gpcpll_coeff_mdiv_v(coeff); |
845 | gpll.N = DIV_ROUND_UP(gpll.M * gpc_pll_params.min_vco, | 895 | gpll.N = DIV_ROUND_UP(gpll.M * gpc_pll_params.min_vco, |
846 | gpll.clk_in); | 896 | gpll.clk_in); |
897 | if (gpll.mode == GPC_PLL_MODE_DVFS) | ||
898 | clk_config_dvfs_ndiv(gpll.dvfs.mv, gpll.N, &gpll.dvfs); | ||
847 | clk_slide_gpc_pll(g, &gpll); | 899 | clk_slide_gpc_pll(g, &gpll); |
848 | } | 900 | } |
849 | 901 | ||
@@ -867,6 +919,7 @@ static int clk_disable_gpcpll(struct gk20a *g, int allow_slide) | |||
867 | gk20a_readl(g, trim_sys_gpcpll_cfg_r()); | 919 | gk20a_readl(g, trim_sys_gpcpll_cfg_r()); |
868 | 920 | ||
869 | clk->gpc_pll.enabled = false; | 921 | clk->gpc_pll.enabled = false; |
922 | clk->gpc_pll_last.enabled = false; | ||
870 | return 0; | 923 | return 0; |
871 | } | 924 | } |
872 | 925 | ||