From: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx> Enabling dma tx for gadget and adapting rx part. Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx> Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx> --- drivers/usb/musb/musb_gadget.c | 273 +++++++++++++++++++++++++--------------- drivers/usb/musb/musb_gadget.h | 1 + 2 files changed, 172 insertions(+), 102 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index aec8baa..65cc7ee 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -107,16 +107,15 @@ __acquires(ep->musb->lock) { struct musb_request *req; struct musb *musb; - int busy = ep->busy; req = to_musb_request(request); + req->complete = false; list_del(&request->list); if (req->request.status == -EINPROGRESS) req->request.status = status; musb = req->musb; - ep->busy = 1; spin_unlock(&musb->lock); if (request->status == 0) DBG(5, "%s done request %p, %d/%d\n", @@ -129,7 +128,6 @@ __acquires(ep->musb->lock) request->status); req->request.complete(&req->ep->end_point, &req->request); spin_lock(&musb->lock); - ep->busy = busy; } /** @@ -266,7 +264,6 @@ static int start_dma(struct musb *musb, struct musb_request *req) * @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) { @@ -315,14 +312,25 @@ static void stop_dma(struct musb *musb, struct musb_ep *ep, */ static void nuke(struct musb_ep *ep, const int status) { + void __iomem *epio; struct musb_request *req = NULL; - void __iomem *epio = ep->musb->endpoints[ep->current_epnum].regs; + struct musb *musb; + musb = ep->musb; + epio = musb->endpoints[ep->current_epnum].regs; ep->busy = 1; - if (is_dma_capable() && ep->dma) { + DBG(2, "%s nuke, DMA %p RxCSR %04x TxCSR %04x\n", ep->name, ep->dma, + musb_readw(epio, MUSB_RXCSR), musb_readw(epio, MUSB_TXCSR)); + + if (ep->dma) { + struct dma_controller *c = ep->musb->dma_controller; - int value; + + BUG_ON(next_request(ep) == NULL); + req = to_musb_request(next_request(ep)); + (void) c->channel_abort(ep->dma); + stop_dma(musb, ep, req); if (ep->is_in) { /* @@ -336,16 +344,12 @@ static void nuke(struct musb_ep *ep, const int status) 0 | MUSB_TXCSR_FLUSHFIFO); } else { musb_writew(epio, MUSB_RXCSR, - 0 | MUSB_RXCSR_FLUSHFIFO); + MUSB_RXCSR_DMAMODE | MUSB_RXCSR_FLUSHFIFO); musb_writew(epio, MUSB_RXCSR, 0 | MUSB_RXCSR_FLUSHFIFO); } - - value = c->channel_abort(ep->dma); - DBG(value ? 1 : 6, "%s: abort DMA --> %d\n", ep->name, value); - c->channel_release(ep->dma); - ep->dma = NULL; } + ep->rx_pending = false; while (!list_empty(&(ep->req_list))) { req = container_of(ep->req_list.next, struct musb_request, @@ -370,11 +374,13 @@ static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep) else return ep->packet_sz; } - -/* +/** + * do_pio_tx - kicks TX pio transfer + * @musb: musb controller pointer + * @req: the request to be transfered via pio + * * An endpoint is transmitting data. This can be called either from - * the IRQ routine or from ep.queue() to kickstart a request on an - * endpoint. + * the IRQ routine. * * Context: controller locked, IRQs blocked, endpoint selected */ @@ -434,99 +440,152 @@ static void do_pio_tx(struct musb *musb, struct musb_request *req) } /* + * Context: controller locked, IRQs blocked. + */ +static void musb_ep_restart(struct musb *musb, struct musb_request *req) +{ + DBG(3, "<== TX/IN request %p len %u on hw_ep%d%s\n", + &req->request, req->request.length, req->epnum, + req->ep->dma ? " (dma)" : "(pio)"); + + musb_ep_select(musb->mregs, req->epnum); + + if (start_dma(musb, req) < 0) + do_pio_tx(musb, req); +} + +/* * FIFO state update (e.g. data ready). * Called from IRQ, with controller locked. */ void musb_g_tx(struct musb *musb, u8 epnum) { u16 csr; + struct musb_request *req; struct usb_request *request; u8 __iomem *mbase = musb->mregs; struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in; void __iomem *epio = musb->endpoints[epnum].regs; + struct dma_channel *dma; musb_ep_select(mbase, epnum); request = next_request(musb_ep); csr = musb_readw(epio, MUSB_TXCSR); + dma = musb_ep->dma; DBG(4, "<== %s, txcsr %04x\n", musb_ep->name, csr); + if (csr & MUSB_TXCSR_P_SENDSTALL) { + DBG(5, "%s stalling, txcsr %04x\n", + musb_ep->name, csr); + return; + } - /* - * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX - * probably rates reporting as a host error. + /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX + * probably rates reporting as a host error */ if (csr & MUSB_TXCSR_P_SENTSTALL) { - csr |= MUSB_TXCSR_P_WZC_BITS; + DBG(5, "ep%d is halted, cannot transfer\n", epnum); + csr |= MUSB_TXCSR_P_WZC_BITS; csr &= ~MUSB_TXCSR_P_SENTSTALL; musb_writew(epio, MUSB_TXCSR, csr); + if (dma != NULL) { + BUG_ON(request == NULL); + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + musb->dma_controller->channel_abort(dma); + stop_dma(musb, musb_ep, to_musb_request(request)); + dma = NULL; + } + return; } if (csr & MUSB_TXCSR_P_UNDERRUN) { - /* We NAKed, no big deal... little reason to care. */ - csr |= MUSB_TXCSR_P_WZC_BITS; - csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); + /* we NAKed, no big deal ... little reason to care */ + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~MUSB_TXCSR_P_UNDERRUN; musb_writew(epio, MUSB_TXCSR, csr); - DBG(20, "underrun on ep%d, req %p\n", epnum, request); + DBG(2, "underrun on ep%d, req %p\n", epnum, request); } - if (request) { - if (request->actual == request->length) { - - /* First, maybe a terminating short packet. - * Some DMA engines might handle this by - * themselves. - */ - if ((request->zero - && request->length - && (request->length - % musb_ep->packet_sz) - == 0) - ) { - /* - * On DMA completion, FIFO may not be - * available yet... - */ - if (csr & MUSB_TXCSR_TXPKTRDY) - return; - - DBG(4, "sending zero pkt\n"); - musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE - | MUSB_TXCSR_TXPKTRDY); - request->zero = 0; - } + if (dma != NULL && dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + /* SHOULD NOT HAPPEN ... has with cppi though, after + * changing SENDSTALL (and other cases); harmless? + */ + DBG(3, "%s dma still busy?\n", musb_ep->name); + return; + } - /* ... or if not, then complete it. */ - musb_g_giveback(musb_ep, request, 0); + if (request == NULL) { + DBG(2, "%s, spurious TX IRQ", musb_ep->name); + return; + } - /* - * Kickstart next transfer if appropriate; - * the packet that just completed might not - * be transmitted for hours or days. - * REVISIT for double buffering... - * FIXME revisit for stalls too... - */ - musb_ep_select(mbase, epnum); + req = to_musb_request(request); + + if (dma) { + int short_packet = 0; + + BUG_ON(!(csr & MUSB_TXCSR_DMAENAB)); + + request->actual += dma->actual_len; + DBG(4, "TxCSR%d %04x, dma finished, len %zu, req %p\n", + epnum, csr, dma->actual_len, request); + + stop_dma(musb, musb_ep, req); + + WARN(request->actual != request->length, + "actual %d length %d\n", request->actual, + request->length); + + if (request->length % musb_ep->packet_sz) + short_packet = 1; + + req->complete = true; + if (request->zero || short_packet) { csr = musb_readw(epio, MUSB_TXCSR); - if (csr & MUSB_TXCSR_FIFONOTEMPTY) - return; + DBG(4, "sending zero pkt, DMA, TxCSR %04x\n", csr); + musb_writew(epio, MUSB_TXCSR, + csr | MUSB_TXCSR_TXPKTRDY); + return; - if (!musb_ep->desc) { - DBG(4, "%s idle now\n", - musb_ep->name); + } + request = next_request(musb_ep); + } + if (request->actual == request->length) { + if (!req->complete) { + /* Maybe we have to send a zero length packet */ + if (request->zero && request->length && + (request->length % musb_ep->packet_sz) == 0) { + csr = musb_readw(epio, MUSB_TXCSR); + DBG(4, "sending zero pkt, TxCSR %04x\n", csr); + musb_writew(epio, MUSB_TXCSR, + csr | MUSB_TXCSR_TXPKTRDY); + req->complete = true; return; - } else - request = next_request(musb_ep); + } } - - do_pio_tx(musb, to_musb_request(request)); + musb_ep->busy = 1; + musb_g_giveback(musb_ep, request, 0); + musb_ep->busy = 0; + request = musb_ep->desc ? next_request(musb_ep) : NULL; + if (!request) { + DBG(4, "%s idle now\n", musb_ep->name); + return; + } + musb_ep_restart(musb, to_musb_request(request)); + return; } + do_pio_tx(musb, to_musb_request(request)); } /* ------------------------------------------------------------ */ /* + * do_pio_rx - kicks RX pio transfer + * @musb: musb controller pointer + * @req: the request to be transfered via pio + * * Context: controller locked, IRQs blocked, endpoint selected */ static void do_pio_rx(struct musb *musb, struct musb_request *req) @@ -712,13 +771,15 @@ static int musb_gadget_enable(struct usb_ep *ep, struct musb *musb; void __iomem *mbase; u8 epnum; - u16 csr; + u16 csr = 0; unsigned tmp; int status = -EINVAL; if (!ep || !desc) return -EINVAL; + DBG(1, "===> enabling %s\n", ep->name); + musb_ep = to_musb_ep(ep); hw_ep = musb_ep->hw_ep; regs = hw_ep->regs; @@ -766,6 +827,7 @@ static int musb_gadget_enable(struct usb_ep *ep, */ musb_writew(regs, MUSB_TXMAXP, tmp); + /* clear DATAx toggle */ csr = MUSB_TXCSR_MODE | MUSB_TXCSR_CLRDATATOG; if (musb_readw(regs, MUSB_TXCSR) & MUSB_TXCSR_FIFONOTEMPTY) @@ -773,9 +835,6 @@ static int musb_gadget_enable(struct usb_ep *ep, if (musb_ep->type == USB_ENDPOINT_XFER_ISOC) csr |= MUSB_TXCSR_P_ISO; - /* set twice in case of double buffering */ - musb_writew(regs, MUSB_TXCSR, csr); - /* REVISIT may be inappropriate w/o FIFONOTEMPTY ... */ musb_writew(regs, MUSB_TXCSR, csr); } else { @@ -803,14 +862,14 @@ static int musb_gadget_enable(struct usb_ep *ep, musb_writew(regs, MUSB_TXCSR, csr); } + /* clear DATAx toggle */ csr = MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_CLRDATATOG; - if (musb_ep->type == USB_ENDPOINT_XFER_ISOC) + + if (usb_endpoint_xfer_isoc(desc)) csr |= MUSB_RXCSR_P_ISO; - else if (musb_ep->type == USB_ENDPOINT_XFER_INT) + else if (usb_endpoint_xfer_int(desc)) csr |= MUSB_RXCSR_DISNYET; - /* set twice in case of double buffering */ - musb_writew(regs, MUSB_RXCSR, csr); musb_writew(regs, MUSB_RXCSR, csr); } @@ -820,19 +879,27 @@ static int musb_gadget_enable(struct usb_ep *ep, musb_ep->wedged = 0; status = 0; - pr_debug("%s periph: enabled %s for %s %s, maxpacket %d\n", + pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", musb_driver_name, musb_ep->name, ({ char *s; switch (musb_ep->type) { - case USB_ENDPOINT_XFER_BULK: s = "bulk"; break; - case USB_ENDPOINT_XFER_INT: s = "int"; break; - default: s = "iso"; break; + case USB_ENDPOINT_XFER_BULK: + s = "bulk"; + break; + case USB_ENDPOINT_XFER_INT: + s = "int"; + break; + default: + s = "iso"; + break; }; s; }), musb_ep->is_in ? "IN" : "OUT", + musb_ep->dma ? "dma, " : "", musb_ep->packet_sz); schedule_work(&musb->irq_work); fail: + musb_ep_select(mbase, 0); spin_unlock_irqrestore(&musb->lock, flags); return status; } @@ -850,6 +917,7 @@ static int musb_gadget_disable(struct usb_ep *ep) int status = 0; musb_ep = to_musb_ep(ep); + DBG(4, "disabling %s\n", musb_ep->name); musb = musb_ep->musb; epnum = musb_ep->current_epnum; epio = musb->endpoints[epnum].regs; @@ -863,11 +931,14 @@ static int musb_gadget_disable(struct usb_ep *ep) int_txe &= ~(1 << epnum); musb_writew(musb->mregs, MUSB_INTRTXE, int_txe); musb_writew(epio, MUSB_TXMAXP, 0); + musb_writew(epio, MUSB_TXCSR, 0); + } else { u16 int_rxe = musb_readw(musb->mregs, MUSB_INTRRXE); int_rxe &= ~(1 << epnum); musb_writew(musb->mregs, MUSB_INTRRXE, int_rxe); musb_writew(epio, MUSB_RXMAXP, 0); + musb_writew(epio, MUSB_RXCSR, 0); } musb_ep->desc = NULL; @@ -926,19 +997,6 @@ struct free_record { dma_addr_t dma; }; -/* - * Context: controller locked, IRQs blocked. - */ -static void musb_ep_restart(struct musb *musb, struct musb_request *req) -{ - DBG(3, "<== %s request %p len %u on hw_ep%d\n", - req->tx ? "TX/IN" : "RX/OUT", - &req->request, req->request.length, req->epnum); - - musb_ep_select(musb->mregs, req->epnum); - do_pio_tx(musb, req); -} - static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { @@ -962,7 +1020,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, if (request->ep != musb_ep) return -EINVAL; - DBG(4, "<== to %s request=%p\n", ep->name, req); + DBG(4, "<== to %s request=%p, length %d\n", ep->name, req, req->length); /* request is mine now... */ request->request.actual = 0; @@ -970,10 +1028,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, request->epnum = musb_ep->current_epnum; request->tx = musb_ep->is_in; - if (!req->buf) - return -ENODATA; - else - request->mapped = 0; + request->mapped = 0; spin_lock_irqsave(&musb->lock, lockflags); @@ -1019,6 +1074,7 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request) int status = 0; struct musb *musb = musb_ep->musb; + DBG(4, "%s, dequeueing request %p\n", ep->name, request); if (!ep || !request || to_musb_request(request)->ep != musb_ep) return -EINVAL; @@ -1035,14 +1091,27 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request) } /* if the hardware doesn't have the request, easy ... */ - if (musb_ep->req_list.next != &request->list || musb_ep->busy) + if (musb_ep->req_list.next != &request->list) { musb_g_giveback(musb_ep, request, -ECONNRESET); - else + + /* ... else abort the dma transfer ... */ + } else if (musb_ep->dma) { + struct dma_controller *c = musb->dma_controller; + + musb_ep_select(musb->mregs, musb_ep->current_epnum); + if (c->channel_abort) + status = c->channel_abort(musb_ep->dma); + else + status = -EBUSY; + stop_dma(musb, musb_ep, to_musb_request(request)); + if (status == 0) + musb_g_giveback(musb_ep, request, -ECONNRESET); + } else { /* NOTE: by sticking to easily tested hardware/driver states, * we leave counting of in-flight packets imprecise. */ musb_g_giveback(musb_ep, request, -ECONNRESET); - + } done: spin_unlock_irqrestore(&musb->lock, flags); return status; diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index f602df8..95d5196 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -42,6 +42,7 @@ struct musb_request { u8 tx; /* endpoint direction */ u8 epnum; u8 mapped; + u8 complete; }; static inline struct musb_request *to_musb_request(struct usb_request *req) -- 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