summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimone Willett <swillett@nvidia.com>2012-02-15 14:22:37 -0800
committerLokesh Pathak <lpathak@nvidia.com>2012-02-16 05:07:10 -0800
commitae1f9efe299b2f24680b04e429c76dec3240b60d (patch)
tree3340a6e656ec9c6e920b534fb6a4413fc8408afd
parent681c2688fc5a7001a2c0e568790e04be67552efd (diff)
Revert "video: tegra: host: use runtime pm for clock management"
This reverts commit 961c60fe7213d92793d6072abc16f58721a33fed Change-Id: I8ef0fbaee30e94c78b8df609f729953fee6d1583 Reviewed-on: http://git-master/r/84135 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-by: Mayuresh Kulkarni <mkulkarni@nvidia.com> Tested-by: Lokesh Pathak <lpathak@nvidia.com> Reviewed-by: Lokesh Pathak <lpathak@nvidia.com>
-rw-r--r--drivers/video/tegra/host/bus.c25
-rw-r--r--drivers/video/tegra/host/dev.c28
-rw-r--r--drivers/video/tegra/host/host1x/host1x_debug.c4
-rw-r--r--drivers/video/tegra/host/host1x/host1x_syncpt.c5
-rw-r--r--drivers/video/tegra/host/nvhost_acm.c228
-rw-r--r--drivers/video/tegra/host/nvhost_acm.h6
-rw-r--r--drivers/video/tegra/host/nvhost_channel.c2
-rw-r--r--include/linux/nvhost.h10
8 files changed, 144 insertions, 164 deletions
diff --git a/drivers/video/tegra/host/bus.c b/drivers/video/tegra/host/bus.c
index 8fd4aa830afe..8234d0fa64c3 100644
--- a/drivers/video/tegra/host/bus.c
+++ b/drivers/video/tegra/host/bus.c
@@ -19,7 +19,7 @@
#include <linux/pm_runtime.h>
#include <linux/nvhost.h>
-#include <mach/clk.h>
+
#include "dev.h"
struct nvhost_master *nvhost;
@@ -487,34 +487,17 @@ static int nvhost_pm_restore_noirq(struct device *dev)
int __weak nvhost_pm_runtime_suspend(struct device *dev)
{
- int i;
- struct nvhost_device *device = to_nvhost_device(dev);
-
- for (i = 0; i < device->num_clks; i++)
- clk_disable(device->clk[i]);
-
- if (device->can_powergate)
- schedule_delayed_work(&device->powerstate_down,
- msecs_to_jiffies(device->powergate_delay));
-
- return 0;
+ return pm_generic_runtime_suspend(dev);
};
int __weak nvhost_pm_runtime_resume(struct device *dev)
{
- int i;
- struct nvhost_device *device = to_nvhost_device(dev);
-
- for (i = 0; i < device->num_clks; i++)
- clk_enable(device->clk[i]);
-
- return 0;
+ return pm_generic_runtime_resume(dev);
};
int __weak nvhost_pm_runtime_idle(struct device *dev)
{
- pm_runtime_autosuspend(dev);
- return 0;
+ return pm_generic_runtime_idle(dev);
};
#else /* !CONFIG_PM_RUNTIME */
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c
index c75448021484..4cd1e4eaf843 100644
--- a/drivers/video/tegra/host/dev.c
+++ b/drivers/video/tegra/host/dev.c
@@ -35,7 +35,7 @@
#include <linux/hrtimer.h>
#define CREATE_TRACE_POINTS
#include <trace/events/nvhost.h>
-#include <linux/pm_runtime.h>
+
#include <linux/io.h>
#include <linux/nvhost.h>
@@ -984,24 +984,20 @@ static int __devinit nvhost_probe(struct platform_device *pdev)
if (err)
goto fail;
- pm_runtime_enable(&hostdev.dev);
err = nvhost_module_init(&hostdev);
if (err)
goto fail;
for (i = 0; i < host->nb_channels; i++) {
struct nvhost_channel *ch = &host->channels[i];
- pm_runtime_enable(&ch->dev->dev);
nvhost_module_init(ch->dev);
}
platform_set_drvdata(pdev, host);
- for (i = 0; i < host->dev->num_clks; i++)
- clk_enable(host->dev->clk[i]);
+ clk_enable(host->dev->clk[0]);
nvhost_syncpt_reset(&host->syncpt);
- for (i = 0; i < host->dev->num_clks; i++)
- clk_disable(host->dev->clk[i]);
+ clk_disable(host->dev->clk[0]);
nvhost_debug_init(host);
@@ -1042,25 +1038,7 @@ static int nvhost_suspend(struct platform_device *pdev, pm_message_t state)
static int nvhost_resume(struct platform_device *pdev)
{
- int i;
- struct nvhost_master *host = platform_get_drvdata(pdev);
-
dev_info(&pdev->dev, "resuming\n");
-
- for (i = 0; i < host->dev->num_clks; i++)
- clk_enable(host->dev->clk[i]);
- if (host->dev->finalize_poweron)
- host->dev->finalize_poweron(host->dev);
- for (i = 0; i < host->dev->num_clks; i++)
- clk_disable(host->dev->clk[i]);
-
- /* enable runtime pm for host1x */
- nvhost_module_resume(host->dev);
-
- /* enable runtime pm for clients */
- for (i = 0; i < host->nb_channels; i++)
- nvhost_module_resume(host->channels[i].dev);
-
return 0;
}
diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c
index c788f82288c9..06b09d20c55b 100644
--- a/drivers/video/tegra/host/host1x/host1x_debug.c
+++ b/drivers/video/tegra/host/host1x/host1x_debug.c
@@ -275,8 +275,8 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m,
cbstat = readl(m->sync_aperture + HOST1X_SYNC_CBSTAT_x(chid));
nvhost_debug_output(o, "%d-%s (%d): ", chid,
- channel->dev->name,
- atomic_read(&channel->dev->dev.power.usage_count));
+ channel->dev->name,
+ channel->dev->refcount);
if (HOST1X_VAL(CHANNEL_DMACTRL, DMASTOP, dmactrl)
|| !channel->cdma.push_buffer.mapped) {
diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c
index 00cca103d18f..622c8e049a7b 100644
--- a/drivers/video/tegra/host/host1x/host1x_syncpt.c
+++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c
@@ -74,10 +74,9 @@ static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id)
if (!nvhost_syncpt_check_max(sp, id, live)) {
dev_err(&syncpt_to_dev(sp)->pdev->dev,
- "%s failed: id=%u, min=%d, max=%d\n",
+ "%s failed: id=%u\n",
__func__,
- id, atomic_read(&sp->min_val[id]),
- atomic_read(&sp->max_val[id]));
+ id);
nvhost_debug_dump(syncpt_to_dev(sp));
BUG();
}
diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c
index 351b70e0b6fb..a2386a257c8f 100644
--- a/drivers/video/tegra/host/nvhost_acm.c
+++ b/drivers/video/tegra/host/nvhost_acm.c
@@ -29,14 +29,13 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <mach/powergate.h>
#include <mach/clk.h>
#include <mach/hardware.h>
-#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ)
-#define POWERGATE_DELAY 10
-#define MAX_DEVID_LENGTH 16
+#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ)
+#define POWERGATE_DELAY 10
+#define MAX_DEVID_LENGTH 16
DEFINE_MUTEX(client_list_lock);
@@ -99,113 +98,139 @@ void nvhost_module_reset(struct nvhost_device *dev)
__func__, dev->name);
}
+static void to_state_clockgated_locked(struct nvhost_device *dev)
+{
+ if (dev->powerstate == NVHOST_POWER_STATE_RUNNING) {
+ int i;
+ for (i = 0; i < dev->num_clks; i++)
+ clk_disable(dev->clk[i]);
+ if (dev->dev.parent)
+ nvhost_module_idle(to_nvhost_device(dev->dev.parent));
+ } else if (dev->powerstate == NVHOST_POWER_STATE_POWERGATED
+ && dev->can_powergate) {
+ do_unpowergate_locked(dev->powergate_ids[0]);
+ do_unpowergate_locked(dev->powergate_ids[1]);
+ }
+ dev->powerstate = NVHOST_POWER_STATE_CLOCKGATED;
+}
+
+static void to_state_running_locked(struct nvhost_device *dev)
+{
+ int prev_state = dev->powerstate;
+ if (dev->powerstate == NVHOST_POWER_STATE_POWERGATED)
+ to_state_clockgated_locked(dev);
+ if (dev->powerstate == NVHOST_POWER_STATE_CLOCKGATED) {
+ int i;
+
+ if (dev->dev.parent)
+ nvhost_module_busy(to_nvhost_device(dev->dev.parent));
+
+ for (i = 0; i < dev->num_clks; i++) {
+ int err = clk_enable(dev->clk[i]);
+ BUG_ON(err);
+ }
+
+ if (prev_state == NVHOST_POWER_STATE_POWERGATED
+ && dev->finalize_poweron)
+ dev->finalize_poweron(dev);
+ }
+ dev->powerstate = NVHOST_POWER_STATE_RUNNING;
+}
+
/* This gets called from powergate_handler() and from module suspend.
* Module suspend is done for all modules, runtime power gating only
* for modules with can_powergate set.
*/
-static int to_state_powergated_locked(struct nvhost_device *dev,
- bool system_suspend)
+static int to_state_powergated_locked(struct nvhost_device *dev)
{
- int err = 0, i = 0;
-
- if (dev->prepare_poweroff && dev->powered) {
- struct nvhost_device *device;
- struct device *parent = dev->dev.parent;
- if (parent)
- device = to_nvhost_device(parent);
-
- if (system_suspend) {
- /* enable parent clock
- * host1x does not have parent */
- if (parent) {
- for (i = 0; i < device->num_clks; i++)
- clk_enable(device->clk[i]);
- }
-
- /* enable module clock */
- for (i = 0; i < dev->num_clks; i++)
- clk_enable(dev->clk[i]);
- } else
- pm_runtime_get_sync(&dev->dev);
+ int err = 0;
+ if (dev->prepare_poweroff
+ && dev->powerstate != NVHOST_POWER_STATE_POWERGATED) {
+ /* Clock needs to be on in prepare_poweroff */
+ to_state_running_locked(dev);
err = dev->prepare_poweroff(dev);
if (err)
return err;
-
- if (system_suspend) {
- /* disable module clock */
- for (i = 0; i < dev->num_clks; i++)
- clk_disable(dev->clk[i]);
-
- /* disable parent clock
- * host1x does not have parent */
- if (parent) {
- for (i = 0; i < device->num_clks; i++)
- clk_disable(device->clk[i]);
- }
- } else
- pm_runtime_put_sync_suspend(&dev->dev);
}
+ if (dev->powerstate == NVHOST_POWER_STATE_RUNNING)
+ to_state_clockgated_locked(dev);
+
if (dev->can_powergate) {
do_powergate_locked(dev->powergate_ids[0]);
do_powergate_locked(dev->powergate_ids[1]);
- dev->powered = false;
}
+ dev->powerstate = NVHOST_POWER_STATE_POWERGATED;
return 0;
}
+static void schedule_powergating_locked(struct nvhost_device *dev)
+{
+ if (dev->can_powergate)
+ schedule_delayed_work(&dev->powerstate_down,
+ msecs_to_jiffies(dev->powergate_delay));
+}
+
+static void schedule_clockgating_locked(struct nvhost_device *dev)
+{
+ schedule_delayed_work(&dev->powerstate_down,
+ msecs_to_jiffies(dev->clockgate_delay));
+}
+
void nvhost_module_busy(struct nvhost_device *dev)
{
if (dev->busy)
dev->busy(dev);
mutex_lock(&dev->lock);
+ cancel_delayed_work(&dev->powerstate_down);
- if (dev->can_powergate) {
- /* cancel power-gate handler */
- cancel_delayed_work_sync(&dev->powerstate_down);
-
- /* unpowergate the module if it was power gated */
- if (!dev->powered) {
- do_unpowergate_locked(dev->powergate_ids[0]);
- do_unpowergate_locked(dev->powergate_ids[1]);
- dev->powered = true;
- }
- }
-
- pm_runtime_get_sync(&dev->dev);
-
+ dev->refcount++;
+ if (dev->refcount > 0 && !nvhost_module_powered(dev))
+ to_state_running_locked(dev);
mutex_unlock(&dev->lock);
}
-static bool is_module_idle(struct nvhost_device *dev, bool system_suspend)
+static void powerstate_down_handler(struct work_struct *work)
{
- /* for system suspend, pm core holds a reference on runtime pm.
- * this is for kernels >= 3.x, it is not there for kernels < 3.x.
- * for more details refer the LKML thread:
- * https://lkml.org/lkml/2011/6/25/93
- * https://lkml.org/lkml/2011/6/25/94
- * https://lkml.org/lkml/2011/6/25/95 */
- if (system_suspend)
- return atomic_read(&dev->dev.power.usage_count) == 1;
- else
- return atomic_read(&dev->dev.power.usage_count) == 0;
+ struct nvhost_device *dev;
+
+ dev = container_of(to_delayed_work(work),
+ struct nvhost_device,
+ powerstate_down);
+
+ mutex_lock(&dev->lock);
+ if (dev->refcount == 0) {
+ switch (dev->powerstate) {
+ case NVHOST_POWER_STATE_RUNNING:
+ to_state_clockgated_locked(dev);
+ schedule_powergating_locked(dev);
+ break;
+ case NVHOST_POWER_STATE_CLOCKGATED:
+ if (to_state_powergated_locked(dev))
+ schedule_powergating_locked(dev);
+ break;
+ default:
+ break;
+ }
+ }
+ mutex_unlock(&dev->lock);
}
+
void nvhost_module_idle_mult(struct nvhost_device *dev, int refs)
{
bool kick = false;
- int i;
mutex_lock(&dev->lock);
- for (i = 0; i < refs; i++)
- pm_runtime_put_sync(&dev->dev);
-
- if (is_module_idle(dev, false))
+ dev->refcount -= refs;
+ if (dev->refcount == 0) {
+ if (nvhost_module_powered(dev))
+ schedule_clockgating_locked(dev);
kick = true;
-
+ }
mutex_unlock(&dev->lock);
if (kick) {
@@ -216,19 +241,6 @@ void nvhost_module_idle_mult(struct nvhost_device *dev, int refs)
}
}
-static void powerstate_down_handler(struct work_struct *work)
-{
- struct nvhost_device *dev;
-
- dev = container_of(to_delayed_work(work), struct nvhost_device,
- powerstate_down);
-
- mutex_lock(&dev->lock);
- if (dev->can_powergate)
- to_state_powergated_locked(dev, false);
- mutex_unlock(&dev->lock);
-}
-
int nvhost_module_get_rate(struct nvhost_device *dev, unsigned long *rate,
int index)
{
@@ -243,6 +255,7 @@ int nvhost_module_get_rate(struct nvhost_device *dev, unsigned long *rate,
*rate = clk_get_rate(c);
nvhost_module_idle(dev);
return 0;
+
}
static int nvhost_module_update_rate(struct nvhost_device *dev, int index)
@@ -280,6 +293,7 @@ int nvhost_module_set_rate(struct nvhost_device *dev, void *priv,
ret = nvhost_module_update_rate(dev, index);
mutex_unlock(&client_list_lock);
return ret;
+
}
int nvhost_module_add_client(struct nvhost_device *dev, void *priv)
@@ -352,25 +366,31 @@ int nvhost_module_init(struct nvhost_device *dev)
mutex_init(&dev->lock);
init_waitqueue_head(&dev->idle_wq);
+ INIT_DELAYED_WORK(&dev->powerstate_down, powerstate_down_handler);
/* power gate units that we can power gate */
if (dev->can_powergate) {
do_powergate_locked(dev->powergate_ids[0]);
do_powergate_locked(dev->powergate_ids[1]);
- INIT_DELAYED_WORK(&dev->powerstate_down, powerstate_down_handler);
- dev->powered = false;
+ dev->powerstate = NVHOST_POWER_STATE_POWERGATED;
} else {
do_unpowergate_locked(dev->powergate_ids[0]);
do_unpowergate_locked(dev->powergate_ids[1]);
- dev->powered = true;
+ dev->powerstate = NVHOST_POWER_STATE_CLOCKGATED;
}
- /* enable runtime pm */
- nvhost_module_resume(dev);
-
return 0;
}
+static int is_module_idle(struct nvhost_device *dev)
+{
+ int count;
+ mutex_lock(&dev->lock);
+ count = dev->refcount;
+ mutex_unlock(&dev->lock);
+ return (count == 0);
+}
+
static void debug_not_idle(struct nvhost_master *host)
{
int i;
@@ -381,8 +401,8 @@ static void debug_not_idle(struct nvhost_master *host)
mutex_lock(&dev->lock);
if (dev->name)
dev_warn(&host->pdev->dev,
- "tegra_grhost: %s: refcnt %d\n", dev->name,
- atomic_read(&dev->dev.power.usage_count));
+ "tegra_grhost: %s: refcnt %d\n",
+ dev->name, dev->refcount);
mutex_unlock(&dev->lock);
}
@@ -404,10 +424,10 @@ int nvhost_module_suspend(struct nvhost_device *dev, bool system_suspend)
int ret;
struct nvhost_master *host = nvhost_get_host(dev);
- if (system_suspend && !is_module_idle(dev, system_suspend))
+ if (system_suspend && !is_module_idle(dev))
debug_not_idle(host);
- ret = wait_event_timeout(dev->idle_wq, is_module_idle(dev, system_suspend),
+ ret = wait_event_timeout(dev->idle_wq, is_module_idle(dev),
ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT);
if (ret == 0) {
dev_info(&dev->dev, "%s prevented suspend\n",
@@ -416,20 +436,16 @@ int nvhost_module_suspend(struct nvhost_device *dev, bool system_suspend)
}
if (system_suspend)
- dev_info(&dev->dev, "tegra_grhost: entered idle\n");
+ dev_dbg(&dev->dev, "tegra_grhost: entered idle\n");
mutex_lock(&dev->lock);
- if (dev->can_powergate)
- cancel_delayed_work_sync(&dev->powerstate_down);
- to_state_powergated_locked(dev, system_suspend);
+ cancel_delayed_work(&dev->powerstate_down);
+ to_state_powergated_locked(dev);
mutex_unlock(&dev->lock);
if (dev->suspend)
dev->suspend(dev);
- if (system_suspend)
- pm_runtime_set_suspended(&dev->dev);
-
return 0;
}
@@ -443,10 +459,6 @@ void nvhost_module_deinit(struct nvhost_device *dev)
nvhost_module_suspend(dev, false);
for (i = 0; i < dev->num_clks; i++)
clk_put(dev->clk[i]);
+ dev->powerstate = NVHOST_POWER_STATE_DEINIT;
}
-void nvhost_module_resume(struct nvhost_device *dev)
-{
- pm_runtime_set_autosuspend_delay(&dev->dev, dev->clockgate_delay);
- pm_runtime_use_autosuspend(&dev->dev);
-}
diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h
index 68b18f6f65eb..06a11ff840f2 100644
--- a/drivers/video/tegra/host/nvhost_acm.h
+++ b/drivers/video/tegra/host/nvhost_acm.h
@@ -28,13 +28,12 @@
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/nvhost.h>
-#include <linux/pm_runtime.h>
/* Sets clocks and powergating state for a module */
int nvhost_module_init(struct nvhost_device *ndev);
void nvhost_module_deinit(struct nvhost_device *dev);
int nvhost_module_suspend(struct nvhost_device *dev, bool system_suspend);
-void nvhost_module_resume(struct nvhost_device *dev);
+
void nvhost_module_reset(struct nvhost_device *dev);
void nvhost_module_busy(struct nvhost_device *dev);
void nvhost_module_idle_mult(struct nvhost_device *dev, int refs);
@@ -51,7 +50,7 @@ int nvhost_module_set_rate(struct nvhost_device *dev, void *priv,
static inline bool nvhost_module_powered(struct nvhost_device *dev)
{
- return !pm_runtime_suspended(&dev->dev);
+ return dev->powerstate == NVHOST_POWER_STATE_RUNNING;
}
static inline void nvhost_module_idle(struct nvhost_device *dev)
@@ -59,4 +58,5 @@ static inline void nvhost_module_idle(struct nvhost_device *dev)
nvhost_module_idle_mult(dev, 1);
}
+
#endif
diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c
index 8457b2398cc7..c7b0c82b3e45 100644
--- a/drivers/video/tegra/host/nvhost_channel.c
+++ b/drivers/video/tegra/host/nvhost_channel.c
@@ -151,7 +151,7 @@ int nvhost_channel_suspend(struct nvhost_channel *ch)
BUG_ON(!channel_cdma_op(ch).stop);
if (ch->refcount) {
- ret = nvhost_module_suspend(ch->dev, true);
+ ret = nvhost_module_suspend(ch->dev, false);
if (!ret)
channel_cdma_op(ch).stop(&ch->cdma);
}
diff --git a/include/linux/nvhost.h b/include/linux/nvhost.h
index 9002620a834a..55b75d048791 100644
--- a/include/linux/nvhost.h
+++ b/include/linux/nvhost.h
@@ -38,6 +38,13 @@ struct nvhost_clock {
long default_rate;
};
+enum nvhost_device_powerstate_t {
+ NVHOST_POWER_STATE_DEINIT,
+ NVHOST_POWER_STATE_RUNNING,
+ NVHOST_POWER_STATE_CLOCKGATED,
+ NVHOST_POWER_STATE_POWERGATED
+};
+
struct nvhost_device {
const char *name; /* Device name */
struct device dev; /* Linux device struct */
@@ -67,7 +74,8 @@ struct nvhost_device {
int num_clks; /* Number of clocks opened for dev */
struct clk *clk[NVHOST_MODULE_MAX_CLOCKS];
struct mutex lock; /* Power management lock */
- bool powered; /* Current power state */
+ int powerstate; /* Current power state */
+ int refcount; /* Number of tasks active */
wait_queue_head_t idle_wq; /* Work queue for idle */
struct list_head client_list; /* List of clients and rate requests */