diff options
author | Sergei Shtylyov <sshtylyov@ru.mvista.com> | 2009-03-27 15:52:43 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:39 -0400 |
commit | a5073b52833e4df8e16c93dc4cbb7e0c558c74a2 (patch) | |
tree | 5cd7e1f2aa6756a041677097138e2bb9a952f8d9 /drivers/usb/musb | |
parent | 003051bfb62513842a9e9efde17afeba46519c95 (diff) |
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@ru.mvista.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r-- | drivers/usb/musb/davinci.c | 7 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 8 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 3 | ||||
-rw-r--r-- | 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 10d11ab113ab..898b52fcff5d 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c | |||
@@ -372,12 +372,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) | |||
372 | 372 | ||
373 | spin_unlock_irqrestore(&musb->lock, flags); | 373 | spin_unlock_irqrestore(&musb->lock, flags); |
374 | 374 | ||
375 | /* REVISIT we sometimes get unhandled IRQs | 375 | return retval; |
376 | * (e.g. ep0). not clear why... | ||
377 | */ | ||
378 | if (retval != IRQ_HANDLED) | ||
379 | DBG(5, "unhandled? %08x\n", tmp); | ||
380 | return IRQ_HANDLED; | ||
381 | } | 376 | } |
382 | 377 | ||
383 | int musb_platform_set_mode(struct musb *musb, u8 mode) | 378 | 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 4000cf6d1e81..324459b619f7 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) | |||
1481 | 1481 | ||
1482 | spin_unlock_irqrestore(&musb->lock, flags); | 1482 | spin_unlock_irqrestore(&musb->lock, flags); |
1483 | 1483 | ||
1484 | /* REVISIT we sometimes get spurious IRQs on g_ep0 | 1484 | return retval; |
1485 | * not clear why... | ||
1486 | */ | ||
1487 | if (retval != IRQ_HANDLED) | ||
1488 | DBG(5, "spurious?\n"); | ||
1489 | |||
1490 | return IRQ_HANDLED; | ||
1491 | } | 1485 | } |
1492 | 1486 | ||
1493 | #else | 1487 | #else |
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index efb39b5e55b5..c2a776ee1947 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h | |||
@@ -171,7 +171,8 @@ enum musb_h_ep0_state { | |||
171 | 171 | ||
172 | /* peripheral side ep0 states */ | 172 | /* peripheral side ep0 states */ |
173 | enum musb_g_ep0_state { | 173 | enum musb_g_ep0_state { |
174 | MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */ | 174 | MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */ |
175 | MUSB_EP0_STAGE_SETUP, /* received SETUP */ | ||
175 | MUSB_EP0_STAGE_TX, /* IN data */ | 176 | MUSB_EP0_STAGE_TX, /* IN data */ |
176 | MUSB_EP0_STAGE_RX, /* OUT data */ | 177 | MUSB_EP0_STAGE_RX, /* OUT data */ |
177 | MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */ | 178 | 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 3f5e30ddfa27..40ed50ecedff 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Copyright 2005 Mentor Graphics Corporation | 4 | * Copyright 2005 Mentor Graphics Corporation |
5 | * Copyright (C) 2005-2006 by Texas Instruments | 5 | * Copyright (C) 2005-2006 by Texas Instruments |
6 | * Copyright (C) 2006-2007 Nokia Corporation | 6 | * Copyright (C) 2006-2007 Nokia Corporation |
7 | * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or | 9 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License | 10 | * modify it under the terms of the GNU General Public License |
@@ -58,7 +59,8 @@ | |||
58 | static char *decode_ep0stage(u8 stage) | 59 | static char *decode_ep0stage(u8 stage) |
59 | { | 60 | { |
60 | switch (stage) { | 61 | switch (stage) { |
61 | case MUSB_EP0_STAGE_SETUP: return "idle"; | 62 | case MUSB_EP0_STAGE_IDLE: return "idle"; |
63 | case MUSB_EP0_STAGE_SETUP: return "setup"; | ||
62 | case MUSB_EP0_STAGE_TX: return "in"; | 64 | case MUSB_EP0_STAGE_TX: return "in"; |
63 | case MUSB_EP0_STAGE_RX: return "out"; | 65 | case MUSB_EP0_STAGE_RX: return "out"; |
64 | case MUSB_EP0_STAGE_ACKWAIT: return "wait"; | 66 | case MUSB_EP0_STAGE_ACKWAIT: return "wait"; |
@@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) | |||
628 | musb_writew(regs, MUSB_CSR0, | 630 | musb_writew(regs, MUSB_CSR0, |
629 | csr & ~MUSB_CSR0_P_SENTSTALL); | 631 | csr & ~MUSB_CSR0_P_SENTSTALL); |
630 | retval = IRQ_HANDLED; | 632 | retval = IRQ_HANDLED; |
631 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | 633 | musb->ep0_state = MUSB_EP0_STAGE_IDLE; |
632 | csr = musb_readw(regs, MUSB_CSR0); | 634 | csr = musb_readw(regs, MUSB_CSR0); |
633 | } | 635 | } |
634 | 636 | ||
@@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) | |||
636 | if (csr & MUSB_CSR0_P_SETUPEND) { | 638 | if (csr & MUSB_CSR0_P_SETUPEND) { |
637 | musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND); | 639 | musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND); |
638 | retval = IRQ_HANDLED; | 640 | retval = IRQ_HANDLED; |
639 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | 641 | /* Transition into the early status phase */ |
642 | switch (musb->ep0_state) { | ||
643 | case MUSB_EP0_STAGE_TX: | ||
644 | musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT; | ||
645 | break; | ||
646 | case MUSB_EP0_STAGE_RX: | ||
647 | musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; | ||
648 | break; | ||
649 | default: | ||
650 | ERR("SetupEnd came in a wrong ep0stage %s", | ||
651 | decode_ep0stage(musb->ep0_state)); | ||
652 | } | ||
640 | csr = musb_readw(regs, MUSB_CSR0); | 653 | csr = musb_readw(regs, MUSB_CSR0); |
641 | /* NOTE: request may need completion */ | 654 | /* NOTE: request may need completion */ |
642 | } | 655 | } |
@@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) | |||
697 | if (req) | 710 | if (req) |
698 | musb_g_ep0_giveback(musb, req); | 711 | musb_g_ep0_giveback(musb, req); |
699 | } | 712 | } |
713 | |||
714 | /* | ||
715 | * In case when several interrupts can get coalesced, | ||
716 | * check to see if we've already received a SETUP packet... | ||
717 | */ | ||
718 | if (csr & MUSB_CSR0_RXPKTRDY) | ||
719 | goto setup; | ||
720 | |||
721 | retval = IRQ_HANDLED; | ||
722 | musb->ep0_state = MUSB_EP0_STAGE_IDLE; | ||
723 | break; | ||
724 | |||
725 | case MUSB_EP0_STAGE_IDLE: | ||
726 | /* | ||
727 | * This state is typically (but not always) indiscernible | ||
728 | * from the status states since the corresponding interrupts | ||
729 | * tend to happen within too little period of time (with only | ||
730 | * a zero-length packet in between) and so get coalesced... | ||
731 | */ | ||
700 | retval = IRQ_HANDLED; | 732 | retval = IRQ_HANDLED; |
701 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | 733 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; |
702 | /* FALLTHROUGH */ | 734 | /* FALLTHROUGH */ |
703 | 735 | ||
704 | case MUSB_EP0_STAGE_SETUP: | 736 | case MUSB_EP0_STAGE_SETUP: |
737 | setup: | ||
705 | if (csr & MUSB_CSR0_RXPKTRDY) { | 738 | if (csr & MUSB_CSR0_RXPKTRDY) { |
706 | struct usb_ctrlrequest setup; | 739 | struct usb_ctrlrequest setup; |
707 | int handled = 0; | 740 | int handled = 0; |
@@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) | |||
783 | stall: | 816 | stall: |
784 | DBG(3, "stall (%d)\n", handled); | 817 | DBG(3, "stall (%d)\n", handled); |
785 | musb->ackpend |= MUSB_CSR0_P_SENDSTALL; | 818 | musb->ackpend |= MUSB_CSR0_P_SENDSTALL; |
786 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | 819 | musb->ep0_state = MUSB_EP0_STAGE_IDLE; |
787 | finish: | 820 | finish: |
788 | musb_writew(regs, MUSB_CSR0, | 821 | musb_writew(regs, MUSB_CSR0, |
789 | musb->ackpend); | 822 | musb->ackpend); |
@@ -803,7 +836,7 @@ finish: | |||
803 | /* "can't happen" */ | 836 | /* "can't happen" */ |
804 | WARN_ON(1); | 837 | WARN_ON(1); |
805 | musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL); | 838 | musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL); |
806 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | 839 | musb->ep0_state = MUSB_EP0_STAGE_IDLE; |
807 | break; | 840 | break; |
808 | } | 841 | } |
809 | 842 | ||
@@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value) | |||
959 | 992 | ||
960 | csr |= MUSB_CSR0_P_SENDSTALL; | 993 | csr |= MUSB_CSR0_P_SENDSTALL; |
961 | musb_writew(regs, MUSB_CSR0, csr); | 994 | musb_writew(regs, MUSB_CSR0, csr); |
962 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | 995 | musb->ep0_state = MUSB_EP0_STAGE_IDLE; |
963 | musb->ackpend = 0; | 996 | musb->ackpend = 0; |
964 | break; | 997 | break; |
965 | default: | 998 | default: |