svn commit: r213869 - head/sys/dev/usb/controller

Hans Petter Selasky hselasky at FreeBSD.org
Thu Oct 14 21:38:06 UTC 2010


Author: hselasky
Date: Thu Oct 14 21:38:06 2010
New Revision: 213869
URL: http://svn.freebsd.org/changeset/base/213869

Log:
  Revert most of r197682 (EHCI Hardware BUG workaround). Implement
  proper solution which is to not use the TERMINATE pointer, but rather
  link to a halted TD. The initial fix was due to a misunderstanding
  about how the EHCI hardware works. Thanks to Alan Stern for clearing
  this up. This patch can increase mass storage read performance
  significantly when the IRQ rate is less than 8000 IRQ/s.
  
  Approved by:    thompsa (mentor)

Modified:
  head/sys/dev/usb/controller/ehci.c
  head/sys/dev/usb/controller/ehci.h

Modified: head/sys/dev/usb/controller/ehci.c
==============================================================================
--- head/sys/dev/usb/controller/ehci.c	Thu Oct 14 21:34:53 2010	(r213868)
+++ head/sys/dev/usb/controller/ehci.c	Thu Oct 14 21:38:06 2010	(r213869)
@@ -145,7 +145,6 @@ struct ehci_std_temp {
 	uint8_t	auto_data_toggle;
 	uint8_t	setup_alt_next;
 	uint8_t	last_frame;
-	uint8_t can_use_next;
 };
 
 void
@@ -157,6 +156,9 @@ ehci_iterate_hw_softc(struct usb_bus *bu
 	cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg,
 	    sizeof(uint32_t) * EHCI_FRAMELIST_COUNT, EHCI_FRAMELIST_ALIGN);
 
+	cb(bus, &sc->sc_hw.terminate_pc, &sc->sc_hw.terminate_pg,
+	    sizeof(struct ehci_qh_sub), EHCI_QH_ALIGN);
+
 	cb(bus, &sc->sc_hw.async_start_pc, &sc->sc_hw.async_start_pg,
 	    sizeof(ehci_qh_t), EHCI_QH_ALIGN);
 
@@ -310,6 +312,24 @@ ehci_init(ehci_softc_t *sc)
 
 	sc->sc_eintrs = EHCI_NORMAL_INTRS;
 
+	if (1) {
+		struct ehci_qh_sub *qh;
+
+		usbd_get_page(&sc->sc_hw.terminate_pc, 0, &buf_res);
+
+		qh = buf_res.buffer;
+
+		sc->sc_terminate_self = htohc32(sc, buf_res.physaddr);
+
+		/* init terminate TD */
+		qh->qtd_next =
+		    htohc32(sc, EHCI_LINK_TERMINATE);
+		qh->qtd_altnext =
+		    htohc32(sc, EHCI_LINK_TERMINATE);
+		qh->qtd_status =
+		    htohc32(sc, EHCI_QTD_HALTED);
+	}
+
 	for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
 		ehci_qh_t *qh;
 
@@ -1416,15 +1436,7 @@ ehci_check_transfer(struct usb_xfer *xfe
 			 */
 			if (status & EHCI_QTD_ACTIVE) {
 				/* update cache */
-				if (xfer->td_transfer_cache != td) {
-					xfer->td_transfer_cache = td;
-					if (qh->qh_qtd.qtd_next & 
-					    htohc32(sc, EHCI_LINK_TERMINATE)) {
-						/* XXX - manually advance to next frame */
-						qh->qh_qtd.qtd_next = td->qtd_self;
-						usb_pc_cpu_flush(td->page_cache);
-					}
-				}
+				xfer->td_transfer_cache = td;
 				goto done;
 			}
 			/*
@@ -1634,10 +1646,12 @@ ehci_setup_standard_chain_sub(struct ehc
 	uint32_t average;
 	uint32_t len_old;
 	uint32_t terminate;
+	uint32_t qtd_altnext;
 	uint8_t shortpkt_old;
 	uint8_t precompute;
 
-	terminate = htohc32(temp->sc, EHCI_LINK_TERMINATE);
+	terminate = temp->sc->sc_terminate_self;
+	qtd_altnext = temp->sc->sc_terminate_self;
 	td_alt_next = NULL;
 	buf_offset = 0;
 	shortpkt_old = temp->shortpkt;
@@ -1771,23 +1785,11 @@ restart:
 			td->qtd_buffer_hi[x] = 0;
 		}
 
-		if (temp->can_use_next) {
-			if (td_next) {
-				/* link the current TD with the next one */
-				td->qtd_next = td_next->qtd_self;
-			}
-		} else {
-			/*
-			 * BUG WARNING: The EHCI HW can use the
-			 * qtd_next field instead of qtd_altnext when
-			 * a short packet is received! We work this
-			 * around in software by not queueing more
-			 * than one job/TD at a time!
-			 */
-			td->qtd_next = terminate;
+		if (td_next) {
+			/* link the current TD with the next one */
+			td->qtd_next = td_next->qtd_self;
 		}
-
-		td->qtd_altnext = terminate;
+		td->qtd_altnext = qtd_altnext;
 		td->alt_next = td_alt_next;
 
 		usb_pc_cpu_flush(td->page_cache);
@@ -1799,9 +1801,15 @@ restart:
 		/* setup alt next pointer, if any */
 		if (temp->last_frame) {
 			td_alt_next = NULL;
+			qtd_altnext = terminate;
 		} else {
 			/* we use this field internally */
 			td_alt_next = td_next;
+			if (temp->setup_alt_next) {
+				qtd_altnext = td_next->qtd_self;
+			} else {
+				qtd_altnext = terminate;
+			}
 		}
 
 		/* restore */
@@ -1846,8 +1854,6 @@ ehci_setup_standard_chain(struct usb_xfe
 	temp.qtd_status = 0;
 	temp.last_frame = 0;
 	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
-	temp.can_use_next = (xfer->flags_int.control_xfr ||
-	    (UE_GET_DIR(xfer->endpointno) == UE_DIR_OUT));
 
 	if (xfer->flags_int.control_xfr) {
 		if (xfer->endpoint->toggle_next) {
@@ -1860,8 +1866,8 @@ ehci_setup_standard_chain(struct usb_xfe
 		temp.auto_data_toggle = 1;
 	}
 
-	if (xfer->xroot->udev->parent_hs_hub != NULL ||
-	    xfer->xroot->udev->address != 0) {
+	if ((xfer->xroot->udev->parent_hs_hub != NULL) ||
+	    (xfer->xroot->udev->address != 0)) {
 		/* max 3 retries */
 		temp.qtd_status |=
 		    htohc32(temp.sc, EHCI_QTD_SET_CERR(3));
@@ -3114,7 +3120,6 @@ ehci_roothub_exec(struct usb_device *ude
 	uint16_t i;
 	uint16_t value;
 	uint16_t index;
-	uint8_t l;
 	usb_error_t err;
 
 	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
@@ -3322,16 +3327,19 @@ ehci_roothub_exec(struct usb_device *ude
 
 		sc->sc_hub_desc.hubd = ehci_hubd;
 		sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport;
-		USETW(sc->sc_hub_desc.hubd.wHubCharacteristics,
-		    (EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH) |
-		    (EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ?
-		    UHD_PORT_IND : 0));
+
+		if (EHCI_HCS_PPC(v))
+			i = UHD_PWR_INDIVIDUAL;
+		else
+			i = UHD_PWR_NO_SWITCH;
+
+		if (EHCI_HCS_P_INDICATOR(v))
+			i |= UHD_PORT_IND;
+
+		USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, i);
 		/* XXX can't find out? */
 		sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200;
-		for (l = 0; l < sc->sc_noport; l++) {
-			/* XXX can't find out? */
-			sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] &= ~(1 << (l % 8));
-		}
+		/* XXX don't know if ports are removable or not */
 		sc->sc_hub_desc.hubd.bDescLength =
 		    8 + ((sc->sc_noport + 7) / 8);
 		len = sc->sc_hub_desc.hubd.bDescLength;

Modified: head/sys/dev/usb/controller/ehci.h
==============================================================================
--- head/sys/dev/usb/controller/ehci.h	Thu Oct 14 21:34:53 2010	(r213868)
+++ head/sys/dev/usb/controller/ehci.h	Thu Oct 14 21:38:06 2010	(r213869)
@@ -285,12 +285,14 @@ typedef struct ehci_fstn ehci_fstn_t;
 
 struct ehci_hw_softc {
 	struct usb_page_cache pframes_pc;
+	struct usb_page_cache terminate_pc;
 	struct usb_page_cache async_start_pc;
 	struct usb_page_cache intr_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT];
 	struct usb_page_cache isoc_hs_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT];
 	struct usb_page_cache isoc_fs_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT];
 
 	struct usb_page pframes_pg;
+	struct usb_page terminate_pg;
 	struct usb_page async_start_pg;
 	struct usb_page intr_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT];
 	struct usb_page isoc_hs_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT];
@@ -329,6 +331,7 @@ typedef struct ehci_softc {
 	bus_space_tag_t sc_io_tag;
 	bus_space_handle_t sc_io_hdl;
 
+	uint32_t sc_terminate_self;	/* TD short packet termination pointer */
 	uint32_t sc_eintrs;
 	uint32_t sc_cmd;		/* shadow of cmd register during
 					 * suspend */


More information about the svn-src-head mailing list