diff options
author | Benn Pörscke <benn.porscke@stericsson.com> | 2011-10-07 15:31:57 +0200 |
---|---|---|
committer | Benn Pörscke <benn.porscke@stericsson.com> | 2011-10-07 15:31:57 +0200 |
commit | 47a4dbf83a75014d6b3467be18997894f1c617db (patch) | |
tree | 7f5d116db48205309fbc4ae0954f20ab8a651e46 /drivers/usb/musb/musb_host.c | |
parent | ea8a52f9f4bcc3420c38ae07f8378a2f18443970 (diff) |
Squashandroid-20111012
Change-Id: If0ae9fa8067740ab2ede33703c79ec134f204a5e
Diffstat (limited to 'drivers/usb/musb/musb_host.c')
-rw-r--r-- | drivers/usb/musb/musb_host.c | 125 |
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, |