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 | 367 ++++++++++++++++++++++++---------------- drivers/usb/musb/musb_gadget.h | 1 + 2 files changed, 219 insertions(+), 149 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index e70dcf1..a12cc55 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -106,16 +106,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", @@ -128,7 +127,6 @@ __acquires(ep->musb->lock) request->status); req->request.complete(&req->ep->end_point, &req->request); spin_lock(&musb->lock); - ep->busy = busy; } /** @@ -267,7 +265,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) { @@ -317,14 +314,23 @@ 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) { /* @@ -338,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, @@ -373,10 +375,13 @@ static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep) 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 */ @@ -390,16 +395,11 @@ static void do_pio_tx(struct musb *musb, struct musb_request *req) musb_ep = req->ep; - /* we shouldn't get here while DMA is active ... but we do ... */ - if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "dma pending...\n"); - return; - } - /* read TXCSR before */ csr = musb_readw(epio, MUSB_TXCSR); request = &req->request; + fifo_count = min(max_ep_writesize(musb, musb_ep), (int)(request->length - request->actual)); @@ -423,6 +423,7 @@ static void do_pio_tx(struct musb *musb, struct musb_request *req) (u8 *) (request->buf + request->actual)); request->actual += fifo_count; csr |= MUSB_TXCSR_TXPKTRDY; + /* REVISIT wasn't this cleared by musb_g_tx() ? */ csr &= ~MUSB_TXCSR_P_UNDERRUN; musb_writew(epio, MUSB_TXCSR, csr); @@ -436,105 +437,155 @@ 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); - do { - /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX - * probably rates reporting as a host error + 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 + */ + if (csr & MUSB_TXCSR_P_SENTSTALL) { + 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; + } + + if (request && musb_ep->stalled) + musb_g_giveback(musb_ep, request, -EPIPE); + + 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_writew(epio, MUSB_TXCSR, csr); + DBG(2, "underrun on ep%d, req %p\n", epnum, request); + } + + 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? */ - if (csr & MUSB_TXCSR_P_SENTSTALL) { - csr |= MUSB_TXCSR_P_WZC_BITS; - csr &= ~MUSB_TXCSR_P_SENTSTALL; - musb_writew(epio, MUSB_TXCSR, csr); + DBG(3, "%s dma still busy?\n", musb_ep->name); + return; + } - if (request) - musb_g_giveback(musb_ep, request, -EPIPE); + if (request == NULL) { + DBG(2, "%s, spurious TX IRQ", musb_ep->name); + return; + } - break; - } + req = to_musb_request(request); - 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); - musb_writew(epio, MUSB_TXCSR, csr); - DBG(20, "underrun on ep%d, req %p\n", epnum, 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); + DBG(4, "sending zero pkt, DMA, TxCSR %04x\n", csr); + musb_writew(epio, MUSB_TXCSR, + csr | MUSB_TXCSR_TXPKTRDY); + return; } + } - 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) - break; - - DBG(4, "sending zero pkt\n"); - musb_writew(epio, MUSB_TXCSR, - MUSB_TXCSR_MODE - | MUSB_TXCSR_TXPKTRDY); - request->zero = 0; - } - - /* ... or if not, then complete it */ - musb_g_giveback(musb_ep, request, 0); - - /* 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); + 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); - if (csr & MUSB_TXCSR_FIFONOTEMPTY) - break; - request = musb_ep->desc - ? next_request(musb_ep) - : NULL; - if (!request) { - DBG(4, "%s idle now\n", - musb_ep->name); - break; - } + DBG(4, "sending zero pkt, TxCSR %04x\n", csr); + musb_writew(epio, MUSB_TXCSR, + csr | MUSB_TXCSR_TXPKTRDY); + req->complete = true; + return; } - - do_pio_tx(musb, to_musb_request(request)); } + musb_ep->busy = 1; + musb_g_giveback(musb_ep, request, 0); + musb_ep->busy = 0; - } while (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) @@ -545,39 +596,43 @@ static void do_pio_rx(struct musb *musb, struct musb_request *req) struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; void __iomem *epio = musb->endpoints[epnum].regs; unsigned fifo_count = 0; - u16 len = musb_ep->packet_sz; + u16 count = musb_ep->packet_sz; csr = musb_readw(epio, MUSB_RXCSR); - if (csr & MUSB_RXCSR_RXPKTRDY) { - len = musb_readw(epio, MUSB_RXCOUNT); - if (request->actual < request->length) { + musb_ep->busy = 1; - fifo_count = request->length - request->actual; - DBG(3, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", - musb_ep->name, - len, fifo_count, - musb_ep->packet_sz); + count = musb_readw(epio, MUSB_RXCOUNT); - fifo_count = min_t(unsigned, len, fifo_count); + if (request->actual < request->length) { + fifo_count = request->length - request->actual; + DBG(3, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", + musb_ep->name, + count, fifo_count, + musb_ep->packet_sz); - musb_read_fifo(musb_ep->hw_ep, fifo_count, (u8 *) - (request->buf + request->actual)); - request->actual += fifo_count; + fifo_count = min_t(unsigned, count, fifo_count); - /* REVISIT if we left anything in the fifo, flush - * it and report -EOVERFLOW - */ + musb_read_fifo(musb_ep->hw_ep, fifo_count, (u8 *) + (request->buf + request->actual)); + request->actual += fifo_count; - /* ack the read! */ - csr |= MUSB_RXCSR_P_WZC_BITS; - csr &= ~MUSB_RXCSR_RXPKTRDY; - musb_writew(epio, MUSB_RXCSR, csr); - } + /* REVISIT if we left anything in the fifo, flush + * it and report -EOVERFLOW + */ + + /* ack the read! */ + csr |= MUSB_RXCSR_P_WZC_BITS; + csr &= ~MUSB_RXCSR_RXPKTRDY; + musb_writew(epio, MUSB_RXCSR, csr); } - /* reach the end or short packet detected */ - if (request->actual == request->length || len < musb_ep->packet_sz) + musb_ep->busy = 0; + + /* we just received a short packet, it's ok to + * giveback() the request already + */ + if (request->actual == request->length || count < musb_ep->packet_sz) musb_g_giveback(musb_ep, request, 0); } @@ -713,13 +768,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; @@ -767,18 +824,15 @@ 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) csr |= MUSB_TXCSR_FLUSHFIFO; 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 { u16 int_rxe = musb_readw(mbase, MUSB_INTRRXE); @@ -804,35 +858,47 @@ 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); } + /* NOTE: all the I/O code _should_ work fine without DMA, in case + * for some reason you run out of channels here. + */ + musb_ep->dma = NULL; musb_ep->desc = desc; musb_ep->busy = 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 +916,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 +930,13 @@ 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; @@ -922,19 +991,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) { @@ -958,7 +1014,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; @@ -966,10 +1022,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); @@ -1015,6 +1068,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; @@ -1031,17 +1085,30 @@ 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; + spin_unlock_irqrestore(&musb->lock, flags); + return status; } /* @@ -1115,6 +1182,8 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value) musb_writew(epio, MUSB_RXCSR, csr); } + musb_ep->stalled = value; + done: /* maybe start the first request in the queue */ diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 3e62716..20d660e 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.4.2.253.g0b1fac -- 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