gpu: nvgpu: Implement own rail gating code

Move rail gating sequence to happen in nvgpu driver instead of
piggybacking on Tegra power gating APIs.

Bug 200115454

Change-Id: I8514686c7b137f200021b05ead7157d0883bddc5
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-on: http://git-master/r/761991
This commit is contained in:
Terje Bergstrom
2015-06-23 14:49:12 -07:00
parent 0c25c820d7
commit 16aaae648b
2 changed files with 262 additions and 9 deletions

View File

@@ -175,6 +175,7 @@ struct gk20a_platform {
int (*reset_assert)(struct platform_device *pdev);
int (*reset_deassert)(struct platform_device *pdev);
struct clk *clk_reset;
struct dvfs_rail *gpu_rail;
bool virtual_dev;
#ifdef CONFIG_TEGRA_GR_VIRTUALIZATION

View File

@@ -20,13 +20,16 @@
#include <linux/debugfs.h>
#include <linux/tegra-powergate.h>
#include <linux/platform_data/tegra_edp.h>
#include <linux/delay.h>
#include <uapi/linux/nvgpu.h>
#include <linux/dma-buf.h>
#include <linux/nvmap.h>
#include <linux/tegra_pm_domains.h>
#include <linux/tegra_soctherm.h>
#include <linux/platform/tegra/clock.h>
#include <linux/platform/tegra/dvfs.h>
#include <linux/platform/tegra/common.h>
#include <linux/platform/tegra/mc.h>
#include <linux/clk/tegra.h>
#include <linux/platform/tegra/tegra_emc.h>
@@ -40,6 +43,8 @@
#define TEGRA_GM20B_BW_PER_FREQ 64
#define TEGRA_DDR3_BW_PER_FREQ 16
#define TEGRA_DDR4_BW_PER_FREQ 16
#define MC_CLIENT_GPU 34
#define PMC_GPU_RG_CNTRL_0 0x2d4
extern struct device tegra_vpr_dev;
@@ -48,6 +53,16 @@ struct gk20a_emc_params {
long freq_last_set;
};
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
static inline u32 pmc_read(unsigned long reg)
{
return readl(pmc + reg);
}
static inline void pmc_write(u32 val, unsigned long reg)
{
writel_relaxed(val, pmc + reg);
}
#define MHZ_TO_HZ(x) ((x) * 1000000)
#define HZ_TO_MHZ(x) ((x) / 1000000)
@@ -283,6 +298,7 @@ static void gk20a_tegra_calibrate_emc(struct platform_device *pdev,
emc_params->bw_ratio = (gpu_bw / emc_bw);
}
#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
/*
* gk20a_tegra_is_railgated()
*
@@ -291,10 +307,11 @@ static void gk20a_tegra_calibrate_emc(struct platform_device *pdev,
static bool gk20a_tegra_is_railgated(struct platform_device *pdev)
{
struct gk20a_platform *platform = platform_get_drvdata(pdev);
bool ret = false;
if (!tegra_platform_is_linsim())
ret = !tegra_powergate_is_powered(TEGRA_POWERGATE_GPU);
ret = !tegra_dvfs_is_rail_up(platform->gpu_rail);
return ret;
}
@@ -307,10 +324,103 @@ static bool gk20a_tegra_is_railgated(struct platform_device *pdev)
static int gk20a_tegra_railgate(struct platform_device *pdev)
{
if (!tegra_platform_is_linsim() &&
tegra_powergate_is_powered(TEGRA_POWERGATE_GPU))
tegra_powergate_partition(TEGRA_POWERGATE_GPU);
struct gk20a_platform *platform = platform_get_drvdata(pdev);
int ret = 0;
if (tegra_platform_is_linsim() ||
!tegra_dvfs_is_rail_up(platform->gpu_rail))
return 0;
tegra_mc_flush(MC_CLIENT_GPU);
udelay(10);
/* enable clamp */
pmc_write(0x1, PMC_GPU_RG_CNTRL_0);
pmc_read(PMC_GPU_RG_CNTRL_0);
udelay(10);
platform->reset_assert(pdev);
udelay(10);
/*
* GPCPLL is already disabled before entering this function; reference
* clocks are enabled until now - disable them just before rail gating
*/
clk_disable(platform->clk[0]);
clk_disable(platform->clk[1]);
udelay(10);
if (tegra_dvfs_is_rail_up(platform->gpu_rail)) {
ret = tegra_dvfs_rail_power_down(platform->gpu_rail);
if (ret)
goto err_power_off;
} else
pr_info("No GPU regulator?\n");
return 0;
err_power_off:
gk20a_err(&pdev->dev, "Could not railgate GPU");
return ret;
}
/*
* gm20b_tegra_railgate()
*
* Gate (disable) gm20b power rail
*/
static int gm20b_tegra_railgate(struct platform_device *pdev)
{
struct gk20a_platform *platform = platform_get_drvdata(pdev);
int ret = 0;
if (tegra_platform_is_linsim() ||
!tegra_dvfs_is_rail_up(platform->gpu_rail))
return 0;
tegra_mc_flush(MC_CLIENT_GPU);
udelay(10);
/* enable clamp */
pmc_write(0x1, PMC_GPU_RG_CNTRL_0);
pmc_read(PMC_GPU_RG_CNTRL_0);
udelay(10);
platform->reset_assert(pdev);
udelay(10);
/*
* GPCPLL is already disabled before entering this function; reference
* clocks are enabled until now - disable them just before rail gating
*/
clk_disable(platform->clk_reset);
clk_disable(platform->clk[0]);
clk_disable(platform->clk[1]);
udelay(10);
tegra_soctherm_gpu_tsens_invalidate(1);
if (tegra_dvfs_is_rail_up(platform->gpu_rail)) {
ret = tegra_dvfs_rail_power_down(platform->gpu_rail);
if (ret)
goto err_power_off;
} else
pr_info("No GPU regulator?\n");
return 0;
err_power_off:
gk20a_err(&pdev->dev, "Could not railgate GPU");
return ret;
}
/*
@@ -321,12 +431,151 @@ static int gk20a_tegra_railgate(struct platform_device *pdev)
static int gk20a_tegra_unrailgate(struct platform_device *pdev)
{
struct gk20a_platform *platform = platform_get_drvdata(pdev);
int ret = 0;
if (!tegra_platform_is_linsim())
ret = tegra_unpowergate_partition(TEGRA_POWERGATE_GPU);
bool first = false;
if (tegra_platform_is_linsim())
return 0;
if (!platform->gpu_rail) {
platform->gpu_rail = tegra_dvfs_get_rail_by_name("vdd_gpu");
if (IS_ERR_OR_NULL(platform->gpu_rail)) {
WARN(1, "No GPU regulator?\n");
return -EINVAL;
}
first = true;
}
ret = tegra_dvfs_rail_power_up(platform->gpu_rail);
if (ret)
return ret;
if (!first) {
ret = clk_enable(platform->clk[0]);
if (ret) {
gk20a_err(&pdev->dev, "could not turn on gpu pll");
goto err_clk_on;
}
ret = clk_enable(platform->clk[1]);
if (ret) {
gk20a_err(&pdev->dev, "could not turn on pwr clock");
goto err_clk_on;
}
}
udelay(10);
platform->reset_assert(pdev);
udelay(10);
pmc_write(0, PMC_GPU_RG_CNTRL_0);
pmc_read(PMC_GPU_RG_CNTRL_0);
udelay(10);
platform->reset_deassert(pdev);
/* Flush MC after boot/railgate/SC7 */
tegra_mc_flush(MC_CLIENT_GPU);
udelay(10);
tegra_mc_flush_done(MC_CLIENT_GPU);
udelay(10);
return 0;
err_clk_on:
tegra_dvfs_rail_power_down(platform->gpu_rail);
return ret;
}
/*
* gm20b_tegra_unrailgate()
*
* Ungate (enable) gm20b power rail
*/
static int gm20b_tegra_unrailgate(struct platform_device *pdev)
{
struct gk20a_platform *platform = platform_get_drvdata(pdev);
int ret = 0;
bool first = false;
if (tegra_platform_is_linsim())
return 0;
if (!platform->gpu_rail) {
platform->gpu_rail = tegra_dvfs_get_rail_by_name("vdd_gpu");
if (IS_ERR_OR_NULL(platform->gpu_rail)) {
WARN(1, "No GPU regulator?\n");
return -EINVAL;
}
first = true;
}
ret = tegra_dvfs_rail_power_up(platform->gpu_rail);
if (ret)
return ret;
tegra_soctherm_gpu_tsens_invalidate(0);
if (!first) {
ret = clk_enable(platform->clk_reset);
if (ret) {
gk20a_err(&pdev->dev, "could not turn on gpu_gate");
goto err_clk_on;
}
ret = clk_enable(platform->clk[0]);
if (ret) {
gk20a_err(&pdev->dev, "could not turn on gpu pll");
goto err_clk_on;
}
ret = clk_enable(platform->clk[1]);
if (ret) {
gk20a_err(&pdev->dev, "could not turn on pwr clock");
goto err_clk_on;
}
}
udelay(10);
platform->reset_assert(pdev);
udelay(10);
pmc_write(0, PMC_GPU_RG_CNTRL_0);
pmc_read(PMC_GPU_RG_CNTRL_0);
udelay(10);
clk_disable(platform->clk_reset);
platform->reset_deassert(pdev);
clk_enable(platform->clk_reset);
/* Flush MC after boot/railgate/SC7 */
tegra_mc_flush(MC_CLIENT_GPU);
udelay(10);
tegra_mc_flush_done(MC_CLIENT_GPU);
udelay(10);
return 0;
err_clk_on:
tegra_dvfs_rail_power_down(platform->gpu_rail);
return ret;
}
#endif
static struct {
char *name;
unsigned long default_rate;
@@ -565,9 +814,11 @@ struct gk20a_platform gk20a_tegra_platform = {
/* power management callbacks */
.suspend = gk20a_tegra_suspend,
#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
.railgate = gk20a_tegra_railgate,
.unrailgate = gk20a_tegra_unrailgate,
.is_railgated = gk20a_tegra_is_railgated,
#endif
.busy = gk20a_tegra_busy,
.idle = gk20a_tegra_idle,
@@ -592,7 +843,6 @@ struct gk20a_platform gm20b_tegra_platform = {
/* power management configuration */
.railgate_delay = 500,
.clockgate_delay = 50,
/* Disable all power features for gm20b */
.can_railgate = true,
.enable_slcg = true,
.enable_blcg = true,
@@ -610,9 +860,11 @@ struct gk20a_platform gm20b_tegra_platform = {
/* power management callbacks */
.suspend = gk20a_tegra_suspend,
.railgate = gk20a_tegra_railgate,
.unrailgate = gk20a_tegra_unrailgate,
#ifdef CONFIG_TEGRA_CLK_FRAMEWORK
.railgate = gm20b_tegra_railgate,
.unrailgate = gm20b_tegra_unrailgate,
.is_railgated = gk20a_tegra_is_railgated,
#endif
.busy = gk20a_tegra_busy,
.idle = gk20a_tegra_idle,