diff options
author | Richard Zhao <richard.zhao@freescale.com> | 2010-12-06 10:50:52 +0800 |
---|---|---|
committer | Richard Zhao <richard.zhao@freescale.com> | 2010-12-07 13:01:46 +0800 |
commit | 50459664ec1e90cc0a890d1453207b84aa5b19b0 (patch) | |
tree | 26b00d6e12badb7607274e239c539a39fc4b002d | |
parent | 39a4339eb829aef051956e9ababf1c0c47a0b559 (diff) |
ENGR00135971 GPU: move clk_enable/disable out of timer handler
For timer based power autogating, we have to move clk_enable/disable out of
timer handler, because they become may sleep.
Signed-off-by: Richard Zhao <richard.zhao@freescale.com>
-rw-r--r-- | drivers/mxc/amd-gpu/platform/hal/linux/misc.c | 66 |
1 files changed, 52 insertions, 14 deletions
diff --git a/drivers/mxc/amd-gpu/platform/hal/linux/misc.c b/drivers/mxc/amd-gpu/platform/hal/linux/misc.c index a356f334b18..877ea332e00 100644 --- a/drivers/mxc/amd-gpu/platform/hal/linux/misc.c +++ b/drivers/mxc/amd-gpu/platform/hal/linux/misc.c @@ -23,6 +23,8 @@ #include <linux/timer.h> #include <linux/spinlock.h> #include <linux/slab.h> +#include <linux/hardirq.h> +#include <linux/semaphore.h> typedef struct _gsl_autogate_t { struct timer_list timer; @@ -30,14 +32,27 @@ typedef struct _gsl_autogate_t { int active; int timeout; gsl_device_t *dev; + struct work_struct dis_task; } gsl_autogate_t; +static gsl_autogate_t *g_autogate[2]; +static DECLARE_MUTEX(sem_dev); #define KGSL_DEVICE_IDLE_TIMEOUT 5000 /* unit ms */ -int kgsl_device_active(gsl_device_t *dev) +static void clk_disable_task(struct work_struct *work) +{ + gsl_autogate_t *autogate; + autogate = container_of(work, gsl_autogate_t, dis_task); + if (autogate->dev->ftbl.device_idle) + autogate->dev->ftbl.device_idle(autogate->dev, GSL_TIMEOUT_DEFAULT); + kgsl_clock(autogate->dev->id, 0); +} + +static int _kgsl_device_active(gsl_device_t *dev, int all) { unsigned long flags; + int to_active = 0; gsl_autogate_t *autogate = dev->autogate; if (!autogate) { printk(KERN_ERR "%s: autogate has exited!\n", __func__); @@ -46,13 +61,32 @@ int kgsl_device_active(gsl_device_t *dev) // printk(KERN_ERR "%s:%d id %d active %d\n", __func__, __LINE__, dev->id, autogate->active); spin_lock_irqsave(&autogate->lock, flags); - if (!autogate->active) - kgsl_clock(autogate->dev->id, 1); - autogate->active = 1; + if (in_interrupt()) { + if (!autogate->active) + BUG(); + } else { + to_active = !autogate->active; + autogate->active = 1; + } mod_timer(&autogate->timer, jiffies + msecs_to_jiffies(autogate->timeout)); spin_unlock_irqrestore(&autogate->lock, flags); + if (to_active) + kgsl_clock(autogate->dev->id, 1); + if (to_active && all) { + int index; + index = autogate->dev->id == GSL_DEVICE_G12 ? GSL_DEVICE_YAMATO - 1 : + GSL_DEVICE_G12 - 1; + down(&sem_dev); + if (g_autogate[index]) + _kgsl_device_active(g_autogate[index]->dev, 0); + up(&sem_dev); + } return 0; } +int kgsl_device_active(gsl_device_t *dev) +{ + return _kgsl_device_active(dev, 1); +} static void kgsl_device_inactive(unsigned long data) { @@ -63,11 +97,10 @@ static void kgsl_device_inactive(unsigned long data) del_timer(&autogate->timer); spin_lock_irqsave(&autogate->lock, flags); WARN(!autogate->active, "GPU Device %d is already inactive\n", autogate->dev->id); - autogate->active = 0; - /* idle check may sleep, so don't use it */ -// if (autogate->dev->ftbl.device_idle) -// autogate->dev->ftbl.device_idle(autogate->dev, GSL_TIMEOUT_DEFAULT); - kgsl_clock(autogate->dev->id, 0); + if (autogate->active) { + autogate->active = 0; + schedule_work(&autogate->dis_task); + } spin_unlock_irqrestore(&autogate->lock, flags); } @@ -100,6 +133,7 @@ int kgsl_device_autogate_init(gsl_device_t *dev) printk(KERN_ERR "%s: out of memory!\n", __func__); return -ENOMEM; } + down(&sem_dev); autogate->dev = dev; autogate->active = 1; spin_lock_init(&autogate->lock); @@ -109,7 +143,10 @@ int kgsl_device_autogate_init(gsl_device_t *dev) autogate->timer.function = kgsl_device_inactive; autogate->timer.data = (unsigned long)autogate; add_timer(&autogate->timer); + INIT_WORK(&autogate->dis_task, clk_disable_task); dev->autogate = autogate; + g_autogate[dev->id - 1] = autogate; + up(&sem_dev); return 0; } @@ -118,12 +155,13 @@ void kgsl_device_autogate_exit(gsl_device_t *dev) gsl_autogate_t *autogate = dev->autogate; // printk(KERN_ERR "%s:%d id %d active %d\n", __func__, __LINE__, dev->id, autogate->active); - if (autogate->active) - del_timer(&autogate->timer); - else + down(&sem_dev); + del_timer_sync(&autogate->timer); + if (!autogate->active) kgsl_clock(autogate->dev->id, 1); - + flush_work(&autogate->dis_task); + g_autogate[dev->id - 1] = NULL; + up(&sem_dev); kfree(autogate); dev->autogate = NULL; - } |