Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 19 Dec 2007 18:56:28 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 131246 for review
Message-ID:  <200712191856.lBJIuSrE002188@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=131246

Change 131246 by hselasky@hselasky_laptop001 on 2007/12/19 18:56:23

	
	HW DMA optimization - new technique (OHCI, UHCI and EHCI)
	
	This commit gets rid of the doorbell and any other
	delays in the critical execution path by introducing
	an extra set of TDs and QHs for every USB transfer.
	
	The idea is that the USB Host Controller does not
	immediately know that we have removed a QH or TD
	from the schedule. Even if the Host Controller
	will not touch any data in the buffers we cannot
	simply start reusing the QH and TD DMA structures.
	Instead we use a second QH and TD set. When the
	second transfer is done we know that the first QH
	and TD set which we dequeued before we enqued the
	second transfer is now out the Host Controllers
	hands. Then we can right away start using the first
	set of QH and TD DMA structures and there is no need
	for doorbell or any other delay.
	
	If you stop, timeout or unsetup an USB transfer there
	will be a delay like usual.
	
	If the USB driver you are using requires a high number
	of interrupts per second you will see a noticable
	decrease in CPU usage and a minor increase in performance.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/ehci.c#63 edit
.. //depot/projects/usb/src/sys/dev/usb/ehci.h#26 edit
.. //depot/projects/usb/src/sys/dev/usb/ohci.c#52 edit
.. //depot/projects/usb/src/sys/dev/usb/uhci.c#53 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_subr.h#80 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#78 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/ehci.c#63 (text+ko) ====

@@ -1287,35 +1287,48 @@
 	DPRINTFN(12, ("xfer=%p checking transfer\n", xfer));
 
 	if (methods == &ehci_device_isoc_fs_methods) {
-		ehci_sitd_t *td = xfer->td_transfer_last;
+		ehci_sitd_t *td;
 
 		/* isochronous full speed transfer */
 
+		td = xfer->td_transfer_last;
 		usbd_pc_cpu_invalidate(td->page_cache);
+		status = le32toh(td->sitd_status);
 
-		status = le32toh(td->sitd_status);
+		/* also check if first is complete */
+
+		td = xfer->td_transfer_first;
+		usbd_pc_cpu_invalidate(td->page_cache);
+		status |= le32toh(td->sitd_status);
 
 		if (!(status & EHCI_SITD_ACTIVE)) {
 			ehci_device_done(xfer, USBD_NORMAL_COMPLETION);
 			goto transferred;
 		}
 	} else if (methods == &ehci_device_isoc_hs_methods) {
-		ehci_itd_t *td = xfer->td_transfer_last;
+		ehci_itd_t *td;
 
 		/* isochronous high speed transfer */
 
+		td = xfer->td_transfer_last;
 		usbd_pc_cpu_invalidate(td->page_cache);
+		status =
+		    td->itd_status[0] | td->itd_status[1] |
+		    td->itd_status[2] | td->itd_status[3] |
+		    td->itd_status[4] | td->itd_status[5] |
+		    td->itd_status[6] | td->itd_status[7];
 
-		status = ((!(td->itd_status[0] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[1] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[2] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[3] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[4] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[5] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[6] & htole32(EHCI_ITD_ACTIVE))) &&
-		    (!(td->itd_status[7] & htole32(EHCI_ITD_ACTIVE))));
+		/* also check first transfer */
+		td = xfer->td_transfer_first;
+		usbd_pc_cpu_invalidate(td->page_cache);
+		status |=
+		    td->itd_status[0] | td->itd_status[1] |
+		    td->itd_status[2] | td->itd_status[3] |
+		    td->itd_status[4] | td->itd_status[5] |
+		    td->itd_status[6] | td->itd_status[7];
 
-		if (status) {
+		/* if no transactions are active we continue */
+		if (!(status & htole32(EHCI_ITD_ACTIVE))) {
 			ehci_device_done(xfer, USBD_NORMAL_COMPLETION);
 			goto transferred;
 		}
@@ -1452,10 +1465,6 @@
 
 	status &= sc->sc_eintrs;
 
-	if (status & EHCI_STS_IAA) {
-		DPRINTF(("door bell\n"));
-		wakeup(&sc->sc_async_p_last);
-	}
 	if (status & EHCI_STS_HSE) {
 		printf("%s: unrecoverable error, "
 		    "controller halted\n", __FUNCTION__);
@@ -1718,11 +1727,17 @@
 	temp.average = xfer->max_usb_frame_size;
 	temp.max_frame_size = xfer->max_frame_size;
 
-	xfer->td_transfer_first = xfer->td_start;
-	xfer->td_transfer_cache = xfer->td_start;
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
+
+	xfer->td_transfer_first = td;
+	xfer->td_transfer_cache = td;
 
 	temp.td = NULL;
-	temp.td_next = xfer->td_start;
+	temp.td_next = td;
 	temp.qtd_status = 0;
 	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
 
@@ -1848,13 +1863,13 @@
 	if (ehcidebug > 8) {
 		DPRINTF(("nexttog=%d; data before transfer:\n",
 		    xfer->pipe->toggle_next));
-		ehci_dump_sqtds(xfer->td_start);
+		ehci_dump_sqtds(xfer->td_transfer_first);
 	}
 #endif
 
 	methods = xfer->pipe->methods;
 
-	qh = xfer->qh_start;
+	qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	/* the "qh_link" field is filled when the QH is added */
 
@@ -1962,14 +1977,13 @@
 	return;
 }
 
-static uint8_t
+static void
 ehci_isoc_fs_done(ehci_softc_t *sc, struct usbd_xfer *xfer)
 {
 	uint32_t nframes = xfer->nframes;
 	uint32_t status;
 	uint32_t *plen = xfer->frlengths;
 	uint16_t len = 0;
-	uint8_t need_delay = 0;
 	ehci_sitd_t *td = xfer->td_transfer_first;
 	ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos];
 
@@ -1993,10 +2007,6 @@
 		usbd_pc_cpu_invalidate(td->page_cache);
 		status = le32toh(td->sitd_status);
 
-		/* check for active transfers */
-		if (status & EHCI_SITD_ACTIVE) {
-			need_delay = 1;
-		}
 		len = EHCI_SITD_GET_LEN(status);
 
 		if (*plen >= len) {
@@ -2017,10 +2027,10 @@
 
 	xfer->aframes = xfer->nframes;
 
-	return (need_delay);
+	return;
 }
 
-static uint8_t
+static void
 ehci_isoc_hs_done(ehci_softc_t *sc, struct usbd_xfer *xfer)
 {
 	uint32_t nframes = xfer->nframes;
@@ -2028,7 +2038,6 @@
 	uint32_t *plen = xfer->frlengths;
 	uint16_t len = 0;
 	uint8_t td_no = 0;
-	uint8_t need_delay = 0;
 	ehci_itd_t *td = xfer->td_transfer_first;
 	ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos];
 
@@ -2053,9 +2062,6 @@
 		usbd_pc_cpu_invalidate(td->page_cache);
 		status = le32toh(td->itd_status[td_no]);
 
-		if (status & EHCI_ITD_ACTIVE) {
-			need_delay = 1;
-		}
 		len = EHCI_ITD_GET_LEN(status);
 
 		if (*plen >= len) {
@@ -2080,7 +2086,7 @@
 	}
 	xfer->aframes = xfer->nframes;
 
-	return (need_delay);
+	return;
 }
 
 /* NOTE: "done" can be run two times in a row,
@@ -2091,39 +2097,28 @@
 {
 	struct usbd_pipe_methods *methods = xfer->pipe->methods;
 	ehci_softc_t *sc = xfer->usb_sc;
-	uint8_t need_delay;
 
 	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
 
-	need_delay = 0;
-
 	DPRINTFN(1, ("xfer=%p, pipe=%p, error=%d\n",
 	    xfer, xfer->pipe, error));
 
-	/*
-	 * ... could check for not-completed transfers, instead of setting
-	 * need_delay ...
-	 */
-	if ((error == USBD_CANCELLED) ||
-	    (error == USBD_TIMEOUT)) {
-		if (xfer->flags_int.transferring) {
-			need_delay = 1;
-		}
-	}
 	if ((methods == &ehci_device_bulk_methods) ||
 	    (methods == &ehci_device_ctrl_methods)) {
 #ifdef USB_DEBUG
 		if (ehcidebug > 8) {
 			DPRINTF(("nexttog=%d; data after transfer:\n",
 			    xfer->pipe->toggle_next));
-			ehci_dump_sqtds(xfer->td_start);
+			ehci_dump_sqtds(xfer->td_transfer_first);
 		}
 #endif
 
-		EHCI_REMOVE_QH(xfer->qh_start, sc->sc_async_p_last);
+		EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+		    sc->sc_async_p_last);
 	}
 	if (methods == &ehci_device_intr_methods) {
-		EHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]);
+		EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+		    sc->sc_intr_p_last[xfer->qh_pos]);
 	}
 	/*
 	 * Only finish isochronous transfers once which will update
@@ -2132,76 +2127,14 @@
 	if (xfer->td_transfer_first &&
 	    xfer->td_transfer_last) {
 		if (methods == &ehci_device_isoc_fs_methods) {
-			if (ehci_isoc_fs_done(sc, xfer)) {
-				need_delay = 1;
-			}
+			ehci_isoc_fs_done(sc, xfer);
 		}
 		if (methods == &ehci_device_isoc_hs_methods) {
-			if (ehci_isoc_hs_done(sc, xfer)) {
-				need_delay = 1;
-			}
+			ehci_isoc_hs_done(sc, xfer);
 		}
 		xfer->td_transfer_first = NULL;
 		xfer->td_transfer_last = NULL;
 	}
-	if ((methods != &ehci_root_ctrl_methods) &&
-	    (methods != &ehci_root_intr_methods)) {
-
-		if (((methods == &ehci_device_ctrl_methods) ||
-		    (methods == &ehci_device_bulk_methods)) &&
-		    (sc->sc_doorbell_disable == 0)) {
-
-			uint32_t to = 100 * 1000;
-
-			/*
-			 * This implementation does not use the doorbell
-			 * interrupt. The reason is that one gets more
-			 * throughput by waiting for the doorbell inline,
-			 * than by using the doorbell interrupt. A typical
-			 * result reading from an USB mass storage disk:
-			 *
-			 * With doorbell interrupt: 27 seconds for 512MB
-			 * With inline doorbell: 21 seconds for 512MB
-			 *
-			 * Although the inline version causes a slightly
-			 * higher CPU usage, it does not block the system
-			 * in any way, because this USB driver uses its
-			 * own mutex.
-			 *
-			 * --hps
-			 */
-
-			/* wait for doorbell ~32us */
-			EOWRITE4(sc, EHCI_USBCMD,
-			    EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
-
-			while (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_IAAD) {
-				if (!to--) {
-					printf("%s: doorbell timeout "
-					    "(disabling)\n", __FUNCTION__);
-					sc->sc_doorbell_disable = 1;
-					break;
-				}
-				DELAY(1);
-			}
-
-			/*
-			 * acknowledge any doorbell interrupt (SiS chipsets
-			 * require this)
-			 */
-			EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_IAA);
-
-		} else {
-			/*
-			 * wait until the hardware has finished any possible
-			 * use of the transfer descriptor and QH
-			 *
-			 * in case "need_delay" is set, wait until the next
-			 * isochronous frame is started
-			 */
-			DELAY(need_delay ? ((3 * 1000) / (2 * 8)) : (5));
-		}
-	}
 	/* dequeue transfer and start next transfer */
 	usbd_transfer_dequeue(xfer, error);
 	return;
@@ -2421,6 +2354,7 @@
 {
 	ehci_sitd_t *td;
 	uint32_t sitd_portaddr;
+	uint8_t ds;
 
 	sitd_portaddr =
 	    EHCI_SITD_SET_ADDR(xfer->address) |
@@ -2435,19 +2369,23 @@
 
 	/* initialize all TD's */
 
-	for (td = xfer->td_start; td; td = td->obj_next) {
+	for (ds = 0; ds != 2; ds++) {
+
+		for (td = xfer->td_start[ds]; td; td = td->obj_next) {
 
-		td->sitd_portaddr = sitd_portaddr;
+			td->sitd_portaddr = sitd_portaddr;
 
-		/*
-		 * TODO: make some kind of automatic SMASK/CMASK selection
-		 * based on micro-frame usage
-		 *
-		 * micro-frame usage (8 microframes per 1ms)
-		 */
-		td->sitd_back = htole32(EHCI_LINK_TERMINATE);
+			/*
+			 * TODO: make some kind of automatic
+			 * SMASK/CMASK selection based on micro-frame
+			 * usage
+			 *
+			 * micro-frame usage (8 microframes per 1ms)
+			 */
+			td->sitd_back = htole32(EHCI_LINK_TERMINATE);
 
-		usbd_pc_cpu_flush(td->page_cache);
+			usbd_pc_cpu_flush(td->page_cache);
+		}
 	}
 	return;
 }
@@ -2534,7 +2472,12 @@
 
 	plen = xfer->frlengths;
 
-	td = (xfer->td_transfer_first = xfer->td_start);
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
+	xfer->td_transfer_first = td;
 
 	pp_last = &sc->sc_isoc_fs_p_last[xfer->pipe->isoc_next];
 
@@ -2688,39 +2631,44 @@
 {
 	ehci_itd_t *td;
 	uint32_t temp;
+	uint8_t ds;
 
 	/* initialize all TD's */
 
-	for (td = xfer->td_start; td; td = td->obj_next) {
+	for (ds = 0; ds != 2; ds++) {
+
+		for (td = xfer->td_start[ds]; td; td = td->obj_next) {
 
-		/* set TD inactive */
-		td->itd_status[0] = 0;
-		td->itd_status[1] = 0;
-		td->itd_status[2] = 0;
-		td->itd_status[3] = 0;
-		td->itd_status[4] = 0;
-		td->itd_status[5] = 0;
-		td->itd_status[6] = 0;
-		td->itd_status[7] = 0;
+			/* set TD inactive */
+			td->itd_status[0] = 0;
+			td->itd_status[1] = 0;
+			td->itd_status[2] = 0;
+			td->itd_status[3] = 0;
+			td->itd_status[4] = 0;
+			td->itd_status[5] = 0;
+			td->itd_status[6] = 0;
+			td->itd_status[7] = 0;
 
-		/* set endpoint and address */
-		td->itd_bp[0] = htole32
-		    (EHCI_ITD_SET_ADDR(xfer->address) |
-		    EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)));
+			/* set endpoint and address */
+			td->itd_bp[0] = htole32
+			    (EHCI_ITD_SET_ADDR(xfer->address) |
+			    EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)));
 
-		temp = EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF);
+			temp =
+			    EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF);
 
-		/* set direction */
-		if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
-			temp |= EHCI_ITD_SET_DIR_IN;
-		}
-		/* set maximum packet size */
-		td->itd_bp[1] = htole32(temp);
+			/* set direction */
+			if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+				temp |= EHCI_ITD_SET_DIR_IN;
+			}
+			/* set maximum packet size */
+			td->itd_bp[1] = htole32(temp);
 
-		/* set transfer multiplier */
-		td->itd_bp[2] = htole32(xfer->max_packet_count & 3);
+			/* set transfer multiplier */
+			td->itd_bp[2] = htole32(xfer->max_packet_count & 3);
 
-		usbd_pc_cpu_flush(td->page_cache);
+			usbd_pc_cpu_flush(td->page_cache);
+		}
 	}
 	return;
 }
@@ -2806,7 +2754,12 @@
 
 	plen = xfer->frlengths;
 
-	td = (xfer->td_transfer_first = xfer->td_start);
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
+	xfer->td_transfer_first = td;
 
 	pp_last = &sc->sc_isoc_hs_p_last[xfer->pipe->isoc_next];
 
@@ -3704,6 +3657,8 @@
 		usbd_transfer_setup_sub(parm);
 	}
 
+alloc_dma_set:
+
 	if (parm->err) {
 		return;
 	}
@@ -3787,7 +3742,7 @@
 		}
 	}
 
-	xfer->td_start = last_obj;
+	xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
 
 	last_obj = NULL;
 
@@ -3816,8 +3771,12 @@
 		}
 	}
 
-	xfer->qh_start = last_obj;
+	xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj;
 
+	if (!xfer->flags_int.curr_dma_set) {
+		xfer->flags_int.curr_dma_set = 1;
+		goto alloc_dma_set;
+	}
 	return;
 }
 
@@ -3891,10 +3850,22 @@
 	return;
 }
 
+static void
+ehci_do_dma_delay(struct usbd_bus *bus)
+{
+	/*
+	 * Wait until the hardware has finished any possible use of
+	 * the transfer descriptor(s) and QH
+	 */
+	DELAY(188);
+	return;
+}
+
 struct usbd_bus_methods ehci_bus_methods =
 {
 	.pipe_init = ehci_pipe_init,
 	.xfer_setup = ehci_xfer_setup,
 	.xfer_unsetup = ehci_xfer_unsetup,
 	.do_poll = ehci_do_poll,
+	.do_dma_delay = ehci_do_dma_delay,
 };

==== //depot/projects/usb/src/sys/dev/usb/ehci.h#26 (text+ko) ====

@@ -113,7 +113,11 @@
 #define	EHCI_STS_INT		0x00000001	/* RWC interrupt */
 #define	EHCI_STS_INTRS(x)	((x) & 0x3f)
 
-#define	EHCI_NORMAL_INTRS	(/* EHCI_STS_IAA | */ EHCI_STS_HSE |	\
+/*
+ * NOTE: the doorbell interrupt is enabled, but the doorbell is never
+ * used! SiS chipsets require this.
+ */
+#define	EHCI_NORMAL_INTRS	(EHCI_STS_IAA | EHCI_STS_HSE |	\
 				EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT)
 
 #define	EHCI_USBINTR		0x08	/* RW Interrupt register */

==== //depot/projects/usb/src/sys/dev/usb/ohci.c#52 (text+ko) ====

@@ -1035,7 +1035,7 @@
 
 	if (td) {
 
-		ed = xfer->qh_start;
+		ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 		ed->ed_headp = td->td_self;
 		usbd_pc_cpu_flush(ed->page_cache);
@@ -1055,13 +1055,15 @@
 static uint8_t
 ohci_check_transfer(struct usbd_xfer *xfer)
 {
-	ohci_ed_t *ed = xfer->qh_start;
+	ohci_ed_t *ed;
 	uint32_t ed_flags;
 	uint32_t ed_headp;
 	uint32_t ed_tailp;
 
 	DPRINTFN(12, ("xfer=%p checking transfer\n", xfer));
 
+	ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
 	usbd_pc_cpu_invalidate(ed->page_cache);
 	ed_flags = le32toh(ed->ed_flags);
 	ed_headp = le32toh(ed->ed_headp);
@@ -1441,11 +1443,17 @@
 	temp.average = xfer->max_usb_frame_size;
 	temp.max_frame_size = xfer->max_frame_size;
 
-	xfer->td_transfer_first = xfer->td_start;
-	xfer->td_transfer_cache = xfer->td_start;
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
+
+	xfer->td_transfer_first = td;
+	xfer->td_transfer_cache = td;
 
 	temp.td = NULL;
-	temp.td_next = xfer->td_start;
+	temp.td_next = td;
 	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
 
 	methods = xfer->pipe->methods;
@@ -1562,11 +1570,11 @@
 	if (ohcidebug > 8) {
 		DPRINTF(("nexttog=%d; data before transfer:\n",
 		    xfer->pipe->toggle_next));
-		ohci_dump_tds(xfer->td_start);
+		ohci_dump_tds(xfer->td_transfer_first);
 	}
 #endif
 
-	ed = xfer->qh_start;
+	ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	ed_flags = (OHCI_ED_SET_FA(xfer->address) |
 	    OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) |
@@ -1652,51 +1660,32 @@
 	struct usbd_pipe_methods *methods = xfer->pipe->methods;
 	ohci_softc_t *sc = xfer->usb_sc;
 	ohci_ed_t *ed;
-	uint8_t need_delay;
 
 	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
 
-	need_delay = 0;
 
 	DPRINTFN(1, ("xfer=%p, pipe=%p, error=%d\n",
 	    xfer, xfer->pipe, error));
 
-	for (ed = xfer->qh_start; ed; ed = ed->obj_next) {
+	ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+	if (ed) {
 		usbd_pc_cpu_invalidate(ed->page_cache);
-
-		if ((!(ed->ed_flags & htole32(OHCI_ED_SKIP))) &&
-		    (!(ed->ed_headp & htole32(OHCI_HALTED))) &&
-		    ((ed->ed_headp ^ ed->ed_tailp) & htole32(-0x10))) {
-			need_delay = 1;
-		}
 	}
-
 	if (methods == &ohci_device_bulk_methods) {
-		OHCI_REMOVE_QH(xfer->qh_start, sc->sc_bulk_p_last);
+		OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last);
 	}
 	if (methods == &ohci_device_ctrl_methods) {
-		OHCI_REMOVE_QH(xfer->qh_start, sc->sc_ctrl_p_last);
+		OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last);
 	}
 	if (methods == &ohci_device_intr_methods) {
-		OHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]);
+		OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]);
 	}
 	if (methods == &ohci_device_isoc_methods) {
-		OHCI_REMOVE_QH(xfer->qh_start, sc->sc_isoc_p_last);
+		OHCI_REMOVE_QH(ed, sc->sc_isoc_p_last);
 	}
 	xfer->td_transfer_first = NULL;
 	xfer->td_transfer_last = NULL;
 
-	if ((methods != &ohci_root_ctrl_methods) &&
-	    (methods != &ohci_root_intr_methods)) {
-
-		/*
-		 * wait until hardware has finished any possible use of the
-		 * transfer and QH
-		 *
-		 * hardware finishes in 1 millisecond
-		 */
-		DELAY(need_delay ? (2 * 1000) : (5));
-	}
 	/* dequeue transfer and start next transfer */
 	usbd_transfer_dequeue(xfer, error);
 	return;
@@ -1961,7 +1950,11 @@
 
 	plen = xfer->frlengths;
 
-	td = xfer->td_start;
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
 
 	xfer->td_transfer_first = td;
 
@@ -2040,10 +2033,10 @@
 #ifdef USB_DEBUG
 	if (ohcidebug > 8) {
 		DPRINTF(("data before transfer:\n"));
-		ohci_dump_itds(xfer->td_start);
+		ohci_dump_itds(xfer->td_transfer_first);
 	}
 #endif
-	ed = xfer->qh_start;
+	ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN)
 		ed_flags = (OHCI_ED_DIR_IN | OHCI_ED_FORMAT_ISO);
@@ -2660,6 +2653,8 @@
 		nqh = 0;
 	}
 
+alloc_dma_set:
+
 	if (parm->err) {
 		return;
 	}
@@ -2715,7 +2710,7 @@
 		}
 	}
 
-	xfer->td_start = last_obj;
+	xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
 
 	last_obj = NULL;
 
@@ -2744,8 +2739,12 @@
 		}
 	}
 
-	xfer->qh_start = last_obj;
+	xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj;
 
+	if (!xfer->flags_int.curr_dma_set) {
+		xfer->flags_int.curr_dma_set = 1;
+		goto alloc_dma_set;
+	}
 	return;
 }
 
@@ -2808,10 +2807,22 @@
 	return;
 }
 
+static void
+ohci_do_dma_delay(struct usbd_bus *bus)
+{
+	/*
+	 * Wait until hardware has finished any possible use of the
+	 * transfer descriptor(s) and QH
+	 */
+	DELAY(1125);
+	return;
+}
+
 struct usbd_bus_methods ohci_bus_methods =
 {
 	.pipe_init = ohci_pipe_init,
 	.xfer_setup = ohci_xfer_setup,
 	.xfer_unsetup = ohci_xfer_unsetup,
 	.do_poll = ohci_do_poll,
+	.do_dma_delay = ohci_do_dma_delay,
 };

==== //depot/projects/usb/src/sys/dev/usb/uhci.c#53 (text+ko) ====

@@ -1019,7 +1019,7 @@
 	return (last);
 }
 
-static uint8_t
+static void
 uhci_isoc_done(uhci_softc_t *sc, struct usbd_xfer *xfer)
 {
 	struct usbd_page_search res;
@@ -1028,7 +1028,6 @@
 	uint32_t offset = 0;
 	uint32_t *plen = xfer->frlengths;
 	uint16_t len = 0;
-	uint8_t need_delay = 0;
 	uhci_td_t *td = xfer->td_transfer_first;
 	uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos];
 
@@ -1056,11 +1055,6 @@
 		usbd_pc_cpu_invalidate(td->page_cache);
 		status = le32toh(td->td_status);
 
-		/* check for active transfers */
-
-		if (status & UHCI_TD_ACTIVE) {
-			need_delay = 1;
-		}
 		len = UHCI_TD_GET_ACTLEN(status);
 
 		if (len > *plen) {
@@ -1091,7 +1085,7 @@
 
 	xfer->aframes = xfer->nframes;
 
-	return (need_delay);
+	return;
 }
 
 static usbd_status_t
@@ -1277,7 +1271,7 @@
 	uint32_t td_self;
 
 	td = xfer->td_transfer_cache;
-	qh = xfer->qh_start;
+	qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	td_token = td->obj_next->td_token;
 	td = td->alt_next;
@@ -1341,6 +1335,13 @@
 		usbd_pc_cpu_invalidate(td->page_cache);
 		status = le32toh(td->td_status);
 
+		/* check also if the first is complete */
+
+		td = xfer->td_transfer_first;
+
+		usbd_pc_cpu_invalidate(td->page_cache);
+		status |= le32toh(td->td_status);
+
 		if (!(status & UHCI_TD_ACTIVE)) {
 			uhci_device_done(xfer, USBD_NORMAL_COMPLETION);
 			goto transferred;
@@ -1691,11 +1692,16 @@
 	temp.average = xfer->max_frame_size;
 	temp.max_frame_size = xfer->max_frame_size;
 
-	xfer->td_transfer_first = xfer->td_start;
-	xfer->td_transfer_cache = xfer->td_start;
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
+	xfer->td_transfer_first = td;
+	xfer->td_transfer_cache = td;
 
 	temp.td = NULL;
-	temp.td_next = xfer->td_start;
+	temp.td_next = td;
 	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
 
 	uhci_mem_layout_init(&(temp.ml), xfer);
@@ -1823,10 +1829,10 @@
 	if (uhcidebug > 8) {
 		DPRINTF(("nexttog=%d; data before transfer:\n",
 		    xfer->pipe->toggle_next));
-		uhci_dump_tds(xfer->td_start);
+		uhci_dump_tds(xfer->td_transfer_first);
 	}
 #endif
-	return (xfer->td_start);
+	return (xfer->td_transfer_first);
 }
 
 /* NOTE: "done" can be run two times in a row,
@@ -1838,45 +1844,38 @@
 {
 	struct usbd_pipe_methods *methods = xfer->pipe->methods;
 	uhci_softc_t *sc = xfer->usb_sc;
-	uhci_td_t *td;
 	uhci_qh_t *qh;
-	uint8_t need_delay;
 
 	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
 
-	need_delay = 0;
-
 	DPRINTFN(1, ("xfer=%p, pipe=%p, error=%d\n",
 	    xfer, xfer->pipe, error));
 
-	for (qh = xfer->qh_start; qh; qh = qh->obj_next) {
+	qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+	if (qh) {
 		usbd_pc_cpu_invalidate(qh->page_cache);
 
-		if (!(qh->qh_e_next & htole32(UHCI_PTR_T))) {
-			need_delay = 1;
-		}
 		qh->e_next = 0;
 		qh->qh_e_next = htole32(UHCI_PTR_T);
 
 		usbd_pc_cpu_flush(qh->page_cache);
 	}
-
 	if (xfer->flags_int.bandwidth_reclaimed) {
 		xfer->flags_int.bandwidth_reclaimed = 0;
 		uhci_rem_loop(sc);
 	}
 	if (methods == &uhci_device_bulk_methods) {
-		UHCI_REMOVE_QH(xfer->qh_start, sc->sc_bulk_p_last);
+		UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last);
 	}
 	if (methods == &uhci_device_ctrl_methods) {
 		if (xfer->udev->speed == USB_SPEED_LOW) {
-			UHCI_REMOVE_QH(xfer->qh_start, sc->sc_ls_ctl_p_last);
+			UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last);
 		} else {
-			UHCI_REMOVE_QH(xfer->qh_start, sc->sc_fs_ctl_p_last);
+			UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last);
 		}
 	}
 	if (methods == &uhci_device_intr_methods) {
-		UHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]);
+		UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
 	}
 	/*
 	 * Only finish isochronous transfers once
@@ -1885,46 +1884,11 @@
 	if (xfer->td_transfer_first &&
 	    xfer->td_transfer_last) {
 		if (methods == &uhci_device_isoc_methods) {
-			if (uhci_isoc_done(sc, xfer)) {
-				need_delay = 1;
-			}
-		}
-		if (need_delay) {
-			td = xfer->td_transfer_first;
-
-			while (1) {
-				if (td == NULL) {
-					panic("%s:%d: out of TD's\n",
-					    __FUNCTION__, __LINE__);
-				}
-				usbd_pc_cpu_invalidate(td->page_cache);
-
-				td->td_status &=
-				    htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
-
-				usbd_pc_cpu_flush(td->page_cache);
-
-				if (((void *)td) == xfer->td_transfer_last) {
-					td = NULL;
-					break;
-				}
-				td = td->obj_next;
-			}
+			uhci_isoc_done(sc, xfer);
 		}
 		xfer->td_transfer_first = NULL;
 		xfer->td_transfer_last = NULL;
 	}
-	if ((methods != &uhci_root_ctrl_methods) &&
-	    (methods != &uhci_root_intr_methods)) {
-
-		/*
-		 * wait until hardware has finished any possible use of the
-		 * transfer and QH
-		 *
-		 * hardware finishes in 1 millisecond
-		 */
-		DELAY(need_delay ? ((3 * 1000) / 2) : UHCI_QH_REMOVE_DELAY);
-	}
 	/* dequeue transfer and start next transfer */
 	usbd_transfer_dequeue(xfer, error);
 	return;
@@ -1965,7 +1929,7 @@
 	td = uhci_setup_standard_chain(xfer);
 
 	/* setup QH */
-	qh = xfer->qh_start;
+	qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	UHCI_APPEND_QH(qh, td, sc->sc_bulk_p_last);
 	uhci_add_loop(sc);
@@ -2024,7 +1988,7 @@
 	td = uhci_setup_standard_chain(xfer);
 
 	/* setup QH */
-	qh = xfer->qh_start;
+	qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	/*
 	 * NOTE: some devices choke on bandwidth- reclamation for control
@@ -2121,7 +2085,7 @@
 	td = uhci_setup_standard_chain(xfer);
 
 	/* setup QH */
-	qh = xfer->qh_start;
+	qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
 
 	/* enter QHs into the controller data structures */
 	UHCI_APPEND_QH(qh, td, sc->sc_intr_p_last[xfer->qh_pos]);
@@ -2152,6 +2116,7 @@
 {
 	uhci_td_t *td;
 	uint32_t td_token;
+	uint8_t ds;
 
 	td_token =
 	    (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ?
@@ -2162,13 +2127,16 @@
 
 	/* initialize all TD's */
 
-	for (td = xfer->td_start; td; td = td->obj_next) {
+	for (ds = 0; ds != 2; ds++) {
+
+		for (td = xfer->td_start[ds]; td; td = td->obj_next) {
 
-		/* mark TD as inactive */
-		td->td_status = htole32(UHCI_TD_IOS);
-		td->td_token = td_token;
+			/* mark TD as inactive */

>>> TRUNCATED FOR MAIL (1000 lines) <<<



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712191856.lBJIuSrE002188>