From 1699eadd4de2e6a4c28351224586f01f6ec99d13 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E6=81=92=E6=98=8E?= Date: Thu, 25 Aug 2011 18:32:52 +0800 Subject: [PATCH] rk29: vpu: add time out error log and error recovery mechanism --- arch/arm/mach-rk29/vpu_service.c | 85 ++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-rk29/vpu_service.c b/arch/arm/mach-rk29/vpu_service.c index 374f9dac4e83..585929479819 100644 --- a/arch/arm/mach-rk29/vpu_service.c +++ b/arch/arm/mach-rk29/vpu_service.c @@ -97,6 +97,7 @@ typedef struct vpu_session { struct list_head done; wait_queue_head_t wait; pid_t pid; + atomic_t task_running; } vpu_session; /** @@ -126,7 +127,7 @@ typedef struct vpu_service_info { struct list_head running; /* link to link_reg in struct vpu_reg */ struct list_head done; /* link to link_reg in struct vpu_reg */ struct list_head session; /* link to list_session in struct vpu_session */ - atomic_t task_running; + atomic_t total_running; bool enabled; vpu_reg *reg_codec; vpu_reg *reg_pproc; @@ -168,6 +169,21 @@ static void vpu_put_clk(void) clk_put(hclk_cpu_vcodec); } +static void vpu_reset(void) +{ + clk_disable(aclk_ddr_vepu); + cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, true); + cru_set_soft_reset(SOFT_RST_DDR_VCODEC_PORT, true); + cru_set_soft_reset(SOFT_RST_VCODEC_AHB_BUS, true); + cru_set_soft_reset(SOFT_RST_VCODEC_AXI_BUS, true); + mdelay(10); + cru_set_soft_reset(SOFT_RST_VCODEC_AXI_BUS, false); + cru_set_soft_reset(SOFT_RST_VCODEC_AHB_BUS, false); + cru_set_soft_reset(SOFT_RST_DDR_VCODEC_PORT, false); + cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, false); + clk_enable(aclk_ddr_vepu); +} + static void vpu_service_power_off(void) { if (!service.enabled) @@ -176,7 +192,7 @@ static void vpu_service_power_off(void) service.enabled = false; printk("vpu: power off\n"); - while(atomic_read(&service.task_running)) { + while(atomic_read(&service.total_running)) { pr_alert("power off when task running!!\n"); udelay(10); } @@ -191,7 +207,7 @@ static void vpu_service_power_off(void) static void vpu_service_power_off_work_func(unsigned long data) { - printk("vpu: delayed power off work\n"); + printk("delayed "); vpu_service_power_off(); } @@ -310,6 +326,7 @@ static void reg_from_run_to_done(vpu_reg *reg) break; } } + atomic_sub(1, ®->session->task_running); wake_up_interruptible_sync(®->session->wait); spin_unlock(&service.lock); } @@ -318,7 +335,8 @@ void reg_copy_to_hw(vpu_reg *reg) { int i; u32 *src = (u32 *)®->reg[0]; - atomic_add(1, &service.task_running); + atomic_add(1, &service.total_running); + atomic_add(1, ®->session->task_running); switch (reg->type) { case VPU_ENC : { u32 *dst = (u32 *)enc_dev.hwregs; @@ -379,7 +397,8 @@ void reg_copy_to_hw(vpu_reg *reg) } break; default : { pr_err("unsupport session type %d", reg->type); - atomic_sub(1, &service.task_running); + atomic_sub(1, &service.total_running); + atomic_sub(1, ®->session->task_running); break; } } @@ -492,6 +511,7 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd, unsigned long case VPU_IOC_GET_REG : { vpu_request req; vpu_reg *reg; + unsigned long flag; if (copy_from_user(&req, (void __user *)arg, sizeof(vpu_request))) { pr_err("VPU_IOC_GET_REG copy_from_user failed\n"); return -EFAULT; @@ -499,19 +519,38 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd, unsigned long int ret = wait_event_interruptible_timeout(session->wait, !list_empty(&session->done), TIMEOUT_DELAY); if (unlikely(ret < 0)) { pr_err("pid %d wait task ret %d\n", session->pid, ret); - return ret; } else if (0 == ret) { - pr_err("pid %d wait task done timeout\n", session->pid); - return -ETIMEDOUT; + int task_running = atomic_read(&session->task_running); + pr_err("pid %d wait %d task done timeout\n", session->pid, task_running); + if (task_running) { + atomic_set(&session->task_running, 0); + atomic_sub(task_running, &service.total_running); + pr_err("%d task is running but not return, reset hardware...", task_running); + vpu_reset(); + pr_err("done\n"); + } + ret = -ETIMEDOUT; + } + if (ret < 0) { + vpu_reg *reg, *n; + spin_lock_irqsave(&service.lock, flag); + list_for_each_entry_safe(reg, n, &session->waiting, session_link) { + reg_deinit(reg); + } + list_for_each_entry_safe(reg, n, &session->running, session_link) { + reg_deinit(reg); + } + list_for_each_entry_safe(reg, n, &session->done, session_link) { + reg_deinit(reg); + } + spin_unlock_irqrestore(&service.lock, flag); + return ret; } } - { - unsigned long flag; - spin_lock_irqsave(&service.lock, flag); - reg = list_entry(session->done.next, vpu_reg, session_link); - return_reg(reg, (u32 __user *)req.req); - spin_unlock_irqrestore(&service.lock, flag); - } + spin_lock_irqsave(&service.lock, flag); + reg = list_entry(session->done.next, vpu_reg, session_link); + return_reg(reg, (u32 __user *)req.req); + spin_unlock_irqrestore(&service.lock, flag); break; } default : { @@ -620,6 +659,7 @@ static int vpu_service_open(struct inode *inode, struct file *filp) init_waitqueue_head(&session->wait); /* no need to protect */ list_add_tail(&session->list_session, &service.session); + atomic_set(&session->task_running, 0); filp->private_data = (void *)session; pr_debug("dev opened\n"); @@ -629,13 +669,18 @@ static int vpu_service_open(struct inode *inode, struct file *filp) static int vpu_service_release(struct inode *inode, struct file *filp) { unsigned long flag; + int task_running; vpu_session *session = (vpu_session *)filp->private_data; if (NULL == session) return -EINVAL; + task_running = atomic_read(&session->task_running); + if (task_running) { + pr_err("vpu_service session %d still has %d task running when closing\n", session->pid, task_running); + msleep(50); + } wake_up_interruptible_sync(&session->wait); - msleep(50); /* remove this filp from the asynchronusly notified filp's */ //vpu_service_fasync(-1, filp, 0); list_del(&session->list_session); @@ -912,7 +957,7 @@ static irqreturn_t vdpu_isr(int irq, void *dev_id) /* clear dec IRQ */ writel(irq_status_dec & (~DEC_INTERRUPT_BIT), dev->hwregs + DEC_INTERRUPT_REGISTER); pr_debug("DEC IRQ received!\n"); - atomic_sub(1, &service.task_running); + atomic_sub(1, &service.total_running); if (NULL == service.reg_codec) { pr_err("dec isr with no task waiting\n"); } else { @@ -924,7 +969,7 @@ static irqreturn_t vdpu_isr(int irq, void *dev_id) /* clear pp IRQ */ writel(irq_status_pp & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER); pr_debug("PP IRQ received!\n"); - atomic_sub(1, &service.task_running); + atomic_sub(1, &service.total_running); if (NULL == service.reg_pproc) { pr_err("pp isr with no task waiting\n"); } else { @@ -946,7 +991,7 @@ static irqreturn_t vepu_isr(int irq, void *dev_id) /* clear enc IRQ */ writel(irq_status & (~ENC_INTERRUPT_BIT), dev->hwregs + ENC_INTERRUPT_REGISTER); pr_debug("ENC IRQ received!\n"); - atomic_sub(1, &service.task_running); + atomic_sub(1, &service.total_running); if (NULL == service.reg_codec) { pr_err("enc isr with no task waiting\n"); } else { @@ -975,7 +1020,7 @@ static int __init vpu_service_init(void) spin_lock_init(&service.lock); service.reg_codec = NULL; service.reg_pproc = NULL; - atomic_set(&service.task_running, 0); + atomic_set(&service.total_running, 0); service.enabled = false; vpu_get_clk(); -- 2.34.1