From a918003694984b6fca9e6b6c07fd7cdf3503055e Mon Sep 17 00:00:00 2001 From: Srikar Srimath Tirumala Date: Mon, 5 Dec 2016 18:28:51 -0800 Subject: gpu: nvgpu: fix gpcclk for K4.4 Move the sw initialization of the gpcclk from gpu rail ungate path to the nvgpu probe path. This allows gpcclk to register itself successfully with CCF and makes it discoverable for other clients early on during boot. Bug 200233943 Bug 200259437 Change-Id: I88d94542092f92e68dc63c40444a70991d1f6129 Signed-off-by: Srikar Srimath Tirumala Reviewed-on: http://git-master/r/1265549 Reviewed-by: Tejal Kudav --- drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c | 5 ++ drivers/gpu/nvgpu/gm20b/clk_gm20b.c | 92 +++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c b/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c index 1b4d2c07..978ad5aa 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c +++ b/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c @@ -18,12 +18,17 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include +#include #include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#include +#endif #include "gk20a.h" #include "gr_gk20a.h" diff --git a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c index 501be537..060a5775 100644 --- a/drivers/gpu/nvgpu/gm20b/clk_gm20b.c +++ b/drivers/gpu/nvgpu/gm20b/clk_gm20b.c @@ -372,6 +372,18 @@ static void clk_config_dvfs_ndiv(int mv, u32 n_eff, struct na_dvfs *d) d->sdm_din = (d->sdm_din >> BITS_PER_BYTE) & 0xff; } +static void gm20b_calc_dvfs_safe_max_freq(struct clk *c) +{ + unsigned long safe_rate; + + if (dvfs_safe_max_freq) + return; + + safe_rate = tegra_dvfs_get_fmax_at_vmin_safe_t(c); + safe_rate = safe_rate * (100 - DVFS_SAFE_MARGIN) / 100; + dvfs_safe_max_freq = rate_gpu_to_gpc2clk(safe_rate); +} + /* Voltage dependent configuration */ static void clk_config_dvfs(struct gk20a *g, struct pll *gpll) { @@ -383,6 +395,7 @@ static void clk_config_dvfs(struct gk20a *g, struct pll *gpll) clk = clk_get_parent(clk); #endif + gm20b_calc_dvfs_safe_max_freq(clk); d->mv = tegra_dvfs_predict_mv_at_hz_cur_tfloor(clk, rate_gpc2clk_to_gpu(gpll->freq)); @@ -1120,32 +1133,23 @@ static int gm20b_init_clk_reset_enable_hw(struct gk20a *g) return 0; } -static int gm20b_init_clk_setup_sw(struct gk20a *g) +#ifdef CONFIG_TEGRA_CLK_FRAMEWORK +static int gm20b_init_gpc_pll(struct gk20a *g) { struct clk_gk20a *clk = &g->clk; - unsigned long safe_rate; - struct clk *ref, *c; - - gk20a_dbg_fn(""); - - if (clk->sw_ready) { - gk20a_dbg_fn("skip init"); - return 0; - } + struct clk *c, *ref; if (!gk20a_clk_get(g)) return -EINVAL; - c = clk->tegra_clk; -#ifdef CONFIG_TEGRA_CLK_FRAMEWORK /* * On Tegra GPU clock exposed to frequency governor is a shared user on * GPCPLL bus (gbus). The latter can be accessed as GPU clock parent. * Respectively the grandparent is PLL reference clock. */ - c = clk_get_parent(c); -#endif + c = clk_get_parent(clk->tegra_clk); ref = clk_get_parent(c); + if (IS_ERR(ref)) { gk20a_err(dev_from_gk20a(g), "failed to get GPCPLL reference clock"); @@ -1155,9 +1159,7 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g) clk->gpc_pll.id = GK20A_GPC_PLL; clk->gpc_pll.clk_in = clk_get_rate(ref) / KHZ; - safe_rate = tegra_dvfs_get_fmax_at_vmin_safe_t(c); - safe_rate = safe_rate * (100 - DVFS_SAFE_MARGIN) / 100; - dvfs_safe_max_freq = rate_gpu_to_gpc2clk(safe_rate); + gm20b_calc_dvfs_safe_max_freq(c); clk->gpc_pll.PL = (dvfs_safe_max_freq == 0) ? 0 : DIV_ROUND_UP(gpc_pll_params.min_vco, dvfs_safe_max_freq); @@ -1169,7 +1171,51 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g) clk->gpc_pll.freq = clk->gpc_pll.clk_in * clk->gpc_pll.N; clk->gpc_pll.freq /= pl_to_div(clk->gpc_pll.PL); - /* + return 0; +} + +#else /*COMMON_CLOCK_FRAMEWORK*/ +static int gm20b_init_gpc_pll(struct gk20a *g) +{ + struct clk_gk20a *clk = &g->clk; + struct clk *ref; + + ref = clk_get_sys("gpu_ref", "gpu_ref"); + if (IS_ERR(ref)) { + gk20a_err(dev_from_gk20a(g), + "failed to get GPCPLL reference clock"); + return -EINVAL; + } + clk->gpc_pll.id = GK20A_GPC_PLL; + clk->gpc_pll.clk_in = clk_get_rate(ref) / KHZ; + + /* Initial freq: low enough to be safe at Vmin (default 1/3 VCO min) */ + clk->gpc_pll.M = 1; + clk->gpc_pll.N = DIV_ROUND_UP(gpc_pll_params.min_vco, + clk->gpc_pll.clk_in); + clk->gpc_pll.PL = 3; + clk->gpc_pll.freq = clk->gpc_pll.clk_in * clk->gpc_pll.N; + clk->gpc_pll.freq /= pl_to_div(clk->gpc_pll.PL); + + return 0; +} +#endif + +static int gm20b_init_clk_setup_sw(struct gk20a *g) +{ + struct clk_gk20a *clk = &g->clk; + + gk20a_dbg_fn(""); + + if (clk->sw_ready) { + gk20a_dbg_fn("skip init"); + return 0; + } + + if (gm20b_init_gpc_pll(g)) + return -EINVAL; + + /* * All production parts should have ADC fuses burnt. Therefore, check * ADC fuses always, regardless of whether NA mode is selected; and if * NA mode is indeed selected, and part can support it, switch to NA @@ -1187,7 +1233,6 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g) #endif mutex_init(&clk->clk_mutex); - clk->sw_ready = true; gk20a_dbg_fn("done"); @@ -1293,6 +1338,11 @@ int gm20b_register_gpcclk(struct gk20a *g) { struct clk_gk20a *clk = &g->clk; struct clk_init_data init; struct clk *c; + int err = 0; + + err = gm20b_init_clk_setup_sw(g); + if (err) + return err; init.name = "gpcclk"; init.ops = &gk20a_clk_ops; @@ -1312,7 +1362,7 @@ int gm20b_register_gpcclk(struct gk20a *g) { clk->tegra_clk = c; clk_register_clkdev(c, "gpcclk", "gpcclk"); - return 0; + return err; } #endif /* CONFIG_COMMON_CLK */ @@ -1522,9 +1572,11 @@ static int gm20b_init_clk_support(struct gk20a *g) if (err) return err; +#ifdef CONFIG_TEGRA_CLK_FRAMEWORK err = gm20b_init_clk_setup_sw(g); if (err) return err; +#endif mutex_lock(&clk->clk_mutex); clk->clk_hw_on = true; -- cgit v1.2.2