]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00236879 Enhance VPU driver to handle API call sequence abnormal abort
authorHongzhang Yang <Hongzhang.Yang@freescale.com>
Thu, 13 Dec 2012 08:44:16 +0000 (16:44 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:35:48 +0000 (08:35 +0200)
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>
drivers/mxc/vpu/mxc_vpu.c

index 3652e54f05cd533f5a013e968ae6d0be8e69f3cd..bbb89f7339e3defec6ce920bd324df78021d9e60 100644 (file)
@@ -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.