diff options
Diffstat (limited to 'drivers/usb/musb/musb_gadget_ep0.c')
-rw-r--r-- | drivers/usb/musb/musb_gadget_ep0.c | 45 |
1 files changed, 39 insertions, 6 deletions
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: |