Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / usb / musb / musb_gadget_ep0.c
index a57652f..522efb3 100644 (file)
@@ -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@mvista.com>
  *
  * 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";
@@ -405,7 +407,7 @@ stall:
                                        csr |= MUSB_RXCSR_P_SENDSTALL
                                                | MUSB_RXCSR_FLUSHFIFO
                                                | MUSB_RXCSR_CLRDATATOG
-                                               | MUSB_TXCSR_P_WZC_BITS;
+                                               | MUSB_RXCSR_P_WZC_BITS;
                                        musb_writew(regs, MUSB_RXCSR,
                                                        csr);
                                }
@@ -437,7 +439,7 @@ static void ep0_rxstate(struct musb *musb)
 {
        void __iomem            *regs = musb->control_ep->regs;
        struct usb_request      *req;
-       u16                     tmp;
+       u16                     count, csr;
 
        req = next_ep0_request(musb);
 
@@ -449,35 +451,35 @@ static void ep0_rxstate(struct musb *musb)
                unsigned        len = req->length - req->actual;
 
                /* read the buffer */
-               tmp = musb_readb(regs, MUSB_COUNT0);
-               if (tmp > len) {
+               count = musb_readb(regs, MUSB_COUNT0);
+               if (count > len) {
                        req->status = -EOVERFLOW;
-                       tmp = len;
+                       count = len;
                }
-               musb_read_fifo(&musb->endpoints[0], tmp, buf);
-               req->actual += tmp;
-               tmp = MUSB_CSR0_P_SVDRXPKTRDY;
-               if (tmp < 64 || req->actual == req->length) {
+               musb_read_fifo(&musb->endpoints[0], count, buf);
+               req->actual += count;
+               csr = MUSB_CSR0_P_SVDRXPKTRDY;
+               if (count < 64 || req->actual == req->length) {
                        musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
-                       tmp |= MUSB_CSR0_P_DATAEND;
+                       csr |= MUSB_CSR0_P_DATAEND;
                } else
                        req = NULL;
        } else
-               tmp = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL;
+               csr = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL;
 
 
        /* Completion handler may choose to stall, e.g. because the
         * message just received holds invalid data.
         */
        if (req) {
-               musb->ackpend = tmp;
+               musb->ackpend = csr;
                musb_g_ep0_giveback(musb, req);
                if (!musb->ackpend)
                        return;
                musb->ackpend = 0;
        }
        musb_ep_select(musb->mregs, 0);
-       musb_writew(regs, MUSB_CSR0, tmp);
+       musb_writew(regs, MUSB_CSR0, csr);
 }
 
 /*
@@ -509,7 +511,8 @@ static void ep0_txstate(struct musb *musb)
 
        /* update the flags */
        if (fifo_count < MUSB_MAX_END0_PACKET
-                       || request->actual == request->length) {
+                       || (request->actual == request->length
+                               && !request->zero)) {
                musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
                csr |= MUSB_CSR0_P_DATAEND;
        } else
@@ -628,7 +631,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 +639,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 +711,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 +817,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 +837,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 +993,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: