From ae1f9efe299b2f24680b04e429c76dec3240b60d Mon Sep 17 00:00:00 2001 From: Simone Willett Date: Wed, 15 Feb 2012 14:22:37 -0800 Subject: 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 Reviewed-by: Mayuresh Kulkarni Tested-by: Lokesh Pathak Reviewed-by: Lokesh Pathak --- drivers/video/tegra/host/bus.c | 25 +-- drivers/video/tegra/host/dev.c | 28 +-- drivers/video/tegra/host/host1x/host1x_debug.c | 4 +- drivers/video/tegra/host/host1x/host1x_syncpt.c | 5 +- drivers/video/tegra/host/nvhost_acm.c | 228 +++++++++++++----------- drivers/video/tegra/host/nvhost_acm.h | 6 +- drivers/video/tegra/host/nvhost_channel.c | 2 +- include/linux/nvhost.h | 10 +- 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 #include -#include + #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 #define CREATE_TRACE_POINTS #include -#include + #include #include @@ -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 #include #include -#include #include #include #include -#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 #include #include -#include /* 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 */ -- cgit v1.2.3