[PATCH 21/22] usb: musb: enabling dma tx for gadget

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux