Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 Sep 2012 07:52: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: r240482 - head/sys/dev/usb/controller
Message-ID:  <201209140752.q8E7qwKx073504@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Fri Sep 14 07:52:57 2012
New Revision: 240482
URL: http://svn.freebsd.org/changeset/base/240482

Log:
  DWC OTG improvements. Implement full support for SPLIT transactions, in other
  words FULL/LOW speed devices through HIGH speed HUBs. Improve support for
  suspend and resume in host mode.

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 Sep 14 05:24:06 2012	(r240481)
+++ head/sys/dev/usb/controller/dwc_otg.c	Fri Sep 14 07:52:57 2012	(r240482)
@@ -510,7 +510,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 	}
 
 	for (; x != max_channel; x++) {
-		if (sc->sc_hcchar[x] == 0) {
+		if (sc->sc_chan_state[x].hcchar == 0) {
 
 			/* check if channel is enabled */
 			temp = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
@@ -519,7 +519,8 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 				continue;
 			}
 
-			sc->sc_hcchar[x] = td->hcchar;
+			sc->sc_chan_state[x].hcchar = td->hcchar;
+			sc->sc_chan_state[x].hcsplt = td->hcsplt;
 
 			DPRINTF("HCCHAR=0x%08x(0x%08x) HCSPLT=0x%08x\n",
 			    td->hcchar, temp, td->hcsplt);
@@ -528,14 +529,14 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 			temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
 			DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
 
-			/* enable interrupts */
-			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
-			    HCINT_STALL | HCINT_BBLERR |
-			    HCINT_AHBERR | HCINT_XACTERR |
-			    HCINT_XFERCOMPL | HCINT_NAK);
+			/* clear buffered interrupts */
+			sc->sc_chan_state[x].hcint = 0;
+
+			/* clear state */
+			sc->sc_chan_state[x].state = 0;
 
-			DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), td->hcsplt);
-			DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0);
+			/* we've requested SOF interrupt */
+			sc->sc_chan_state[x].sof_requested = 1;
 
 			/* set channel */
 			td->channel = x;
@@ -558,8 +559,6 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 	struct usb_device_request req __aligned(4);
 	struct dwc_otg_softc *sc;
 	uint32_t temp;
-	uint32_t max_buffer;
-	uint8_t max_frames;
 
 	if (dwc_otg_host_channel_alloc(td))
 		return (1);		/* busy */
@@ -567,57 +566,103 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 	/* get pointer to softc */
 	sc = DWC_OTG_PC2SC(td->pc);
 
-	temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-	DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
+	temp = sc->sc_chan_state[td->channel].hcint;
 
-	DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-	    td->channel,
-	    temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+	DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+	    td->channel, sc->sc_chan_state[td->channel].state, temp,
+	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
 	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
-	if (temp & HCINT_NAK)
-		td->did_nak = 1;
-
 	if (temp & HCINT_STALL) {
 		td->error_stall = 1;
 		td->error_any = 1;
 		return (0);		/* complete */
 	}
 
-	if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) {
+	if (temp & (HCINT_BBLERR | HCINT_XACTERR)) {
 		td->error_any = 1;
 		return (0);		/* complete */
 	}
 
-	if (temp & HCINT_XFERCOMPL)
-		td->did_complete = 1;
+	if (temp & HCINT_NAK) {
+		if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
+			return (1);	/* busy */
+		td->sof_val += 1;
+	}
+
+	/* channel must be disabled before we can complete the transfer */
+
+	if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) {
+		uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+		if (hcchar & HCCHAR_CHENA) {
+			DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
+			    HCCHAR_CHENA | HCCHAR_CHDIS);
+		} else {
+			sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD;
+		}
+	}
 
-	if (td->did_complete) {
-		if (td->did_nak == 0) {
+	switch (sc->sc_chan_state[td->channel].state) {
+	case DWC_CHAN_ST_START:
+		if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+			sc->sc_chan_state[td->channel].hcsplt &=
+			    ~HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_S_ANE;
+		} else {
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_ANE;
+		}
+		goto send_pkt;
+	case DWC_CHAN_ST_WAIT_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto send_pkt;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle = 1;
+			sc->sc_chan_state[td->channel].hcint = 0;
+			sc->sc_chan_state[td->channel].state = 0;
 			return (0);	/* complete */
-		} else {
-			if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
-				return (1);	/* busy */
-			td->sof_val += 1;
 		}
-	} else {
-		return (1);	/* busy */
+		break;
+	case DWC_CHAN_ST_WAIT_S_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto send_pkt;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
+			sc->sc_chan_state[td->channel].hcsplt |= HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_C_ANE;
+			goto send_cpkt;
+		}
+		break;
+	case DWC_CHAN_ST_WAIT_C_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto send_cpkt;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
+			td->offset += td->tx_bytes;
+			td->remainder -= td->tx_bytes;
+			td->toggle = 1;
+			sc->sc_chan_state[td->channel].hcint = 0;
+			sc->sc_chan_state[td->channel].state = 0;
+			return (0);	/* complete */
+		}
+		break;
+	default:
+		break;
 	}
-	temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS);
-
-	DPRINTF("HPTXSTS=0x%08x\n", temp);
-
-	max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK);
-	max_frames = (temp & HPTXSTS_PTXQSPCAVAIL_MASK)
-	    >> HPTXSTS_PTXQSPCAVAIL_SHIFT;
-
-	max_buffer = max_buffer - (max_buffer % td->max_packet_size);
-	if (max_buffer == 0 || max_frames == 0)
-		return (1);	/* busy */
+	return (1);		/* busy */
 
+send_pkt:
 	if (sizeof(req) != td->remainder) {
 		td->error_any = 1;
 		return (0);		/* complete */
@@ -630,7 +675,10 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 	    (1 << HCTSIZ_PKTCNT_SHIFT) |
 	    (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-	temp = sc->sc_hcchar[td->channel];
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+	    sc->sc_chan_state[td->channel].hcsplt);
+
+	temp = sc->sc_chan_state[td->channel].hcchar;
 	temp &= ~HCCHAR_EPDIR_IN;
 
 	/* must enable channel before writing data to FIFO */
@@ -640,11 +688,30 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 	bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
 	    DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
 
-	/* reset statemachine */
-	td->did_complete = 0;
-	td->did_nak = 0;
+	/* store number of bytes transmitted */
 	td->tx_bytes = sizeof(req);
 
+	/* clear interrupts */
+	sc->sc_chan_state[td->channel].hcint = 0;
+
+	return (1);	/* busy */
+
+send_cpkt:
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+	    (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+	    sc->sc_chan_state[td->channel].hcsplt);
+
+	temp = sc->sc_chan_state[td->channel].hcchar;
+	temp &= ~HCCHAR_EPDIR_IN;
+
+	/* must enable channel before writing data to FIFO */
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
+
+	/* clear interrupts */
+	sc->sc_chan_state[td->channel].hcint = 0;
+
 	return (1);	/* busy */
 }
 
@@ -793,21 +860,15 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 	/* get pointer to softc */
 	sc = DWC_OTG_PC2SC(td->pc);
 
-	temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-	DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
-
-	DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-	    td->channel,
-	    temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+	ep_type = ((sc->sc_chan_state[td->channel].hcchar &
+	    HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
 
-	if (temp & HCINT_NAK) {
-		td->did_nak = 1;
+	temp = sc->sc_chan_state[td->channel].hcint;
 
-		/* disable channel - will generate a halted event */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-		    HCCHAR_CHENA | HCCHAR_CHDIS);
-	}
+	DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+	    td->channel, sc->sc_chan_state[td->channel].state, temp,
+	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
 	if (temp & HCINT_STALL) {
 		td->error_stall = 1;
@@ -815,11 +876,23 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 		return (0);		/* complete */
 	}
 
-	if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) {
+	if (temp & (HCINT_BBLERR | HCINT_XACTERR)) {
 		td->error_any = 1;
 		return (0);		/* complete */
 	}
 
+	/* channel must be disabled before we can complete the transfer */
+
+	if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) {
+		uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+		if (hcchar & HCCHAR_CHENA) {
+			DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
+			    HCCHAR_CHENA | HCCHAR_CHDIS);
+		} else {
+			sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD;
+		}
+	}
+
 	/* check endpoint status */
 	if (sc->sc_last_rx_status == 0)
 		goto not_complete;
@@ -828,16 +901,6 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 		goto not_complete;
 
 	switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
-	case GRXSTSRH_HALTED:
-		DPRINTF("HALTED\n");
-		td->did_complete = 1;
-		break;
-	case GRXSTSRH_IN_COMPLETE:
-		DPRINTF("COMPLETE\n");
-		/* disable channel - will generate a halted event */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-		    HCCHAR_CHENA | HCCHAR_CHDIS);
-		break;
 	case GRXSTSRH_IN_DATA:
 
 		DPRINTF("DATA\n");
@@ -878,6 +941,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 
 		td->remainder -= count;
 		td->offset += count;
+		sc->sc_chan_state[td->channel].hcint |= HCINT_SOFTWARE_ONLY;
 		break;
 
 	default:
@@ -888,45 +952,102 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 	dwc_otg_common_rx_ack(sc);
 
 not_complete:
-	if (td->did_complete == 0)
+	if (temp & HCINT_SUSPEND_ONLY)
 		return (1);	/* busy */
 
-	/* check if we are complete */
-	if ((td->remainder == 0) || (td->got_short != 0)) {
-		if (td->short_pkt) {
-			/* we are complete */
-			return (0);
-		}
-		/* else need to receive a zero length packet */
-	}
-
-	temp = sc->sc_hcchar[td->channel];
-
-	ep_type = ((temp & HCCHAR_EPTYPE_MASK) >>
-	    HCCHAR_EPTYPE_SHIFT);
-
 	if (ep_type == UE_ISOCHRONOUS) {
 		if ((sc->sc_sof_val & 0xFF) != td->sof_val)
 			return (1);	/* busy */
 		if (td->sof_val & 1)
-			temp |= HCCHAR_ODDFRM;
+			sc->sc_chan_state[td->channel].hcchar |= HCCHAR_ODDFRM;
+		else
+			sc->sc_chan_state[td->channel].hcchar &= ~HCCHAR_ODDFRM;
 		td->sof_val += td->sof_res;
-		/* DATA 0 */
-		td->toggle = 0;
 	} else if (ep_type == UE_INTERRUPT) {
 		if ((sc->sc_sof_val & 0xFF) != td->sof_val)
 			return (1);	/* busy */
 		td->sof_val += td->sof_res;
-	} else {
-		if (td->did_nak) {
-			if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
-				return (1);	/* busy */
-			td->sof_val += 1;
+	} else if (temp & HCINT_NAK) {
+		if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
+			return (1);	/* busy */
+		td->sof_val += 1;
+	}
+
+	switch (sc->sc_chan_state[td->channel].state) {
+	case DWC_CHAN_ST_START:
+		if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+			sc->sc_chan_state[td->channel].hcsplt &=
+			    ~HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_S_ANE;
+			goto receive_spkt;
+		} else {
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_ANE;
+			goto receive_pkt;
 		}
-		if (td->set_toggle) {
-			td->set_toggle = 0;
-			td->toggle = 1;
+	case DWC_CHAN_ST_WAIT_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto receive_pkt;
+		if (!(sc->sc_chan_state[td->channel].hcint &
+		    HCINT_SOFTWARE_ONLY))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
+			/* check if we are complete */
+			if ((td->remainder == 0) || (td->got_short != 0)) {
+				if (td->short_pkt) {
+					/* we are complete */
+					sc->sc_chan_state[
+					    td->channel].hcint = 0;
+					sc->sc_chan_state[
+					    td->channel].state = 0;
+					return (0);
+				}
+				/*
+				 * Else need to receive a zero length
+				 * packet.
+				 */
+			}
+			if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+				sc->sc_chan_state[td->channel].hcsplt &=
+				    ~HCSPLT_COMPSPLT;
+				sc->sc_chan_state[td->channel].state =
+				    DWC_CHAN_ST_WAIT_S_ANE;
+				goto receive_spkt;
+			} else {
+				sc->sc_chan_state[td->channel].state =
+				    DWC_CHAN_ST_WAIT_ANE;
+				goto receive_pkt;
+			}
 		}
+		break;
+	case DWC_CHAN_ST_WAIT_S_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto receive_spkt;
+		if (sc->sc_chan_state[td->channel].hcint & (HCINT_ACK | HCINT_NYET)) {
+			sc->sc_chan_state[td->channel].hcsplt |=
+			    HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_ANE;
+			goto receive_pkt;
+		}
+		break;
+	default:
+		break;
+	}
+	return (1);		/* busy */
+
+receive_pkt:
+	if (ep_type == UE_ISOCHRONOUS) {
+		td->toggle = 0;
+	} else if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
 	}
 
 	/* receive one packet */
@@ -936,16 +1057,46 @@ not_complete:
 	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
 	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+	    sc->sc_chan_state[td->channel].hcsplt);
+
+	temp = sc->sc_chan_state[td->channel].hcchar;
 	temp |= HCCHAR_EPDIR_IN;
 
 	/* must enable channel before data can be received */
 	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
 
-	/* reset statemachine */
-	td->did_complete = 0;
-	td->did_nak = 0;
+	/* clear interrupts */
+	sc->sc_chan_state[td->channel].hcint = 0;
 
-	return (1);			/* not complete */
+	return (1);	/* busy */
+
+receive_spkt:
+	if (ep_type == UE_ISOCHRONOUS) {
+		td->toggle = 0;
+	} else if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+
+	/* receive one packet */
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+	    sc->sc_chan_state[td->channel].hcsplt);
+
+	temp = sc->sc_chan_state[td->channel].hcchar;
+	temp |= HCCHAR_EPDIR_IN;
+
+	/* must enable channel before data can be received */
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
+
+	/* clear interrupts */
+	sc->sc_chan_state[td->channel].hcint = 0;
+
+	return (1);	/* busy */
 }
 
 static uint8_t
@@ -1065,11 +1216,9 @@ static uint8_t
 dwc_otg_host_data_tx(struct dwc_otg_td *td)
 {
 	struct dwc_otg_softc *sc;
-	uint32_t max_buffer;
 	uint32_t count;
 	uint32_t temp;
 	uint8_t ep_type;
-	uint8_t max_frames;
 
 	if (dwc_otg_host_channel_alloc(td))
 		return (1);		/* busy */
@@ -1077,18 +1226,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 	/* get pointer to softc */
 	sc = DWC_OTG_PC2SC(td->pc);
 
-	temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-	DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
+	ep_type = ((sc->sc_chan_state[td->channel].hcchar &
+	    HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
 
-	DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-	    td->channel,
-	    temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+	temp = sc->sc_chan_state[td->channel].hcint;
 
-	if (temp & HCINT_NAK) {
-		td->did_nak = 1;
-		td->did_complete = 1;
-	}
+	DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+	    td->channel, sc->sc_chan_state[td->channel].state, temp,
+	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
 	if (temp & HCINT_STALL) {
 		td->error_stall = 1;
@@ -1096,70 +1242,132 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 		return (0);		/* complete */
 	}
 
-	if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) {
+	if (temp & (HCINT_BBLERR | HCINT_XACTERR)) {
 		td->error_any = 1;
 		return (0);		/* complete */
 	}
 
-	if (temp & HCINT_XFERCOMPL)
-		td->did_complete = 1;
+	/* channel must be disabled before we can complete the transfer */
+
+	if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) {
+		uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+		if (hcchar & HCCHAR_CHENA) {
+			DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
+			    HCCHAR_CHENA | HCCHAR_CHDIS);
+		} else {
+			sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD;
+		}
+	}
 
-	if (td->did_complete) {
-		if (td->did_nak == 0) {
+	if (ep_type == UE_ISOCHRONOUS) {
+		if ((sc->sc_sof_val & 0xFF) != td->sof_val)
+			return (1);	/* busy */
+		if (td->sof_val & 1)
+			sc->sc_chan_state[td->channel].hcchar |= HCCHAR_ODDFRM;
+		else
+			sc->sc_chan_state[td->channel].hcchar &= ~HCCHAR_ODDFRM;
+		td->sof_val += td->sof_res;
+	} else if (ep_type == UE_INTERRUPT) {
+		if ((sc->sc_sof_val & 0xFF) != td->sof_val)
+			return (1);	/* busy */
+		td->sof_val += td->sof_res;
+	} else if (temp & HCINT_NAK) {
+		if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
+			return (1);	/* busy */
+		td->sof_val += 1;
+	}
+
+	switch (sc->sc_chan_state[td->channel].state) {
+	case DWC_CHAN_ST_START:
+		if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+			sc->sc_chan_state[td->channel].hcsplt &= ~HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_S_ANE;
+		} else {
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_ANE;
+		}
+		goto send_pkt;
+	case DWC_CHAN_ST_WAIT_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto send_pkt;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle ^= 1;
-			td->did_nak = 1;
 
 			/* check remainder */
 			if (td->remainder == 0) {
-				if (td->short_pkt)
+				if (td->short_pkt) {
+					sc->sc_chan_state[td->channel].hcint = 0;
+					sc->sc_chan_state[td->channel].state = 0;
 					return (0);	/* complete */
+				}
 
 				/* else we need to transmit a short packet */
 			}
-		} else {
-			if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
-				return (1);	/* busy */
-			td->sof_val += 1;
- 		}
-	} else {
-		return (1);	/* busy */
-	}
-	temp = sc->sc_hcchar[td->channel];
+			goto send_pkt;
+		}
+		break;
+	case DWC_CHAN_ST_WAIT_S_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto send_pkt;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
+			sc->sc_chan_state[td->channel].hcsplt |= HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_C_ANE;
+			goto send_cpkt;
+		}
+		break;
+	case DWC_CHAN_ST_WAIT_C_ANE:
+		if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+			break;
+		if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+			goto send_cpkt;
+		if (sc->sc_chan_state[td->channel].hcint &
+		    (HCINT_ACK | HCINT_NYET)) {
+			td->offset += td->tx_bytes;
+			td->remainder -= td->tx_bytes;
+			td->toggle ^= 1;
+
+			/* check remainder */
+			if (td->remainder == 0) {
+				if (td->short_pkt) {
+					sc->sc_chan_state[
+					    td->channel].hcint = 0;
+					sc->sc_chan_state[
+					    td->channel].state = 0;
+					return (0);	/* complete */
+				}
 
-	ep_type = ((temp & HCCHAR_EPTYPE_MASK) >>
-	    HCCHAR_EPTYPE_SHIFT);
+				/* else we need to transmit a short packet */
+			}
+			sc->sc_chan_state[td->channel].hcsplt &=
+			    ~HCSPLT_COMPSPLT;
+			sc->sc_chan_state[td->channel].state =
+			    DWC_CHAN_ST_WAIT_S_ANE;
+			goto send_pkt;
+		}
+		break;
+	default:
+		break;
+	}
+	return (1);		/* busy */
 
+send_pkt:
 	if (ep_type == UE_ISOCHRONOUS) {
-		if ((sc->sc_sof_val & 0xFF) != td->sof_val)
-			return (1);	/* busy */
-		if (td->sof_val & 1)
-			sc->sc_hcchar[td->channel] |= HCCHAR_ODDFRM;
-		else
-			sc->sc_hcchar[td->channel] &= ~HCCHAR_ODDFRM;
-		td->sof_val += td->sof_res;
 		td->toggle = 0;
-
-	} else if (ep_type == UE_INTERRUPT) {
-		if ((sc->sc_sof_val & 0xFF) != td->sof_val)
-			return (1);	/* busy */
-		td->sof_val += td->sof_res;
 	} else if (td->set_toggle) {
 		td->set_toggle = 0;
 		td->toggle = 1;
 	}
 
-	temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS);
-
-	max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK);
-	max_frames = (temp & HPTXSTS_PTXQSPCAVAIL_MASK)
-	    >> HPTXSTS_PTXQSPCAVAIL_SHIFT;
-
-	max_buffer = max_buffer - (max_buffer % td->max_packet_size);
-	if (max_buffer == 0 || max_frames == 0)
-		return (1);	/* busy */
-
 	/* send one packet at a time */
 	count = td->max_packet_size;
 	if (td->remainder < count) {
@@ -1176,7 +1384,10 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
 	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
-	temp = sc->sc_hcchar[td->channel];
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+	    sc->sc_chan_state[td->channel].hcsplt);
+
+	temp = sc->sc_chan_state[td->channel].hcchar;
 	temp &= ~HCCHAR_EPDIR_IN;
 
 	/* must enable before writing data to FIFO */
@@ -1197,11 +1408,31 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
 		    sc->sc_tx_bounce_buffer, (count + 3) / 4);
 	}
 
-	/* reset statemachine */
-	td->did_complete = 0;
-	td->did_nak = 0;
+	/* store number of bytes transmitted */
 	td->tx_bytes = count;
 
+	/* clear interrupts */
+	sc->sc_chan_state[td->channel].hcint = 0;
+
+	return (1);	/* busy */
+
+send_cpkt:
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+	    sc->sc_chan_state[td->channel].hcsplt);
+
+	temp = sc->sc_chan_state[td->channel].hcchar;
+	temp &= ~HCCHAR_EPDIR_IN;
+
+	/* must enable channel before writing data to FIFO */
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
+
+	/* clear interrupts */
+	sc->sc_chan_state[td->channel].hcint = 0;
+
 	return (1);	/* busy */
 }
 
@@ -1524,23 +1755,12 @@ repeat:
 			/* non-data messages we simply skip */
 			if (temp != GRXSTSRD_STP_DATA &&
 			    temp != GRXSTSRD_OUT_DATA) {
-				if (sc->sc_flags.status_device_mode) {
-					dwc_otg_common_rx_ack(sc);
-					goto repeat;
-				} else if (temp != GRXSTSRD_OUT_COMPLETE &&
-				    temp != GRXSTSRH_HALTED) {
-					dwc_otg_common_rx_ack(sc);
-					goto repeat;
-				}
+				dwc_otg_common_rx_ack(sc);
+				goto repeat;
 			}
 
-			if (temp == GRXSTSRH_HALTED || 
-			    temp == GRXSTSRD_OUT_COMPLETE) {
-				temp = 0;
-			} else {
-				temp = GRXSTSRD_BCNT_GET(
-				    sc->sc_last_rx_status);
-			}
+			temp = GRXSTSRD_BCNT_GET(
+			    sc->sc_last_rx_status);
 			ep_no = GRXSTSRD_CHNUM_GET(
 			    sc->sc_last_rx_status);
 
@@ -1633,7 +1853,7 @@ void
 dwc_otg_interrupt(struct dwc_otg_softc *sc)
 {
 	uint32_t status;
-	uint32_t haint;
+	uint8_t x;
 
 	USB_BUS_LOCK(&sc->sc_bus);
 
@@ -1641,24 +1861,20 @@ dwc_otg_interrupt(struct dwc_otg_softc *
 	status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
 	DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status);
 
-	haint = DWC_OTG_READ_4(sc, DOTG_HAINT);
-
 	DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n",
-	    status, haint, DWC_OTG_READ_4(sc, DOTG_HFNUM));
+	    status, DWC_OTG_READ_4(sc, DOTG_HAINT),
+	    DWC_OTG_READ_4(sc, DOTG_HFNUM));
 
-	if (haint != 0) {
-		uint8_t x;
+	/* get all channel interrupts */
+	for (x = 0; x != sc->sc_host_ch_max; x++) {
+		uint32_t temp;
 
-		/* clear left-over interrupts */
-		for (x = 0; x != sc->sc_host_ch_max; x++) {
-			if (!(haint & (1 << x)))
-				continue;
-			/* check if channel is disabled */
-			if (sc->sc_hcchar[x] == 0) {
-				uint32_t temp;
-				temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
-				DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
-			}
+		temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+		if (temp != 0) {
+			DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
+			sc->sc_chan_state[x].hcint |=
+			    (temp & ~(HCINT_SOFTWARE_ONLY |
+			    HCINT_SUSPEND_ONLY | HCINT_CHHLTD));
 		}
 	}
 
@@ -1856,8 +2072,6 @@ dwc_otg_setup_standard_chain_sub(struct 
 	td->error_any = 0;
 	td->npkt = 0;
 	td->did_stall = temp->did_stall;
-	td->did_nak = 1;
-	td->did_complete = 1;
 	td->short_pkt = temp->short_pkt;
 	td->alt_next = temp->setup_alt_next;
 	td->set_toggle = 0;
@@ -2324,13 +2538,14 @@ dwc_otg_device_done(struct usb_xfer *xfe
 			DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
 			    HCCHAR_CHENA | HCCHAR_CHDIS);
 
-			sc->sc_hcchar[td->channel] = 0;
+			sc->sc_chan_state[td->channel].hcchar = 0;
 			sc->sc_active_rx_ep &= ~(1 << td->channel);
 
-			td->channel = DWC_OTG_MAX_CHANNELS;
-
 			/* release SOF's */
-			dwc_otg_release_sof(sc);
+			if (sc->sc_chan_state[td->channel].sof_requested != 0)
+				dwc_otg_release_sof(sc);
+
+			td->channel = DWC_OTG_MAX_CHANNELS;
 		}
 	}
 	/* dequeue transfer and start next transfer */
@@ -2671,8 +2886,12 @@ dwc_otg_init(struct dwc_otg_softc *sc)
 		uint8_t x;
 
 		for (x = 0; x != sc->sc_host_ch_max; x++) {
-			/* disable channel interrupts */
-			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0);
+			/* enable interrupts */
+			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+			    HCINT_STALL | HCINT_BBLERR |
+			    HCINT_XACTERR | HCINT_XFERCOMPL |
+			    HCINT_NAK | HCINT_ACK | HCINT_NYET |
+			    HCINT_CHHLTD);
 			DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
 			    HCCHAR_CHENA | HCCHAR_CHDIS);
 		}
@@ -3613,18 +3832,21 @@ dwc_otg_device_resume(struct usb_device 
 			td = xfer->td_transfer_cache;
 			if (td != NULL &&
 			    td->channel < DWC_OTG_MAX_CHANNELS) {
+				sc->sc_chan_state[td->channel].hcint &= 
+				    ~HCINT_SUSPEND_ONLY;
 
-				sc->sc_hcchar[td->channel] =
-				    (sc->sc_hcchar[td->channel] & ~HCCHAR_CHDIS) |
-				    HCCHAR_CHENA;
-
-				DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-				    sc->sc_hcchar[td->channel]);
+				if (sc->sc_chan_state[td->channel].sof_requested == 0) {
+					sc->sc_chan_state[td->channel].sof_requested = 1;
+					dwc_otg_request_sof(sc);
+				}
 			}
 		}
 	}
 
 	USB_BUS_UNLOCK(udev->bus);
+
+	/* poll all transfers again to restart resumed ones */
+	dwc_otg_do_poll(udev->bus);
 }
 
 static void
@@ -3648,12 +3870,15 @@ dwc_otg_device_suspend(struct usb_device
 			if (td != NULL &&
 			    td->channel < DWC_OTG_MAX_CHANNELS) {
 
-				sc->sc_hcchar[td->channel] =
-				    (DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)) |
-				    HCCHAR_CHDIS) & ~HCCHAR_CHENA;
-
+				sc->sc_chan_state[td->channel].hcint |= 
+				    HCINT_NAK | HCINT_CHHLTD | HCINT_SUSPEND_ONLY;
 				DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-				    sc->sc_hcchar[td->channel]);
+				    HCCHAR_CHENA | HCCHAR_CHDIS);
+
+				if (sc->sc_chan_state[td->channel].sof_requested != 0) {
+					sc->sc_chan_state[td->channel].sof_requested = 0;
+					dwc_otg_release_sof(sc);
+				}
 			}
 		}
 	}

Modified: head/sys/dev/usb/controller/dwc_otg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.h	Fri Sep 14 05:24:06 2012	(r240481)
+++ head/sys/dev/usb/controller/dwc_otg.h	Fri Sep 14 07:52:57 2012	(r240482)
@@ -68,8 +68,6 @@ struct dwc_otg_td {
 	uint8_t	did_stall:1;
 	uint8_t toggle:1;
 	uint8_t set_toggle:1;
-	uint8_t did_nak:1;
-	uint8_t did_complete:1;
 	uint8_t got_short:1;
 };
 
@@ -128,6 +126,18 @@ struct dwc_otg_profile {
 	uint16_t max_buffer;
 };
 
+struct dwc_otg_chan_state {
+	uint32_t hcchar;
+	uint32_t hcint;
+	uint32_t hcsplt;
+	uint8_t state;
+#define	DWC_CHAN_ST_START 0
+#define	DWC_CHAN_ST_WAIT_ANE 1
+#define	DWC_CHAN_ST_WAIT_S_ANE 2
+#define	DWC_CHAN_ST_WAIT_C_ANE 3
+	uint8_t sof_requested;
+};
+
 struct dwc_otg_softc {
 	struct usb_bus sc_bus;
 	union dwc_otg_hub_temp sc_hub_temp;
@@ -149,7 +159,7 @@ struct dwc_otg_softc {
 	uint32_t sc_last_rx_status;
 	uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS];
 	uint32_t sc_in_ctl[DWC_OTG_MAX_ENDPOINTS];
-	uint32_t sc_hcchar[DWC_OTG_MAX_CHANNELS];
+	struct dwc_otg_chan_state sc_chan_state[DWC_OTG_MAX_CHANNELS];
 	uint32_t sc_sof_refs;
 	uint32_t sc_sof_val;
 	uint32_t sc_hprt_val;

Modified: head/sys/dev/usb/controller/dwc_otgreg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otgreg.h	Fri Sep 14 05:24:06 2012	(r240481)
+++ head/sys/dev/usb/controller/dwc_otgreg.h	Fri Sep 14 07:52:57 2012	(r240482)
@@ -541,6 +541,8 @@
 #define	HCSPLT_PRTADDR_SHIFT		0
 #define	HCSPLT_PRTADDR_MASK		0x0000007f
 
+#define	HCINT_SUSPEND_ONLY		(1<<21)	/* BSD only */
+#define	HCINT_SOFTWARE_ONLY		(1<<20)	/* BSD only */
 #define	HCINT_DATATGLERR		(1<<10)
 #define	HCINT_FRMOVRUN			(1<<9)
 #define	HCINT_BBLERR			(1<<8)



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