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 <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
committed by
Mauro Carvalho Chehab
parent
d670b6ff4e
commit
ac50441720
@@ -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);
|
cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
|
||||||
|
|
||||||
if (sw1 || sw2 || 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
|
/* To do: interrupt-based I2C handling
|
||||||
if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
|
if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
|
||||||
|
@@ -92,48 +92,6 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
|
|||||||
return NULL;
|
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)
|
long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu)
|
||||||
{
|
{
|
||||||
struct cx18_mailbox __iomem *ack_mb;
|
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[])
|
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
|
||||||
{
|
{
|
||||||
const struct cx18_api_info *info = find_api_info(cmd);
|
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;
|
struct cx18_mailbox __iomem *mb;
|
||||||
|
u32 __iomem *xpu_state;
|
||||||
wait_queue_head_t *waitq;
|
wait_queue_head_t *waitq;
|
||||||
struct mutex *mb_lock;
|
struct mutex *mb_lock;
|
||||||
int timeout = 100;
|
int timeout = 100;
|
||||||
int sig = 0;
|
long unsigned int j, ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (info == NULL) {
|
if (info == NULL) {
|
||||||
@@ -186,10 +145,16 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
|
|||||||
case APU:
|
case APU:
|
||||||
waitq = &cx->mb_apu_waitq;
|
waitq = &cx->mb_apu_waitq;
|
||||||
mb_lock = &cx->epu2apu_mb_lock;
|
mb_lock = &cx->epu2apu_mb_lock;
|
||||||
|
irq = IRQ_EPU_TO_APU;
|
||||||
|
mb = &cx->scb->epu2apu_mb;
|
||||||
|
xpu_state = &cx->scb->apu_state;
|
||||||
break;
|
break;
|
||||||
case CPU:
|
case CPU:
|
||||||
waitq = &cx->mb_cpu_waitq;
|
waitq = &cx->mb_cpu_waitq;
|
||||||
mb_lock = &cx->epu2cpu_mb_lock;
|
mb_lock = &cx->epu2cpu_mb_lock;
|
||||||
|
irq = IRQ_EPU_TO_CPU;
|
||||||
|
mb = &cx->scb->epu2cpu_mb;
|
||||||
|
xpu_state = &cx->scb->cpu_state;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
|
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);
|
mutex_lock(mb_lock);
|
||||||
cx18_setup_page(cx, SCB_OFFSET);
|
cx18_setup_page(cx, SCB_OFFSET);
|
||||||
mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
|
|
||||||
|
|
||||||
if (mb == NULL) {
|
/*
|
||||||
mutex_unlock(mb_lock);
|
* Wait for an in-use mailbox to complete
|
||||||
CX18_ERR("mb %s busy\n", info->name);
|
*
|
||||||
return -EBUSY;
|
* 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);
|
cx18_writel(cx, cmd, &mb->cmd);
|
||||||
for (i = 0; i < args; i++)
|
for (i = 0; i < args; i++)
|
||||||
cx18_writel(cx, data[i], &mb->args[i]);
|
cx18_writel(cx, data[i], &mb->args[i]);
|
||||||
cx18_writel(cx, 0, &mb->error);
|
cx18_writel(cx, 0, &mb->error);
|
||||||
cx18_writel(cx, req, &mb->request);
|
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)
|
if (info->flags & API_FAST)
|
||||||
timeout /= 2;
|
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);
|
cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
|
||||||
|
|
||||||
sig = wait_event_interruptible_timeout(
|
ret = wait_event_interruptible_timeout(
|
||||||
*waitq,
|
*waitq,
|
||||||
cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
|
cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
|
||||||
msecs_to_jiffies(timeout));
|
j);
|
||||||
if (sig == 0) {
|
if (ret == 0) {
|
||||||
/* Timed out */
|
/* Timed out */
|
||||||
cx18_writel(cx, oldreq, &mb->request);
|
|
||||||
mutex_unlock(mb_lock);
|
mutex_unlock(mb_lock);
|
||||||
CX18_ERR("sending %s timed out waiting for RPU to respond\n",
|
CX18_ERR("sending %s timed out waiting for RPU "
|
||||||
info->name);
|
"acknowledgement\n", info->name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (sig < 0) {
|
} else if (ret < 0) {
|
||||||
/* Interrupted */
|
/* Interrupted */
|
||||||
cx18_writel(cx, oldreq, &mb->request);
|
|
||||||
mutex_unlock(mb_lock);
|
mutex_unlock(mb_lock);
|
||||||
CX18_WARN("sending %s interrupted waiting for RPU to respond\n",
|
CX18_WARN("sending %s was interrupted waiting for RPU"
|
||||||
info->name);
|
"acknowledgement\n", info->name);
|
||||||
return -EINTR;
|
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++)
|
for (i = 0; i < MAX_MB_ARGUMENTS; i++)
|
||||||
data[i] = cx18_readl(cx, &mb->args[i]);
|
data[i] = cx18_readl(cx, &mb->args[i]);
|
||||||
err = cx18_readl(cx, &mb->error);
|
err = cx18_readl(cx, &mb->error);
|
||||||
mutex_unlock(mb_lock);
|
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)
|
if (info->flags & API_SLOW)
|
||||||
cx18_msleep_timeout(300, 0);
|
cx18_msleep_timeout(300, 0);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
|
CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
|
||||||
info->name);
|
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 cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
|
||||||
{
|
{
|
||||||
int res = cx18_api_call(cx, cmd, args, data);
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cx18_set_filter_param(struct cx18_stream *s)
|
static int cx18_set_filter_param(struct cx18_stream *s)
|
||||||
|
Reference in New Issue
Block a user