[PATCH 2/3] staging: dwc2: add NAK holdoff patch from downstream Pi kernel

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

 



From: Dom Cobley <popcornmix@xxxxxxxxx>

Add the NAK holdoff patch from the downstream Raspberry Pi kernel.
This allows the transfer scheduler to better handle "cheeky devices
that just hold off using NAKs".

[original patch from Dom Cobley]
Signed-off-by: Dom Cobley <popcornmix@xxxxxxxxx>
[adapted to dwc2 driver by Paul Zimmerman]
Signed-off-by: Paul Zimmerman <paulz@xxxxxxxxxxxx>
---
 drivers/staging/dwc2/core.h      |  2 ++
 drivers/staging/dwc2/hcd.c       | 20 ++++++++++++++++++++
 drivers/staging/dwc2/hcd.h       |  2 ++
 drivers/staging/dwc2/hcd_intr.c  | 24 ++++++++++++++++++++++--
 drivers/staging/dwc2/hcd_queue.c | 19 +++++++++++++++----
 5 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/dwc2/core.h b/drivers/staging/dwc2/core.h
index e5b4dc8..d65e8a5 100644
--- a/drivers/staging/dwc2/core.h
+++ b/drivers/staging/dwc2/core.h
@@ -292,6 +292,7 @@ struct dwc2_core_params {
  * @otg_port:           OTG port number
  * @frame_list:         Frame list
  * @frame_list_dma:     Frame list DMA address
+ * @next_sched_frame:   Next scheduled frame, used by the NAK holdoff code
  */
 struct dwc2_hsotg {
 	struct device *dev;
@@ -365,6 +366,7 @@ struct dwc2_hsotg {
 	u8 otg_port;
 	u32 *frame_list;
 	dma_addr_t frame_list_dma;
+	int next_sched_frame;
 
 	/* DWC OTG HW Release versions */
 #define DWC2_CORE_REV_2_71a	0x4f54271a
diff --git a/drivers/staging/dwc2/hcd.c b/drivers/staging/dwc2/hcd.c
index 32b52ad..3a4ff79 100644
--- a/drivers/staging/dwc2/hcd.c
+++ b/drivers/staging/dwc2/hcd.c
@@ -890,6 +890,24 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		if (list_empty(&hsotg->free_hc_list))
 			break;
 		qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
+
+		/*
+		 * Check to see if this is a NAK'd retransmit, in which case
+		 * ignore for retransmission. We hold off on bulk
+		 * retransmissions to reduce NAK interrupt overhead for
+		 * cheeky devices that just hold off using NAKs.
+		 */
+		if (dwc2_full_frame_num(qh->nak_frame) ==
+		    dwc2_full_frame_num(dwc2_hcd_get_frame_number(hsotg))) {
+			/* Make interrupt run on next frame (i.e. 8 uframes) */
+			hsotg->next_sched_frame = ((qh->nak_frame + 8) & ~7) &
+						  HFNUM_MAX_FRNUM;
+			qh_ptr = qh_ptr->next;
+			continue;
+		} else {
+			qh->nak_frame = 0xffff;
+		}
+
 		dwc2_assign_and_init_hc(hsotg, qh);
 
 		/*
@@ -2914,6 +2932,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
+	hsotg->next_sched_frame = 0;
+
 	/* Initialize hsotg start work */
 	INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
 
diff --git a/drivers/staging/dwc2/hcd.h b/drivers/staging/dwc2/hcd.h
index 3b06024..4f9f6c5 100644
--- a/drivers/staging/dwc2/hcd.h
+++ b/drivers/staging/dwc2/hcd.h
@@ -238,6 +238,7 @@ enum dwc2_transaction_type {
  * @interval:           Interval between transfers in (micro)frames
  * @sched_frame:        (Micro)frame to initialize a periodic transfer.
  *                      The transfer executes in the following (micro)frame.
+ * @nak_frame:          Internal variable used by the NAK holdoff code
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
  * @dw_align_buf:       Used instead of original buffer if its physical address
@@ -271,6 +272,7 @@ struct dwc2_qh {
 	u16 usecs;
 	u16 interval;
 	u16 sched_frame;
+	u16 nak_frame;
 	u16 start_split_frame;
 	u16 ntd;
 	u8 *dw_align_buf;
diff --git a/drivers/staging/dwc2/hcd_intr.c b/drivers/staging/dwc2/hcd_intr.c
index 9e68ef1..b190d2b 100644
--- a/drivers/staging/dwc2/hcd_intr.c
+++ b/drivers/staging/dwc2/hcd_intr.c
@@ -118,9 +118,10 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
  */
 static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 {
+	enum dwc2_transaction_type tr_type;
 	struct list_head *qh_entry;
 	struct dwc2_qh *qh;
-	enum dwc2_transaction_type tr_type;
+	int next_sched_frame = -1;
 
 #ifdef DEBUG_SOF
 	dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
@@ -135,14 +136,23 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
+		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
 			/*
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
 			list_move(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
+		} else {
+			if (next_sched_frame < 0 ||
+			    dwc2_frame_num_le(qh->sched_frame,
+					      next_sched_frame))
+				next_sched_frame = qh->sched_frame;
+		}
 	}
+
+	hsotg->next_sched_frame = next_sched_frame;
+
 	tr_type = dwc2_hcd_select_transactions(hsotg);
 	if (tr_type != DWC2_TRANSACTION_NONE)
 		dwc2_hcd_queue_transactions(hsotg, tr_type);
@@ -1205,6 +1215,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
 			 chnum);
 
 	/*
+	 * When we get bulk NAKs then remember this so we holdoff on this qh
+	 * until the beginning of the next frame
+	 */
+	switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+	case USB_ENDPOINT_XFER_BULK:
+		chan->qh->nak_frame = dwc2_hcd_get_frame_number(hsotg);
+		break;
+	}
+
+	/*
 	 * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
 	 * interrupt. Re-start the SSPLIT transfer.
 	 */
diff --git a/drivers/staging/dwc2/hcd_queue.c b/drivers/staging/dwc2/hcd_queue.c
index 5461e3b..262e11f 100644
--- a/drivers/staging/dwc2/hcd_queue.c
+++ b/drivers/staging/dwc2/hcd_queue.c
@@ -83,6 +83,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
 
 	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+	qh->nak_frame = 0xffff;
 
 	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
 	    hub_addr != 0 && hub_addr != 1) {
@@ -391,13 +392,18 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		return status;
 	}
 
-	if (hsotg->core_params->dma_desc_enable > 0)
+	if (hsotg->core_params->dma_desc_enable > 0) {
 		/* Don't rely on SOF and start in ready schedule */
 		list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
-	else
+	} else {
+		if (list_empty(&hsotg->periodic_sched_inactive) ||
+		    dwc2_frame_num_le(qh->sched_frame, hsotg->next_sched_frame))
+			hsotg->next_sched_frame = qh->sched_frame;
+
 		/* Always start in inactive schedule */
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
+	}
 
 	/* Reserve periodic channel */
 	hsotg->periodic_channels++;
@@ -581,12 +587,17 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			 * Remove from periodic_sched_queued and move to
 			 * appropriate queue
 			 */
-			if (qh->sched_frame == frame_number)
+			if (qh->sched_frame == frame_number) {
 				list_move(&qh->qh_list_entry,
 					  &hsotg->periodic_sched_ready);
-			else
+			} else {
+				if (!dwc2_frame_num_le(hsotg->next_sched_frame,
+						       qh->sched_frame))
+					hsotg->next_sched_frame =
+							qh->sched_frame;
 				list_move(&qh->qh_list_entry,
 					  &hsotg->periodic_sched_inactive);
+			}
 		}
 	}
 }
-- 
1.8.2.rc0.16.g20a599e

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel




[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux