From ac50441720332f22a9d85ac03151d6acb7bc55d6 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 7 Nov 2008 23:57:46 -0300 Subject: [PATCH] V4L/DVB (9595): cx18: Improve handling of outgoing mailboxes detected to be busy cx18: Improve handling of outgoing mailboxes detected to be busy. When encountering a busy mailbox, sleep instead of polling, and wait for interrupt or timeout. If the mailbox is still busy, force it free. When sending commands, make sure we never create a situation where we mark the mailbox busy upon sending, and ensure we always have a method to cleanly recover from a busy mailbox. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-irq.c | 3 +- drivers/media/video/cx18/cx18-mailbox.c | 130 +++++++++++------------- 2 files changed, 64 insertions(+), 69 deletions(-) diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 37b931055d3..4912b3c8e6a 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -167,7 +167,8 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); if (sw1 || sw2 || hw2) - CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); + CX18_DEBUG_HI_IRQ("received interrupts " + "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); /* To do: interrupt-based I2C handling if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index d2975a2e6cb..0de4b9a7bbc 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -92,48 +92,6 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, - u32 *state, u32 *irq, u32 *req) -{ - struct cx18_mailbox __iomem *mb = NULL; - int wait_count = 0; - u32 ack; - - switch (rpu) { - case APU: - mb = &cx->scb->epu2apu_mb; - *state = cx18_readl(cx, &cx->scb->apu_state); - *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); - break; - - case CPU: - mb = &cx->scb->epu2cpu_mb; - *state = cx18_readl(cx, &cx->scb->cpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); - break; - - default: - break; - } - - if (mb == NULL) - return mb; - - do { - *req = cx18_readl(cx, &mb->request); - ack = cx18_readl(cx, &mb->ack); - wait_count++; - } while (*req != ack && wait_count < 600); - - if (*req == ack) { - (*req)++; - if (*req == 0 || *req == 0xffffffff) - *req = 1; - return mb; - } - return NULL; -} - long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) { struct cx18_mailbox __iomem *ack_mb; @@ -164,12 +122,13 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); - u32 state = 0, irq = 0, req, oldreq, err; + u32 state, irq, req, ack, err; struct cx18_mailbox __iomem *mb; + u32 __iomem *xpu_state; wait_queue_head_t *waitq; struct mutex *mb_lock; int timeout = 100; - int sig = 0; + long unsigned int j, ret; int i; if (info == NULL) { @@ -186,10 +145,16 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) case APU: waitq = &cx->mb_apu_waitq; mb_lock = &cx->epu2apu_mb_lock; + irq = IRQ_EPU_TO_APU; + mb = &cx->scb->epu2apu_mb; + xpu_state = &cx->scb->apu_state; break; case CPU: waitq = &cx->mb_cpu_waitq; mb_lock = &cx->epu2cpu_mb_lock; + irq = IRQ_EPU_TO_CPU; + mb = &cx->scb->epu2cpu_mb; + xpu_state = &cx->scb->cpu_state; break; default: CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); @@ -198,51 +163,85 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) mutex_lock(mb_lock); cx18_setup_page(cx, SCB_OFFSET); - mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); - if (mb == NULL) { - mutex_unlock(mb_lock); - CX18_ERR("mb %s busy\n", info->name); - return -EBUSY; - } + /* + * Wait for an in-use mailbox to complete + * + * If the XPU is responding with Ack's, the mailbox shouldn't be in + * a busy state, since we serialize access to it on our end. + * + * If the wait for ack after sending a previous command was interrupted + * by a signal, we may get here and find a busy mailbox. After waiting, + * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. + */ + state = cx18_readl(cx, xpu_state); + req = cx18_readl(cx, &mb->request); + j = msecs_to_jiffies(timeout); + ret = wait_event_timeout(*waitq, + (ack = cx18_readl(cx, &mb->ack)) == req, + j); + if (req != ack) { + /* waited long enough, make the mbox "not busy" from our end */ + cx18_writel(cx, req, &mb->ack); + CX18_ERR("mbox was found stuck busy when setting up for %s; " + "clearing busy and trying to proceed\n", info->name); + } else if (ret != j) + CX18_DEBUG_API("waited %u usecs for busy mbox to be acked\n", + jiffies_to_usecs(j-ret)); + + /* Build the outgoing mailbox */ + req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; - oldreq = req - 1; cx18_writel(cx, cmd, &mb->cmd); for (i = 0; i < args; i++) cx18_writel(cx, data[i], &mb->args[i]); cx18_writel(cx, 0, &mb->error); cx18_writel(cx, req, &mb->request); + cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ + /* Notify the XPU and wait for it to send an Ack back */ if (info->flags & API_FAST) timeout /= 2; + j = msecs_to_jiffies(timeout); + + CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", + irq, info->name); cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); - sig = wait_event_interruptible_timeout( + ret = wait_event_interruptible_timeout( *waitq, cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), - msecs_to_jiffies(timeout)); - if (sig == 0) { + j); + if (ret == 0) { /* Timed out */ - cx18_writel(cx, oldreq, &mb->request); mutex_unlock(mb_lock); - CX18_ERR("sending %s timed out waiting for RPU to respond\n", - info->name); + CX18_ERR("sending %s timed out waiting for RPU " + "acknowledgement\n", info->name); return -EINVAL; - } else if (sig < 0) { + } else if (ret < 0) { /* Interrupted */ - cx18_writel(cx, oldreq, &mb->request); mutex_unlock(mb_lock); - CX18_WARN("sending %s interrupted waiting for RPU to respond\n", - info->name); + CX18_WARN("sending %s was interrupted waiting for RPU" + "acknowledgement\n", info->name); return -EINTR; - } + } else if (ret != j) + CX18_DEBUG_HI_API("waited %u usecs for %s to be acked\n", + jiffies_to_usecs(j-ret), info->name); + /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) data[i] = cx18_readl(cx, &mb->args[i]); err = cx18_readl(cx, &mb->error); mutex_unlock(mb_lock); + + /* + * Wait for XPU to perform extra actions for the caller in some cases. + * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers + * back in a burst shortly thereafter + */ if (info->flags & API_SLOW) cx18_msleep_timeout(300, 0); + if (err) CX18_DEBUG_API("mailbox error %08x for command %s\n", err, info->name); @@ -251,12 +250,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) { - int res = cx18_api_call(cx, cmd, args, data); - - /* Allow a single retry, probably already too late though. - If there is no free mailbox then that is usually an indication - of a more serious problem. */ - return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; + return cx18_api_call(cx, cmd, args, data); } static int cx18_set_filter_param(struct cx18_stream *s) -- 2.41.0