203 lines
6.8 KiB
Diff
203 lines
6.8 KiB
Diff
From 5424305125492a2417bde7c6d23ee4b84e25f6be Mon Sep 17 00:00:00 2001
|
|
From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
|
|
Date: Fri, 27 Mar 2009 12:52:43 -0700
|
|
Subject: [PATCH] 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: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
|
|
Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
|
|
---
|
|
drivers/usb/musb/davinci.c | 7 +----
|
|
drivers/usb/musb/musb_core.c | 8 +-----
|
|
drivers/usb/musb/musb_core.h | 3 +-
|
|
drivers/usb/musb/musb_gadget_ep0.c | 45 +++++++++++++++++++++++++++++++----
|
|
4 files changed, 43 insertions(+), 20 deletions(-)
|
|
|
|
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
|
|
index 2dc7606..399c435 100644
|
|
--- a/drivers/usb/musb/davinci.c
|
|
+++ b/drivers/usb/musb/davinci.c
|
|
@@ -357,12 +357,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
|
|
|
spin_unlock_irqrestore(&musb->lock, flags);
|
|
|
|
- /* REVISIT we sometimes get unhandled IRQs
|
|
- * (e.g. ep0). not clear why...
|
|
- */
|
|
- if (retval != IRQ_HANDLED)
|
|
- DBG(5, "unhandled? %08x\n", tmp);
|
|
- return IRQ_HANDLED;
|
|
+ return retval;
|
|
}
|
|
|
|
int musb_platform_set_mode(struct musb *musb, u8 mode)
|
|
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
|
|
index 3019725..a1de43b 100644
|
|
--- a/drivers/usb/musb/musb_core.c
|
|
+++ b/drivers/usb/musb/musb_core.c
|
|
@@ -1481,13 +1481,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
|
|
|
|
spin_unlock_irqrestore(&musb->lock, flags);
|
|
|
|
- /* REVISIT we sometimes get spurious IRQs on g_ep0
|
|
- * not clear why...
|
|
- */
|
|
- if (retval != IRQ_HANDLED)
|
|
- DBG(5, "spurious?\n");
|
|
-
|
|
- return IRQ_HANDLED;
|
|
+ return retval;
|
|
}
|
|
|
|
#else
|
|
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
|
|
index adf1806..f56a56c 100644
|
|
--- a/drivers/usb/musb/musb_core.h
|
|
+++ b/drivers/usb/musb/musb_core.h
|
|
@@ -171,7 +171,8 @@ enum musb_h_ep0_state {
|
|
|
|
/* peripheral side ep0 states */
|
|
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_RX, /* OUT data */
|
|
MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
|
|
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
|
|
index 3f5e30d..ec0e899 100644
|
|
--- a/drivers/usb/musb/musb_gadget_ep0.c
|
|
+++ b/drivers/usb/musb/musb_gadget_ep0.c
|
|
@@ -4,6 +4,7 @@
|
|
* Copyright 2005 Mentor Graphics Corporation
|
|
* Copyright (C) 2005-2006 by Texas Instruments
|
|
* Copyright (C) 2006-2007 Nokia Corporation
|
|
+ * Copyright (C) 2008-2009 MontaVista Software, Inc. <source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
@@ -58,7 +59,8 @@
|
|
static char *decode_ep0stage(u8 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_RX: return "out";
|
|
case MUSB_EP0_STAGE_ACKWAIT: return "wait";
|
|
@@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
|
musb_writew(regs, MUSB_CSR0,
|
|
csr & ~MUSB_CSR0_P_SENTSTALL);
|
|
retval = IRQ_HANDLED;
|
|
- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
|
+ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
|
csr = musb_readw(regs, MUSB_CSR0);
|
|
}
|
|
|
|
@@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
|
if (csr & MUSB_CSR0_P_SETUPEND) {
|
|
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
|
|
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);
|
|
/* NOTE: request may need completion */
|
|
}
|
|
@@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
|
if (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;
|
|
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
|
/* FALLTHROUGH */
|
|
|
|
case MUSB_EP0_STAGE_SETUP:
|
|
+setup:
|
|
if (csr & MUSB_CSR0_RXPKTRDY) {
|
|
struct usb_ctrlrequest setup;
|
|
int handled = 0;
|
|
@@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
|
stall:
|
|
DBG(3, "stall (%d)\n", handled);
|
|
musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
|
|
- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
|
+ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
|
finish:
|
|
musb_writew(regs, MUSB_CSR0,
|
|
musb->ackpend);
|
|
@@ -803,7 +836,7 @@ finish:
|
|
/* "can't happen" */
|
|
WARN_ON(1);
|
|
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
|
|
- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
|
+ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
|
break;
|
|
}
|
|
|
|
@@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value)
|
|
|
|
csr |= MUSB_CSR0_P_SENDSTALL;
|
|
musb_writew(regs, MUSB_CSR0, csr);
|
|
- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
|
+ musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
|
musb->ackpend = 0;
|
|
break;
|
|
default:
|
|
--
|
|
1.6.0.4
|
|
|