summaryrefslogtreecommitdiff
path: root/drivers/usb/musb/musb_gadget.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/musb/musb_gadget.c')
-rw-r--r--drivers/usb/musb/musb_gadget.c273
1 files changed, 135 insertions, 138 deletions
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 8b3c4e2ed7b8..c49b9ba025ab 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.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) 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
@@ -428,120 +429,102 @@ void musb_g_tx(struct musb *musb, u8 epnum)
DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr);
dma = is_dma_capable() ? musb_ep->dma : NULL;
- do {
- /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX
- * probably rates reporting as a host error
- */
- if (csr & MUSB_TXCSR_P_SENTSTALL) {
- csr |= MUSB_TXCSR_P_WZC_BITS;
- csr &= ~MUSB_TXCSR_P_SENTSTALL;
- musb_writew(epio, MUSB_TXCSR, csr);
- if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
- dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- musb->dma_controller->channel_abort(dma);
- }
- if (request)
- musb_g_giveback(musb_ep, request, -EPIPE);
+ /*
+ * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX
+ * probably rates reporting as a host error.
+ */
+ if (csr & MUSB_TXCSR_P_SENTSTALL) {
+ csr |= MUSB_TXCSR_P_WZC_BITS;
+ csr &= ~MUSB_TXCSR_P_SENTSTALL;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ return;
+ }
- break;
- }
+ if (csr & MUSB_TXCSR_P_UNDERRUN) {
+ /* We NAKed, no big deal... little reason to care. */
+ csr |= MUSB_TXCSR_P_WZC_BITS;
+ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
+ musb_writew(epio, MUSB_TXCSR, csr);
+ DBG(20, "underrun on ep%d, req %p\n", epnum, request);
+ }
+
+ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
+ /*
+ * SHOULD NOT HAPPEN... has with CPPI though, after
+ * changing SENDSTALL (and other cases); harmless?
+ */
+ DBG(5, "%s dma still busy?\n", musb_ep->end_point.name);
+ return;
+ }
- if (csr & MUSB_TXCSR_P_UNDERRUN) {
- /* we NAKed, no big deal ... little reason to care */
+ if (request) {
+ u8 is_dma = 0;
+
+ if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
+ is_dma = 1;
csr |= MUSB_TXCSR_P_WZC_BITS;
- csr &= ~(MUSB_TXCSR_P_UNDERRUN
- | MUSB_TXCSR_TXPKTRDY);
+ csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
+ MUSB_TXCSR_TXPKTRDY);
musb_writew(epio, MUSB_TXCSR, csr);
- DBG(20, "underrun on ep%d, req %p\n", epnum, request);
+ /* Ensure writebuffer is empty. */
+ csr = musb_readw(epio, MUSB_TXCSR);
+ request->actual += musb_ep->dma->actual_len;
+ DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n",
+ epnum, csr, musb_ep->dma->actual_len, request);
}
- if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
- /* SHOULD NOT HAPPEN ... has with cppi though, after
- * changing SENDSTALL (and other cases); harmless?
+ if (is_dma || request->actual == request->length) {
+ /*
+ * First, maybe a terminating short packet. Some DMA
+ * engines might handle this by themselves.
*/
- DBG(5, "%s dma still busy?\n", musb_ep->end_point.name);
- break;
- }
-
- if (request) {
- u8 is_dma = 0;
-
- if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
- is_dma = 1;
- csr |= MUSB_TXCSR_P_WZC_BITS;
- csr &= ~(MUSB_TXCSR_DMAENAB
- | MUSB_TXCSR_P_UNDERRUN
- | MUSB_TXCSR_TXPKTRDY);
- musb_writew(epio, MUSB_TXCSR, csr);
- /* ensure writebuffer is empty */
- csr = musb_readw(epio, MUSB_TXCSR);
- request->actual += musb_ep->dma->actual_len;
- DBG(4, "TXCSR%d %04x, dma off, "
- "len %zu, req %p\n",
- epnum, csr,
- musb_ep->dma->actual_len,
- request);
- }
-
- if (is_dma || request->actual == request->length) {
-
- /* First, maybe a terminating short packet.
- * Some DMA engines might handle this by
- * themselves.
- */
- if ((request->zero
- && request->length
- && (request->length
- % musb_ep->packet_sz)
- == 0)
+ if ((request->zero && request->length
+ && request->length % musb_ep->packet_sz == 0)
#ifdef CONFIG_USB_INVENTRA_DMA
- || (is_dma &&
- ((!dma->desired_mode) ||
- (request->actual &
- (musb_ep->packet_sz - 1))))
+ || (is_dma && (!dma->desired_mode ||
+ (request->actual &
+ (musb_ep->packet_sz - 1))))
#endif
- ) {
- /* on dma completion, fifo may not
- * be available yet ...
- */
- if (csr & MUSB_TXCSR_TXPKTRDY)
- break;
-
- DBG(4, "sending zero pkt\n");
- musb_writew(epio, MUSB_TXCSR,
- MUSB_TXCSR_MODE
- | MUSB_TXCSR_TXPKTRDY);
- request->zero = 0;
- }
-
- /* ... or if not, then complete it */
- musb_g_giveback(musb_ep, request, 0);
-
- /* kickstart next transfer if appropriate;
- * the packet that just completed might not
- * be transmitted for hours or days.
- * REVISIT for double buffering...
- * FIXME revisit for stalls too...
+ ) {
+ /*
+ * On DMA completion, FIFO may not be
+ * available yet...
*/
- musb_ep_select(mbase, epnum);
- csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY)
- break;
- request = musb_ep->desc
- ? next_request(musb_ep)
- : NULL;
- if (!request) {
- DBG(4, "%s idle now\n",
- musb_ep->end_point.name);
- break;
- }
+ if (csr & MUSB_TXCSR_TXPKTRDY)
+ return;
+
+ DBG(4, "sending zero pkt\n");
+ musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE
+ | MUSB_TXCSR_TXPKTRDY);
+ request->zero = 0;
}
- txstate(musb, to_musb_request(request));
+ /* ... or if not, then complete it. */
+ musb_g_giveback(musb_ep, request, 0);
+
+ /*
+ * Kickstart next transfer if appropriate;
+ * the packet that just completed might not
+ * be transmitted for hours or days.
+ * REVISIT for double buffering...
+ * FIXME revisit for stalls too...
+ */
+ musb_ep_select(mbase, epnum);
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (csr & MUSB_TXCSR_FIFONOTEMPTY)
+ return;
+
+ if (!musb_ep->desc) {
+ DBG(4, "%s idle now\n",
+ musb_ep->end_point.name);
+ return;
+ } else
+ request = next_request(musb_ep);
}
- } while (0);
+ txstate(musb, to_musb_request(request));
+ }
}
/* ------------------------------------------------------------ */
@@ -582,15 +565,25 @@ void musb_g_tx(struct musb *musb, u8 epnum)
*/
static void rxstate(struct musb *musb, struct musb_request *req)
{
- u16 csr = 0;
const u8 epnum = req->epnum;
struct usb_request *request = &req->request;
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out;
void __iomem *epio = musb->endpoints[epnum].regs;
unsigned fifo_count = 0;
u16 len = musb_ep->packet_sz;
+ u16 csr = musb_readw(epio, MUSB_RXCSR);
- csr = musb_readw(epio, MUSB_RXCSR);
+ /* We shouldn't get here while DMA is active, but we do... */
+ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
+ DBG(4, "DMA pending...\n");
+ return;
+ }
+
+ if (csr & MUSB_RXCSR_P_SENDSTALL) {
+ DBG(5, "%s stalling, RXCSR %04x\n",
+ musb_ep->end_point.name, csr);
+ return;
+ }
if (is_cppi_enabled() && musb_ep->dma) {
struct dma_controller *c = musb->dma_controller;
@@ -761,19 +754,10 @@ void musb_g_rx(struct musb *musb, u8 epnum)
csr, dma ? " (dma)" : "", request);
if (csr & MUSB_RXCSR_P_SENTSTALL) {
- if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
- dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
- request->actual += musb_ep->dma->actual_len;
- }
-
csr |= MUSB_RXCSR_P_WZC_BITS;
csr &= ~MUSB_RXCSR_P_SENTSTALL;
musb_writew(epio, MUSB_RXCSR, csr);
-
- if (request)
- musb_g_giveback(musb_ep, request, -EPIPE);
- goto done;
+ return;
}
if (csr & MUSB_RXCSR_P_OVERRUN) {
@@ -795,7 +779,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
DBG((csr & MUSB_RXCSR_DMAENAB) ? 4 : 1,
"%s busy, csr %04x\n",
musb_ep->end_point.name, csr);
- goto done;
+ return;
}
if (dma && (csr & MUSB_RXCSR_DMAENAB)) {
@@ -826,22 +810,15 @@ void musb_g_rx(struct musb *musb, u8 epnum)
if ((request->actual < request->length)
&& (musb_ep->dma->actual_len
== musb_ep->packet_sz))
- goto done;
+ return;
#endif
musb_g_giveback(musb_ep, request, 0);
request = next_request(musb_ep);
if (!request)
- goto done;
-
- /* don't start more i/o till the stall clears */
- musb_ep_select(mbase, epnum);
- csr = musb_readw(epio, MUSB_RXCSR);
- if (csr & MUSB_RXCSR_P_SENDSTALL)
- goto done;
+ return;
}
-
/* analyze request if the ep is hot */
if (request)
rxstate(musb, to_musb_request(request));
@@ -849,8 +826,6 @@ void musb_g_rx(struct musb *musb, u8 epnum)
DBG(3, "packet waiting for %s%s request\n",
musb_ep->desc ? "" : "inactive ",
musb_ep->end_point.name);
-
-done:
return;
}
@@ -981,6 +956,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
musb_ep->desc = desc;
musb_ep->busy = 0;
+ musb_ep->wedged = 0;
status = 0;
pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n",
@@ -1235,7 +1211,7 @@ done:
*
* exported to ep0 code
*/
-int musb_gadget_set_halt(struct usb_ep *ep, int value)
+static int musb_gadget_set_halt(struct usb_ep *ep, int value)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
u8 epnum = musb_ep->current_epnum;
@@ -1244,7 +1220,7 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value)
void __iomem *mbase;
unsigned long flags;
u16 csr;
- struct musb_request *request = NULL;
+ struct musb_request *request;
int status = 0;
if (!ep)
@@ -1260,24 +1236,30 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value)
musb_ep_select(mbase, epnum);
- /* cannot portably stall with non-empty FIFO */
request = to_musb_request(next_request(musb_ep));
- if (value && musb_ep->is_in) {
- csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY) {
- DBG(3, "%s fifo busy, cannot halt\n", ep->name);
- spin_unlock_irqrestore(&musb->lock, flags);
- return -EAGAIN;
+ if (value) {
+ if (request) {
+ DBG(3, "request in progress, cannot halt %s\n",
+ ep->name);
+ status = -EAGAIN;
+ goto done;
}
-
- }
+ /* Cannot portably stall with non-empty FIFO */
+ if (musb_ep->is_in) {
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (csr & MUSB_TXCSR_FIFONOTEMPTY) {
+ DBG(3, "FIFO busy, cannot halt %s\n", ep->name);
+ status = -EAGAIN;
+ goto done;
+ }
+ }
+ } else
+ musb_ep->wedged = 0;
/* set/clear the stall and toggle bits */
DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear");
if (musb_ep->is_in) {
csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY)
- csr |= MUSB_TXCSR_FLUSHFIFO;
csr |= MUSB_TXCSR_P_WZC_BITS
| MUSB_TXCSR_CLRDATATOG;
if (value)
@@ -1300,18 +1282,32 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value)
musb_writew(epio, MUSB_RXCSR, csr);
}
-done:
-
/* maybe start the first request in the queue */
if (!musb_ep->busy && !value && request) {
DBG(3, "restarting the request\n");
musb_ep_restart(musb, request);
}
+done:
spin_unlock_irqrestore(&musb->lock, flags);
return status;
}
+/*
+ * Sets the halt feature with the clear requests ignored
+ */
+static int musb_gadget_set_wedge(struct usb_ep *ep)
+{
+ struct musb_ep *musb_ep = to_musb_ep(ep);
+
+ if (!ep)
+ return -EINVAL;
+
+ musb_ep->wedged = 1;
+
+ return usb_ep_set_halt(ep);
+}
+
static int musb_gadget_fifo_status(struct usb_ep *ep)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
@@ -1382,6 +1378,7 @@ static const struct usb_ep_ops musb_ep_ops = {
.queue = musb_gadget_queue,
.dequeue = musb_gadget_dequeue,
.set_halt = musb_gadget_set_halt,
+ .set_wedge = musb_gadget_set_wedge,
.fifo_status = musb_gadget_fifo_status,
.fifo_flush = musb_gadget_fifo_flush
};