summaryrefslogtreecommitdiff
path: root/drivers/usb/musb/musb_host.c
diff options
context:
space:
mode:
authorBenn Pörscke <benn.porscke@stericsson.com>2011-10-07 15:31:57 +0200
committerBenn Pörscke <benn.porscke@stericsson.com>2011-10-07 15:31:57 +0200
commit47a4dbf83a75014d6b3467be18997894f1c617db (patch)
tree7f5d116db48205309fbc4ae0954f20ab8a651e46 /drivers/usb/musb/musb_host.c
parentea8a52f9f4bcc3420c38ae07f8378a2f18443970 (diff)
Change-Id: If0ae9fa8067740ab2ede33703c79ec134f204a5e
Diffstat (limited to 'drivers/usb/musb/musb_host.c')
-rw-r--r--drivers/usb/musb/musb_host.c125
1 files changed, 104 insertions, 21 deletions
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 877d20b1dff..25cbeadb8b0 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -45,6 +45,9 @@
#include "musb_core.h"
#include "musb_host.h"
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
/* MUSB HOST status 22-mar-2006
*
@@ -106,24 +109,41 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
{
void __iomem *epio = ep->regs;
+ void __iomem *regs = ep->musb->mregs;
u16 csr;
- u16 lastcsr = 0;
- int retries = 1000;
+ u8 addr;
+ int retries = 3000; /* 3ms */
+ /*
+ * NOTE: We are using a hack here because the FIFO-FLUSH
+ * bit is broken in hardware! The hack consists of changing
+ * the TXFUNCADDR to an unused device address and waiting
+ * for any pending USB packets to hit the 3-strikes and your
+ * gone rule.
+ */
+ addr = musb_readb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR));
csr = musb_readw(epio, MUSB_TXCSR);
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
- if (csr != lastcsr)
- DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
- lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
- musb_writew(epio, MUSB_TXCSR, csr);
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum,
+ MUSB_TXFUNCADDR), 127);
csr = musb_readw(epio, MUSB_TXCSR);
- if (WARN(retries-- < 1,
- "Could not flush host TX%d fifo: csr: %04x\n",
- ep->epnum, csr))
- return;
- mdelay(1);
+ retries--;
+ if (retries == 0) {
+ /* can happen if the USB clocks are OFF */
+ DBG(3, "Could not flush host TX%d "
+ "fifo: csr=0x%04x\n", ep->epnum, csr);
+ break;
+ }
+ udelay(1);
}
+ /* clear any errors */
+ csr &= ~(MUSB_TXCSR_H_ERROR
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_NAKTIMEOUT);
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ /* restore endpoint address */
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR), addr);
}
static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep)
@@ -175,9 +195,16 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
/* NOTE: no locks here; caller should lock and select EP */
txcsr = musb_readw(ep->regs, MUSB_TXCSR);
+
+#ifdef CONFIG_USB_U8500_DMA
+ txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_H_WZC_BITS;
+ txcsr |= MUSB_TXCSR_DMAMODE;
+#else
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
if (is_cppi_enabled())
txcsr |= MUSB_TXCSR_DMAMODE;
+#endif
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
}
@@ -290,8 +317,13 @@ start:
if (!hw_ep->tx_channel)
musb_h_tx_start(hw_ep);
+#ifdef CONFIG_USB_U8500_DMA
+ else
+ musb_h_tx_dma_start(hw_ep);
+#else
else if (is_cppi_enabled() || tusb_dma_omap())
musb_h_tx_dma_start(hw_ep);
+#endif
}
}
@@ -323,8 +355,15 @@ __acquires(musb->lock)
urb->actual_length, urb->transfer_buffer_length
);
+
usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
spin_unlock(&musb->lock);
+#ifdef CONFIG_USB_U8500_DMA
+ /* Make it safe to call this routine more than once */
+ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
+ URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
+ URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
+#endif
usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);
spin_lock(&musb->lock);
}
@@ -627,7 +666,35 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
u16 csr;
u8 mode;
-#ifdef CONFIG_USB_INVENTRA_DMA
+#ifdef CONFIG_USB_U8500_DMA
+ if (length > channel->max_len)
+ length = channel->max_len;
+
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (length > pkt_size) {
+ mode = 1;
+ csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
+ /* autoset shouldn't be set in high bandwidth */
+ if (qh->hb_mult == 1)
+ csr |= MUSB_TXCSR_AUTOSET;
+ } else {
+ mode = 0;
+ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
+ csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
+ }
+ channel->desired_mode = mode;
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ channel->actual_len = 0;
+
+ /*
+ * TX uses "RNDIS" mode automatically but needs help
+ * to identify the zero-length-final-packet case.
+ */
+ mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
+
+#else
+#ifdef CONFIG_USB_INVENTRA_DMA
if (length > channel->max_len)
length = channel->max_len;
@@ -657,6 +724,7 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
*/
mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
#endif
+#endif
qh->segsize = length;
@@ -842,7 +910,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
}
/* kick things off */
-
if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
/* candidate for DMA */
if (dma_channel) {
@@ -872,7 +939,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
csr |= MUSB_RXCSR_DMAENAB;
}
}
-
csr |= MUSB_RXCSR_H_REQPKT;
DBG(7, "RXCSR%d := %04x\n", epnum, csr);
musb_writew(hw_ep->regs, MUSB_RXCSR, csr);
@@ -1141,7 +1207,9 @@ void musb_host_tx(struct musb *musb, u8 epnum)
DBG(3, "TX 3strikes on ep=%d\n", epnum);
status = -ETIMEDOUT;
-
+ } else if (tx_csr & MUSB_TXCSR_TXPKTRDY) {
+ /* BUSY - can happen during USB transfer cancel */
+ return;
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
DBG(6, "TX end=%d device not responding\n", epnum);
@@ -1437,6 +1505,9 @@ void musb_host_rx(struct musb *musb, u8 epnum)
u32 status;
struct dma_channel *dma;
+#if defined(CONFIG_USB_U8500_DMA)
+ u16 tmp_val;
+#endif
musb_ep_select(mbase, epnum);
urb = next_urb(qh);
@@ -1527,7 +1598,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)
done = true;
goto finish;
}
-
if (unlikely(dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY)) {
/* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */
ERR("RX%d dma busy, csr %04x\n", epnum, rx_csr);
@@ -1541,7 +1611,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
-#ifndef CONFIG_USB_INVENTRA_DMA
+#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_U8500_DMA)
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
/* REVISIT this happened for a while on some short reads...
* the cleanup still needs investigation... looks bad...
@@ -1564,6 +1634,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
MUSB_RXCSR_H_WZC_BITS | rx_csr);
}
#endif
+
if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) {
xfer_len = dma->actual_len;
@@ -1573,7 +1644,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
@@ -1629,7 +1700,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
@@ -1713,6 +1784,14 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
val = musb_readw(epio, MUSB_RXCSR);
+
+#if defined(CONFIG_USB_U8500_DMA)
+ /* retain the original value,
+ *which will be used to reset CSR
+ */
+ tmp_val = val;
+#endif
+
val &= ~MUSB_RXCSR_H_REQPKT;
if (dma->desired_mode == 0)
@@ -1741,9 +1820,13 @@ void musb_host_rx(struct musb *musb, u8 epnum)
hw_ep->rx_channel = NULL;
dma = NULL;
/* REVISIT reset CSR */
+
+#if defined(CONFIG_USB_U8500_DMA)
+ musb_writew(epio, MUSB_RXCSR, tmp_val);
+#endif
}
}
-#endif /* Mentor DMA */
+#endif /* Mentor DMA || U8500 DMA */
if (!dma) {
done = musb_host_packet_rx(musb, urb,