Commit 1bd8372f authored by Hongzhang Yang's avatar Hongzhang Yang Committed by Jason Liu

ENGR00232530 Refine VPU suspend/resume according to open_count

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: default avatarHongzhang Yang <Hongzhang.Yang@freescale.com>
parent 4b5f7bee
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment