]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00232530 Refine VPU suspend/resume according to open_count
authorHongzhang Yang <Hongzhang.Yang@freescale.com>
Tue, 6 Nov 2012 06:49:38 +0000 (14:49 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:35:47 +0000 (08:35 +0200)
1. Refine VPU suspend/resume according to open_count to completely
fix bug: ENGR00230203 [Android_MX6DL_SD] Gallery: System hang
after resume from suspend during video playback. 20%

open_count == 0 case can be simplified because VPU is released
(all instances are freed), so
- clock is already off
- context is no longer needed
- power is already off on MX6

VPU reset is removed from resume because power is ensured to be off
before entering resume on MX6 by calling regulator API.

2. Fix bug: VPU always busy after suspend/resume

Error log (VPU refused to suspend due to VPU busy):
pm_op(): platform_pm_suspend+0x0/0x54 returns -11
PM: Device mxc_vpu failed to suspend: error -11
PM: Some devices failed to suspend

Root cause:
- Suspend happened during vpu_Init(), somewhere after VPU lib started
  to download FW (when PC == 0), but before run FW. (BIT_BUSY_FLAG=1,
  BIT_CODE_RUN=1).
- In such case, VPU resume downloaded FW and run VPU to idle because
  suspend was triggered after VPU was opened (active).
- vpu_Init run VPU again with BIT_BUSY_FLAG=1. So VPU was trapped in
  idle loop but BIT_BUSY_FLAG was never cleared. VPU lib regarded VPU
  as always busy.

Solution (in VPU resume):
- run VPU FW only if VPU was opened and PC before suspend is not 0
- restore host register is required
- download FW is required, because program memory is lost after power
  off.

Signed-off-by: Hongzhang Yang <Hongzhang.Yang@freescale.com>
drivers/mxc/vpu/mxc_vpu.c

index f473fdf681ba43598e59770914efd11b3be6b7b1..3652e54f05cd533f5a013e968ae6d0be8e69f3cd 100644 (file)
@@ -112,6 +112,7 @@ static int vpu_jpu_irq;
 
 static unsigned int regBk[64];
 static struct regulator *vpu_regulator;
+static unsigned int pc_before_suspend;
 
 #define        READ_REG(x)             __raw_readl(vpu_base + x)
 #define        WRITE_REG(val, x)       __raw_writel(val, vpu_base + x)
@@ -716,9 +717,16 @@ static int vpu_dev_probe(struct platform_device *pdev)
                goto err_out_class;
        vpu_regulator = regulator_get(NULL, "cpu_vddvpu");
        if (IS_ERR(vpu_regulator)) {
-               printk(KERN_ERR
-                       "%s: failed to get vpu regulator\n", __func__);
-               goto err_out_class;
+               if (!(cpu_is_mx51() || cpu_is_mx53())) {
+                       printk(KERN_ERR
+                               "%s: failed to get vpu regulator\n", __func__);
+                       goto err_out_class;
+               } else {
+                       /* regulator_get will return error on MX5x,
+                        * just igore it everywhere*/
+                       printk(KERN_WARNING
+                               "%s: failed to get vpu regulator\n", __func__);
+               }
        }
 
 #ifdef MXC_VPU_HAS_JPU
@@ -778,140 +786,152 @@ static int vpu_suspend(struct platform_device *pdev, pm_message_t state)
        int i;
        unsigned long timeout;
 
-       if (!IS_ERR(vpu_regulator))
-               regulator_enable(vpu_regulator);
-       /* Wait for vpu go to idle state, suspect vpu cannot be changed
-          to idle state after about 1 sec */
-       if (open_count > 0) {
+       mutex_lock(&vpu_data.lock);
+       if (open_count == 0) {
+               /* VPU is released (all instances are freed),
+                * clock is already off, context is no longer needed,
+                * power is already off on MX6,
+                * gate power on MX51 */
+               if (cpu_is_mx51()) {
+                       if (vpu_plat->pg)
+                               vpu_plat->pg(1);
+               }
+       } else {
+               /* Wait for vpu go to idle state, suspect vpu cannot be changed
+                  to idle state after about 1 sec */
                timeout = jiffies + HZ;
                clk_enable(vpu_clk);
                while (READ_REG(BIT_BUSY_FLAG)) {
                        msleep(1);
-                       if (time_after(jiffies, timeout))
-                               goto out;
+                       if (time_after(jiffies, timeout)) {
+                               clk_disable(vpu_clk);
+                               mutex_unlock(&vpu_data.lock);
+                               return -EAGAIN;
+                       }
                }
                clk_disable(vpu_clk);
-       }
 
-       /* Make sure clock is disabled before suspend */
-       vpu_clk_usercount = clk_get_usecount(vpu_clk);
-       for (i = 0; i < vpu_clk_usercount; i++)
-               clk_disable(vpu_clk);
+               /* Make sure clock is disabled before suspend */
+               vpu_clk_usercount = clk_get_usecount(vpu_clk);
+               for (i = 0; i < vpu_clk_usercount; i++)
+                       clk_disable(vpu_clk);
 
-       if (cpu_is_mx53())
-               return 0;
+               if (cpu_is_mx53()) {
+                       mutex_unlock(&vpu_data.lock);
+                       return 0;
+               }
 
-       if (bitwork_mem.cpu_addr != 0) {
-               clk_enable(vpu_clk);
-               /* Save 64 registers from BIT_CODE_BUF_ADDR */
-               for (i = 0; i < 64; i++)
-                       regBk[i] = READ_REG(BIT_CODE_BUF_ADDR + (i * 4));
-               clk_disable(vpu_clk);
-       }
+               if (bitwork_mem.cpu_addr != 0) {
+                       clk_enable(vpu_clk);
+                       /* Save 64 registers from BIT_CODE_BUF_ADDR */
+                       for (i = 0; i < 64; i++)
+                               regBk[i] = READ_REG(BIT_CODE_BUF_ADDR + (i * 4));
+                       pc_before_suspend = READ_REG(BIT_CUR_PC);
+                       clk_disable(vpu_clk);
+               }
 
-       if (vpu_plat->pg)
-               vpu_plat->pg(1);
+               if (vpu_plat->pg)
+                       vpu_plat->pg(1);
 
-       /* If VPU is working before suspend, disable
-        * regulator to make usecount right. */
-       if (open_count > 0) {
+               /* If VPU is working before suspend, disable
+                * regulator to make usecount right. */
                if (!IS_ERR(vpu_regulator))
                        regulator_disable(vpu_regulator);
        }
 
-       if (!IS_ERR(vpu_regulator))
-               regulator_disable(vpu_regulator);
+       mutex_unlock(&vpu_data.lock);
        return 0;
-
-out:
-       clk_disable(vpu_clk);
-       if (!IS_ERR(vpu_regulator))
-               regulator_disable(vpu_regulator);
-       return -EAGAIN;
-
 }
 
 static int vpu_resume(struct platform_device *pdev)
 {
        int i;
 
-       if (cpu_is_mx53())
-               goto recover_clk;
-
-       if (!IS_ERR(vpu_regulator))
-               regulator_enable(vpu_regulator);
-       if (vpu_plat->pg)
-               vpu_plat->pg(0);
+       mutex_lock(&vpu_data.lock);
+       if (open_count == 0) {
+               /* VPU is released (all instances are freed),
+                * clock should be kept off, context is no longer needed,
+                * power should be kept off on MX6,
+                * disable power gating on MX51 */
+               if (cpu_is_mx51()) {
+                       if (vpu_plat->pg)
+                               vpu_plat->pg(0);
+               }
+       } else {
+               if (cpu_is_mx53())
+                       goto recover_clk;
 
-       /* If VPU is working before suspend, enable
-        * regulator to make usecount right. */
-       if (open_count > 0) {
+               /* If VPU is working before suspend, enable
+                * regulator to make usecount right. */
                if (!IS_ERR(vpu_regulator))
                        regulator_enable(vpu_regulator);
-       }
 
-       if (bitwork_mem.cpu_addr != 0) {
-               u32 *p = (u32 *) bitwork_mem.cpu_addr;
-               u32 data, pc;
-               u16 data_hi;
-               u16 data_lo;
+               if (vpu_plat->pg)
+                       vpu_plat->pg(0);
 
-               clk_enable(vpu_clk);
+               if (bitwork_mem.cpu_addr != 0) {
+                       u32 *p = (u32 *) bitwork_mem.cpu_addr;
+                       u32 data, pc;
+                       u16 data_hi;
+                       u16 data_lo;
 
-               /* reset VPU in case of voltage change */
-               if (vpu_plat->reset)
-                       vpu_plat->reset();
+                       clk_enable(vpu_clk);
 
-               pc = READ_REG(BIT_CUR_PC);
-               if (pc) {
-                       clk_disable(vpu_clk);
-                       goto recover_clk;
-               }
+                       pc = READ_REG(BIT_CUR_PC);
+                       if (pc) {
+                               printk(KERN_WARNING "Not power off after suspend (PC=0x%x)\n", pc);
+                               clk_disable(vpu_clk);
+                               goto recover_clk;
+                       }
 
-               /* Restore registers */
-               for (i = 0; i < 64; i++)
-                       WRITE_REG(regBk[i], BIT_CODE_BUF_ADDR + (i * 4));
-
-               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 */
-               WRITE_REG(0x0, MBC_SET_SUBBLK_EN);
-
-               /*
-                * Re-load boot code, from the codebuffer in external RAM.
-                * Thankfully, we only need 4096 bytes, same for all platforms.
-                */
-               for (i = 0; i < 2048; i += 4) {
-                       data = p[(i / 2) + 1];
-                       data_hi = (data >> 16) & 0xFFFF;
-                       data_lo = data & 0xFFFF;
-                       WRITE_REG((i << 16) | data_hi, BIT_CODE_DOWN);
-                       WRITE_REG(((i + 1) << 16) | data_lo,
-                                 BIT_CODE_DOWN);
-
-                       data = p[i / 2];
-                       data_hi = (data >> 16) & 0xFFFF;
-                       data_lo = data & 0xFFFF;
-                       WRITE_REG(((i + 2) << 16) | data_hi,
-                                 BIT_CODE_DOWN);
-                       WRITE_REG(((i + 3) << 16) | data_lo,
-                                 BIT_CODE_DOWN);
-               }
+                       /* Restore registers */
+                       for (i = 0; i < 64; i++)
+                               WRITE_REG(regBk[i], BIT_CODE_BUF_ADDR + (i * 4));
+
+                       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 */
+                       WRITE_REG(0x0, MBC_SET_SUBBLK_EN);
+
+                       /*
+                        * Re-load boot code, from the codebuffer in external RAM.
+                        * Thankfully, we only need 4096 bytes, same for all platforms.
+                        */
+                       for (i = 0; i < 2048; i += 4) {
+                               data = p[(i / 2) + 1];
+                               data_hi = (data >> 16) & 0xFFFF;
+                               data_lo = data & 0xFFFF;
+                               WRITE_REG((i << 16) | data_hi, BIT_CODE_DOWN);
+                               WRITE_REG(((i + 1) << 16) | data_lo,
+                                               BIT_CODE_DOWN);
+
+                               data = p[i / 2];
+                               data_hi = (data >> 16) & 0xFFFF;
+                               data_lo = data & 0xFFFF;
+                               WRITE_REG(((i + 2) << 16) | data_hi,
+                                               BIT_CODE_DOWN);
+                               WRITE_REG(((i + 3) << 16) | data_lo,
+                                               BIT_CODE_DOWN);
+                       }
 
-               WRITE_REG(0x1, BIT_BUSY_FLAG);
-               WRITE_REG(0x1, BIT_CODE_RUN);
-               while (READ_REG(BIT_BUSY_FLAG))
-                       ;
-               clk_disable(vpu_clk);
-       }
+                       if (pc_before_suspend) {
+                               WRITE_REG(0x1, BIT_BUSY_FLAG);
+                               WRITE_REG(0x1, BIT_CODE_RUN);
+                               while (READ_REG(BIT_BUSY_FLAG))
+                                       ;
+                       } else {
+                               printk(KERN_WARNING "PC=0 before suspend\n");
+                       }
+                       clk_disable(vpu_clk);
+               }
 
 recover_clk:
-       /* Recover vpu clock */
-       for (i = 0; i < vpu_clk_usercount; i++)
-               clk_enable(vpu_clk);
-       if (!IS_ERR(vpu_regulator))
-               regulator_disable(vpu_regulator);
+               /* Recover vpu clock */
+               for (i = 0; i < vpu_clk_usercount; i++)
+                       clk_enable(vpu_clk);
+       }
 
+       mutex_unlock(&vpu_data.lock);
        return 0;
 }
 #else