From: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx> new start and stop generic dma functions. Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx> Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx> --- drivers/usb/musb/musb_core.h | 3 + drivers/usb/musb/musb_gadget.c | 177 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 1 deletions(-) diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 3156293..cdef6e7 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -447,6 +447,9 @@ struct musb { struct usb_gadget_driver *gadget_driver; /* its driver */ #endif + /* true if we're using dma */ + unsigned use_dma:1; + struct musb_hdrc_config *config; #ifdef MUSB_CONFIG_PROC_FS diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6a4087d..240e16e 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -132,7 +132,182 @@ __acquires(ep->musb->lock) ep->busy = busy; } -/* ----------------------------------------------------------------------- */ +/** + * start_dma - starts dma for a transfer + * @musb: musb controller pointer + * @req: musb request to be received + * + * Context: controller locked, IRQs blocked, endpoint selected + */ +static int start_dma(struct musb *musb, struct musb_request *req) +{ + struct musb_ep *musb_ep = req->ep; + struct dma_controller *cntr = musb->dma_controller; + struct musb_hw_ep *hw_ep = musb_ep->hw_ep; + struct dma_channel *dma; + void __iomem *epio; + size_t transfer_size; + int packet_sz; + u16 csr; + + if (!musb->use_dma || musb->dma_controller == NULL) + return -1; + + if (musb_ep->type == USB_ENDPOINT_XFER_INT) { + DBG(5, "not allocating DMA for interrupt endpoint\n"); + return -1; + } + + if ((unsigned long) req->request.buf & 0x01) { + DBG(5, "unaligned buffer %p for %s\n", req->request.buf, + musb_ep->name); + return -1; + } + + packet_sz = musb_ep->packet_sz; + transfer_size = req->request.length; + + if (transfer_size < packet_sz || + (transfer_size == packet_sz && packet_sz < 512)) { + DBG(4, "small transfer, using PIO\n"); + return -1; + } + + epio = musb->endpoints[musb_ep->current_epnum].regs; + if (!musb_ep->is_in) { + csr = musb_readw(epio, MUSB_RXCSR); + + /* If RXPKTRDY we might have something already waiting + * in the FIFO. If that something is less than packet_sz + * it means we only have a short packet waiting in the FIFO + * so we unload it with PIO. + */ + if (csr & MUSB_RXCSR_RXPKTRDY) { + u16 count; + + count = musb_readw(epio, MUSB_RXCOUNT); + if (count < packet_sz) { + DBG(4, "small packet in FIFO (%d bytes), " + "using PIO\n", count); + return -1; + } + } + } + + dma = cntr->channel_alloc(cntr, hw_ep, musb_ep->is_in); + if (dma == NULL) { + DBG(4, "unable to allocate DMA channel for %s\n", + musb_ep->name); + return -1; + } + + if (transfer_size > dma->max_len) + transfer_size = dma->max_len; + + if (req->request.dma == DMA_ADDR_INVALID) { + req->request.dma = dma_map_single(musb->controller, + req->request.buf, + transfer_size, + musb_ep->is_in ? + DMA_TO_DEVICE : + DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device(musb->controller, + req->request.dma, + transfer_size, + musb_ep->is_in ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + req->mapped = 0; + } + + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_AUTOSET | MUSB_TXCSR_MODE; + csr &= ~MUSB_TXCSR_P_UNDERRUN; + musb_writew(epio, MUSB_TXCSR, csr); + } else { + /* We only use mode1 DMA and assume we never know the size of + * the data we're receiving. For anything else, we're gonna use + * PIO. + */ + + /* this special sequence is necessary to get DMAReq to + * activate + */ + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_AUTOCLEAR; + musb_writew(epio, MUSB_RXCSR, csr); + + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + + csr |= MUSB_RXCSR_DMAMODE; + musb_writew(epio, MUSB_RXCSR, csr); + + csr = musb_readw(epio, MUSB_RXCSR); + } + + musb_ep->dma = dma; + + (void) cntr->channel_program(dma, packet_sz, true, req->request.dma, + transfer_size); + + DBG(4, "%s DMA started (addr 0x%08x, len %u, CSR %04x)\n", + musb_ep->name, req->request.dma, transfer_size, csr); + + return 0; +} + +/** + * stop_dma - stops a dma transfer and unmaps a buffer + * @musb: the musb controller pointer + * @ep: the enpoint being used + * @req: the request to stop + */ + +static void stop_dma(struct musb *musb, struct musb_ep *ep, + struct musb_request *req) +{ + void __iomem *epio; + + DBG(4, "%s dma stopped (addr 0x%08x, len %d)\n", ep->name, + req->request.dma, req->request.actual); + + if (req->mapped) { + dma_unmap_single(musb->controller, req->request.dma, + req->request.actual, req->tx ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->request.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else { + dma_sync_single_for_cpu(musb->controller, req->request.dma, + req->request.actual, req->tx ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + } + + epio = musb->endpoints[ep->current_epnum].regs; + if (req->tx) { + u16 csr; + + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_AUTOSET); + musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_P_WZC_BITS); + csr &= ~MUSB_TXCSR_DMAMODE; + musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_P_WZC_BITS); + } else { + u16 csr; + + csr = musb_readw(epio, MUSB_RXCSR); + csr &= ~(MUSB_RXCSR_DMAENAB | MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr | MUSB_RXCSR_P_WZC_BITS); + } + + musb->dma_controller->channel_release(ep->dma); + ep->dma = NULL; +} /* * Abort requests queued to an endpoint using the status. Synchronous. -- 1.6.6.rc0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html