aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Zhao <richard.zhao@freescale.com>2010-12-06 10:50:52 +0800
committerRichard Zhao <richard.zhao@freescale.com>2010-12-07 13:01:46 +0800
commit50459664ec1e90cc0a890d1453207b84aa5b19b0 (patch)
tree26b00d6e12badb7607274e239c539a39fc4b002d
parent39a4339eb829aef051956e9ababf1c0c47a0b559 (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.c66
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 a356f334b187..877ea332e00f 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;
-
}