svn commit: r287271 - stable/10/sys/dev/usb/controller

Hans Petter Selasky hselasky at FreeBSD.org
Sat Aug 29 06:07:56 UTC 2015


Author: hselasky
Date: Sat Aug 29 06:07:55 2015
New Revision: 287271
URL: https://svnweb.freebsd.org/changeset/base/287271

Log:
  MFC r283067, r286118, r285638, r285935, r286778, r286780 and r286802:
  - Make the FIFO configuration a bit more flexible for the DWC OTG in
  device side mode.
  - Limit the number of times we loop inside the DWC OTG poll handler to
  avoid starving other fast interrupts. Fix a comment while at it.
  - Optimise the DWC OTG host mode driver's transmit path
  - Optimise the DWC OTG host mode driver's receive path
  - Minor code refactor to avoid duplicating code.
  - Handle NYET high speed tokens and predict NAK'ing is up next.
  - Fixes for HIGH speed ISOCHRONOUS traffic.

Modified:
  stable/10/sys/dev/usb/controller/dwc_otg.c
  stable/10/sys/dev/usb/controller/dwc_otg.h
  stable/10/sys/dev/usb/controller/dwc_otgreg.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	Sat Aug 29 04:33:31 2015	(r287270)
+++ stable/10/sys/dev/usb/controller/dwc_otg.c	Sat Aug 29 06:07:55 2015	(r287271)
@@ -1,6 +1,7 @@
 /* $FreeBSD$ */
 /*-
- * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2015 Daisuke Aoyama. All rights reserved.
+ * Copyright (c) 2012-2015 Hans Petter Selasky. All rights reserved.
  * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -152,7 +153,6 @@ static void dwc_otg_do_poll(struct usb_b
 static void dwc_otg_standard_done(struct usb_xfer *);
 static void dwc_otg_root_intr(struct dwc_otg_softc *);
 static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *);
-static void dwc_otg_host_channel_disable(struct dwc_otg_softc *, uint8_t);
 
 /*
  * Here is a configuration that the chip supports.
@@ -225,7 +225,7 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 	/* split equally for IN and OUT */
 	fifo_size /= 2;
 
-	/* align to 4 bytes boundary */
+	/* Align to 4 bytes boundary (refer to PGM) */
 	fifo_size &= ~3;
 
 	/* set global receive FIFO size */
@@ -238,13 +238,6 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		return (EINVAL);
 	}
 
-	/* disable any leftover host channels */
-	for (x = 0; x != sc->sc_host_ch_max; x++) {
-		if (sc->sc_chan_state[x].wait_sof == 0)
-			continue;
-		dwc_otg_host_channel_disable(sc, x);
-	}
-
 	if (mode == DWC_MODE_HOST) {
 
 		/* reset active endpoints */
@@ -253,6 +246,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		/* split equally for periodic and non-periodic */
 		fifo_size /= 2;
 
+		DPRINTF("PTX/NPTX FIFO=%u\n", fifo_size);
+
 		/* align to 4 bytes boundary */
 		fifo_size &= ~3;
 
@@ -263,7 +258,7 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		tx_start += fifo_size;
 
 		for (x = 0; x != sc->sc_host_ch_max; x++) {
-			/* disable all host interrupts */
+			/* enable all host interrupts */
 			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
 			    HCINT_DEFAULT_MASK);
 		}
@@ -275,13 +270,6 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		/* reset host channel state */
 		memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
 
-		/* reset FIFO TX levels */
-		sc->sc_tx_cur_p_level = 0;
-		sc->sc_tx_cur_np_level = 0;
-
-		/* store maximum periodic and non-periodic FIFO TX size */
-		sc->sc_tx_max_size = fifo_size;
-
 		/* enable all host channel interrupts */
 		DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
 		    (1U << sc->sc_host_ch_max) - 1U);
@@ -314,32 +302,29 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		if (x < sc->sc_dev_in_ep_max) {
 			uint32_t limit;
 
-			limit = (x == 1) ? DWC_OTG_MAX_TXN :
-			    (DWC_OTG_MAX_TXN / 2);
+			limit = (x == 1) ? MIN(DWC_OTG_TX_MAX_FIFO_SIZE,
+			    DWC_OTG_MAX_TXN) : MIN(DWC_OTG_MAX_TXN / 2,
+			    DWC_OTG_TX_MAX_FIFO_SIZE);
 
-			if (fifo_size >= limit) {
-				DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x),
-				    ((limit / 4) << 16) |
-				    (tx_start / 4));
-				tx_start += limit;
-				fifo_size -= limit;
-				pf->usb.max_in_frame_size = 0x200;
-				pf->usb.support_in = 1;
+			/* see if there is enough FIFO space */
+			if (limit <= fifo_size) {
 				pf->max_buffer = limit;
-
-			} else if (fifo_size >= 0x80) {
-				DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x),
-				    ((0x80 / 4) << 16) | (tx_start / 4));
-				tx_start += 0x80;
-				fifo_size -= 0x80;
-				pf->usb.max_in_frame_size = 0x40;
 				pf->usb.support_in = 1;
-
 			} else {
-				pf->usb.is_simplex = 1;
-				DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x),
-				    (0x0 << 16) | (tx_start / 4));
+				limit = MIN(DWC_OTG_TX_MAX_FIFO_SIZE, 0x40);
+				if (limit <= fifo_size) {
+					pf->usb.support_in = 1;
+				} else {
+					pf->usb.is_simplex = 1;
+					limit = 0;
+				}
 			}
+			/* set FIFO size */
+			DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x),
+			    ((limit / 4) << 16) | (tx_start / 4));
+			tx_start += limit;
+			fifo_size -= limit;
+			pf->usb.max_in_frame_size = limit;
 		} else {
 			pf->usb.is_simplex = 1;
 		}
@@ -362,15 +347,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 		/* reset active endpoints */
 		sc->sc_active_rx_ep = 0;
 
-		/* reset periodic and non-periodic FIFO TX size */
-		sc->sc_tx_max_size = fifo_size;
-
 		/* reset host channel state */
 		memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
-
-		/* reset FIFO TX levels */
-		sc->sc_tx_cur_p_level = 0;
-		sc->sc_tx_cur_np_level = 0;
 	}
 	return (0);
 }
@@ -476,8 +454,12 @@ static void
 dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc)
 {
 	/* In device mode we don't use the SOF interrupt */
-	if (sc->sc_flags.status_device_mode != 0 ||
-	    (sc->sc_irq_mask & GINTMSK_SOFMSK) != 0)
+	if (sc->sc_flags.status_device_mode != 0)
+		return;
+	/* Ensure the SOF interrupt is not disabled */
+	sc->sc_needsof = 1;
+	/* Check if the SOF interrupt is already enabled */
+	if ((sc->sc_irq_mask & GINTMSK_SOFMSK) != 0)
 		return;
 	sc->sc_irq_mask |= GINTMSK_SOFMSK;
 	DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
@@ -616,13 +598,48 @@ 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 is_out)
+dwc_otg_host_check_tx_fifo_empty(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+	uint32_t temp;
+
+	temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
+
+	if (td->ep_type == UE_ISOCHRONOUS) {
+		/*
+		 * NOTE: USB INTERRUPT transactions are executed like
+		 * USB CONTROL transactions! See the setup standard
+		 * chain function for more information.
+		 */
+		if (!(temp & GINTSTS_PTXFEMP)) {
+			DPRINTF("Periodic TX FIFO is not empty\n");
+			if (!(sc->sc_irq_mask & GINTMSK_PTXFEMPMSK)) {
+				sc->sc_irq_mask |= GINTMSK_PTXFEMPMSK;
+				DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+			}
+			return (1);	/* busy */
+		}
+	} else {
+		if (!(temp & GINTSTS_NPTXFEMP)) {
+			DPRINTF("Non-periodic TX FIFO is not empty\n");
+			if (!(sc->sc_irq_mask & GINTMSK_NPTXFEMPMSK)) {
+				sc->sc_irq_mask |= GINTMSK_NPTXFEMPMSK;
+				DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+			}
+			return (1);	/* busy */
+		}
+	}
+	return (0);	/* ready for transmit */
+}
+
+static uint8_t
+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;
+	uint8_t y;
+	uint8_t z;
 
-	if (td->channel < DWC_OTG_MAX_CHANNELS)
+	if (td->channel[0] < DWC_OTG_MAX_CHANNELS)
 		return (0);		/* already allocated */
 
 	/* check if device is suspended */
@@ -631,45 +648,41 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 
 	/* compute needed TX FIFO size */
 	if (is_out != 0) {
-		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)
-				tx_p_size = HCSPLT_XACTLEN_BURST;
-			if ((sc->sc_tx_cur_p_level + tx_p_size) > sc->sc_tx_max_size) {
-				DPRINTF("Too little FIFO space\n");
-				return (1);	/* too little FIFO */
-			}
-		} else {
-			tx_p_size = 0;
-			tx_np_size = td->max_packet_size;
-			if (td->hcsplt != 0 && tx_np_size > HCSPLT_XACTLEN_BURST)
-				tx_np_size = HCSPLT_XACTLEN_BURST;
-			if ((sc->sc_tx_cur_np_level + tx_np_size) > sc->sc_tx_max_size) {
-				DPRINTF("Too little FIFO space\n");
-				return (1);	/* too little FIFO */
-			}
-		}
-	} else {
-		/* not a TX transaction */
-		tx_p_size = 0;
-		tx_np_size = 0;
+		if (dwc_otg_host_check_tx_fifo_empty(sc, td) != 0)
+			return (1);	/* busy - cannot transfer data */
 	}
-
-	for (x = 0; x != sc->sc_host_ch_max; x++) {
+	z = td->max_packet_count;
+	for (x = y = 0; x != sc->sc_host_ch_max; x++) {
+		/* check if channel is allocated */
 		if (sc->sc_chan_state[x].allocated != 0)
 			continue;
 		/* check if channel is still enabled */
-		if (sc->sc_chan_state[x].wait_sof != 0)
+		if (sc->sc_chan_state[x].wait_halted != 0)
 			continue;
+		/* store channel number */
+		td->channel[y++] = x;
+		/* check if we got all channels */
+		if (y == z)
+			break;
+	}
+	if (y != z) {
+		/* reset channel variable */
+		td->channel[0] = DWC_OTG_MAX_CHANNELS;
+		td->channel[1] = DWC_OTG_MAX_CHANNELS;
+		td->channel[2] = DWC_OTG_MAX_CHANNELS;
+		/* wait a bit */
+		dwc_otg_enable_sof_irq(sc);
+		return (1);	/* busy - not enough channels */
+	}
+
+	for (y = 0; y != z; y++) {
+		x = td->channel[y];
 
+		/* set allocated */
 		sc->sc_chan_state[x].allocated = 1;
-		sc->sc_chan_state[x].tx_p_size = tx_p_size;
-		sc->sc_chan_state[x].tx_np_size = tx_np_size;
 
-		/* keep track of used TX FIFO, if any */
-		sc->sc_tx_cur_p_level += tx_p_size;
-		sc->sc_tx_cur_np_level += tx_np_size;
+		/* set wait halted */
+		sc->sc_chan_state[x].wait_halted = 1;
 
 		/* clear interrupts */
 		dwc_otg_clear_hcint(sc, x);
@@ -679,45 +692,29 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 
 		/* set active channel */
 		sc->sc_active_rx_ep |= (1 << x);
-
-		/* set channel */
-		td->channel = x;
-
-		return (0);	/* allocated */
 	}
-	/* wait a bit */
-	dwc_otg_enable_sof_irq(sc);
-	return (1);	/* busy */
+	return (0);	/* allocated */
 }
 
 static void
-dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+dwc_otg_host_channel_free_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t index)
 {
+	uint32_t hcchar;
 	uint8_t x;
 
-	if (td->channel >= DWC_OTG_MAX_CHANNELS)
+	if (td->channel[index] >= DWC_OTG_MAX_CHANNELS)
 		return;		/* already freed */
 
 	/* free channel */
-	x = td->channel;
-	td->channel = DWC_OTG_MAX_CHANNELS;
+	x = td->channel[index];
+	td->channel[index] = DWC_OTG_MAX_CHANNELS;
 
 	DPRINTF("CH=%d\n", x);
 
 	/*
 	 * We need to let programmed host channels run till complete
-	 * else the host channel will stop functioning. Assume that
-	 * after a fixed given amount of time the host channel is no
-	 * longer doing any USB traffic:
+	 * else the host channel will stop functioning.
 	 */
-	if (td->ep_type == UE_ISOCHRONOUS) {
-		/* double buffered */
-		sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX;
-	} else {
-		/* single buffered */
-		sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MIN;
-	}
-
 	sc->sc_chan_state[x].allocated = 0;
 
 	/* ack any pending messages */
@@ -728,17 +725,43 @@ dwc_otg_host_channel_free(struct dwc_otg
 
 	/* clear active channel */
 	sc->sc_active_rx_ep &= ~(1 << x);
+
+	/* check if already halted */
+	if (sc->sc_chan_state[x].wait_halted == 0)
+		return;
+
+	/* disable host channel */
+	hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
+	if (hcchar & HCCHAR_CHENA) {
+		DPRINTF("Halting channel %d\n", x);
+		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
+		    hcchar | HCCHAR_CHDIS);
+		/* don't write HCCHAR until the channel is halted */
+	} else {
+		sc->sc_chan_state[x].wait_halted = 0;
+	}
+}
+
+static void
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+	uint8_t x;
+	for (x = 0; x != td->max_packet_count; x++)
+		dwc_otg_host_channel_free_sub(sc, td, x);
 }
 
 static void
 dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
+	uint8_t x;
 	/* 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);
-		}
+	if (sc->sc_last_rx_status == 0)
+		return;
+	for (x = 0; x != td->max_packet_count; x++) {
+		if (td->channel[x] >= DWC_OTG_MAX_CHANNELS ||
+		    td->channel[x] != GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status))
+			continue;
+		dwc_otg_common_rx_ack(sc);
 	}
 }
 
@@ -752,13 +775,13 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
 
 	dwc_otg_host_dump_rx(sc, td);
 
-	if (td->channel < DWC_OTG_MAX_CHANNELS) {
-		hcint = sc->sc_chan_state[td->channel].hcint;
+	if (td->channel[0] < DWC_OTG_MAX_CHANNELS) {
+		hcint = sc->sc_chan_state[td->channel[0]].hcint;
 
 		DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-		    td->channel, td->state, hcint,
-		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+		    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])));
 	} else {
 		hcint = 0;
 		goto check_state;
@@ -768,12 +791,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);
+		DPRINTF("CH=%d STALL\n", td->channel[0]);
 		td->error_stall = 1;
 		td->error_any = 1;
 		goto complete;
 	} else if (hcint & HCINT_ERRORS) {
-		DPRINTF("CH=%d ERROR\n", td->channel);
+		DPRINTF("CH=%d ERROR\n", td->channel[0]);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
 			td->error_any = 1;
@@ -794,7 +817,7 @@ check_state:
 
 	case DWC_CHAN_ST_WAIT_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto send_pkt;
 		} else if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -808,7 +831,7 @@ check_state:
 
 	case DWC_CHAN_ST_WAIT_S_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto send_pkt;
 		} else if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -820,7 +843,7 @@ check_state:
 		if (hcint & HCINT_NYET) {
 			goto send_cpkt;
 		} else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto send_pkt;
 		} else if (hcint & HCINT_ACK) {
@@ -878,23 +901,23 @@ send_pkt:
 
 	usbd_copy_out(td->pc, 0, &req, sizeof(req));
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
 	    (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
 	    (1 << HCTSIZ_PKTCNT_SHIFT) |
 	    (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), 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), hcchar);
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
 
 	/* transfer data into FIFO */
 	bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
-	    DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
+	    DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4);
 
 	/* wait until next slot before trying complete split */
 	td->tt_complete_slot = sc->sc_last_frame_num + 1;
@@ -931,17 +954,17 @@ send_cpkt:
 	td->hcsplt |= HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_C_ANE;
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
 	    (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), 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), hcchar);
+	DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
 
 busy:
 	return (1);	/* busy */
@@ -1075,41 +1098,51 @@ dwc_otg_host_rate_check_interrupt(struct
 static uint8_t
 dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
+	uint8_t frame_num = (uint8_t)sc->sc_last_frame_num;
+
 	if (td->ep_type == UE_ISOCHRONOUS) {
 		/* non TT isochronous traffic */
-		if ((td->tmr_val != 0) ||
-		    (sc->sc_last_frame_num & (td->tmr_res - 1))) {
+		if (frame_num & (td->tmr_res - 1))
 			goto busy;
-		}
-		td->tmr_val = 1;	/* executed */
+		if ((frame_num ^ td->tmr_val) & td->tmr_res)
+			goto busy;
+		td->tmr_val = td->tmr_res + sc->sc_last_frame_num;
 		td->toggle = 0;
-
+		return (0);
 	} else if (td->ep_type == UE_INTERRUPT) {
 		if (!td->tt_scheduled)
 			goto busy;
 		td->tt_scheduled = 0;
-	} else if (td->did_nak >= DWC_OTG_NAK_MAX) {
-		goto busy;
+		return (0);
+	} else if (td->did_nak != 0) {
+		/* check if we should pause sending queries for 125us */
+		if (td->tmr_res == frame_num) {
+			/* wait a bit */
+			dwc_otg_enable_sof_irq(sc);
+			goto busy;
+		}
 	} else if (td->set_toggle) {
 		td->set_toggle = 0;
 		td->toggle = 1;
 	}
+	/* query for data one more time */
+	td->tmr_res = frame_num;
+	td->did_nak = 0;
 	return (0);
 busy:
 	return (1);
 }
 
 static uint8_t
-dwc_otg_host_data_rx_sub(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,
+    uint8_t channel)
 {
 	uint32_t count;
-	uint8_t channel;
 
 	/* check endpoint status */
 	if (sc->sc_last_rx_status == 0)
 		goto busy;
 
-	channel = td->channel;
 	if (channel >= DWC_OTG_MAX_CHANNELS)
 		goto busy;
 
@@ -1134,21 +1167,22 @@ dwc_otg_host_data_rx_sub(struct dwc_otg_
 		/* get the packet byte count */
 		count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
 
-		/* check for isochronous transfer or high-speed bandwidth endpoint */
-		if (td->ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) {
-			if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != GRXSTSRD_DPID_DATA0) {
+		/* check for ISOCHRONOUS endpoint */
+		if (td->ep_type == UE_ISOCHRONOUS) {
+			if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+			    GRXSTSRD_DPID_DATA0) {
+				/* more data to be received */
 				td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
 			} else {
+				/* all data received */
 				td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
-
 				/* verify the packet byte count */
-				if (count < td->max_packet_size) {
+				if (count != td->remainder) {
 					/* 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) {
@@ -1200,15 +1234,17 @@ complete:
 static uint8_t
 dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
-	uint32_t hcint;
+	uint32_t hcint = 0;
 	uint32_t hcchar;
 	uint8_t delta;
 	uint8_t channel;
+	uint8_t x;
 
-	channel = td->channel;
-
-	if (channel < DWC_OTG_MAX_CHANNELS) {
-		hcint = sc->sc_chan_state[channel].hcint;
+	for (x = 0; x != td->max_packet_count; x++) {
+		channel = td->channel[x];
+		if (channel >= DWC_OTG_MAX_CHANNELS)
+			continue;
+		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,
@@ -1236,19 +1272,17 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 		}
 
 		/* check channels for data, if any */
-		if (dwc_otg_host_data_rx_sub(sc, td))
+		if (dwc_otg_host_data_rx_sub(sc, td, channel))
 			goto complete;
 
 		/* refresh interrupt status */
-		hcint = sc->sc_chan_state[channel].hcint;
+		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) {
@@ -1275,8 +1309,10 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 					td->toggle ^= 1;
 					goto receive_pkt;
 				}
+			} else if (td->ep_type == UE_ISOCHRONOUS) {
+				goto complete;
 			}
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			if (td->hcsplt != 0)
 				goto receive_spkt;
@@ -1298,12 +1334,12 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 
 			if (td->ep_type == UE_ISOCHRONOUS) {
 				/* check if we are complete */
-				if ((td->remainder == 0) ||
-				    (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) {
+				if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) {
 					goto complete;
+				} else {
+					/* get more packets */
+					goto busy;
 				}
-				/* get another packet */
-				goto receive_pkt;
 			} else {
 				/* check if we are complete */
 				if ((td->remainder == 0) || (td->got_short != 0)) {
@@ -1331,7 +1367,7 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 		 * case of interrupt and isochronous transfers:
 		 */ 
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto receive_spkt;
 		} else if (hcint & HCINT_NYET) {
@@ -1371,8 +1407,7 @@ receive_pkt:
 		}
 		/* complete split */
 		td->hcsplt |= HCSPLT_COMPSPLT;
-	} else if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN &&
-	    dwc_otg_host_rate_check(sc, td)) {
+	} else if (dwc_otg_host_rate_check(sc, td)) {
 		td->state = DWC_CHAN_ST_WAIT_C_PKT;
 		goto busy;
 	}
@@ -1383,8 +1418,6 @@ receive_pkt:
 		goto busy;
 	}
 
-	channel = td->channel;
-
 	/* set toggle, if any */
 	if (td->set_toggle) {
 		td->set_toggle = 0;
@@ -1393,27 +1426,31 @@ receive_pkt:
 
 	td->state = DWC_CHAN_ST_WAIT_ANE;
 
-	/* receive one packet */
-	DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
-	    (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
-	    (1 << HCTSIZ_PKTCNT_SHIFT) |
-	    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
-	    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+	for (x = 0; x != td->max_packet_count; x++) {
+	  	channel = td->channel[x];
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+		/* receive one packet */
+		DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+		    (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
+		    (1 << HCTSIZ_PKTCNT_SHIFT) |
+		    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+		    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
-	hcchar = td->hcchar;
-	hcchar |= HCCHAR_EPDIR_IN;
+		DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
 
-	/* receive complete split ASAP */
-	if ((sc->sc_last_frame_num & 1) != 0)
-		hcchar |= HCCHAR_ODDFRM;
-	else
-		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(channel), hcchar);
+		/* receive complete split ASAP */
+		if ((sc->sc_last_frame_num & 1) != 0 &&
+		    td->ep_type == UE_ISOCHRONOUS)
+			hcchar |= HCCHAR_ODDFRM;
+		else
+			hcchar &= ~HCCHAR_ODDFRM;
 
+		/* 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;
 
@@ -1442,7 +1479,7 @@ receive_spkt:
 		goto busy;
 	}
 
-	channel = td->channel;
+	channel = td->channel[0];
 
 	td->hcsplt &= ~HCSPLT_COMPSPLT;
 	td->state = DWC_CHAN_ST_WAIT_S_ANE;
@@ -1454,7 +1491,8 @@ receive_spkt:
 	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
 
 	/* send after next SOF event */
-	if ((sc->sc_last_frame_num & 1) == 0)
+	if ((sc->sc_last_frame_num & 1) == 0 &&
+	    td->ep_type == UE_ISOCHRONOUS)
 		td->hcchar |= HCCHAR_ODDFRM;
 	else
 		td->hcchar &= ~HCCHAR_ODDFRM;
@@ -1609,10 +1647,12 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 	uint32_t hcchar;
 	uint8_t delta;
 	uint8_t channel;
+	uint8_t x;
 
 	dwc_otg_host_dump_rx(sc, td);
 
-	channel = td->channel;
+	/* check that last channel is complete */
+	channel = td->channel[td->npkt];
 
 	if (channel < DWC_OTG_MAX_CHANNELS) {
 		hcint = sc->sc_chan_state[channel].hcint;
@@ -1655,14 +1695,18 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 
 	case DWC_CHAN_ST_WAIT_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto send_pkt;
 		} else if (hcint & (HCINT_ACK | HCINT_NYET)) {
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle ^= 1;
-			td->did_nak = 0;
+			/* check if next response will be a NAK */
+			if (hcint & HCINT_NYET)
+				td->did_nak = 1;
+			else
+				td->did_nak = 0;
 			td->tt_scheduled = 0;
 
 			/* check remainder */
@@ -1681,7 +1725,7 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 
 	case DWC_CHAN_ST_WAIT_S_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto send_pkt;
 		} else if (hcint & (HCINT_ACK | HCINT_NYET)) {
@@ -1694,7 +1738,7 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 		if (hcint & HCINT_NYET) {
 			goto send_cpkt;
 		} else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			td->did_nak++;
+			td->did_nak = 1;
 			td->tt_scheduled = 0;
 			goto send_pkt;
 		} else if (hcint & HCINT_ACK) {
@@ -1719,33 +1763,13 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
 		goto send_cpkt;
 
 	case DWC_CHAN_ST_TX_WAIT_ISOC:
-
-		/* Check if isochronous OUT traffic is complete */
+		/* Check if ISOCHRONOUS OUT traffic is complete */
 		if ((hcint & HCINT_HCH_DONE_MASK) == 0)
 			break;
 
 		td->offset += td->tx_bytes;
 		td->remainder -= td->tx_bytes;
-
-		if (td->hcsplt != 0 || td->remainder == 0)
-			goto complete;
-
-		/* check for next packet */
-		if (td->max_packet_count > 1)
-			td->tt_xactpos++;
-
-		/* free existing channel, if any */
-		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, 1))
-			break;
-		channel = td->channel;
-		goto send_isoc_pkt;
+		goto complete;
 	default:
 		break;
 	}
@@ -1779,8 +1803,6 @@ send_pkt:
 		goto busy;
 	}
 
-	channel = td->channel;
-
 	/* set toggle, if any */
 	if (td->set_toggle) {
 		td->set_toggle = 0;
@@ -1788,8 +1810,7 @@ send_pkt:
 	}
 
 	if (td->ep_type == UE_ISOCHRONOUS) {
-send_isoc_pkt:
-		/* Isochronous OUT transfers don't have any ACKs */
+		/* ISOCHRONOUS OUT transfers don't have any ACKs */
 		td->state = DWC_CHAN_ST_TX_WAIT_ISOC;
 		td->hcsplt &= ~HCSPLT_COMPSPLT;
 		if (td->hcsplt != 0) {
@@ -1803,122 +1824,110 @@ send_isoc_pkt:
 			/* Update transaction position */
 			td->hcsplt &= ~HCSPLT_XACTPOS_MASK;
 			td->hcsplt |= (HCSPLT_XACTPOS_ALL << 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;
+	}
+
+	td->tx_bytes = 0;
+	
+	for (x = 0; x != td->max_packet_count; x++) {
+		uint32_t rem_bytes;
+
+		channel = td->channel[x];
 
 		/* send one packet at a time */
 		count = td->max_packet_size;
-		if (td->remainder < count) {
+		rem_bytes = td->remainder - td->tx_bytes;
+		if (rem_bytes < count) {
 			/* we have a short packet */
 			td->short_pkt = 1;
-			count = td->remainder;
+			count = rem_bytes;
 		}
-	}
-
-	/* 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;
-
-			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) {
+		if (count == rem_bytes) {
+			/* last packet */
+			switch (x) {
+			case 0:
 				DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
 				    (count << HCTSIZ_XFERSIZE_SHIFT) |
 				    (1 << HCTSIZ_PKTCNT_SHIFT) |
-				    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
-			} else if (td->npkt == 2) {
+				    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+				    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+				break;
+			case 1:
 				DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
 				    (count << HCTSIZ_XFERSIZE_SHIFT) |
 				    (1 << HCTSIZ_PKTCNT_SHIFT) |
 				    (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT));
-			} else {
+				break;
+			default:
 				DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
 				    (count << HCTSIZ_XFERSIZE_SHIFT) |
 				    (1 << HCTSIZ_PKTCNT_SHIFT) |
 				    (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT));
+				break;
 			}
-			td->npkt = 0;
-		} else {
+		} else if (td->ep_type == UE_ISOCHRONOUS &&
+			   td->max_packet_count > 1) {
+			/* ISOCHRONOUS multi packet */
 			DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
 			    (count << HCTSIZ_XFERSIZE_SHIFT) |
 			    (1 << HCTSIZ_PKTCNT_SHIFT) |
 			    (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT));
+		} else {
+			/* TODO: HCTSIZ_DOPNG */
+			/* standard BULK/INTERRUPT/CONTROL packet */
+			DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+			    (count << HCTSIZ_XFERSIZE_SHIFT) |
+			    (1 << HCTSIZ_PKTCNT_SHIFT) |
+			    (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+			    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 		}
-	} else {
-		/* TODO: HCTSIZ_DOPNG */
 
-		DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(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(channel), td->hcsplt);
 
-	DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+		hcchar = td->hcchar;
+		hcchar &= ~HCCHAR_EPDIR_IN;
 
-	hcchar = td->hcchar;
-	hcchar &= ~HCCHAR_EPDIR_IN;
+		/* send after next SOF event */
+		if ((sc->sc_last_frame_num & 1) == 0 &&
+		    td->ep_type == UE_ISOCHRONOUS)
+			hcchar |= HCCHAR_ODDFRM;
+		else
+			hcchar &= ~HCCHAR_ODDFRM;
 
-	/* send after next SOF event */
-	if ((sc->sc_last_frame_num & 1) == 0)

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


More information about the svn-src-all mailing list