Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Nov 2012 16:28:58 +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: r242829 - head/sys/dev/usb/controller
Message-ID:  <201211091628.qA9GSwDa010552@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Fri Nov  9 16:28:58 2012
New Revision: 242829
URL: http://svnweb.freebsd.org/changeset/base/242829

Log:
  Fix LOW and FULL speed USB INTERRUPT endpoint support for the
  DWC OTG driver. Fix a hang issue when using LOW and FULL speed
  BULK traffic. Make sure we don't ask for data in the last
  microframe. This allows using devices like USB mice and USB
  keyboards connected to the RPI-B.
  
  Suggested by:	gonzo @

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	Fri Nov  9 16:00:30 2012	(r242828)
+++ head/sys/dev/usb/controller/dwc_otg.c	Fri Nov  9 16:28:58 2012	(r242829)
@@ -372,6 +372,15 @@ 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)
+		return;
+	sc->sc_irq_mask |= GINTSTS_SOF;
+	DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+}
+
+static void
 dwc_otg_resume_irq(struct dwc_otg_softc *sc)
 {
 	if (sc->sc_flags.status_suspend) {
@@ -523,10 +532,6 @@ dwc_otg_host_channel_wait(struct dwc_otg
 	if (x == 0)
 		return (0);	/* wait */
 
-	/* assume NAK-ing is next */
-	if (sc->sc_chan_state[x].hcint & HCINT_NYET)
-		return (0);	/* wait */
-
 	/* find new disabled channel */
 	for (x = 1; x != sc->sc_host_ch_max; x++) {
 
@@ -629,8 +634,7 @@ dwc_otg_host_channel_disable(struct dwc_
 		/* don't re-use channel until next SOF is transmitted */
 		sc->sc_chan_state[x].wait_sof = 2;
 		/* enable SOF interrupt */
-		sc->sc_irq_mask |= GINTMSK_SOFMSK; 
-		DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+		dwc_otg_enable_sof_irq(sc);
 	}
 }
 
@@ -688,14 +692,15 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
 	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
-	if (hcint & HCINT_STALL) {
+	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", td->channel);
 		td->error_stall = 1;
 		td->error_any = 1;
 		return (0);		/* complete */
-	}
-
-	if (hcint & HCINT_ERRORS) {
+	} else if (hcint & HCINT_ERRORS) {
 		DPRINTF("CH=%d ERROR\n", td->channel);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
@@ -769,6 +774,8 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 			return (0);	/* complete */
 		}
 		break;
+	case DWC_CHAN_ST_TX_PKT_SYNC:
+		goto send_pkt_sync;
 	default:
 		break;
 	}
@@ -780,7 +787,21 @@ 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 */
+		}
+
 		td->hcsplt &= ~HCSPLT_COMPSPLT;
 		td->state = DWC_CHAN_ST_WAIT_S_ANE;
 	} else {
@@ -961,8 +982,7 @@ not_complete:
 }
 
 static uint8_t
-dwc_otg_host_rate_check(struct dwc_otg_td *td,
-    uint8_t do_inc)
+dwc_otg_host_rate_check(struct dwc_otg_td *td)
 {
 	struct dwc_otg_softc *sc;
 	uint8_t ep_type;
@@ -981,13 +1001,14 @@ dwc_otg_host_rate_check(struct dwc_otg_t
 			td->hcchar |= HCCHAR_ODDFRM;
 		else
 			td->hcchar &= ~HCCHAR_ODDFRM;
-		if (do_inc)
-			td->tmr_val += td->tmr_res;
+		td->tmr_val += td->tmr_res;
 	} else if (ep_type == UE_INTERRUPT) {
-		if ((sc->sc_tmr_val & 0xFF) != td->tmr_val)
+		uint8_t delta;
+
+		delta = sc->sc_tmr_val - td->tmr_val;
+		if (delta >= 128)
 			goto busy;
-		if (do_inc)
-			td->tmr_val += td->tmr_res;
+		td->tmr_val = sc->sc_tmr_val + td->tmr_res;
 	} else if (td->did_nak != 0) {
 		goto busy;
 	} 
@@ -1010,6 +1031,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 	uint32_t hcint;
 	uint32_t hcchar;
 	uint32_t count;
+	uint8_t ep_type;
 
 	if (dwc_otg_host_channel_alloc(td))
 		return (1);		/* busy */
@@ -1017,6 +1039,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 	/* get pointer to softc */
 	sc = DWC_OTG_PC2SC(td->pc);
 
+	ep_type = ((td->hcchar &
+	    HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
+
 	hcint = sc->sc_chan_state[td->channel].hcint;
 
 	DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
@@ -1026,14 +1051,15 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 
 	/* check interrupt bits */
 
-	if (hcint & HCINT_STALL) {
+	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", td->channel);
 		td->error_stall = 1;
 		td->error_any = 1;
 		return (0);		/* complete */
-	}
-
-	if (hcint & HCINT_ERRORS) {
+	} else if (hcint & HCINT_ERRORS) {
 		DPRINTF("CH=%d ERROR\n", td->channel);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
@@ -1063,7 +1089,17 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 	switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
 	case GRXSTSRH_IN_DATA:
 
-		DPRINTF("DATA\n");
+		DPRINTF("DATA ST=%d STATUS=0x%08x\n",
+		    (int)td->state, (int)sc->sc_last_rx_status);
+
+		if (hcint & HCINT_SOFTWARE_ONLY) {
+			/*
+			 * When using SPLIT transactions on interrupt
+			 * endpoints, sometimes data occurs twice.
+			 */
+			DPRINTF("Data already received\n");
+			break;
+		}
 
 		td->toggle ^= 1;
 
@@ -1131,12 +1167,16 @@ check_state:
 			else
 				goto receive_pkt;
 		}
-		if (hcint & HCINT_NYET) {
-			if (td->hcsplt != 0)
-				goto receive_pkt;
-		}
-		if (!(hcint & HCINT_SOFTWARE_ONLY))
+		if (!(hcint & HCINT_SOFTWARE_ONLY)) {
+			if (hcint & HCINT_NYET) {
+				if (td->hcsplt != 0) {
+					if (!dwc_otg_host_channel_wait(td))
+						break;
+					goto receive_pkt;
+				}
+			}
 			break;
+		}
 		if (hcint & (HCINT_ACK | HCINT_NYET)) {
 			if (!dwc_otg_host_channel_wait(td))
 				break;
@@ -1179,20 +1219,38 @@ check_state:
 	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 (dwc_otg_host_rate_check(td, 1)) {
+	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;
+		}
+
+		td->curr_frame = count;
+		td->hcsplt |= HCSPLT_COMPSPLT;
+	} else if (dwc_otg_host_rate_check(td)) {
 		td->state = DWC_CHAN_ST_RX_PKT;
 		dwc_otg_host_channel_free(td);
 		goto busy;
 	}
 
-	if (td->hcsplt != 0)
-		td->hcsplt |= HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_ANE;
 
 	/* receive one packet */
@@ -1213,12 +1271,42 @@ receive_pkt:
 	goto busy;
 
 receive_spkt:
-	if (dwc_otg_host_rate_check(td, 0)) {
+	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;
+			dwc_otg_host_channel_free(td);
+			goto busy;
+		}
+	}
+
 	td->hcsplt &= ~HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_S_ANE;
 
@@ -1377,14 +1465,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
 	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
-	if (hcint & HCINT_STALL) {
+	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", td->channel);
 		td->error_stall = 1;
 		td->error_any = 1;
 		return (0);		/* complete */
-	}
-
-	if (hcint & HCINT_ERRORS) {
+	} else if (hcint & HCINT_ERRORS) {
 		DPRINTF("CH=%d ERROR\n", td->channel);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
@@ -1482,6 +1571,9 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 	case DWC_CHAN_ST_TX_PKT:
 		goto send_pkt;
 
+	case DWC_CHAN_ST_TX_PKT_SYNC:
+		goto send_pkt_sync;
+
 	case DWC_CHAN_ST_TX_CPKT:
 		goto send_cpkt;
 
@@ -1491,13 +1583,25 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 	goto busy;
 
 send_pkt:
-	if (dwc_otg_host_rate_check(td, 1)) {
+	if (dwc_otg_host_rate_check(td)) {
 		td->state = DWC_CHAN_ST_TX_PKT;
 		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;
+		}
+
 		td->hcsplt &= ~HCSPLT_COMPSPLT;
 		td->state = DWC_CHAN_ST_WAIT_S_ANE;
 	} else {
@@ -1549,6 +1653,13 @@ send_pkt:
 	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;
+	}
+
 	td->hcsplt |= HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_C_ANE;
 
@@ -2242,6 +2353,9 @@ dwc_otg_interrupt(struct dwc_otg_softc *
 		if (sc->sc_irq_mask & GINTMSK_SOFMSK) {
 			uint8_t x;
 			uint8_t y;
+
+			DPRINTFN(12, "SOF interrupt\n");
+
 			for (x = y = 0; x != sc->sc_host_ch_max; x++) {
 				if (sc->sc_chan_state[x].wait_sof != 0) {
 					if (--(sc->sc_chan_state[x].wait_sof) != 0)
@@ -2280,6 +2394,7 @@ dwc_otg_setup_standard_chain_sub(struct 
 	td->remainder = temp->len;
 	td->tx_bytes = 0;
 	td->error_any = 0;
+	td->error_stall = 0;
 	td->npkt = 0;
 	td->did_stall = temp->did_stall;
 	td->short_pkt = temp->short_pkt;
@@ -2531,8 +2646,8 @@ dwc_otg_setup_standard_chain(struct usb_
 				ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
 				if (ival == 0)
 					ival = 1;
-				else if (ival > 255)
-					ival = 255;
+				else if (ival > 127)
+					ival = 127;
 				td->tmr_val = sc->sc_tmr_val + ival;
 				td->tmr_res = ival;
 			}
@@ -2549,8 +2664,8 @@ dwc_otg_setup_standard_chain(struct usb_
 				ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
 				if (ival == 0)
 					ival = 1;
-				else if (ival > 255)
-					ival = 255;
+				else if (ival > 127)
+					ival = 127;
 				td->tmr_val = sc->sc_tmr_val + ival;
 				td->tmr_res = ival;
 			}

Modified: head/sys/dev/usb/controller/dwc_otg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.h	Fri Nov  9 16:00:30 2012	(r242828)
+++ head/sys/dev/usb/controller/dwc_otg.h	Fri Nov  9 16:28:58 2012	(r242829)
@@ -60,6 +60,7 @@ struct dwc_otg_td {
 	uint8_t errcnt;
 	uint8_t tmr_res;
 	uint8_t tmr_val;
+	uint8_t curr_frame;
 	uint8_t	ep_no;
 	uint8_t channel;
 	uint8_t state;
@@ -69,8 +70,10 @@ struct dwc_otg_td {
 #define	DWC_CHAN_ST_WAIT_C_ANE 3
 #define	DWC_CHAN_ST_RX_PKT 4
 #define	DWC_CHAN_ST_RX_SPKT 5
+#define	DWC_CHAN_ST_RX_SPKT_SYNC 6
 #define	DWC_CHAN_ST_TX_PKT 4
 #define	DWC_CHAN_ST_TX_CPKT 5
+#define	DWC_CHAN_ST_TX_PKT_SYNC 6
 	uint8_t	error:1;
 	uint8_t	error_any:1;
 	uint8_t	error_stall:1;

Modified: head/sys/dev/usb/controller/dwc_otgreg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otgreg.h	Fri Nov  9 16:00:30 2012	(r242828)
+++ head/sys/dev/usb/controller/dwc_otgreg.h	Fri Nov  9 16:28:58 2012	(r242829)
@@ -299,7 +299,7 @@
 #define	GRXSTSRD_DPID_DATA0		(0<<15)
 #define	GRXSTSRD_DPID_DATA1		(2<<15)
 #define	GRXSTSRD_DPID_DATA2		(1<<15)
-#define	GRXSTSRD_PID_MDATA		(3<<15)
+#define	GRXSTSRD_DPID_MDATA		(3<<15)
 #define	GRXSTSRD_BCNT_MASK		0x00007ff0
 #define	GRXSTSRD_BCNT_GET(x)		(((x) >> 4) & 0x7FF)
 #define	GRXSTSRD_BCNT_SHIFT		4



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