summaryrefslogtreecommitdiff
path: root/drivers/mxc/vpu
diff options
context:
space:
mode:
authorHongzhang Yang <Hongzhang.Yang@freescale.com>2012-12-13 16:44:16 +0800
committerHongzhang Yang <Hongzhang.Yang@freescale.com>2012-12-13 18:56:23 +0800
commit48e8037b7a683d41d127fbc267bee3aaa05b5b6e (patch)
tree3a927c96cdb6e65d730fbef4f6588897988fc229 /drivers/mxc/vpu
parent1d9cd23bfc4556c177c390e56bcb0453fc0f62bf (diff)
ENGR00236879 Enhance VPU driver to handle API call sequence abnormal abort
Some application may exit without calling neccessay API to wrap up VPU after it receives error message. This could lead to system hang because driver will power off VPU (vpu_release) while VPU may still be busy. We require application to strictly follow the API call sequence even in error handling case. Meanwhile, we enhance VPU driver to protect against such abnormal abort, to prevent system hang at least. If the last instance is closed, VPU will gate off or power off only if VPU is idle. Signed-off-by: Hongzhang Yang <Hongzhang.Yang@freescale.com>
Diffstat (limited to 'drivers/mxc/vpu')
-rw-r--r--drivers/mxc/vpu/mxc_vpu.c86
1 files changed, 82 insertions, 4 deletions
diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c
index 3652e54f05cd..bbb89f7339e3 100644
--- a/drivers/mxc/vpu/mxc_vpu.c
+++ b/drivers/mxc/vpu/mxc_vpu.c
@@ -251,8 +251,17 @@ static int vpu_open(struct inode *inode, struct file *filp)
mutex_lock(&vpu_data.lock);
- if (open_count++ == 0 && !IS_ERR(vpu_regulator))
- regulator_enable(vpu_regulator);
+ if (open_count++ == 0) {
+ if (!IS_ERR(vpu_regulator))
+ regulator_enable(vpu_regulator);
+
+#ifdef CONFIG_SOC_IMX6Q
+ clk_enable(vpu_clk);
+ if (READ_REG(BIT_CUR_PC))
+ printk(KERN_DEBUG "Not power off before vpu open!\n");
+ clk_disable(vpu_clk);
+#endif
+ }
filp->private_data = (void *)(&vpu_data);
mutex_unlock(&vpu_data.lock);
@@ -535,11 +544,70 @@ static long vpu_ioctl(struct file *filp, u_int cmd,
*/
static int vpu_release(struct inode *inode, struct file *filp)
{
+ int i;
+ unsigned long timeout;
mutex_lock(&vpu_data.lock);
+
if (open_count > 0 && !(--open_count)) {
- if (!IS_ERR(vpu_regulator))
- regulator_disable(vpu_regulator);
+
+ /* Wait for vpu go to idle state */
+ clk_enable(vpu_clk);
+ timeout = jiffies + HZ;
+ while (READ_REG(BIT_BUSY_FLAG)) {
+ msleep(1);
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_WARNING "VPU timeout during release\n");
+ break;
+ }
+ }
+ clk_disable(vpu_clk);
+
+ /* Clean up interrupt */
+ cancel_work_sync(&vpu_data.work);
+ flush_workqueue(vpu_data.workqueue);
+ irq_status = 0;
+
+ clk_enable(vpu_clk);
+ if (READ_REG(BIT_BUSY_FLAG)) {
+
+ if (cpu_is_mx51() || cpu_is_mx53()) {
+ printk(KERN_ERR
+ "fatal error: can't gate/power off when VPU is busy\n");
+ clk_disable(vpu_clk);
+ mutex_unlock(&vpu_data.lock);
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_SOC_IMX6Q
+ if (cpu_is_mx6dl() || cpu_is_mx6q()) {
+ WRITE_REG(0x11, 0x10F0);
+ timeout = jiffies + HZ;
+ while (READ_REG(0x10F4) != 0x77) {
+ msleep(1);
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ if (READ_REG(0x10F4) != 0x77) {
+ printk(KERN_ERR
+ "fatal error: can't gate/power off when VPU is busy\n");
+ WRITE_REG(0x0, 0x10F0);
+ clk_disable(vpu_clk);
+ mutex_unlock(&vpu_data.lock);
+ return -EFAULT;
+ } else {
+ if (vpu_plat->reset)
+ vpu_plat->reset();
+ WRITE_REG(0x1, BIT_BUSY_FLAG);
+ WRITE_REG(0x1, BIT_CODE_RUN);
+ while (READ_REG(BIT_BUSY_FLAG))
+ ;
+ }
+ }
+#endif
+ }
+ clk_disable(vpu_clk);
vpu_free_buffers();
@@ -548,6 +616,14 @@ static int vpu_release(struct inode *inode, struct file *filp)
share_mem.cpu_addr = 0;
vfree((void *)vshare_mem.cpu_addr);
vshare_mem.cpu_addr = 0;
+
+ vpu_clk_usercount = clk_get_usecount(vpu_clk);
+ for (i = 0; i < vpu_clk_usercount; i++)
+ clk_disable(vpu_clk);
+
+ if (!IS_ERR(vpu_regulator))
+ regulator_disable(vpu_regulator);
+
}
mutex_unlock(&vpu_data.lock);
@@ -891,7 +967,9 @@ static int vpu_resume(struct platform_device *pdev)
WRITE_REG(0x0, BIT_RESET_CTRL);
WRITE_REG(0x0, BIT_CODE_RUN);
/* MX6 RTL has a bug not to init MBC_SET_SUBBLK_EN on reset */
+#ifdef CONFIG_SOC_IMX6Q
WRITE_REG(0x0, MBC_SET_SUBBLK_EN);
+#endif
/*
* Re-load boot code, from the codebuffer in external RAM.