Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 8 Jun 2014 20:39:39 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r267242 - stable/10/sys/dev/usb/controller
Message-ID:  <201406082039.s58Kdd3d021706@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Sun Jun  8 20:39:39 2014
New Revision: 267242
URL: http://svnweb.freebsd.org/changeset/base/267242

Log:
  MFC r267044, r267120, r267210 and r267211:
  Multiple fixes for FULL and LOW speed USB transfers going through
  the DWC OTG as split transactions. INTERRUPT transfers should have
  a higher chance of success after this series of patches and the
  chance of data loss should be reduced.

Modified:
  stable/10/sys/dev/usb/controller/dwc_otg.c
  stable/10/sys/dev/usb/controller/dwc_otg.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- stable/10/sys/dev/usb/controller/dwc_otg.c	Sun Jun  8 20:20:08 2014	(r267241)
+++ stable/10/sys/dev/usb/controller/dwc_otg.c	Sun Jun  8 20:39:39 2014	(r267242)
@@ -98,6 +98,8 @@
    GINTMSK_USBSUSPMSK |			\
    GINTMSK_IEPINTMSK |			\
    GINTMSK_SESSREQINTMSK |		\
+   GINTMSK_RXFLVLMSK |			\
+   GINTMSK_HCHINTMSK |			\
    GINTMSK_OTGINTMSK |			\
    GINTMSK_PRTINTMSK)
 
@@ -239,7 +241,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 
 		for (x = 0; x != sc->sc_host_ch_max; x++) {
 			/* disable all host interrupts */
-			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0);
+			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+			    HCINT_DEFAULT_MASK);
 		}
 
 		DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ,
@@ -256,8 +259,9 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		/* store maximum periodic and non-periodic FIFO TX size */
 		sc->sc_tx_max_size = fifo_size;
 
-		/* disable all host channel interrupts */
-		DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0);
+		/* enable all host channel interrupts */
+		DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
+		    (1U << sc->sc_host_ch_max) - 1U);
 	}
 
 	if (mode == DWC_MODE_DEVICE) {
@@ -568,11 +572,9 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof
 {
 	DPRINTFN(5, "RX status clear\n");
 
-	if (sc->sc_flags.status_device_mode != 0) {
-		/* enable RX FIFO level interrupt */
-		sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
-		DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
-	}
+	/* enable RX FIFO level interrupt */
+	sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
+	DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 
 	/* clear cached status */
 	sc->sc_last_rx_status = 0;
@@ -592,13 +594,13 @@ dwc_otg_clear_hcint(struct dwc_otg_softc
 }
 
 static uint8_t
-dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which, uint8_t is_out)
+dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t is_out)
 {
 	uint32_t tx_p_size;
 	uint32_t tx_np_size;
 	uint8_t x;
 
-	if (td->channel[which] < DWC_OTG_MAX_CHANNELS)
+	if (td->channel < DWC_OTG_MAX_CHANNELS)
 		return (0);		/* already allocated */
 
 	/* check if device is suspended */
@@ -607,8 +609,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 
 	/* compute needed TX FIFO size */
 	if (is_out != 0) {
-		if (td->ep_type == UE_INTERRUPT ||
-		    td->ep_type == UE_ISOCHRONOUS) {
+		if (td->ep_type == UE_ISOCHRONOUS) {
 			tx_p_size = td->max_packet_size;
 			tx_np_size = 0;
 			if (td->hcsplt != 0 && tx_p_size > HCSPLT_XACTLEN_BURST)
@@ -658,7 +659,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 		sc->sc_active_rx_ep |= (1 << x);
 
 		/* set channel */
-		td->channel[which] = x;
+		td->channel = x;
 
 		return (0);	/* allocated */
 	}
@@ -668,16 +669,16 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 }
 
 static void
-dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which)
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
 	uint8_t x;
 
-	if (td->channel[which] >= DWC_OTG_MAX_CHANNELS)
+	if (td->channel >= DWC_OTG_MAX_CHANNELS)
 		return;		/* already freed */
 
 	/* free channel */
-	x = td->channel[which];
-	td->channel[which] = DWC_OTG_MAX_CHANNELS;
+	x = td->channel;
+	td->channel = DWC_OTG_MAX_CHANNELS;
 
 	DPRINTF("CH=%d\n", x);
 
@@ -687,7 +688,7 @@ dwc_otg_host_channel_free(struct dwc_otg
 	 * after a fixed given amount of time the host channel is no
 	 * longer doing any USB traffic:
 	 */
-	if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
+	if (td->ep_type == UE_ISOCHRONOUS) {
 		/* double buffered */
 		sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX;
 	} else {
@@ -707,6 +708,18 @@ dwc_otg_host_channel_free(struct dwc_otg
 	sc->sc_active_rx_ep &= ~(1 << x);
 }
 
+static void
+dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+	/* dump any pending messages */
+	if (sc->sc_last_rx_status != 0) {
+		if (td->channel < DWC_OTG_MAX_CHANNELS &&
+		    td->channel == GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) {
+			dwc_otg_common_rx_ack(sc);
+		}
+	}
+}
+
 static uint8_t
 dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
@@ -715,13 +728,15 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
 	uint32_t hcchar;
 	uint8_t delta;
 
-	if (td->channel[0] < DWC_OTG_MAX_CHANNELS) {
-		hcint = sc->sc_chan_state[td->channel[0]].hcint;
+	dwc_otg_host_dump_rx(sc, td);
+
+	if (td->channel < DWC_OTG_MAX_CHANNELS) {
+		hcint = sc->sc_chan_state[td->channel].hcint;
 
 		DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-		    td->channel[0], td->state, hcint,
-		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])),
-		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0])));
+		    td->channel, td->state, hcint,
+		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 	} else {
 		hcint = 0;
 		goto check_state;
@@ -731,12 +746,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
 	    HCINT_ACK | HCINT_NYET)) {
 		/* give success bits priority over failure bits */
 	} else if (hcint & HCINT_STALL) {
-		DPRINTF("CH=%d STALL\n", td->channel[0]);
+		DPRINTF("CH=%d STALL\n", td->channel);
 		td->error_stall = 1;
 		td->error_any = 1;
 		goto complete;
 	} else if (hcint & HCINT_ERRORS) {
-		DPRINTF("CH=%d ERROR\n", td->channel[0]);
+		DPRINTF("CH=%d ERROR\n", td->channel);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
 			td->error_any = 1;
@@ -804,7 +819,7 @@ check_state:
 
 send_pkt:
 	/* free existing channel, if any */
-	dwc_otg_host_channel_free(sc, td, 0);
+	dwc_otg_host_channel_free(sc, td);
 
 	if (sizeof(req) != td->remainder) {
 		td->error_any = 1;
@@ -827,7 +842,7 @@ send_pkt:
 	}
 
 	/* allocate a new channel */
-	if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) {
+	if (dwc_otg_host_channel_alloc(sc, td, 1)) {
 		td->state = DWC_CHAN_ST_START;
 		goto busy;
 	}
@@ -841,23 +856,26 @@ send_pkt:
 
 	usbd_copy_out(td->pc, 0, &req, sizeof(req));
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
 	    (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
 	    (1 << HCTSIZ_PKTCNT_SHIFT) |
 	    (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt);
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
 
 	hcchar = td->hcchar;
 	hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
 	hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
 
 	/* must enable channel before writing data to FIFO */
-	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
 
 	/* transfer data into FIFO */
 	bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
-	    DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4);
+	    DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
+
+	/* wait until next slot before trying complete split */
+	td->tt_complete_slot = sc->sc_last_frame_num + 1;
 
 	/* store number of bytes transmitted */
 	td->tx_bytes = sizeof(req);
@@ -865,7 +883,7 @@ send_pkt:
 
 send_cpkt:
 	/* free existing channel, if any */
-	dwc_otg_host_channel_free(sc, td, 0);
+	dwc_otg_host_channel_free(sc, td);
 
 	delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
 	if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
@@ -880,34 +898,34 @@ send_cpkt:
 		goto complete;
 	}
 	/* allocate a new channel */
-	if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) {
+	if (dwc_otg_host_channel_alloc(sc, td, 0)) {
 		td->state = DWC_CHAN_ST_WAIT_C_PKT;
 		goto busy;
 	}
 
-	/* wait until next slot before trying again */
-	td->tt_complete_slot++;
+	/* wait until next slot before trying complete split */
+	td->tt_complete_slot = sc->sc_last_frame_num + 1;
 
 	td->hcsplt |= HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_C_ANE;
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
 	    (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt);
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
 
 	hcchar = td->hcchar;
 	hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
 	hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
 
 	/* must enable channel before writing data to FIFO */
-	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
 
 busy:
 	return (1);	/* busy */
 
 complete:
-	dwc_otg_host_channel_free(sc, td, 0);
+	dwc_otg_host_channel_free(sc, td);
 	return (0);	/* complete */
 }
 
@@ -1085,55 +1103,21 @@ busy:
 }
 
 static uint8_t
-dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
-	uint32_t hcint;
-	uint32_t hcchar;
 	uint32_t count;
-	uint8_t delta;
 	uint8_t channel;
 
-	channel = td->channel[td->tt_channel_tog];
-
-	if (channel < DWC_OTG_MAX_CHANNELS) {
-		hcint = sc->sc_chan_state[channel].hcint;
-
-		DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-		    channel, td->state, hcint,
-		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
-		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
-	} else {
-		hcint = 0;
-		goto check_state;
-	}
-
-	/* check interrupt bits */
-
-	if (hcint & (HCINT_RETRY |
-	    HCINT_ACK | HCINT_NYET)) {
-		/* give success bits priority over failure bits */
-	} else if (hcint & HCINT_STALL) {
-		DPRINTF("CH=%d STALL\n", channel);
-		td->error_stall = 1;
-		td->error_any = 1;
-		goto complete;
-	} else if (hcint & HCINT_ERRORS) {
-		DPRINTF("CH=%d ERROR\n", channel);
-		td->errcnt++;
-		if (td->hcsplt != 0 || td->errcnt >= 3) {
-			if (td->ep_type != UE_ISOCHRONOUS) {
-				td->error_any = 1;
-				goto complete;
-			}
-		}
-	}
-
 	/* check endpoint status */
 	if (sc->sc_last_rx_status == 0)
-		goto check_state;
+		goto busy;
+
+	channel = td->channel;
+	if (channel >= DWC_OTG_MAX_CHANNELS)
+		goto busy;
 
 	if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel)
-		goto check_state;
+		goto busy;
 
 	switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
 	case GRXSTSRH_IN_DATA:
@@ -1141,7 +1125,7 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 		DPRINTF("DATA ST=%d STATUS=0x%08x\n",
 		    (int)td->state, (int)sc->sc_last_rx_status);
 
-		if (hcint & HCINT_SOFTWARE_ONLY) {
+		if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) {
 			/*
 			 * When using SPLIT transactions on interrupt
 			 * endpoints, sometimes data occurs twice.
@@ -1203,21 +1187,71 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 
 		td->remainder -= count;
 		td->offset += count;
-		hcint |= HCINT_SOFTWARE_ONLY;
-		sc->sc_chan_state[channel].hcint = hcint;
+		sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY;
 		break;
-
 	default:
 		break;
 	}
 	/* release FIFO */
 	dwc_otg_common_rx_ack(sc);
+busy:
+	return (0);
+complete:
+	return (1);
+}
 
-check_state:
-	if (hcint & (HCINT_ERRORS | HCINT_RETRY |
-	    HCINT_ACK | HCINT_NYET)) {
-		if (!(hcint & HCINT_ERRORS))
-			td->errcnt = 0;
+static uint8_t
+dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+	uint32_t hcint;
+	uint32_t hcchar;
+	uint8_t delta;
+	uint8_t channel;
+
+	channel = td->channel;
+
+	if (channel < DWC_OTG_MAX_CHANNELS) {
+		hcint = sc->sc_chan_state[channel].hcint;
+
+		DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+		    channel, td->state, hcint,
+		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
+
+		/* check interrupt bits */
+		if (hcint & (HCINT_RETRY |
+		    HCINT_ACK | HCINT_NYET)) {
+			/* give success bits priority over failure bits */
+		} else if (hcint & HCINT_STALL) {
+			DPRINTF("CH=%d STALL\n", channel);
+			td->error_stall = 1;
+			td->error_any = 1;
+			goto complete;
+		} else if (hcint & HCINT_ERRORS) {
+			DPRINTF("CH=%d ERROR\n", channel);
+			td->errcnt++;
+			if (td->hcsplt != 0 || td->errcnt >= 3) {
+				if (td->ep_type != UE_ISOCHRONOUS) {
+					td->error_any = 1;
+					goto complete;
+				}
+			}
+		}
+
+		/* check channels for data, if any */
+		if (dwc_otg_host_data_rx_sub(sc, td))
+			goto complete;
+
+		/* refresh interrupt status */
+		hcint = sc->sc_chan_state[channel].hcint;
+
+		if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+		    HCINT_ACK | HCINT_NYET)) {
+			if (!(hcint & HCINT_ERRORS))
+				td->errcnt = 0;
+		}
+	} else {
+		hcint = 0;
 	}
 
 	switch (td->state) {
@@ -1229,6 +1263,22 @@ check_state:
 
 	case DWC_CHAN_ST_WAIT_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+			if (td->ep_type == UE_INTERRUPT) {
+				/*
+				 * The USB specification does not
+				 * mandate a particular data toggle
+				 * value for USB INTERRUPT
+				 * transfers. Switch the data toggle
+				 * value to receive the packet
+				 * correctly:
+				 */
+				if (hcint & HCINT_DATATGLERR) {
+					DPRINTF("Retrying packet due to "
+					    "data toggle error\n");
+					td->toggle ^= 1;
+					goto receive_pkt;
+				}
+			}
 			td->did_nak++;
 			td->tt_scheduled = 0;
 			if (td->hcsplt != 0)
@@ -1306,7 +1356,7 @@ check_state:
 
 receive_pkt:
 	/* free existing channel, if any */
-	dwc_otg_host_channel_free(sc, td, td->tt_channel_tog);
+	dwc_otg_host_channel_free(sc, td);
 
   	if (td->hcsplt != 0) {
 		delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
@@ -1316,9 +1366,10 @@ receive_pkt:
 		}
 		delta = sc->sc_last_frame_num - td->tt_start_slot;
 		if (delta > DWC_OTG_TT_SLOT_MAX) {
-			/* we missed the service interval */
-			if (td->ep_type != UE_ISOCHRONOUS)
+			if (td->ep_type != UE_ISOCHRONOUS) {
+				/* we missed the service interval */
 				td->error_any = 1;
+			}
 			goto complete;
 		}
 		/* complete split */
@@ -1330,12 +1381,12 @@ receive_pkt:
 	}
 
 	/* allocate a new channel */
-	if (dwc_otg_host_channel_alloc(sc, td, td->tt_channel_tog, 0)) {
+	if (dwc_otg_host_channel_alloc(sc, td, 0)) {
 		td->state = DWC_CHAN_ST_WAIT_C_PKT;
 		goto busy;
 	}
 
-	channel = td->channel[td->tt_channel_tog];
+	channel = td->channel;
 
 	/* set toggle, if any */
 	if (td->set_toggle) {
@@ -1357,46 +1408,23 @@ receive_pkt:
 	hcchar = td->hcchar;
 	hcchar |= HCCHAR_EPDIR_IN;
 
-	/* check if other channel is allocated */
-	if (td->channel[td->tt_channel_tog ^ 1] < DWC_OTG_MAX_CHANNELS) {
-		/* second - receive after next SOF event */
-		if ((sc->sc_last_frame_num & 1) != 0)
-			hcchar |= HCCHAR_ODDFRM;
-		else
-			hcchar &= ~HCCHAR_ODDFRM;
-
-		/* other channel is next */
-		td->tt_channel_tog ^= 1;
-		td->tt_complete_slot++;
+	/* receive complete split ASAP */
+	if ((sc->sc_last_frame_num & 1) != 0)
+		hcchar |= HCCHAR_ODDFRM;
+	else
+		hcchar &= ~HCCHAR_ODDFRM;
 
-		/* must enable channel before data can be received */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
-	} else {
-		/* first - receive after second next SOF event */
-		if ((sc->sc_last_frame_num & 1) == 0)
-			hcchar |= HCCHAR_ODDFRM;
-		else
-			hcchar &= ~HCCHAR_ODDFRM;
+	/* must enable channel before data can be received */
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
 
-		/* must enable channel before data can be received */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+	/* wait until next slot before trying complete split */
+	td->tt_complete_slot = sc->sc_last_frame_num + 1;
 
-		if (td->hcsplt != 0) {
-			if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
-				/* allocate a second channel */
-				td->tt_channel_tog ^= 1;
-				goto receive_pkt;
-			} else {
-				td->tt_complete_slot++;
-			}
-		}
-	}
 	goto busy;
 
 receive_spkt:
 	/* free existing channel(s), if any */
-	dwc_otg_host_channel_free(sc, td, 0);
-	dwc_otg_host_channel_free(sc, td, 1);
+	dwc_otg_host_channel_free(sc, td);
 
 	delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
 	if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
@@ -1412,19 +1440,16 @@ receive_spkt:
 	}
 
 	/* allocate a new channel */
-	if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) {
+	if (dwc_otg_host_channel_alloc(sc, td, 0)) {
 		td->state = DWC_CHAN_ST_START;
 		goto busy;
 	}
 
-	channel = td->channel[0];
+	channel = td->channel;
 
 	td->hcsplt &= ~HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_S_ANE;
 
-	/* reset channel toggle */
-	td->tt_channel_tog = 0;
-
 	/* receive one packet */
 	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
 	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
@@ -1440,14 +1465,16 @@ receive_spkt:
 	hcchar = td->hcchar;
 	hcchar |= HCCHAR_EPDIR_IN;
 
+	/* wait until next slot before trying complete split */
+	td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
 	/* must enable channel before data can be received */
 	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
 busy:
 	return (1);	/* busy */
 
 complete:
-	dwc_otg_host_channel_free(sc, td, 0);
-	dwc_otg_host_channel_free(sc, td, 1);
+	dwc_otg_host_channel_free(sc, td);
 	return (0);	/* complete */
 }
 
@@ -1569,7 +1596,9 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 	uint8_t delta;
 	uint8_t channel;
 
-	channel = td->channel[td->tt_channel_tog];
+	dwc_otg_host_dump_rx(sc, td);
+
+	channel = td->channel;
 
 	if (channel < DWC_OTG_MAX_CHANNELS) {
 		hcint = sc->sc_chan_state[channel].hcint;
@@ -1578,36 +1607,34 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 		    channel, td->state, hcint,
 		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
 		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
-	} else {
-		hcint = 0;
-		goto check_state;
-	}
 
-	if (hcint & (HCINT_RETRY |
-	    HCINT_ACK | HCINT_NYET)) {
-		/* give success bits priority over failure bits */
-	} else if (hcint & HCINT_STALL) {
-		DPRINTF("CH=%d STALL\n", channel);
-		td->error_stall = 1;
-		td->error_any = 1;
-		goto complete;
-	} else if (hcint & HCINT_ERRORS) {
-		DPRINTF("CH=%d ERROR\n", channel);
-		td->errcnt++;
-		if (td->hcsplt != 0 || td->errcnt >= 3) {
+		if (hcint & (HCINT_RETRY |
+		    HCINT_ACK | HCINT_NYET)) {
+			/* give success bits priority over failure bits */
+		} else if (hcint & HCINT_STALL) {
+			DPRINTF("CH=%d STALL\n", channel);
+			td->error_stall = 1;
 			td->error_any = 1;
 			goto complete;
+		} else if (hcint & HCINT_ERRORS) {
+			DPRINTF("CH=%d ERROR\n", channel);
+			td->errcnt++;
+			if (td->hcsplt != 0 || td->errcnt >= 3) {
+				td->error_any = 1;
+				goto complete;
+			}
 		}
-	}
 
-	if (hcint & (HCINT_ERRORS | HCINT_RETRY |
-	    HCINT_ACK | HCINT_NYET)) {
+		if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+		    HCINT_ACK | HCINT_NYET)) {
 
-		if (!(hcint & HCINT_ERRORS))
-			td->errcnt = 0;
+			if (!(hcint & HCINT_ERRORS))
+				td->errcnt = 0;
+		}
+	} else {
+		hcint = 0;
 	}
 
-check_state:
 	switch (td->state) {
 	case DWC_CHAN_ST_START:
 		goto send_pkt;
@@ -1694,16 +1721,16 @@ check_state:
 			td->tt_xactpos++;
 
 		/* free existing channel, if any */
-		dwc_otg_host_channel_free(sc, td, td->tt_channel_tog);
+		dwc_otg_host_channel_free(sc, td);
 
 		td->state = DWC_CHAN_ST_TX_PKT_ISOC;
 
 		/* FALLTHROUGH */
 
 	case DWC_CHAN_ST_TX_PKT_ISOC:
-		if (dwc_otg_host_channel_alloc(sc, td, 0, 1))
+		if (dwc_otg_host_channel_alloc(sc, td, 1))
 			break;
-		channel = td->channel[0];
+		channel = td->channel;
 		goto send_isoc_pkt;
 	default:
 		break;
@@ -1712,8 +1739,7 @@ check_state:
 
 send_pkt:
 	/* free existing channel(s), if any */
-	dwc_otg_host_channel_free(sc, td, 0);
-	dwc_otg_host_channel_free(sc, td, 1);
+	dwc_otg_host_channel_free(sc, td);
 
 	if (td->hcsplt != 0) {
 		delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
@@ -1734,12 +1760,12 @@ send_pkt:
 	}
 
 	/* allocate a new channel */
-	if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) {
+	if (dwc_otg_host_channel_alloc(sc, td, 1)) {
 		td->state = DWC_CHAN_ST_START;
 		goto busy;
 	}
 
-	channel = td->channel[0];
+	channel = td->channel;
 
 	/* set toggle, if any */
 	if (td->set_toggle) {
@@ -1757,7 +1783,7 @@ send_isoc_pkt:
 			count = td->remainder;
 			if (count > HCSPLT_XACTLEN_BURST) {
 				DPRINTF("TT overflow\n");
-				td->error = 1;
+				td->error_any = 1;
 				goto complete;
 			}
 			/* Update transaction position */
@@ -1883,7 +1909,7 @@ send_isoc_pkt:
 
 send_cpkt:
 	/* free existing channel, if any */
-	dwc_otg_host_channel_free(sc, td, td->tt_channel_tog);
+	dwc_otg_host_channel_free(sc, td);
 
 	delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
 	if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
@@ -1899,12 +1925,12 @@ send_cpkt:
 	}
 
 	/* allocate a new channel */
-	if (dwc_otg_host_channel_alloc(sc, td, td->tt_channel_tog, 0)) {
+	if (dwc_otg_host_channel_alloc(sc, td, 0)) {
 		td->state = DWC_CHAN_ST_WAIT_C_PKT;
 		goto busy;
 	}
 
-	channel = td->channel[td->tt_channel_tog];
+	channel = td->channel;
 
  	td->hcsplt |= HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_C_ANE;
@@ -1917,48 +1943,22 @@ send_cpkt:
 	hcchar = td->hcchar;
 	hcchar &= ~HCCHAR_EPDIR_IN;
 
-	/* check if other channel is allocated */
-	if (td->channel[td->tt_channel_tog ^ 1] < DWC_OTG_MAX_CHANNELS) {
-		/* second - receive after next SOF event */
-		if ((sc->sc_last_frame_num & 1) != 0)
-			hcchar |= HCCHAR_ODDFRM;
-		else
-			hcchar &= ~HCCHAR_ODDFRM;
-
-		/* other channel is next */
-		td->tt_channel_tog ^= 1;
-		/* wait until next slot before trying again */
-		td->tt_complete_slot++;
-
-		/* must enable channel before data can be received */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
-	} else {
-		/* first - receive after second next SOF event */
-		if ((sc->sc_last_frame_num & 1) == 0)
-			hcchar |= HCCHAR_ODDFRM;
-		else
-			hcchar &= ~HCCHAR_ODDFRM;
+	/* receive complete split ASAP */
+	if ((sc->sc_last_frame_num & 1) != 0)
+		hcchar |= HCCHAR_ODDFRM;
+	else
+		hcchar &= ~HCCHAR_ODDFRM;
 
-		/* must enable channel before data can be received */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+	/* must enable channel before data can be received */
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
 
-		if (td->hcsplt != 0) {
-			if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
-				/* allocate a second channel */
-				td->tt_channel_tog ^= 1;
-				goto send_cpkt;
-			} else {
-				/* wait until next slot before trying again */
-				td->tt_complete_slot++;
-			}
-		}
-	}
+	/* wait until next slot before trying complete split */
+	td->tt_complete_slot = sc->sc_last_frame_num + 1;
 busy:
 	return (1);	/* busy */
 
 complete:
-	dwc_otg_host_channel_free(sc, td, 0);
-	dwc_otg_host_channel_free(sc, td, 1);
+	dwc_otg_host_channel_free(sc, td);
 	return (0);	/* complete */
 }
 
@@ -2348,6 +2348,14 @@ dwc_otg_host_channel_disable(struct dwc_
 	sc->sc_chan_state[x].tx_np_size = 0;
 }
 
+static uint16_t
+dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo)
+{
+	if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX)
+		pinfo->slot_index++;
+	return (pinfo->slot_index);
+}
+
 static uint8_t
 dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc)
 {
@@ -2356,6 +2364,7 @@ dwc_otg_update_host_transfer_schedule_lo
 	struct usb_xfer *xfer_next;
 	struct dwc_otg_td *td;
 	uint16_t temp;
+	uint16_t slot;
 	uint8_t x;
 
 	temp = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK;
@@ -2377,11 +2386,53 @@ dwc_otg_update_host_transfer_schedule_lo
 	}
 
 	if ((temp & 7) == 0) {
+
+		/* reset the schedule */
+		memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
+
 		TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
 			td = xfer->td_transfer_cache;
 			if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
 				continue;
 
+			/* check for IN direction */
+			if ((td->hcchar & HCCHAR_EPDIR_IN) != 0)
+				continue;
+
+			/* execute more frames */
+			td->tmr_val = 0;
+
+			sc->sc_needsof = 1;
+
+			if (td->hcsplt == 0 || td->tt_scheduled != 0)
+				continue;
+
+			/* compute slot */
+			slot = dwc_otg_compute_isoc_rx_tt_slot(
+			    sc->sc_tt_info + td->tt_index);
+			if (slot > 3) {
+				/* 
+				 * Not enough time to get complete
+				 * split executed.
+				 */
+				continue;
+			}
+			/* Delayed start */
+			td->tt_start_slot = temp + slot;
+			td->tt_scheduled = 1;
+			TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+			TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+		}
+
+		TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
+			td = xfer->td_transfer_cache;
+			if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
+				continue;
+
+			/* check for OUT direction */
+			if ((td->hcchar & HCCHAR_EPDIR_IN) == 0)
+				continue;
+
 			/* execute more frames */
 			td->tmr_val = 0;
 
@@ -2391,8 +2442,7 @@ dwc_otg_update_host_transfer_schedule_lo
 				continue;
 
 			/* Start ASAP */
-			td->tt_start_slot = temp + 0;
-			td->tt_complete_slot = temp + 2;
+			td->tt_start_slot = temp;
 			td->tt_scheduled = 1;
 			TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
 			TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2418,8 +2468,7 @@ dwc_otg_update_host_transfer_schedule_lo
 			}
 
 			/* start ASAP */
-			td->tt_start_slot = temp + 0;
-			td->tt_complete_slot = temp + 2;
+			td->tt_start_slot = temp;
 			sc->sc_needsof = 1;
 			td->tt_scheduled = 1;
 			TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
@@ -2440,8 +2489,7 @@ dwc_otg_update_host_transfer_schedule_lo
 				continue;
 
 			/* start ASAP */
-			td->tt_start_slot = temp + 0;
-			td->tt_complete_slot = temp + 1;
+			td->tt_start_slot = temp;
 			td->tt_scheduled = 1;
 			TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
 			TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2462,8 +2510,7 @@ dwc_otg_update_host_transfer_schedule_lo
 				continue;
 
 			/* start ASAP */
-			td->tt_start_slot = temp + 0;
-			td->tt_complete_slot = temp + 1;
+			td->tt_start_slot = temp;
 			td->tt_scheduled = 1;
 			TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
 			TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2739,8 +2786,6 @@ dwc_otg_interrupt(void *arg)
 
 		/* Disable SOF interrupt */
 		sc->sc_irq_mask &= ~GINTMSK_SOFMSK;
-		/* Enable RX frame interrupt */
-		sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
 		DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 
 		/* complete root HUB interrupt endpoint */
@@ -2781,7 +2826,7 @@ dwc_otg_interrupt(void *arg)
 		 * suspend and RX frame interrupt:
 		 */
 		sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK);
-		sc->sc_irq_mask |= (GINTMSK_USBSUSPMSK | GINTMSK_RXFLVLMSK);
+		sc->sc_irq_mask |= GINTMSK_USBSUSPMSK;
 		DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 
 		/* complete root HUB interrupt endpoint */
@@ -2852,10 +2897,6 @@ dwc_otg_interrupt(void *arg)
 		/* complete root HUB interrupt endpoint */
 		dwc_otg_root_intr(sc);
 
-		/* disable RX FIFO level interrupt */
-		sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK;
-		DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
-
 		/* update host frame interval */
 		dwc_otg_update_host_frame_interval(sc);
 	}
@@ -2935,12 +2976,10 @@ dwc_otg_setup_standard_chain_sub(struct 
 	td->set_toggle = 0;
 	td->got_short = 0;
 	td->did_nak = 0;
-	td->channel[0] = DWC_OTG_MAX_CHANNELS;
-	td->channel[1] = DWC_OTG_MAX_CHANNELS;
+	td->channel = DWC_OTG_MAX_CHANNELS;
 	td->state = 0;
 	td->errcnt = 0;
 	td->tt_scheduled = 0;
-	td->tt_channel_tog = 0;
 	td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
 }
 
@@ -3156,9 +3195,21 @@ dwc_otg_setup_standard_chain(struct usb_
 			(xfer->max_packet_size << HCCHAR_MPS_SHIFT) |
 			HCCHAR_CHENA;
 
-		/* XXX stability hack - possible HW issue */
-		if (td->ep_type == UE_CONTROL)
-			hcchar |= (UE_BULK << HCCHAR_EPTYPE_SHIFT);
+		/*
+		 * We are not always able to meet the timing
+		 * requirements of the USB interrupt endpoint's
+		 * complete split token, when doing transfers going
+		 * via a transaction translator. Use the CONTROL
+		 * transfer type instead of the INTERRUPT transfer
+		 * type in general, as a means to workaround
+		 * that. This trick should work for both FULL and LOW
+		 * speed USB traffic going through a TT. For non-TT
+		 * traffic it works aswell. The reason for using
+		 * CONTROL type instead of BULK is that some TTs might
+		 * reject LOW speed BULK traffic.
+		 */
+		if (td->ep_type == UE_INTERRUPT)
+			hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT);
 		else
 			hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT);
 
@@ -3199,13 +3250,12 @@ dwc_otg_setup_standard_chain(struct usb_
 			break;
 		case USB_SPEED_HIGH:
 			hcsplt = 0;
-			if (td->ep_type == UE_ISOCHRONOUS ||
-			    td->ep_type == UE_INTERRUPT) {
-				hcchar |= ((xfer->max_packet_count & 3)
-				    << HCCHAR_MC_SHIFT);
-			}
 			if (td->ep_type == UE_INTERRUPT) {
 				uint32_t ival;
+#if 0
+				hcchar |= ((xfer->max_packet_count & 3)
+				    << HCCHAR_MC_SHIFT);
+#endif
 				ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
 				if (ival == 0)
 					ival = 1;
@@ -3214,6 +3264,8 @@ dwc_otg_setup_standard_chain(struct usb_
 				td->tmr_val = sc->sc_tmr_val + ival;
 				td->tmr_res = ival;
 			} else if (td->ep_type == UE_ISOCHRONOUS) {
+				hcchar |= ((xfer->max_packet_count & 3)
+				    << HCCHAR_MC_SHIFT);
 				td->tmr_val = 0;
 				td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer);
 			} else {
@@ -3458,12 +3510,9 @@ dwc_otg_device_done(struct usb_xfer *xfe
 	} else {
 		struct dwc_otg_td *td;
 
-		td = xfer->td_transfer_first;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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