Commit a5073b52 authored by Sergei Shtylyov's avatar Sergei Shtylyov Committed by Greg Kroah-Hartman

musb_gadget: fix unhandled endpoint 0 IRQs

The gadget EP0 code routinely ignores an interrupt at end of
the data phase because of musb_g_ep0_giveback() resetting the
state machine to "idle, waiting for SETUP" phase prematurely.

The driver also prematurely leaves the status phase on
receiving the SetupEnd interrupt.

As there were still unhandled endpoint 0 interrupts happening
from time to time after fixing these issues, there turned to
be yet another culprit: two distinct gadget states collapsed
into one.

The (missing) state that comes after STATUS IN/OUT states was
typically indiscernible from them since the corresponding
interrupts tend to happen within too little period of time
(due to only a zero-length status packet in between) and so
they got coalesced; yet this state is not the same as the next
one which is associated with the reception of a SETUP packet.

Adding this extra state seems to have fixed the rest of the
unhandled interrupts that generic_interrupt() and
davinci_interrupt() hid by faking their result and only
emitting a debug message -- so, stop doing that.
Signed-off-by: default avatarSergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 003051bf
...@@ -372,12 +372,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) ...@@ -372,12 +372,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
/* REVISIT we sometimes get unhandled IRQs return retval;
* (e.g. ep0). not clear why...
*/
if (retval != IRQ_HANDLED)
DBG(5, "unhandled? %08x\n", tmp);
return IRQ_HANDLED;
} }
int musb_platform_set_mode(struct musb *musb, u8 mode) int musb_platform_set_mode(struct musb *musb, u8 mode)
......
...@@ -1481,13 +1481,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci) ...@@ -1481,13 +1481,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
/* REVISIT we sometimes get spurious IRQs on g_ep0 return retval;
* not clear why...
*/
if (retval != IRQ_HANDLED)
DBG(5, "spurious?\n");
return IRQ_HANDLED;
} }
#else #else
......
...@@ -171,7 +171,8 @@ enum musb_h_ep0_state { ...@@ -171,7 +171,8 @@ enum musb_h_ep0_state {
/* peripheral side ep0 states */ /* peripheral side ep0 states */
enum musb_g_ep0_state { enum musb_g_ep0_state {
MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */ MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */
MUSB_EP0_STAGE_SETUP, /* received SETUP */
MUSB_EP0_STAGE_TX, /* IN data */ MUSB_EP0_STAGE_TX, /* IN data */
MUSB_EP0_STAGE_RX, /* OUT data */ MUSB_EP0_STAGE_RX, /* OUT data */
MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */ MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright 2005 Mentor Graphics Corporation * Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -58,7 +59,8 @@ ...@@ -58,7 +59,8 @@
static char *decode_ep0stage(u8 stage) static char *decode_ep0stage(u8 stage)
{ {
switch (stage) { switch (stage) {
case MUSB_EP0_STAGE_SETUP: return "idle"; case MUSB_EP0_STAGE_IDLE: return "idle";
case MUSB_EP0_STAGE_SETUP: return "setup";
case MUSB_EP0_STAGE_TX: return "in"; case MUSB_EP0_STAGE_TX: return "in";
case MUSB_EP0_STAGE_RX: return "out"; case MUSB_EP0_STAGE_RX: return "out";
case MUSB_EP0_STAGE_ACKWAIT: return "wait"; case MUSB_EP0_STAGE_ACKWAIT: return "wait";
...@@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) ...@@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
musb_writew(regs, MUSB_CSR0, musb_writew(regs, MUSB_CSR0,
csr & ~MUSB_CSR0_P_SENTSTALL); csr & ~MUSB_CSR0_P_SENTSTALL);
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP; musb->ep0_state = MUSB_EP0_STAGE_IDLE;
csr = musb_readw(regs, MUSB_CSR0); csr = musb_readw(regs, MUSB_CSR0);
} }
...@@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) ...@@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
if (csr & MUSB_CSR0_P_SETUPEND) { if (csr & MUSB_CSR0_P_SETUPEND) {
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND); musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP; /* Transition into the early status phase */
switch (musb->ep0_state) {
case MUSB_EP0_STAGE_TX:
musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
break;
case MUSB_EP0_STAGE_RX:
musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
break;
default:
ERR("SetupEnd came in a wrong ep0stage %s",
decode_ep0stage(musb->ep0_state));
}
csr = musb_readw(regs, MUSB_CSR0); csr = musb_readw(regs, MUSB_CSR0);
/* NOTE: request may need completion */ /* NOTE: request may need completion */
} }
...@@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) ...@@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
if (req) if (req)
musb_g_ep0_giveback(musb, req); musb_g_ep0_giveback(musb, req);
} }
/*
* In case when several interrupts can get coalesced,
* check to see if we've already received a SETUP packet...
*/
if (csr & MUSB_CSR0_RXPKTRDY)
goto setup;
retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
break;
case MUSB_EP0_STAGE_IDLE:
/*
* This state is typically (but not always) indiscernible
* from the status states since the corresponding interrupts
* tend to happen within too little period of time (with only
* a zero-length packet in between) and so get coalesced...
*/
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP; musb->ep0_state = MUSB_EP0_STAGE_SETUP;
/* FALLTHROUGH */ /* FALLTHROUGH */
case MUSB_EP0_STAGE_SETUP: case MUSB_EP0_STAGE_SETUP:
setup:
if (csr & MUSB_CSR0_RXPKTRDY) { if (csr & MUSB_CSR0_RXPKTRDY) {
struct usb_ctrlrequest setup; struct usb_ctrlrequest setup;
int handled = 0; int handled = 0;
...@@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) ...@@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
stall: stall:
DBG(3, "stall (%d)\n", handled); DBG(3, "stall (%d)\n", handled);
musb->ackpend |= MUSB_CSR0_P_SENDSTALL; musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
musb->ep0_state = MUSB_EP0_STAGE_SETUP; musb->ep0_state = MUSB_EP0_STAGE_IDLE;
finish: finish:
musb_writew(regs, MUSB_CSR0, musb_writew(regs, MUSB_CSR0,
musb->ackpend); musb->ackpend);
...@@ -803,7 +836,7 @@ finish: ...@@ -803,7 +836,7 @@ finish:
/* "can't happen" */ /* "can't happen" */
WARN_ON(1); WARN_ON(1);
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL); musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
musb->ep0_state = MUSB_EP0_STAGE_SETUP; musb->ep0_state = MUSB_EP0_STAGE_IDLE;
break; break;
} }
...@@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value) ...@@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value)
csr |= MUSB_CSR0_P_SENDSTALL; csr |= MUSB_CSR0_P_SENDSTALL;
musb_writew(regs, MUSB_CSR0, csr); musb_writew(regs, MUSB_CSR0, csr);
musb->ep0_state = MUSB_EP0_STAGE_SETUP; musb->ep0_state = MUSB_EP0_STAGE_IDLE;
musb->ackpend = 0; musb->ackpend = 0;
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment