From: supriya karanth <supriya.karanth@xxxxxxxxxxxxxx> Generic changes for enabling DMA Mode1. Cleanup of rxtsate function making programming same for all platforms Handles: 1) Known transfer length a) Non multiple of packet size b) Multiple of packet size 2) Unknown transfer lengths - Short packet indicates end of transfer ---------------> OUT Endpoint interrupt recieved | | | ____________________|_________________ | | | | -> No ongoing transfer ->DMA Transfer ongoing and RXPKTRDY set? | | ->Short Packet recieved | request queued? ->PAUSE DMA transfer, Read Residue | | ________________|____________ | | | | | | residue!=0 residue=0 | | -> abort DMA ->Resume DMA | | ->Update request->actual_len ->Wait for DMA | | -> Clear DMA bits in CSR completion | | -> Read Short Packet | | |__________________________| |______ | | | | call rxstate | | ->RXPKTRDY set? Read RXCOUNT | | _____________________|____________________ | | | | | | ->RXCOUNT == EP MAX packet Size ->RXCOUNT < EP MAX packet Size| | ->Program DMA in Mode1 for length -> Program in PIO / Mode0 | | which is multiple of packet -> Read Short packet from FIFO| | size -> Update request->actual_len | | | -> call musb_giveback | | |________________________________________| | |____________________________| | | | | | |____________________________________| | | | DMA completion interrupt recieved | _______________________|_________________ | | | | ->req->len = req->actual ->req->len < req->actual | ->Clear DMA bits -> Short packet expected | ->Call musb_giveback ->Clear DMA bits | | ->wait for next OUT | |_________________________________________| |_______________________________| Signed-off-by: Supriya Karanth <supriya.karanth@xxxxxxxxxxxxxx> Signed-off-by: Praveena NADAHALLY <praveen.nadahally@xxxxxxxxxxxxxx> Acked-by: Linus Walleij <linus.walleij@xxxxxxxxxx> --- drivers/usb/musb/musb_dma.h | 12 +++ drivers/usb/musb/musb_gadget.c | 215 +++++++++++++++++++++++----------------- drivers/usb/musb/musb_gadget.h | 3 + 3 files changed, 138 insertions(+), 92 deletions(-) diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1b6b827..7754515 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -116,6 +116,7 @@ struct dma_controller; * @max_len: the maximum number of bytes the channel can move in one * transaction (typically representing many USB maximum-sized packets) * @actual_len: how many bytes have been transferred + * @prog_len: how many bytes have been programmed for transfer * @status: current channel status (updated e.g. on interrupt) * @desired_mode: true if mode 1 is desired; false if mode 0 is desired * @@ -127,6 +128,7 @@ struct dma_channel { /* FIXME not void* private_data, but a dma_controller * */ size_t max_len; size_t actual_len; + size_t prog_len; enum dma_channel_status status; bool desired_mode; }; @@ -155,6 +157,11 @@ dma_channel_status(struct dma_channel *c) * @channel_release: call this to release a DMA channel * @channel_abort: call this to abort a pending DMA transaction, * returning it to FREE (but allocated) state + * @channel_pause: This function pauses the ongoing DMA transfer + * @channel_resume: This function resumes the ongoing DMA transfer + * @tx_status: Gets the residue of an ongoing DMA transfer + * @check_resiudue: checks if the residue of an ongoing DMA + * transfer is valid * * Controllers manage dma channels. */ @@ -169,6 +176,11 @@ struct dma_controller { dma_addr_t dma_addr, u32 length); int (*channel_abort)(struct dma_channel *); + int (*channel_pause)(struct dma_channel *); + int (*channel_resume)(struct dma_channel *); + int (*tx_status)(struct dma_channel *); + int (*check_residue)(struct dma_channel *, + u32 residue); int (*is_compatible)(struct dma_channel *channel, u16 maxpacket, void *buf, u32 length); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 9db664a..9d47276 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -666,13 +666,17 @@ static void rxstate(struct musb *musb, struct musb_request *req) * file_storage and f_mass_storage drivers */ - if (request->short_not_ok && fifo_count == musb_ep->packet_sz) + if (fifo_count == musb_ep->packet_sz) use_mode_1 = 1; else use_mode_1 = 0; +#ifdef CONFIG_USB_TUSB_OMAP_DMA + if (tusb_dma_omap()) + use_mode_1 = 0; +#endif + if (request->actual < request->length) { -#ifdef CONFIG_USB_INVENTRA_DMA struct dma_controller *c; struct dma_channel *channel; int use_dma = 0; @@ -681,45 +685,35 @@ static void rxstate(struct musb *musb, struct musb_request *req) c = musb->dma_controller; channel = musb_ep->dma; - /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in - * mode 0 only. So we do not get endpoint interrupts due to DMA - * completion. We only get interrupts from DMA controller. - * - * We could operate in DMA mode 1 if we knew the size of the tranfer - * in advance. For mass storage class, request->length = what the host - * sends, so that'd work. But for pretty much everything else, - * request->length is routinely more than what the host sends. For - * most these gadgets, end of is signified either by a short packet, - * or filling the last byte of the buffer. (Sending extra data in - * that last pckate should trigger an overflow fault.) But in mode 1, - * we don't get DMA completion interrupt for short packets. - * - * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1), - * to get endpoint interrupt on every DMA req, but that didn't seem - * to work reliably. - * - * REVISIT an updated g_file_storage can set req->short_not_ok, which - * then becomes usable as a runtime "use mode 1" hint... - */ - - /* Experimental: Mode1 works with mass storage use cases */ if (use_mode_1) { csr |= MUSB_RXCSR_AUTOCLEAR; musb_writew(epio, MUSB_RXCSR, csr); csr |= MUSB_RXCSR_DMAENAB; musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, + csr | MUSB_RXCSR_DMAMODE); + +#ifdef CONFIG_USB_INVENTRA_DMA /* * this special sequence (enabling and then * disabling MUSB_RXCSR_DMAMODE) is required * to get DMAReq to activate */ - musb_writew(epio, MUSB_RXCSR, - csr | MUSB_RXCSR_DMAMODE); musb_writew(epio, MUSB_RXCSR, csr); - +#endif transfer_size = min(request->length - request->actual, channel->max_len); + + /* Program the transfer length to be + * a multiple of packet size because + * short packets cant be transferred + * over mode1 + */ + transfer_size = transfer_size - + (transfer_size + % musb_ep->packet_sz); + musb_ep->dma->prog_len = transfer_size; musb_ep->dma->desired_mode = 1; } else { @@ -742,54 +736,16 @@ static void rxstate(struct musb *musb, struct musb_request *req) + request->actual, transfer_size); - if (use_dma) + if (use_dma) { + dev_dbg(musb->controller, + "%s OUT/RX DMA Size %d/%d maxpacket %d\n", + musb_ep->end_point.name, + fifo_count, transfer_size, + musb_ep->packet_sz); return; -#elif defined(CONFIG_USB_UX500_DMA) - if (request->actual < request->length) { - struct dma_controller *c; - struct dma_channel *channel; - int transfer_size = 0; - - c = musb->dma_controller; - channel = musb_ep->dma; - - /* In case first packet is short */ - if (fifo_count < musb_ep->packet_sz) - transfer_size = fifo_count; - else if (request->short_not_ok) - transfer_size = min(request->length - - request->actual, - channel->max_len); - else - transfer_size = min(request->length - - request->actual, - (unsigned)fifo_count); - - csr &= ~MUSB_RXCSR_DMAMODE; - csr |= (MUSB_RXCSR_DMAENAB | - MUSB_RXCSR_AUTOCLEAR); - - musb_writew(epio, MUSB_RXCSR, csr); - - if (transfer_size <= musb_ep->packet_sz) { - musb_ep->dma->desired_mode = 0; - } else { - musb_ep->dma->desired_mode = 1; - /* Mode must be set after DMAENAB */ - csr |= MUSB_RXCSR_DMAMODE; - musb_writew(epio, MUSB_RXCSR, csr); - } - - if (c->channel_program(channel, - musb_ep->packet_sz, - channel->desired_mode, - request->dma - + request->actual, - transfer_size)) - - return; } -#endif /* Mentor's DMA */ + + /* DMA programming failed. Use PIO mode */ len = request->length - request->actual; dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", @@ -799,22 +755,6 @@ static void rxstate(struct musb *musb, struct musb_request *req) fifo_count = min_t(unsigned, len, fifo_count); -#ifdef CONFIG_USB_TUSB_OMAP_DMA - if (tusb_dma_omap()) { - struct dma_controller *c = musb->dma_controller; - struct dma_channel *channel = musb_ep->dma; - u32 dma_addr = request->dma + request->actual; - int ret; - - ret = c->channel_program(channel, - musb_ep->packet_sz, - channel->desired_mode, - dma_addr, - fifo_count); - if (ret) - return; - } -#endif /* * Unmap the dma buffer back to cpu if dma channel * programming fails. This buffer is mapped if the @@ -863,6 +803,10 @@ void musb_g_rx(struct musb *musb, u8 epnum) void __iomem *epio = musb->endpoints[epnum].regs; struct dma_channel *dma; struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + u16 len; + u32 residue; + struct dma_controller *c = musb->dma_controller; + int status; if (hw_ep->is_shared_fifo) musb_ep = &hw_ep->ep_in; @@ -872,8 +816,12 @@ void musb_g_rx(struct musb *musb, u8 epnum) musb_ep_select(mbase, epnum); req = next_request(musb_ep); - if (!req) + if (!req) { + musb_ep->rx_pending = 1; + dev_dbg(musb->controller, "Packet recieved on %s but no request queued\n", + musb_ep->end_point.name); return; + } request = &req->request; @@ -905,9 +853,56 @@ void musb_g_rx(struct musb *musb, u8 epnum) } if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { - /* "should not happen"; likely RXPKTRDY pending for DMA */ + dev_dbg(musb->controller, "%s busy, csr %04x\n", musb_ep->end_point.name, csr); + + /* For short_not_ok type transfers and mode0 transfers */ + if (dma->desired_mode == 0 || request->short_not_ok) + return; + + if (!(csr & MUSB_RXCSR_RXPKTRDY)) { + dev_dbg(musb->controller, + "%s: %s, DMA busy and Packet not ready\n", + __func__, musb_ep->end_point.name); + return; + } + + /* For Mode1 we get here for the last short packet */ + len = musb_readw(epio, MUSB_RXCOUNT); + + /* We should get here only for a short packet.*/ + if (len == musb_ep->packet_sz) { + dev_dbg(musb->controller, + "%s: %s, Packet not short RXCOUNT=%d\n", + __func__, musb_ep->end_point.name, len); + return; + } + /* Pause the channel to get the correct transfer residue.*/ + status = c->channel_pause(musb_ep->dma); + residue = c->tx_status(musb_ep->dma); + status = c->check_residue(musb_ep->dma, residue); + + if (status) { + /* Something's wrong */ + status = c->channel_resume(musb_ep->dma); + return; + } + /* In cases when we don't know the transfer length the short + * packet indicates end of current transfer. + */ + status = c->channel_abort(musb_ep->dma); + /* Update with the actual number of bytes transferred */ + request->actual = musb_ep->dma->prog_len - residue; + /* Clear DMA bits in the CSR */ + csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + /* Proceed to read the short packet */ + rxstate(musb, req); + /* Don't program next transfer, it will tamper with the DMA + * busy condition. Wait for next OUT + */ return; } @@ -936,6 +931,26 @@ void musb_g_rx(struct musb *musb, u8 epnum) musb_writew(epio, MUSB_RXCSR, csr); } + /* We get here after DMA completion */ + if ((dma->desired_mode == 1) && (!request->short_not_ok)) { + /* Incomplete? wait for next OUT packet */ + if (request->actual < request->length) { + dev_dbg(musb->controller, + "%s: %s, Wait for next OUT\n", + __func__, musb_ep->end_point.name); + } else if (request->actual == request->length) { + dev_dbg(musb->controller, + "%s: %s, Transfer over mode1 done\n", + __func__, musb_ep->end_point.name); + musb_g_giveback(musb_ep, request, 0); + } else { + dev_dbg(musb->controller, + "%s: %s, Transfer length exceeded!!\n", + __func__, musb_ep->end_point.name); + } + return; + } + /* incomplete, and not short? wait for next IN packet */ if ((request->actual < request->length) && (musb_ep->dma->actual_len @@ -1305,8 +1320,24 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, list_add_tail(&request->list, &musb_ep->req_list); /* it this is the head of the queue, start i/o ... */ - if (!musb_ep->busy && &request->list == musb_ep->req_list.next) + if (!musb_ep->busy && &request->list == musb_ep->req_list.next) { + + /* In case of RX, if there is no packet pending to be read + * from fifo then wait for next interrupt + */ + if (!request->tx) { + if (!musb_ep->rx_pending) { + dev_dbg(musb->controller, "No packet pending for %s\n", + ep->name); + goto cleanup; + } else { + musb_ep->rx_pending = 0; + dev_dbg(musb->controller, "Read packet from fifo %s\n", + ep->name); + } + } musb_ep_restart(musb, request); + } cleanup: spin_unlock_irqrestore(&musb->lock, lockflags); diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 66b7c5e..55eb686 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -90,6 +90,9 @@ struct musb_ep { u8 busy; u8 hb_mult; + + /* true if packet is received in fifo and req_list is empty */ + u8 rx_pending; }; static inline struct musb_ep *to_musb_ep(struct usb_ep *ep) -- 1.7.1 -- 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