V4L/DVB (9593): cx18: Add outgoing mailbox mutexes and check for ack via waitq vs poll

Add mutexes to ensure exclusive access for outgoing driver to CX23418 mailboxes.
Also wait on a waitq for mailbox acknowledgement from the CX23418 instead of
polling.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Andy Walls 2008-11-06 01:15:41 -03:00 committed by Mauro Carvalho Chehab
parent f68d0cf567
commit 72c2d6d3ac
5 changed files with 60 additions and 63 deletions

View file

@ -446,6 +446,8 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
mutex_init(&cx->i2c_bus_lock[0]); mutex_init(&cx->i2c_bus_lock[0]);
mutex_init(&cx->i2c_bus_lock[1]); mutex_init(&cx->i2c_bus_lock[1]);
mutex_init(&cx->gpio_lock); mutex_init(&cx->gpio_lock);
mutex_init(&cx->epu2apu_mb_lock);
mutex_init(&cx->epu2cpu_mb_lock);
spin_lock_init(&cx->lock); spin_lock_init(&cx->lock);
@ -466,8 +468,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->cap_w);
init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_apu_waitq);
init_waitqueue_head(&cx->mb_cpu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq);
init_waitqueue_head(&cx->mb_epu_waitq);
init_waitqueue_head(&cx->mb_hpu_waitq);
init_waitqueue_head(&cx->dma_waitq); init_waitqueue_head(&cx->dma_waitq);
/* VBI */ /* VBI */

View file

@ -379,6 +379,9 @@ struct cx18 {
u32 hw_flags; /* Hardware description of the board */ u32 hw_flags; /* Hardware description of the board */
unsigned mdl_offset; unsigned mdl_offset;
struct cx18_scb __iomem *scb; /* pointer to SCB */ struct cx18_scb __iomem *scb; /* pointer to SCB */
struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct cx18_av_state av_state; struct cx18_av_state av_state;
@ -428,8 +431,6 @@ struct cx18 {
wait_queue_head_t mb_apu_waitq; wait_queue_head_t mb_apu_waitq;
wait_queue_head_t mb_cpu_waitq; wait_queue_head_t mb_cpu_waitq;
wait_queue_head_t mb_epu_waitq;
wait_queue_head_t mb_hpu_waitq;
wait_queue_head_t cap_w; wait_queue_head_t cap_w;
/* when the current DMA is finished this queue is woken up */ /* when the current DMA is finished this queue is woken up */
wait_queue_head_t dma_waitq; wait_queue_head_t dma_waitq;

View file

@ -38,7 +38,7 @@ void cx18_work_handler(struct work_struct *work)
cx18_dvb_work_handler(cx); cx18_dvb_work_handler(cx);
} }
static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb, int rpu)
{ {
u32 handle = mb->args[0]; u32 handle = mb->args[0];
struct cx18_stream *s = NULL; struct cx18_stream *s = NULL;
@ -59,7 +59,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
" handle %d\n", handle); " handle %d\n", handle);
mb->error = CXERR_NOT_OPEN; mb->error = CXERR_NOT_OPEN;
mb->cmd = 0; mb->cmd = 0;
cx18_mb_ack(cx, mb); cx18_mb_ack(cx, mb, rpu);
return; return;
} }
@ -86,13 +86,13 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
} }
mb->error = 0; mb->error = 0;
mb->cmd = 0; mb->cmd = 0;
cx18_mb_ack(cx, mb); cx18_mb_ack(cx, mb, rpu);
wake_up(&cx->dma_waitq); wake_up(&cx->dma_waitq);
if (s->id != -1) if (s->id != -1)
wake_up(&s->waitq); wake_up(&s->waitq);
} }
static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu)
{ {
char str[256] = { 0 }; char str[256] = { 0 };
char *p; char *p;
@ -102,7 +102,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
str[252] = 0; str[252] = 0;
} }
cx18_mb_ack(cx, mb); cx18_mb_ack(cx, mb, rpu);
CX18_DEBUG_INFO("%x %s\n", mb->args[0], str); CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
p = strchr(str, '.'); p = strchr(str, '.');
if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
@ -119,10 +119,10 @@ static void epu_cmd(struct cx18 *cx, u32 sw1)
switch (mb.cmd) { switch (mb.cmd) {
case CX18_EPU_DMA_DONE: case CX18_EPU_DMA_DONE:
epu_dma_done(cx, &mb); epu_dma_done(cx, &mb, CPU);
break; break;
case CX18_EPU_DEBUG: case CX18_EPU_DEBUG:
epu_debug(cx, &mb); epu_debug(cx, &mb, CPU);
break; break;
default: default:
CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n", CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n",
@ -135,11 +135,6 @@ static void epu_cmd(struct cx18 *cx, u32 sw1)
cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb)); cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb));
CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd); CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd);
} }
if (sw1 & IRQ_HPU_TO_EPU) {
cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb));
CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd);
}
} }
static void xpu_ack(struct cx18 *cx, u32 sw2) static void xpu_ack(struct cx18 *cx, u32 sw2)
@ -148,8 +143,6 @@ static void xpu_ack(struct cx18 *cx, u32 sw2)
wake_up(&cx->mb_cpu_waitq); wake_up(&cx->mb_cpu_waitq);
if (sw2 & IRQ_APU_TO_EPU_ACK) if (sw2 & IRQ_APU_TO_EPU_ACK)
wake_up(&cx->mb_apu_waitq); wake_up(&cx->mb_apu_waitq);
if (sw2 & IRQ_HPU_TO_EPU_ACK)
wake_up(&cx->mb_hpu_waitq);
} }
irqreturn_t cx18_irq_handler(int irq, void *dev_id) irqreturn_t cx18_irq_handler(int irq, void *dev_id)

View file

@ -30,11 +30,6 @@
#define API_FAST (1 << 2) /* Short timeout */ #define API_FAST (1 << 2) /* Short timeout */
#define API_SLOW (1 << 3) /* Additional 300ms timeout */ #define API_SLOW (1 << 3) /* Additional 300ms timeout */
#define APU 0
#define CPU 1
#define EPU 2
#define HPU 3
struct cx18_api_info { struct cx18_api_info {
u32 cmd; u32 cmd;
u8 flags; /* Flags, see above */ u8 flags; /* Flags, see above */
@ -117,10 +112,7 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu
*irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
break; break;
case HPU: default:
mb = &cx->scb->epu2hpu_mb;
*state = cx18_readl(cx, &cx->scb->hpu_state);
*irq = cx18_readl(cx, &cx->scb->epu2hpu_irq);
break; break;
} }
@ -142,25 +134,12 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu
return NULL; return NULL;
} }
long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu)
{ {
const struct cx18_api_info *info = find_api_info(mb->cmd);
struct cx18_mailbox __iomem *ack_mb; struct cx18_mailbox __iomem *ack_mb;
u32 ack_irq; u32 ack_irq;
u8 rpu = CPU;
if (info == NULL && mb->cmd) {
CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
return -EINVAL;
}
if (info)
rpu = info->rpu;
switch (rpu) { switch (rpu) {
case HPU:
ack_irq = IRQ_EPU_TO_HPU_ACK;
ack_mb = &cx->scb->hpu2epu_mb;
break;
case APU: case APU:
ack_irq = IRQ_EPU_TO_APU_ACK; ack_irq = IRQ_EPU_TO_APU_ACK;
ack_mb = &cx->scb->apu2epu_mb; ack_mb = &cx->scb->apu2epu_mb;
@ -170,7 +149,8 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
ack_mb = &cx->scb->cpu2epu_mb; ack_mb = &cx->scb->cpu2epu_mb;
break; break;
default: default:
CX18_WARN("Unknown RPU for command %x\n", mb->cmd); CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
rpu, mb->cmd);
return -EINVAL; return -EINVAL;
} }
@ -187,8 +167,8 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
u32 state = 0, irq = 0, req, oldreq, err; u32 state = 0, irq = 0, req, oldreq, err;
struct cx18_mailbox __iomem *mb; struct cx18_mailbox __iomem *mb;
wait_queue_head_t *waitq; wait_queue_head_t *waitq;
struct mutex *mb_lock;
int timeout = 100; int timeout = 100;
int cnt = 0;
int sig = 0; int sig = 0;
int i; int i;
@ -201,10 +181,27 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
CX18_DEBUG_HI_API("%s\n", info->name); CX18_DEBUG_HI_API("%s\n", info->name);
else else
CX18_DEBUG_API("%s\n", info->name); CX18_DEBUG_API("%s\n", info->name);
switch (info->rpu) {
case APU:
waitq = &cx->mb_apu_waitq;
mb_lock = &cx->epu2apu_mb_lock;
break;
case CPU:
waitq = &cx->mb_cpu_waitq;
mb_lock = &cx->epu2cpu_mb_lock;
break;
default:
CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
return -EINVAL;
}
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); mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
if (mb == NULL) { if (mb == NULL) {
mutex_unlock(mb_lock);
CX18_ERR("mb %s busy\n", info->name); CX18_ERR("mb %s busy\n", info->name);
return -EBUSY; return -EBUSY;
} }
@ -216,34 +213,35 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
cx18_writel(cx, 0, &mb->error); cx18_writel(cx, 0, &mb->error);
cx18_writel(cx, req, &mb->request); cx18_writel(cx, req, &mb->request);
switch (info->rpu) {
case APU: waitq = &cx->mb_apu_waitq; break;
case CPU: waitq = &cx->mb_cpu_waitq; break;
case EPU: waitq = &cx->mb_epu_waitq; break;
case HPU: waitq = &cx->mb_hpu_waitq; break;
default: return -EINVAL;
}
if (info->flags & API_FAST) if (info->flags & API_FAST)
timeout /= 2; timeout /= 2;
cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) sig = wait_event_interruptible_timeout(
&& cnt < 660) { *waitq,
if (cnt > 200 && !in_atomic()) cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
sig = cx18_msleep_timeout(10, 1); msecs_to_jiffies(timeout));
cnt++; if (sig == 0) {
} /* Timed out */
if (sig)
return -EINTR;
if (cnt == 660) {
cx18_writel(cx, oldreq, &mb->request); cx18_writel(cx, oldreq, &mb->request);
CX18_ERR("mb %s failed\n", info->name); mutex_unlock(mb_lock);
CX18_ERR("sending %s timed out waiting for RPU to respond\n",
info->name);
return -EINVAL; return -EINVAL;
} else if (sig < 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);
return -EINTR;
} }
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);
if (!in_atomic() && (info->flags & API_SLOW)) mutex_unlock(mb_lock);
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,

View file

@ -30,6 +30,11 @@
#define MB_RESERVED_HANDLE_0 0 #define MB_RESERVED_HANDLE_0 0
#define MB_RESERVED_HANDLE_1 0xFFFFFFFF #define MB_RESERVED_HANDLE_1 0xFFFFFFFF
#define APU 0
#define CPU 1
#define EPU 2
#define HPU 3
struct cx18; struct cx18;
/* The cx18_mailbox struct is the mailbox structure which is used for passing /* The cx18_mailbox struct is the mailbox structure which is used for passing
@ -68,6 +73,6 @@ int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
int cx18_api_func(void *priv, u32 cmd, int in, int out, int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA]); u32 data[CX2341X_MBOX_MAX_DATA]);
long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb); long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu);
#endif #endif