[U-Boot] [PATCH v4 7/7] ehci: Optimize qTD allocations

Benoît Thébaudeau benoit.thebaudeau at advansee.com
Fri Aug 10 18:27:23 CEST 2012


Relax the qTD transfer alignment constraints in order to need less qTDs for
buffers that are aligned to 512 bytes but not to pages.

Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau at advansee.com>
Cc: Marek Vasut <marex at denx.de>
Cc: Ilya Yanok <ilya.yanok at cogentembedded.com>
Cc: Stefan Herbrechtsmeier <stefan at herbrechtsmeier.net>
---
Changes for v2: N/A.
Changes for v3:
 - New patch.
Changes for v4:
 - Optimize away the qtd_toggle variable.

 .../drivers/usb/host/ehci-hcd.c                    |   67 +++++++++++---------
 1 file changed, 37 insertions(+), 30 deletions(-)

diff --git u-boot-usb-4f8254e.orig/drivers/usb/host/ehci-hcd.c u-boot-usb-4f8254e/drivers/usb/host/ehci-hcd.c
index a0ef5db..18b4bc6 100644
--- u-boot-usb-4f8254e.orig/drivers/usb/host/ehci-hcd.c
+++ u-boot-usb-4f8254e/drivers/usb/host/ehci-hcd.c
@@ -215,7 +215,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 	volatile struct qTD *vtd;
 	unsigned long ts;
 	uint32_t *tdp;
-	uint32_t endpt, token, usbsts;
+	uint32_t endpt, maxpacket, token, usbsts;
 	uint32_t c, toggle;
 	uint32_t cmd;
 	int timeout;
@@ -230,6 +230,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 		      le16_to_cpu(req->value), le16_to_cpu(req->value),
 		      le16_to_cpu(req->index));
 
+#define PKT_ALIGN	512
 	/*
 	 * The USB transfer is split into qTD transfers. Eeach qTD transfer is
 	 * described by a transfer descriptor (the qTD). The qTDs form a linked
@@ -251,43 +252,41 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 	if (length > 0 || req == NULL) {
 		/*
 		 * Determine the qTD transfer size that will be used for the
-		 * data payload (not considering the final qTD transfer, which
-		 * may be shorter).
+		 * data payload (not considering the first qTD transfer, which
+		 * may be longer or shorter, and the final one, which may be
+		 * shorter).
 		 *
 		 * In order to keep each packet within a qTD transfer, the qTD
-		 * transfer size is aligned to EHCI_PAGE_SIZE, which is a
-		 * multiple of wMaxPacketSize (except in some cases for
-		 * interrupt transfers, see comment in submit_int_msg()).
+		 * transfer size is aligned to PKT_ALIGN, which is a multiple of
+		 * wMaxPacketSize (except in some cases for interrupt transfers,
+		 * see comment in submit_int_msg()).
 		 *
-		 * By default, i.e. if the input buffer is page-aligned,
+		 * By default, i.e. if the input buffer is aligned to PKT_ALIGN,
 		 * QT_BUFFER_CNT full pages will be used.
 		 */
 		int xfr_sz = QT_BUFFER_CNT;
 		/*
-		 * However, if the input buffer is not page-aligned, the qTD
-		 * transfer size will be one page shorter, and the first qTD
+		 * However, if the input buffer is not aligned to PKT_ALIGN, the
+		 * qTD transfer size will be one page shorter, and the first qTD
 		 * data buffer of each transfer will be page-unaligned.
 		 */
-		if ((uint32_t)buffer & (EHCI_PAGE_SIZE - 1))
+		if ((uint32_t)buffer & (PKT_ALIGN - 1))
 			xfr_sz--;
 		/* Convert the qTD transfer size to bytes. */
 		xfr_sz *= EHCI_PAGE_SIZE;
 		/*
-		 * Determine the number of qTDs that will be required for the
-		 * data payload. This value has to be rounded up since the final
-		 * qTD transfer may be shorter than the regular qTD transfer
-		 * size that has just been computed.
+		 * Approximate by excess the number of qTDs that will be
+		 * required for the data payload. The exact formula is way more
+		 * complicated and saves at most 2 qTDs, i.e. a total of 128
+		 * bytes.
 		 */
-		qtd_count += DIV_ROUND_UP(length, xfr_sz);
-		/* ZLPs also need a qTD. */
-		if (!qtd_count)
-			qtd_count++;
+		qtd_count += 2 + length / xfr_sz;
 	}
 /*
- * Threshold value based on the worst-case total size of the qTDs to allocate
- * for a mass-storage transfer of 65535 blocks of 512 bytes.
+ * Threshold value based on the worst-case total size of the allocated qTDs for
+ * a mass-storage transfer of 65535 blocks of 512 bytes.
  */
-#if CONFIG_SYS_MALLOC_LEN <= 128 * 1024
+#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024
 #warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI
 #endif
 	qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD));
@@ -313,8 +312,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 	 */
 	qh->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
 	c = usb_pipespeed(pipe) != USB_SPEED_HIGH && !usb_pipeendpoint(pipe);
+	maxpacket = usb_maxpacket(dev, pipe);
 	endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) |
-		QH_ENDPT1_MAXPKTLEN(usb_maxpacket(dev, pipe)) | QH_ENDPT1_H(0) |
+		QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) |
 		QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) |
 		QH_ENDPT1_EPS(usb_pipespeed(pipe)) |
 		QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) |
@@ -373,9 +373,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 			xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1);
 			/*
 			 * In order to keep each packet within a qTD transfer,
-			 * align the qTD transfer size to EHCI_PAGE_SIZE.
+			 * align the qTD transfer size to PKT_ALIGN.
 			 */
-			xfr_bytes &= ~(EHCI_PAGE_SIZE - 1);
+			xfr_bytes &= ~(PKT_ALIGN - 1);
 			/*
 			 * This transfer may be shorter than the available qTD
 			 * transfer size that has just been computed.
@@ -411,6 +411,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 			/* Update previous qTD! */
 			*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
 			tdp = &qtd[qtd_counter++].qt_next;
+			/*
+			 * Data toggle has to be adjusted since the qTD transfer
+			 * size is not always an even multiple of
+			 * wMaxPacketSize.
+			 */
+			if ((xfr_bytes / maxpacket) & 1)
+				toggle ^= 1;
 			buf_ptr += xfr_bytes;
 			left_length -= xfr_bytes;
 		} while (left_length > 0);
@@ -426,7 +433,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 		 */
 		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
 		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
-		token = QT_TOKEN_DT(toggle) | QT_TOKEN_TOTALBYTES(0) |
+		token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) |
 			QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
 			QT_TOKEN_PID(usb_pipein(pipe) ?
 				QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) |
@@ -931,11 +938,11 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 	 * because bInterval is ignored.
 	 *
 	 * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
-	 * if several qTDs are required, while the USB specification does not
-	 * constrain this for interrupt transfers. That means that
-	 * ehci_submit_async() would support interrupt transfers requiring
-	 * several transactions only as long as the transfer size does not
-	 * require more than a single qTD.
+	 * <= PKT_ALIGN if several qTDs are required, while the USB
+	 * specification does not constrain this for interrupt transfers. That
+	 * means that ehci_submit_async() would support interrupt transfers
+	 * requiring several transactions only as long as the transfer size does
+	 * not require more than a single qTD.
 	 */
 	if (length > usb_maxpacket(dev, pipe)) {
 		printf("%s: Interrupt transfers requiring several transactions "


More information about the U-Boot mailing list