rk29: vpu: add time out error log and error recovery mechanism
author陈恒明 <chm@rock-chips.com>
Thu, 25 Aug 2011 10:32:52 +0000 (18:32 +0800)
committer陈恒明 <chm@rock-chips.com>
Thu, 25 Aug 2011 10:32:52 +0000 (18:32 +0800)
arch/arm/mach-rk29/vpu_service.c

index 374f9dac4e832594a25413f1b531d778364bda86..5859294798193106ae5ed0d67a86f1212a0d6b36 100644 (file)
@@ -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, &reg->session->task_running);
        wake_up_interruptible_sync(&reg->session->wait);
        spin_unlock(&service.lock);
 }
@@ -318,7 +335,8 @@ void reg_copy_to_hw(vpu_reg *reg)
 {
        int i;
        u32 *src = (u32 *)&reg->reg[0];
-       atomic_add(1, &service.task_running);
+       atomic_add(1, &service.total_running);
+       atomic_add(1, &reg->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, &reg->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();