Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 5 May 2014 11:50:52 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r265358 - head/sys/dev/usb/controller
Message-ID:  <201405051150.s45BoqID055514@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Mon May  5 11:50:52 2014
New Revision: 265358
URL: http://svnweb.freebsd.org/changeset/base/265358

Log:
  Improve DWC OTG USB host side support for isochronous FULL and HIGH
  speed data traffic going directly to a USB device or through a
  so-called USB transaction translator.
  
  Add checks that we are not overusing the TX FIFO.
  
  MFC after:	2 weeks

Modified:
  head/sys/dev/usb/controller/dwc_otg.c
  head/sys/dev/usb/controller/dwc_otg.h
  head/sys/dev/usb/controller/dwc_otgreg.h

Modified: head/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.c	Mon May  5 11:30:45 2014	(r265357)
+++ head/sys/dev/usb/controller/dwc_otg.c	Mon May  5 11:50:52 2014	(r265358)
@@ -207,6 +207,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		/* reset active endpoints */
 		sc->sc_active_rx_ep = 0;
 
+		/* reset TX size */
+		sc->sc_tx_cur_size = 0;
+
+		/* reset TT info */
+		memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
+
 		fifo_size /= 2;
 
 		DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
@@ -215,19 +221,17 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 
 		tx_start += fifo_size;
 
+		for (x = 0; x != sc->sc_host_ch_max; x++) {
+			/* enable all needed interrupts */
+			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_DEFAULT_MASK);
+		}
+
 		DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ,
 		    ((fifo_size / 4) << 16) |
 		    (tx_start / 4));
 
-		for (x = 0; x != sc->sc_host_ch_max; x++) {
-			/* enable interrupts */
-			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
-			    HCINT_STALL | HCINT_BBLERR |
-			    HCINT_XACTERR |
-			    HCINT_NAK | HCINT_ACK | HCINT_NYET |
-			    HCINT_CHHLTD | HCINT_FRMOVRUN |
-			    HCINT_DATATGLERR);
-		}
+		/* store maximum TX FIFO size */
+		sc->sc_tx_max_size = fifo_size;
 
 		/* enable host channel interrupts */
 		DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
@@ -309,6 +313,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 	} else {
 		/* reset active endpoints */
 		sc->sc_active_rx_ep = 0;
+
+		/* reset TX size */
+		sc->sc_tx_cur_size = 0;
+
+		/* reset TT info */
+		memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
 	}
 	return (0);
 }
@@ -376,9 +386,9 @@ dwc_otg_pull_down(struct dwc_otg_softc *
 static void
 dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc)
 {
-	if (sc->sc_irq_mask & GINTSTS_SOF)
+	if (sc->sc_irq_mask & GINTMSK_SOFMSK)
 		return;
-	sc->sc_irq_mask |= GINTSTS_SOF;
+	sc->sc_irq_mask |= GINTMSK_SOFMSK;
 	DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 }
 
@@ -506,6 +516,7 @@ dwc_otg_clear_hcint(struct dwc_otg_softc
 {
 	uint32_t hcint;
 
+	/* clear all pending interrupts */
 	hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
 	DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint);
 
@@ -513,6 +524,10 @@ dwc_otg_clear_hcint(struct dwc_otg_softc
 	sc->sc_chan_state[x].hcint = 0;
 }
 
+/*
+ * This function waits until a DWC OTG host channel is ready to be
+ * used again:
+ */
 static uint8_t
 dwc_otg_host_channel_wait(struct dwc_otg_td *td)
 {
@@ -545,6 +560,9 @@ dwc_otg_host_channel_wait(struct dwc_otg
 		sc->sc_chan_state[td->channel].allocated = 0;
 		sc->sc_chan_state[x].allocated = 1;
 
+		sc->sc_chan_state[x].tx_size =
+		    sc->sc_chan_state[td->channel].tx_size;
+
 		if (sc->sc_chan_state[td->channel].suspended) {
 			sc->sc_chan_state[td->channel].suspended = 0;
 			sc->sc_chan_state[x].suspended = 1;
@@ -579,6 +597,7 @@ static uint8_t
 dwc_otg_host_channel_alloc(struct dwc_otg_td *td)
 {
 	struct dwc_otg_softc *sc;
+	uint32_t tx_size;
 	uint8_t x;
 	uint8_t max_channel;
 
@@ -591,9 +610,25 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 	if ((td->hcchar & HCCHAR_EPNUM_MASK) == 0) {
 		max_channel = 1;
 		x = 0;
+		tx_size = td->max_packet_size;
+		if ((sc->sc_tx_cur_size + tx_size) > sc->sc_tx_max_size) {
+			DPRINTF("Too little FIFO space\n");
+			return (1);	/* too little FIFO */
+		}
 	} else {
 		max_channel = sc->sc_host_ch_max;
 		x = 1;
+		if ((td->hcchar & HCCHAR_EPDIR) == HCCHAR_EPDIR_OUT) {
+			tx_size = td->max_packet_size;
+			if (td->hcsplt != 0 && tx_size > HCSPLT_XACTLEN_MAX)
+				tx_size = HCSPLT_XACTLEN_MAX;
+			if ((sc->sc_tx_cur_size + tx_size) > sc->sc_tx_max_size) {
+				DPRINTF("Too little FIFO space\n");
+				return (1);	/* too little FIFO */
+			}
+		} else {
+			tx_size = 0;
+		}
 	}
 
 	for (; x != max_channel; x++) {
@@ -604,6 +639,10 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 			continue;
 
 		sc->sc_chan_state[x].allocated = 1;
+		sc->sc_chan_state[x].tx_size = tx_size;
+
+		/* keep track of used FIFO */
+		sc->sc_tx_cur_size += tx_size;
 
 		/* clear interrupts */
 		dwc_otg_clear_hcint(sc, x);
@@ -663,6 +702,9 @@ dwc_otg_host_channel_free(struct dwc_otg
 	sc->sc_chan_state[x].allocated = 0;
 	sc->sc_chan_state[x].suspended = 0;
 
+	/* keep track of used FIFO */
+	sc->sc_tx_cur_size -= sc->sc_chan_state[x].tx_size;
+
 	/* ack any pending messages */
 	if (sc->sc_last_rx_status != 0 &&
 	    GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) {
@@ -724,6 +766,8 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 
 	switch (td->state) {
 	case DWC_CHAN_ST_START:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
 		goto send_pkt;
 
 	case DWC_CHAN_ST_WAIT_ANE:
@@ -731,6 +775,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto send_pkt;
 		}
 		if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -739,14 +784,17 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle = 1;
+			td->tt_scheduled = 0;
 			return (0);	/* complete */
 		}
 		break;
+
 	case DWC_CHAN_ST_WAIT_S_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto send_pkt;
 		}
 		if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -755,6 +803,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 			goto send_cpkt;
 		}
 		break;
+
 	case DWC_CHAN_ST_WAIT_C_ANE:
 		if (hcint & HCINT_NYET) {
 			if (!dwc_otg_host_channel_wait(td))
@@ -765,6 +814,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto send_pkt;
 		}
 		if (hcint & HCINT_ACK) {
@@ -776,8 +826,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 			return (0);	/* complete */
 		}
 		break;
-	case DWC_CHAN_ST_TX_PKT_SYNC:
-		goto send_pkt_sync;
+
+	case DWC_CHAN_ST_WAIT_C_PKT:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
+		goto send_cpkt;
+
 	default:
 		break;
 	}
@@ -789,19 +843,13 @@ send_pkt:
 		return (0);		/* complete */
 	}
 
-send_pkt_sync:
 	if (td->hcsplt != 0) {
-		uint32_t count;
-
-		count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
-		/* check for not first microframe */
-		if (count != 0) {
-			/* enable SOF interrupt */
-			dwc_otg_enable_sof_irq(sc);
-			/* set state */
-			td->state = DWC_CHAN_ST_TX_PKT_SYNC;
-			dwc_otg_host_channel_free(td);
-			return (1);	/* busy */
+		/* Wait for our turn, if TT transfer */
+		if (td->tt_scheduled == 0 ||
+		    (sc->sc_last_frame_num & 7) < td->tt_start_slot) {
+			/* set return state */
+			td->state = DWC_CHAN_ST_START;
+			goto tt_wait;
 		}
 
 		td->hcsplt &= ~HCSPLT_COMPSPLT;
@@ -835,6 +883,16 @@ send_pkt_sync:
 	return (1);	/* busy */
 
 send_cpkt:
+	/* Wait for our turn, if TT transfer */
+	if (td->tt_scheduled == 0 ||
+	    (sc->sc_last_frame_num & 7) < td->tt_complete_slot) {
+		/* set return state */
+		td->state = DWC_CHAN_ST_WAIT_C_PKT;
+		goto tt_wait;
+	}
+	/* wait until next slot before trying again */
+	td->tt_complete_slot++;
+
 	td->hcsplt |= HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_C_ANE;
 
@@ -848,7 +906,14 @@ send_cpkt:
 
 	/* must enable channel before writing data to FIFO */
 	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+	return (1);	/* busy */
 
+tt_wait:
+	/* enable SOF interrupt */
+	dwc_otg_enable_sof_irq(sc);
+
+	/* free allocated channel */
+	dwc_otg_host_channel_free(td);
 	return (1);	/* busy */
 }
 
@@ -984,6 +1049,25 @@ not_complete:
 }
 
 static uint8_t
+dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+	uint8_t delta;
+
+	delta = sc->sc_tmr_val - td->tmr_val;
+	if (delta >= 128)
+		return (1);	/* busy */
+
+	td->tmr_val = sc->sc_tmr_val + td->tmr_res;
+
+	/* set toggle, if any */
+	if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+	return (0);
+}
+
+static uint8_t
 dwc_otg_host_rate_check(struct dwc_otg_td *td)
 {
 	struct dwc_otg_softc *sc;
@@ -992,31 +1076,30 @@ dwc_otg_host_rate_check(struct dwc_otg_t
 	/* get pointer to softc */
 	sc = DWC_OTG_PC2SC(td->pc);
 
+	if (td->channel < DWC_OTG_MAX_CHANNELS &&
+	    sc->sc_chan_state[td->channel].suspended)
+		goto busy;
+
 	ep_type = ((td->hcchar &
 	    HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
 
-	if (sc->sc_chan_state[td->channel].suspended)
-		goto busy;
-
 	if (ep_type == UE_ISOCHRONOUS) {
-		if (td->tmr_val & 1)
-			td->hcchar |= HCCHAR_ODDFRM;
-		else
-			td->hcchar &= ~HCCHAR_ODDFRM;
-		td->tmr_val += td->tmr_res;
-	} else if (ep_type == UE_INTERRUPT) {
-		uint8_t delta;
 
-		delta = sc->sc_tmr_val - td->tmr_val;
-		if (delta >= 128)
+		/* non TT isochronous traffic */
+		if ((td->tmr_val != 0) ||
+		    (sc->sc_last_frame_num & (td->tmr_res - 1))) {
+			/* enable SOF interrupt */
+			dwc_otg_enable_sof_irq(sc);
 			goto busy;
-		td->tmr_val = sc->sc_tmr_val + td->tmr_res;
+		}
+		td->tmr_val = 1;	/* executed */
+		td->toggle = 0;
+
+	} else if (ep_type == UE_INTERRUPT) {
+		/* non TT interrupt traffic */
+		return (dwc_otg_host_rate_check_interrupt(sc, td));
 	} else if (td->did_nak != 0) {
 		goto busy;
-	} 
-
-	if (ep_type == UE_ISOCHRONOUS) {
-		td->toggle = 0;
 	} else if (td->set_toggle) {
 		td->set_toggle = 0;
 		td->toggle = 1;
@@ -1065,8 +1148,10 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 		DPRINTF("CH=%d ERROR\n", td->channel);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
-			td->error_any = 1;
-			return (0);		/* complete */
+			if (ep_type != UE_ISOCHRONOUS) {
+				td->error_any = 1;
+				return (0);	/* complete */
+			}
 		}
 	}
 
@@ -1103,25 +1188,42 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 			break;
 		}
 
-		td->toggle ^= 1;
-
 		/* get the packet byte count */
 		count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
 
-		/* verify the packet byte count */
-		if (count != td->max_packet_size) {
-			if (count < td->max_packet_size) {
-				/* we have a short packet */
-				td->short_pkt = 1;
-				td->got_short = 1;
+		/* check for isochronous transfer or high-speed bandwidth endpoint */
+		if (ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) {
+			if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != GRXSTSRD_DPID_DATA0) {
+				td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
 			} else {
-				/* invalid USB packet */
-				td->error_any = 1;
+				td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
+
+				/* verify the packet byte count */
+				if (count < td->max_packet_size) {
+					/* we have a short packet */
+					td->short_pkt = 1;
+					td->got_short = 1;
+				}
+			}
+			td->toggle = 0;
+		} else {
+			/* verify the packet byte count */
+			if (count != td->max_packet_size) {
+				if (count < td->max_packet_size) {
+					/* we have a short packet */
+					td->short_pkt = 1;
+					td->got_short = 1;
+				} else {
+					/* invalid USB packet */
+					td->error_any = 1;
 			  
-				/* release FIFO */
-				dwc_otg_common_rx_ack(sc);
-				return (0);	/* we are complete */
+					/* release FIFO */
+					dwc_otg_common_rx_ack(sc);
+					return (0);	/* we are complete */
+				}
 			}
+			td->toggle ^= 1;
+			td->tt_scheduled = 0;
 		}
 
 		/* verify the packet byte count */
@@ -1144,7 +1246,6 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 		break;
 
 	default:
-		DPRINTF("OTHER\n");
 		break;
 	}
 	/* release FIFO */
@@ -1153,6 +1254,8 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 check_state:
 	switch (td->state) {
 	case DWC_CHAN_ST_START:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
 		if (td->hcsplt != 0)
 			goto receive_spkt;
 		else
@@ -1164,6 +1267,7 @@ check_state:
 				break;
 
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			if (td->hcsplt != 0)
 				goto receive_spkt;
 			else
@@ -1171,11 +1275,13 @@ check_state:
 		}
 		if (!(hcint & HCINT_SOFTWARE_ONLY)) {
 			if (hcint & HCINT_NYET) {
-				if (td->hcsplt != 0) {
-					if (!dwc_otg_host_channel_wait(td))
-						break;
-					goto receive_pkt;
+				if (ep_type == UE_ISOCHRONOUS) {
+					/* we missed the service interval */
+					return (0);	/* complete */
 				}
+				if (!dwc_otg_host_channel_wait(td))
+					break;
+				goto receive_pkt;
 			}
 			break;
 		}
@@ -1183,29 +1289,44 @@ check_state:
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 
-			/* check if we are complete */
-			if ((td->remainder == 0) || (td->got_short != 0)) {
-				if (td->short_pkt)
+			if (ep_type == UE_ISOCHRONOUS) {
+				/* check if we are complete */
+				if ((td->remainder == 0) ||
+				    (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN))
 					return (0);	/* complete */
 
-				/*
-				 * Else need to receive a zero length
-				 * packet.
-				 */
-			}
-			if (td->hcsplt != 0)
-				goto receive_spkt;
-			else
 				goto receive_pkt;
+			} else {
+				/* check if we are complete */
+				if ((td->remainder == 0) || (td->got_short != 0)) {
+					if (td->short_pkt)
+						return (0);	/* complete */
+
+					/*
+					 * Else need to receive a zero length
+					 * packet.
+					 */
+				}
+				td->tt_scheduled = 0;
+				if (td->hcsplt != 0)
+					goto receive_spkt;
+				else
+					goto receive_pkt;
+			}
 		}
 		break;
 
 	case DWC_CHAN_ST_WAIT_S_ANE:
+		/*
+		 * NOTE: The DWC OTG hardware provides a fake ACK in
+		 * case of interrupt and isochronous transfers:
+		 */ 
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto receive_spkt;
 		}
 		if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -1215,100 +1336,91 @@ check_state:
 		}
 		break;
 
-	case DWC_CHAN_ST_RX_PKT:
+	case DWC_CHAN_ST_WAIT_C_PKT:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
 		goto receive_pkt;
 
-	case DWC_CHAN_ST_RX_SPKT:
-		goto receive_spkt;
-
-	case DWC_CHAN_ST_RX_SPKT_SYNC:
-		goto receive_spkt_sync;
-
 	default:
 		break;
 	}
 	goto busy;
 
 receive_pkt:
-	if (td->hcsplt != 0) {
-		count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
-
-		/* check for even microframes */
-		if (count == td->curr_frame) {
-			td->state = DWC_CHAN_ST_RX_PKT;
-			dwc_otg_host_channel_free(td);
-			/* enable SOF interrupt */
-			dwc_otg_enable_sof_irq(sc);
-			goto busy;
-		} else if (count == 0) {
-			/* check for start split timeout */
-			goto receive_spkt;
+  	if (td->hcsplt != 0) {
+		/* Wait for our turn, if TT transfer */
+		if (td->tt_scheduled == 0 ||
+		    (sc->sc_last_frame_num & 7) < td->tt_complete_slot) {
+			/* set return state */
+			td->state = DWC_CHAN_ST_WAIT_C_PKT;
+			goto tt_wait;
+		}
+		/* wait until next slot before trying again */
+		td->tt_complete_slot++;
+
+		/* set toggle, if any */
+		if (td->set_toggle) {
+			td->set_toggle = 0;
+			td->toggle = 1;
 		}
-
-		td->curr_frame = count;
 		td->hcsplt |= HCSPLT_COMPSPLT;
-	} else if (dwc_otg_host_rate_check(td)) {
-		td->state = DWC_CHAN_ST_RX_PKT;
+		count = HCSPLT_XACTLEN_MAX;
+	} else if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN &&
+	    dwc_otg_host_rate_check(td)) {
+		td->state = DWC_CHAN_ST_START;
 		dwc_otg_host_channel_free(td);
 		goto busy;
+	} else {
+		count = td->max_packet_size;
 	}
-
 	td->state = DWC_CHAN_ST_WAIT_ANE;
 
 	/* receive one packet */
 	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
-	    (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
+	    (count << HCTSIZ_XFERSIZE_SHIFT) |
 	    (1 << HCTSIZ_PKTCNT_SHIFT) |
 	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
 	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
 	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
 
+	/* send ASAP */
+	if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1))
+		td->hcchar |= HCCHAR_ODDFRM;
+	else
+		td->hcchar &= ~HCCHAR_ODDFRM;
+
 	hcchar = td->hcchar;
 	hcchar |= HCCHAR_EPDIR_IN;
 
 	/* must enable channel before data can be received */
 	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
-
 	goto busy;
 
 receive_spkt:
-	if (dwc_otg_host_rate_check(td)) {
-		td->state = DWC_CHAN_ST_RX_SPKT;
-		dwc_otg_host_channel_free(td);
-		goto busy;
-	}
-
-receive_spkt_sync:
-	if (ep_type == UE_INTERRUPT ||
-	    ep_type == UE_ISOCHRONOUS) {
-		count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
-		td->curr_frame = count;
-
-		/* check for non-zero microframe */
-		if (count != 0) {
-			/* enable SOF interrupt */
-			dwc_otg_enable_sof_irq(sc);
-			/* set state */
-			td->state = DWC_CHAN_ST_RX_SPKT_SYNC;
-			dwc_otg_host_channel_free(td);
-			goto busy;
-		}
-	} else {
-		count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
-		td->curr_frame = count;
-
-		/* check for two last frames */
-		if (count >= 6) {
-			/* enable SOF interrupt */
-			dwc_otg_enable_sof_irq(sc);
-			/* set state */
-			td->state = DWC_CHAN_ST_RX_SPKT_SYNC;
+	/* Wait for our turn, if TT transfer */
+	if (td->tt_scheduled == 0) {
+		if (ep_type == UE_INTERRUPT) {
+			td->state = DWC_CHAN_ST_START;
 			dwc_otg_host_channel_free(td);
 			goto busy;
 		}
+		/* set return state */
+		td->state = DWC_CHAN_ST_START;
+		goto tt_wait;
+	}
+	if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) {
+		/* set return state */
+		td->state = DWC_CHAN_ST_START;
+		goto tt_wait;
 	}
 
+	/* send ASAP */
+	if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1))
+		td->hcchar |= HCCHAR_ODDFRM;
+	else
+		td->hcchar &= ~HCCHAR_ODDFRM;
+
 	td->hcsplt &= ~HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_S_ANE;
 
@@ -1324,7 +1436,14 @@ receive_spkt_sync:
 
 	/* must enable channel before data can be received */
 	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+	goto busy;
+
+tt_wait:
+	/* enable SOF interrupt */
+	dwc_otg_enable_sof_irq(sc);
 
+	/* free allocated channel */
+	dwc_otg_host_channel_free(td);
 busy:
 	return (1);	/* busy */
 }
@@ -1497,6 +1616,8 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 
 	switch (td->state) {
 	case DWC_CHAN_ST_START:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
 		goto send_pkt;
 
 	case DWC_CHAN_ST_WAIT_ANE:
@@ -1504,6 +1625,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto send_pkt;
 		}
 		if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -1513,6 +1635,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle ^= 1;
+			td->tt_scheduled = 0;
 
 			/* check remainder */
 			if (td->remainder == 0) {
@@ -1527,11 +1650,13 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 			goto send_pkt;
 		}
 		break;
+
 	case DWC_CHAN_ST_WAIT_S_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto send_pkt;
 		}
 		if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -1540,6 +1665,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 			goto send_cpkt;
 		}
 		break;
+
 	case DWC_CHAN_ST_WAIT_C_ANE:
 		if (hcint & HCINT_NYET) {
 			if (!dwc_otg_host_channel_wait(td))
@@ -1550,6 +1676,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 			if (!dwc_otg_host_channel_wait(td))
 				break;
 			td->did_nak = 1;
+			td->tt_scheduled = 0;
 			goto send_pkt;
 		}
 		if (hcint & HCINT_ACK) {
@@ -1558,6 +1685,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle ^= 1;
+			td->tt_scheduled = 0;
 
 			/* check remainder */
 			if (td->remainder == 0) {
@@ -1570,64 +1698,204 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 		}
 		break;
 
-	case DWC_CHAN_ST_TX_PKT:
-		goto send_pkt;
+	case DWC_CHAN_ST_WAIT_C_PKT:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
+		goto send_cpkt;
 
-	case DWC_CHAN_ST_TX_PKT_SYNC:
-		goto send_pkt_sync;
+	case DWC_CHAN_ST_TX_WAIT_ISOC:
 
-	case DWC_CHAN_ST_TX_CPKT:
-		goto send_cpkt;
+		/* Check if isochronous OUT traffic is complete */
+		if ((hcint & HCINT_ACK) == 0)
+			break;
+
+		td->offset += td->tx_bytes;
+		td->remainder -= td->tx_bytes;
 
+		/* Update split token according to specification */
+		if (td->hcsplt != 0) {
+			if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)
+				td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
+		} else if (td->max_packet_count > 1) {
+			td->tt_xactpos++;
+		}
+
+		dwc_otg_host_channel_disable(sc, td->channel);
+
+		if (td->remainder == 0)
+			return (0);	/* complete */
+
+		td->state = DWC_CHAN_ST_TX_PKT_ISOC;
+
+		/* FALLTHROUGH */
+
+	case DWC_CHAN_ST_TX_PKT_ISOC:
+		if (!dwc_otg_host_channel_wait(td))
+			break;
+
+		if (td->hcsplt != 0) {
+			if ((sc->sc_last_frame_num & 7) < td->tt_start_slot)
+				goto tt_wait;
+			/* packets must be 125us apart */
+			td->tt_start_slot++;
+		}
+		goto send_isoc_pkt;
 	default:
 		break;
 	}
 	goto busy;
 
 send_pkt:
-	if (dwc_otg_host_rate_check(td)) {
-		td->state = DWC_CHAN_ST_TX_PKT;
+	if (td->hcsplt != 0) {
+		/* Wait for our turn, if TT transfer */
+		if (td->tt_scheduled == 0) {
+			if (ep_type == UE_INTERRUPT) {
+				td->state = DWC_CHAN_ST_START;
+				dwc_otg_host_channel_free(td);
+				goto busy;
+			}
+			/* set return state */
+			td->state = DWC_CHAN_ST_START;
+			goto tt_wait;
+		}
+		if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) {
+			/* set return state */
+			td->state = DWC_CHAN_ST_START;
+			goto tt_wait;
+		}
+
+		/* packets must be 125us apart */
+		td->tt_start_slot++;
+
+		/* set toggle, if any */
+		if (td->set_toggle) {
+			td->set_toggle = 0;
+			td->toggle = 1;
+		}
+	} else if (dwc_otg_host_rate_check(td)) {
+		td->state = DWC_CHAN_ST_START;
 		dwc_otg_host_channel_free(td);
 		goto busy;
 	}
 
-send_pkt_sync:
-	if (td->hcsplt != 0) {
- 		count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
-		/* check for first or last microframe */
-		if (count == 7 || count == 0) {
-			/* enable SOF interrupt */
-			dwc_otg_enable_sof_irq(sc);
-			/* set state */
-			td->state = DWC_CHAN_ST_TX_PKT_SYNC;
-			dwc_otg_host_channel_free(td);
-			goto busy;
+	if (ep_type == UE_ISOCHRONOUS) {
+send_isoc_pkt:
+		/* Isochronous OUT transfers don't have any ACKs */
+		td->state = DWC_CHAN_ST_TX_WAIT_ISOC;
+		td->hcsplt &= ~HCSPLT_COMPSPLT;
+		if (td->hcsplt != 0) {
+			/* get maximum transfer length */
+			count = td->remainder;
+
+			/* Update split token according to specification */
+			if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) {
+				if (count <= HCSPLT_XACTLEN_MAX)
+					td->tt_xactpos = HCSPLT_XACTPOS_ALL;
+				else
+					count = HCSPLT_XACTLEN_MAX;
+			} else if (td->tt_xactpos == HCSPLT_XACTPOS_MIDDLE) {
+				if (count <= HCSPLT_XACTLEN_MAX)
+					td->tt_xactpos = HCSPLT_XACTPOS_LAST;
+				else
+					count = HCSPLT_XACTLEN_MAX;
+			}
+
+			/* Update transaction position */
+			td->hcsplt &= ~HCSPLT_XACTPOS_MASK;
+			td->hcsplt |= ((uint32_t)td->tt_xactpos << HCSPLT_XACTPOS_SHIFT);
+		} else {
+			/* send one packet at a time */
+			count = td->max_packet_size;
+			if (td->remainder < count) {
+				/* we have a short packet */
+				td->short_pkt = 1;
+				count = td->remainder;
+			}
 		}
+	} else if (td->hcsplt != 0) {
 
 		td->hcsplt &= ~HCSPLT_COMPSPLT;
+
+		/* Wait for ACK/NAK/ERR from TT */
 		td->state = DWC_CHAN_ST_WAIT_S_ANE;
+
+		/* send one packet at a time */
+		count = td->max_packet_size;
+		if (td->remainder < count) {
+			/* we have a short packet */
+			td->short_pkt = 1;
+			count = td->remainder;
+		}
 	} else {
+		/* Wait for ACK/NAK/STALL from device */
 		td->state = DWC_CHAN_ST_WAIT_ANE;
-	}
 
-	/* send one packet at a time */
-	count = td->max_packet_size;
-	if (td->remainder < count) {
-		/* we have a short packet */
-		td->short_pkt = 1;
-		count = td->remainder;
+		/* send one packet at a time */
+		count = td->max_packet_size;
+		if (td->remainder < count) {
+			/* we have a short packet */
+			td->short_pkt = 1;
+			count = td->remainder;
+		}
 	}
 
-	/* TODO: HCTSIZ_DOPNG */
+	/* check for High-Speed multi-packets */
+	if ((td->hcsplt == 0) && (td->max_packet_count > 1)) {
+		if (td->npkt == 0) {
+			if (td->remainder >= (3 * td->max_packet_size))
+				td->npkt = 3;
+			else if (td->remainder >= (2 * td->max_packet_size))
+				td->npkt = 2;
+			else
+				td->npkt = 1;
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
-	    (count << HCTSIZ_XFERSIZE_SHIFT) |
-	    (1 << HCTSIZ_PKTCNT_SHIFT) |
-	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
-	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+			if (td->npkt > td->max_packet_count)
+				td->npkt = td->max_packet_count;
+
+			td->tt_xactpos = 1;	/* overload */
+		}
+		if (td->tt_xactpos == td->npkt) {
+			if (td->npkt == 1) {
+				DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+				    (count << HCTSIZ_XFERSIZE_SHIFT) |
+				    (1 << HCTSIZ_PKTCNT_SHIFT) |
+				    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
+			} else if (td->npkt == 2) {
+				DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+				    (count << HCTSIZ_XFERSIZE_SHIFT) |
+				    (1 << HCTSIZ_PKTCNT_SHIFT) |
+				    (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT));
+			} else {
+				DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+				    (count << HCTSIZ_XFERSIZE_SHIFT) |
+				    (1 << HCTSIZ_PKTCNT_SHIFT) |
+				    (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT));
+			}
+			td->npkt = 0;
+		} else {
+			DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+			    (count << HCTSIZ_XFERSIZE_SHIFT) |
+			    (1 << HCTSIZ_PKTCNT_SHIFT) |
+			    (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT));
+		}
+	} else {
+		/* TODO: HCTSIZ_DOPNG */
+
+		DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+		    (count << HCTSIZ_XFERSIZE_SHIFT) |
+		    (1 << HCTSIZ_PKTCNT_SHIFT) |
+		    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+		    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+	}
 
 	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
 
+	/* send ASAP */
+	if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1))
+		td->hcchar |= HCCHAR_ODDFRM;
+	else
+		td->hcchar &= ~HCCHAR_ODDFRM;
+
 	hcchar = td->hcchar;
 	hcchar &= ~HCCHAR_EPDIR_IN;
 
@@ -1651,18 +1919,20 @@ send_pkt_sync:
 
 	/* store number of bytes transmitted */
 	td->tx_bytes = count;
-
 	goto busy;
 
 send_cpkt:
-	count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
-	/* check for first microframe */
-	if (count == 0) {
-		/* send packet again */
-		goto send_pkt;
+	/* Wait for our turn, if TT transfer */
+	if (td->tt_scheduled == 0 ||
+	    (sc->sc_last_frame_num & 7) < td->tt_complete_slot) {
+		/* set return state */
+		td->state = DWC_CHAN_ST_WAIT_C_PKT;

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



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