gpu: nvgpu: gk20a: add do_{idle()/unidle()} APIs

Add below two new APIs for gk20a :

1) gk20a_do_idle()
this API will force GPU to idle and railgate

2) gk20a_do_unidle()
this API will unblock all the tasks blocked by do_idle()

Bug 1487804

Change-Id: Ic5e7f2d19fb8d35f43666d0e309dde3022349d92
Signed-off-by: Deepak Nibade <dnibade@nvidia.com>
Reviewed-on: http://git-master/r/412061
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>
This commit is contained in:
Deepak Nibade
2014-05-20 15:05:28 +05:30
committed by Dan Willemsen
parent 6eab11c6e8
commit ab386e54a5

View File

@@ -42,6 +42,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/tegra-powergate.h> #include <linux/tegra-powergate.h>
#include <linux/tegra_pm_domains.h> #include <linux/tegra_pm_domains.h>
#include <linux/clk/tegra.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/input-cfboost.h> #include <linux/input-cfboost.h>
@@ -1610,6 +1611,79 @@ void gk20a_reset(struct gk20a *g, u32 units)
gk20a_enable(g, units); gk20a_enable(g, units);
} }
/**
* gk20a_do_idle() - force the GPU to idle and railgate
*
* In success, this call MUST be balanced by caller with gk20a_do_unidle()
*/
int gk20a_do_idle(void)
{
struct platform_device *pdev = to_platform_device(
bus_find_device_by_name(&platform_bus_type,
NULL, "gk20a.0"));
struct gk20a *g = get_gk20a(pdev);
struct gk20a_platform *platform = dev_get_drvdata(&pdev->dev);
struct fifo_gk20a *f = &g->fifo;
unsigned long timeout = jiffies + msecs_to_jiffies(200);
int chid, ref_cnt;
if (!platform->can_railgate)
return -ENOSYS;
/* acquire busy lock to block other busy() calls */
down_write(&g->busy_lock);
/* prevent suspend by incrementing usage counter */
pm_runtime_get_noresume(&pdev->dev);
/* check and wait until GPU is idle (with a timeout) */
pm_runtime_barrier(&pdev->dev);
for (chid = 0; chid < f->num_channels; chid++)
if (gk20a_wait_channel_idle(&f->channel[chid]))
goto fail;
do {
mdelay(1);
ref_cnt = atomic_read(&pdev->dev.power.usage_count);
} while (ref_cnt != 1 && time_before(jiffies, timeout));
if (ref_cnt != 1)
goto fail;
/*
* if GPU is now idle, we will have only one ref count
* drop this ref which will rail gate the GPU
*/
pm_runtime_put_sync(&pdev->dev);
/* add sufficient delay to allow GPU to rail gate */
mdelay(platform->railgate_delay + 100);
return 0;
fail:
pm_runtime_put_noidle(&pdev->dev);
up_write(&g->busy_lock);
return -EBUSY;
}
/**
* gk20a_do_unidle() - unblock all the tasks blocked by gk20a_do_idle()
*/
int gk20a_do_unidle(void)
{
struct platform_device *pdev = to_platform_device(
bus_find_device_by_name(&platform_bus_type,
NULL, "gk20a.0"));
struct gk20a *g = get_gk20a(pdev);
/* release the lock and open up all other busy() calls */
up_write(&g->busy_lock);
return 0;
}
int gk20a_init_gpu_characteristics(struct gk20a *g) int gk20a_init_gpu_characteristics(struct gk20a *g)
{ {
struct nvhost_gpu_characteristics *gpu = &g->gpu_characteristics; struct nvhost_gpu_characteristics *gpu = &g->gpu_characteristics;