aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike J. Chen <mjchen@google.com>2012-01-25 14:31:42 -0800
committerMike J. Chen <mjchen@google.com>2012-03-03 17:29:32 -0800
commit7a15922b92090837bd6dab22f2e40df8507ff630 (patch)
tree04a957fd3e5de40231961c44f8e623e78609a078
parentb63f1c2c9084e4c518ccaf8fb9037c66478c120e (diff)
downloaduboot-7a15922b92090837bd6dab22f2e40df8507ff630.tar.gz
musb: Implement mode1 dma reads, conditionalized on CONFIG_MUSB_DMA_MODE1
Mode1 does all the full packet transfers using the dma engine, rather than mode0 which required the CPU to do copy out the packet from the receive buffer after each is received. It's a bit trickier to implement because the last packet is generally not full size and you need to handle that specially (we do the last transfer using mode0 dma). Change-Id: I1791cb746ee424c5ce5713af2156dd30ea468da9 Signed-off-by: Mike J. Chen <mjchen@google.com>
-rw-r--r--drivers/usb/musb/musb_udc.c116
1 files changed, 115 insertions, 1 deletions
diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c
index f2569a33c..efce8a594 100644
--- a/drivers/usb/musb/musb_udc.c
+++ b/drivers/usb/musb/musb_udc.c
@@ -679,6 +679,104 @@ static void musb_peri_ep0(void)
musb_peri_ep0_rx();
}
+#ifdef CONFIG_MUSB_DMA_MODE1
+static int read_fifo_dma_mode1(unsigned int ep, u32 count, u8 *buf)
+{
+ int rc = -1;
+ u16 peri_rxcsr;
+ u16 dma_control;
+ u16 peri_rxcount;
+
+ /* for large transfers, where speed matters most, it's
+ * more efficient to flush the entire dcache using
+ * flush_dcache_all() rather than using flush_dcache_range()
+ */
+ flush_dcache_all();
+
+ peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
+ peri_rxcsr |= (MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB);
+ writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
+ /* special sequence required to get dma started, from Linux driver */
+ peri_rxcsr |= MUSB_RXCSR_DMAMODE;
+ writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
+ peri_rxcsr |= MUSB_RXCSR_DMAENAB;
+ writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
+
+ /* hard coded for dma channel 1 for now */
+
+ /* set the address */
+ writel((u32)buf, &musbr->dma[0].address);
+ /* set the transfer size */
+ writel(count, &musbr->dma[0].count);
+
+ /* set the control parts. we already checked that
+ * the size is a multiple of 16 so we can use
+ * burst mode of 16 bytes.
+ */
+ dma_control = (MUSB_DMA_CNTL_BURST_MODE_3 |
+ MUSB_DMA_CNTL_END_POINT(ep) |
+ MUSB_DMA_CNTL_MODE_1 |
+ MUSB_DMA_CNTL_WRITE |
+ MUSB_DMA_CNTL_ENABLE);
+ writew(dma_control, &musbr->dma[0].ctrl);
+
+ while (1) {
+ if (readw(&musbr->dma[0].ctrl) & MUSB_DMA_CNTL_ERR) {
+ serial_printf("dma error\n");
+ break;
+ }
+ if (readl(&musbr->dma[0].count) < epinfo[(ep - 1) * 2].epsize) {
+ rc = 0;
+ break;
+ }
+ }
+
+ /* handle last short packet, if any, using dma mode 0 */
+ if (rc) {
+ goto out;
+ }
+
+ if (readl(&musbr->dma[0].count)) {
+ /* reset for mode0 */
+ peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
+ peri_rxcsr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAMODE);
+ writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
+
+ /* first read of rxcount may not be right */
+ while (1) {
+ peri_rxcount = readw(&musbr->ep[ep].epN.rxcount);
+ if (peri_rxcount == readl(&musbr->dma[0].count)) {
+ break;
+ }
+ }
+
+ dma_control = (MUSB_DMA_CNTL_BURST_MODE_3 |
+ MUSB_DMA_CNTL_END_POINT(ep) |
+ MUSB_DMA_CNTL_WRITE |
+ MUSB_DMA_CNTL_ENABLE);
+ writew(dma_control, &musbr->dma[0].ctrl);
+
+ while (1) {
+ if (readw(&musbr->dma[0].ctrl) & MUSB_DMA_CNTL_ERR) {
+ serial_printf("dma error\n");
+ rc = -1;
+ break;
+ }
+ if (readl(&musbr->dma[0].count) == 0) {
+ break;
+ }
+ }
+ }
+out:
+ /* clear DMAENAB and do the rx_ack */
+ peri_rxcsr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB |
+ MUSB_RXCSR_DMAMODE | MUSB_RXCSR_RXPKTRDY);
+ writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
+ writew(0, &musbr->dma[0].ctrl);
+ return rc;
+}
+#endif
+
/* to make compatiblity easier, we dma to a fixed buffer and
* memcpy it out. otherwise, the buffer we're given might not
* be cache aligned. buffer must be word aligned, but we make
@@ -738,7 +836,7 @@ static int read_fifo_dma(unsigned int ep, u32 count, u8 *buf)
}
}
- /* clear DMAENAB and DMAMODE and do the rx_ack */
+ /* clear DMAENAB and do the rx_ack */
peri_rxcsr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB |
MUSB_RXCSR_RXPKTRDY);
writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
@@ -776,6 +874,22 @@ static void musb_peri_rx_ep(unsigned int ep)
}
+#ifdef CONFIG_MUSB_DMA_MODE1
+ /* we use mode1 only if the buffer address satisfies the
+ * alignment requirements for DMA, and is not the
+ * default size (that's our hint that it's been
+ * set to be the length of the actual transfer)
+ */
+ if ((urb->buffer_length != sizeof(urb->buffer_data)) &&
+ (((unsigned)urb->buffer & 0x3) == 0)) {
+ if (!read_fifo_dma_mode1(ep, urb->buffer_length, urb->buffer)) {
+ /* update actual_length to indicate success */
+ urb->actual_length = urb->buffer_length;
+ }
+ /* any error is not generally recoverable so we just return */
+ return;
+ }
+#endif
do {
u16 peri_rxcount = readw(&musbr->ep[ep].epN.rxcount);
unsigned int remaining_space;