From cfe2329be3af16b324a2e338ad29484f36e753ab Mon Sep 17 00:00:00 2001
From: Bin Yang <yangbin@rock-chips.com>
Date: Wed, 29 Mar 2017 17:43:23 +0800
Subject: [PATCH] USB: fix handle NAK for IN/OUT SSPLIT/CSPLIT transfers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

IN SSPLIT/CSPLIT transfers, NAK packet will cause DWC_OTG
ctrl to generate lots of  SSPLIT/CSPLIT transfer interrupts,
and seriously affect the performance of the system. So need
to stop queue transactions after receive SSPLIT/CSPLIT NAK.

Change-Id: Ie1b1e9e6971c6546a4239a10b06fba7360978ce0
Signed-off-by: Wu Liang feng <wulf@rock-chips.com>
Reviewed-on: https://tp-biosrd-v02/gerrit/80149
Reviewed-by: Scorpio Chang(張志賢) <Scorpio_Chang@asus.com>
Tested-by: Scorpio Chang(張志賢) <Scorpio_Chang@asus.com>
---
 drivers/usb/dwc2/hcd.c      |  2 ++
 drivers/usb/dwc2/hcd.h      |  1 +
 drivers/usb/dwc2/hcd_intr.c | 19 ++++++++++++++-----
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 3ed5d3398831..7bca8ffe9146 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2700,6 +2700,8 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	chan->xfer_len = urb->length - urb->actual_length;
 	chan->xfer_count = 0;
 
+	chan->csplit_nak = 0;
+
 	/* Set the split attributes if required */
 	if (qh->do_split)
 		dwc2_hc_init_split(hsotg, chan, qtd, urb);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 7758bfb644ff..91091d9009ef 100755
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -142,6 +142,7 @@ struct dwc2_host_chan {
 	u8 halt_pending;
 	u8 do_split;
 	u8 complete_split;
+	u8 csplit_nak;
 	u8 hub_addr;
 	u8 hub_port;
 	u8 xact_pos;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 906f223542ee..d0ee6fb3d088 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -691,6 +691,7 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
 	enum dwc2_transaction_type tr_type;
 	u32 haintmsk;
 	int free_qtd = 0;
+	int continue_trans = 1;
 
 	if (dbg_hc(chan))
 		dev_vdbg(hsotg->dev, "  %s: channel %d, halt_status %d\n",
@@ -719,6 +720,7 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
 		 * deactivated. Don't want to do anything except release the
 		 * host channel and try to queue more transfers.
 		 */
+		continue_trans = 0;
 		goto cleanup;
 	case DWC2_HC_XFER_PERIODIC_INCOMPLETE:
 		dev_vdbg(hsotg->dev, "  Complete URB with I/O error\n");
@@ -730,6 +732,10 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
 		break;
 	}
 
+       if (chan->csplit_nak) {
+               continue_trans = 0;
+               chan->csplit_nak = 0;
+       }
 	dwc2_deactivate_qh(hsotg, chan->qh, free_qtd);
 
 cleanup:
@@ -767,11 +773,13 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
 	dwc2_writel(haintmsk, hsotg->regs + HAINTMSK);
 
 	/* Try to queue more transfers now that there's a free channel */
-	tr_type = dwc2_hcd_select_transactions(hsotg);
-	if (tr_type != DWC2_TRANSACTION_NONE)
-		dwc2_hcd_queue_transactions(hsotg, tr_type);
-}
-
+	if (continue_trans) {
+		tr_type = dwc2_hcd_select_transactions(hsotg);
+		if (tr_type != DWC2_TRANSACTION_NONE)
+			dwc2_hcd_queue_transactions(hsotg, tr_type);
+	}
+ }
+ 
 /*
  * Halts a host channel. If the channel cannot be halted immediately because
  * the request queue is full, this function ensures that the FIFO empty
@@ -1199,6 +1207,7 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
 	if (chan->do_split) {
 		if (chan->complete_split)
 			qtd->error_count = 0;
+		chan->csplit_nak = 1;
 		qtd->complete_split = 0;
 		dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
 		goto handle_nak_done;
