[PATCH 4/6] USB: s3c-hsotg: Fix stall condition processing

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

 



From: Anton Tikhomirov <av.tikhomirov@xxxxxxxxxxx>

The following should be done for requests after endpoint stall
condition is cleared:
1) 'in progress' request (if any) should be completed since
   Tx FIFO was flushed;
2) next request from queue (if any) should be started.

This commit does that.

Additionally set/clear stall condition code is fixed.

Signed-off-by: Anton Tikhomirov <av.tikhomirov@xxxxxxxxxxx>
Reviewed-by: Kyoungil Kim<ki0351.kim@xxxxxxxxxxx>
Cc: Ben Dooks <ben-linux@xxxxxxxxx>
Signed-off-by: Kukjin Kim <kgene.kim@xxxxxxxxxxx>
---
 drivers/usb/gadget/s3c-hsotg.c |  142 +++++++++++++++++++++++++++++++---------
 1 files changed, 111 insertions(+), 31 deletions(-)

diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index cbbc336..6be424e 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -679,6 +679,14 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
 		__func__, readl(hsotg->regs + epctrl_reg), index,
 		hs_ep->dir_in ? "in" : "out");
 
+	/* If endpoint is stalled, we will restart request later */
+	ctrl = readl(hsotg->regs + epctrl_reg);
+
+	if (ctrl & S3C_DxEPCTL_Stall) {
+		dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
+		return;
+	}
+
 	length = ureq->length - ureq->actual;
 
 	if (0)
@@ -731,18 +739,6 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
 	/* write size / packets */
 	writel(epsize, hsotg->regs + epsize_reg);
 
-	ctrl = readl(hsotg->regs + epctrl_reg);
-
-	if (ctrl & S3C_DxEPCTL_Stall) {
-		dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
-
-		/* not sure what we can do here, if it is EP0 then we should
-		 * get this cleared once the endpoint has transmitted the
-		 * STALL packet, otherwise it needs to be cleared by the
-		 * host.
-		 */
-	}
-
 	if (using_dma(hsotg)) {
 		unsigned int dma_reg;
 
@@ -1048,6 +1044,20 @@ static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg,
 static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value);
 
 /**
+ * get_ep_head - return the first request on the endpoint
+ * @hs_ep: The controller endpoint to get
+ *
+ * Get the first request on the endpoint.
+ */
+static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
+{
+	if (list_empty(&hs_ep->queue))
+		return NULL;
+
+	return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue);
+}
+
+/**
  * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
  * @hsotg: The device state
  * @ctrl: USB control request
@@ -1056,6 +1066,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
 					 struct usb_ctrlrequest *ctrl)
 {
 	struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+	struct s3c_hsotg_req *hs_req;
+	bool restart;
 	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
 	struct s3c_hsotg_ep *ep;
 	int ret;
@@ -1081,6 +1093,29 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
 					"%s: failed to send reply\n", __func__);
 				return ret;
 			}
+
+			if (!set) {
+				/*
+				 * If we have request in progress,
+				 * then complete it
+				 */
+				if (ep->req) {
+					hs_req = ep->req;
+					ep->req = NULL;
+					list_del_init(&hs_req->queue);
+					hs_req->req.complete(&ep->ep,
+							     &hs_req->req);
+				}
+
+				/* If we have pending request, then start it */
+				restart = !list_empty(&ep->queue);
+				if (restart) {
+					hs_req = get_ep_head(ep);
+					s3c_hsotg_start_req(hsotg, ep,
+							    hs_req, false);
+				}
+			}
+
 			break;
 
 		default:
@@ -1248,20 +1283,6 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
 }
 
 /**
- * get_ep_head - return the first request on the endpoint
- * @hs_ep: The controller endpoint to get
- *
- * Get the first request on the endpoint.
-*/
-static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
-{
-	if (list_empty(&hs_ep->queue))
-		return NULL;
-
-	return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue);
-}
-
-/**
  * s3c_hsotg_complete_request - complete a request given to us
  * @hsotg: The device state.
  * @hs_ep: The endpoint the request was on.
@@ -1684,6 +1705,37 @@ bad_mps:
 	dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps);
 }
 
+/**
+ * s3c_hsotg_txfifo_flush - flush Tx FIFO
+ * @hsotg: The driver state
+ * @idx: The index for the endpoint (0..15)
+ */
+static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
+{
+	int timeout;
+	int val;
+
+	writel(S3C_GRSTCTL_TxFNum(idx) | S3C_GRSTCTL_TxFFlsh,
+		hsotg->regs + S3C_GRSTCTL);
+
+	/* wait until the fifo is flushed */
+	timeout = 100;
+
+	while (1) {
+		val = readl(hsotg->regs + S3C_GRSTCTL);
+
+		if ((val & (S3C_GRSTCTL_TxFFlsh)) == 0)
+			break;
+
+		if (--timeout == 0) {
+			dev_err(hsotg->dev,
+				"%s: timeout flushing fifo (GRSTCTL=%08x)\n",
+				__func__, val);
+		}
+
+		udelay(1);
+	}
+}
 
 /**
  * s3c_hsotg_trytx - check to see if anything needs transmitting
@@ -1806,9 +1858,24 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
 		}
 	}
 
-	if (ints & S3C_DxEPINT_EPDisbld)
+	if (ints & S3C_DxEPINT_EPDisbld) {
 		dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
 
+		if (dir_in) {
+			int epctl = readl(hsotg->regs + epctl_reg);
+
+			s3c_hsotg_txfifo_flush(hsotg, idx);
+
+			if ((epctl & S3C_DxEPCTL_Stall) &&
+				(epctl & S3C_DxEPCTL_EPType_Bulk)) {
+				int dctl = readl(hsotg->regs + S3C_DCTL);
+
+				dctl |= S3C_DCTL_CGNPInNAK;
+				writel(dctl, hsotg->regs + S3C_DCTL);
+			}
+		}
+	}
+
 	if (ints & S3C_DxEPINT_AHBErr)
 		dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__);
 
@@ -2406,6 +2473,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 	unsigned long irqflags;
 	u32 epreg;
 	u32 epctl;
+	u32 xfertype;
 
 	dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
 
@@ -2416,10 +2484,17 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 	epreg = S3C_DIEPCTL(index);
 	epctl = readl(hs->regs + epreg);
 
-	if (value)
-		epctl |= S3C_DxEPCTL_Stall;
-	else
+	if (value) {
+		epctl |= S3C_DxEPCTL_Stall + S3C_DxEPCTL_SNAK;
+		if (epctl & S3C_DxEPCTL_EPEna)
+			epctl |= S3C_DxEPCTL_EPDis;
+	} else {
 		epctl &= ~S3C_DxEPCTL_Stall;
+		xfertype = epctl & S3C_DxEPCTL_EPType_MASK;
+		if (xfertype == S3C_DxEPCTL_EPType_Bulk ||
+			xfertype == S3C_DxEPCTL_EPType_Intterupt)
+				epctl |= S3C_DxEPCTL_SetD0PID;
+	}
 
 	writel(epctl, hs->regs + epreg);
 
@@ -2428,8 +2503,13 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 
 	if (value)
 		epctl |= S3C_DxEPCTL_Stall;
-	else
+	else {
 		epctl &= ~S3C_DxEPCTL_Stall;
+		xfertype = epctl & S3C_DxEPCTL_EPType_MASK;
+		if (xfertype == S3C_DxEPCTL_EPType_Bulk ||
+			xfertype == S3C_DxEPCTL_EPType_Intterupt)
+				epctl |= S3C_DxEPCTL_SetD0PID;
+	}
 
 	writel(epctl, hs->regs + epreg);
 
-- 
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


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

  Powered by Linux