Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 23 May 2014 06:20:26 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r266575 - in stable/10/sys/dev/usb: . controller
Message-ID:  <201405230620.s4N6KQa3047214@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Fri May 23 06:20:25 2014
New Revision: 266575
URL: http://svnweb.freebsd.org/changeset/base/266575

Log:
  MFC r265358, r265427, r265777, r265783,
   r265806, r265872, r266012 and r266394:
  
  - Multiple DWC OTG host mode related fixes, improvements and optimisations.
  - Add full support for ISOCHRONOUS transfers to the DWC OTG driver.
  - Use the interrupt filter to handle basic USB FIFO interrupts.
  - Fixed unbalanced unlock in case of "dwc_otg_init_fifo()" failure.
  - Add common spinlock to the USB bus structure.

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_otg_atmelarm.c
  stable/10/sys/dev/usb/controller/dwc_otg_fdt.c
  stable/10/sys/dev/usb/controller/dwc_otgreg.h
  stable/10/sys/dev/usb/controller/usb_controller.c
  stable/10/sys/dev/usb/usb_bus.h
  stable/10/sys/dev/usb/usb_core.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	Fri May 23 05:35:43 2014	(r266574)
+++ stable/10/sys/dev/usb/controller/dwc_otg.c	Fri May 23 06:20:25 2014	(r266575)
@@ -89,19 +89,22 @@
    ((struct dwc_otg_softc *)(((uint8_t *)(bus)) - \
     ((uint8_t *)&(((struct dwc_otg_softc *)0)->sc_bus))))
 
-#define	DWC_OTG_PC2SC(pc) \
-   DWC_OTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus)
+#define	DWC_OTG_PC2UDEV(pc) \
+   (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev)
 
 #define	DWC_OTG_MSK_GINT_ENABLED	\
-   (GINTSTS_ENUMDONE |			\
-   GINTSTS_USBRST |			\
-   GINTSTS_USBSUSP |			\
-   GINTSTS_IEPINT |			\
-   GINTSTS_RXFLVL |			\
-   GINTSTS_SESSREQINT |			\
+   (GINTMSK_ENUMDONEMSK |		\
+   GINTMSK_USBRSTMSK |			\
+   GINTMSK_USBSUSPMSK |			\
+   GINTMSK_IEPINTMSK |			\
+   GINTMSK_SESSREQINTMSK |		\
    GINTMSK_OTGINTMSK |			\
-   GINTMSK_HCHINTMSK |			\
-   GINTSTS_PRTINT)
+   GINTMSK_PRTINTMSK)
+
+#define	DWC_OTG_MSK_GINT_THREAD_IRQ				\
+   (GINTSTS_USBRST | GINTSTS_ENUMDONE | GINTSTS_PRTINT |	\
+   GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTMSK_OTGINTMSK |	\
+   GINTSTS_SESSREQINT)
 
 static int dwc_otg_use_hsic;
 
@@ -138,8 +141,9 @@ static dwc_otg_cmd_t dwc_otg_host_data_r
 static void dwc_otg_device_done(struct usb_xfer *, usb_error_t);
 static void dwc_otg_do_poll(struct usb_bus *);
 static void dwc_otg_standard_done(struct usb_xfer *);
-static void dwc_otg_root_intr(struct dwc_otg_softc *sc);
-static void dwc_otg_interrupt_poll(struct dwc_otg_softc *sc);
+static void dwc_otg_root_intr(struct dwc_otg_softc *);
+static void dwc_otg_interrupt_poll(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.
@@ -179,59 +183,81 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 
 	fifo_size = sc->sc_fifo_size;
 
-	fifo_regs = 4 * (sc->sc_dev_ep_max + sc->sc_dev_in_ep_max);
+	/*
+	 * NOTE: Reserved fixed size area at end of RAM, which must
+	 * not be allocated to the FIFOs:
+	 */
+	fifo_regs = 4 * 16;
 
-	if (fifo_size >= fifo_regs)
-		fifo_size -= fifo_regs;
-	else
-		fifo_size = 0;
+	if (fifo_size < fifo_regs) {
+		DPRINTF("Too little FIFO\n");
+		return (EINVAL);
+	}
+
+	/* subtract FIFO regs from total once */
+	fifo_size -= fifo_regs;
 
 	/* split equally for IN and OUT */
 	fifo_size /= 2;
 
-	DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4);
-
-	/* align to 4-bytes */
+	/* align to 4 bytes boundary */
 	fifo_size &= ~3;
 
+	/* set global receive FIFO size */
+	DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4);
+
 	tx_start = fifo_size;
 
-	if (fifo_size < 0x40) {
+	if (fifo_size < 64) {
 		DPRINTFN(-1, "Not enough data space for EP0 FIFO.\n");
-		USB_BUS_UNLOCK(&sc->sc_bus);
 		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 */
 		sc->sc_active_rx_ep = 0;
 
+		/* split equally for periodic and non-periodic */
 		fifo_size /= 2;
 
+		/* align to 4 bytes boundary */
+		fifo_size &= ~3;
+
 		DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
 		    ((fifo_size / 4) << 16) |
 		    (tx_start / 4));
 
 		tx_start += fifo_size;
 
+		for (x = 0; x != sc->sc_host_ch_max; x++) {
+			/* disable all host interrupts */
+			DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0);
+		}
+
 		DWC_OTG_WRITE_4(sc, DOTG_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);
-		}
+		/* 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 host channel interrupts */
-		DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
-		    (1U << sc->sc_host_ch_max) - 1U);
+		/* disable all host channel interrupts */
+		DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0);
 	}
 
 	if (mode == DWC_MODE_DEVICE) {
@@ -309,11 +335,58 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 	} else {
 		/* 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);
 }
 
 static void
+dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc)
+{
+
+  /*
+   * Disabled until further. Assuming that the register is already
+   * programmed correctly by the boot loader.
+   */
+#if 0
+	uint32_t temp;
+
+	/* setup HOST frame interval register, based on existing value */
+	temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK;
+	if (temp >= 10000)
+		temp /= 1000;
+	else
+		temp /= 125;
+
+	/* figure out nearest X-tal value */
+	if (temp >= 54)
+		temp = 60;	/* MHz */
+	else if (temp >= 39)
+		temp = 48;	/* MHz */
+	else
+		temp = 30;	/* MHz */
+
+	if (sc->sc_flags.status_high_speed)
+		temp *= 125;
+	else
+		temp *= 1000;
+
+	DPRINTF("HFIR=0x%08x\n", temp);
+
+	DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp);
+#endif
+}
+
+static void
 dwc_otg_clocks_on(struct dwc_otg_softc *sc)
 {
 	if (sc->sc_flags.clocks_off &&
@@ -376,9 +449,11 @@ 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)
+	/* 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)
 		return;
-	sc->sc_irq_mask |= GINTSTS_SOF;
+	sc->sc_irq_mask |= GINTMSK_SOFMSK;
 	DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 }
 
@@ -395,8 +470,8 @@ dwc_otg_resume_irq(struct dwc_otg_softc 
 			 * Disable resume interrupt and enable suspend
 			 * interrupt:
 			 */
-			sc->sc_irq_mask &= ~GINTSTS_WKUPINT;
-			sc->sc_irq_mask |= GINTSTS_USBSUSP;
+			sc->sc_irq_mask &= ~GINTMSK_WKUPINTMSK;
+			sc->sc_irq_mask |= GINTMSK_USBSUSPMSK;
 			DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 		}
 
@@ -418,8 +493,8 @@ dwc_otg_suspend_irq(struct dwc_otg_softc
 			 * Disable suspend interrupt and enable resume
 			 * interrupt:
 			 */
-			sc->sc_irq_mask &= ~GINTSTS_USBSUSP;
-			sc->sc_irq_mask |= GINTSTS_WKUPINT;
+			sc->sc_irq_mask &= ~GINTMSK_USBSUSPMSK;
+			sc->sc_irq_mask |= GINTMSK_WKUPINTMSK;
 			DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
 		}
 
@@ -493,9 +568,11 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof
 {
 	DPRINTFN(5, "RX status clear\n");
 
-	/* enable RX FIFO level interrupt */
-	sc->sc_irq_mask |= GINTSTS_RXFLVL;
-	DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+	if (sc->sc_flags.status_device_mode != 0) {
+		/* enable RX FIFO level interrupt */
+		sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
+		DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+	}
 
 	/* clear cached status */
 	sc->sc_last_rx_status = 0;
@@ -506,6 +583,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);
 
@@ -514,96 +592,61 @@ dwc_otg_clear_hcint(struct dwc_otg_softc
 }
 
 static uint8_t
-dwc_otg_host_channel_wait(struct dwc_otg_td *td)
-{
-	struct dwc_otg_softc *sc;
-	uint8_t x;
-
-	x = td->channel;
-
-	DPRINTF("CH=%d\n", x);
-
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
-
-	if (sc->sc_chan_state[x].wait_sof == 0) {
-		dwc_otg_clear_hcint(sc, x);
-		return (1);	/* done */
-	}
-
-	if (x == 0)
-		return (0);	/* wait */
-
-	/* find new disabled channel */
-	for (x = 1; x != sc->sc_host_ch_max; x++) {
-
-		if (sc->sc_chan_state[x].allocated)
-			continue;
-		if (sc->sc_chan_state[x].wait_sof != 0)
-			continue;
-
-		sc->sc_chan_state[td->channel].allocated = 0;
-		sc->sc_chan_state[x].allocated = 1;
-
-		if (sc->sc_chan_state[td->channel].suspended) {
-			sc->sc_chan_state[td->channel].suspended = 0;
-			sc->sc_chan_state[x].suspended = 1;
-		}
-
-		/* clear interrupts */
-		dwc_otg_clear_hcint(sc, x);
-
-		DPRINTF("CH=%d HCCHAR=0x%08x "
-		    "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt);
-
-		/* ack any pending messages */
-		if (sc->sc_last_rx_status != 0 &&
-		    GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == td->channel) {
-			/* get rid of message */
-			dwc_otg_common_rx_ack(sc);
-		}
-
-		/* move active channel */
-		sc->sc_active_rx_ep &= ~(1 << td->channel);
-		sc->sc_active_rx_ep |= (1 << x);
-
-		/* set channel */
-		td->channel = x;
-
-		return (1);	/* new channel allocated */
-	}
-	return (0);	/* wait */
-}
-
-static uint8_t
-dwc_otg_host_channel_alloc(struct dwc_otg_td *td)
+dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which, uint8_t is_out)
 {
-	struct dwc_otg_softc *sc;
+	uint32_t tx_p_size;
+	uint32_t tx_np_size;
 	uint8_t x;
-	uint8_t max_channel;
 
-	if (td->channel < DWC_OTG_MAX_CHANNELS)
+	if (td->channel[which] < DWC_OTG_MAX_CHANNELS)
 		return (0);		/* already allocated */
 
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
-
-	if ((td->hcchar & HCCHAR_EPNUM_MASK) == 0) {
-		max_channel = 1;
-		x = 0;
+	/* check if device is suspended */
+	if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0)
+		return (1);		/* busy - cannot transfer data */
+
+	/* compute needed TX FIFO size */
+	if (is_out != 0) {
+		if (td->ep_type == UE_INTERRUPT ||
+		    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 {
-		max_channel = sc->sc_host_ch_max;
-		x = 1;
+		/* not a TX transaction */
+		tx_p_size = 0;
+		tx_np_size = 0;
 	}
 
-	for (; x != max_channel; x++) {
-
-		if (sc->sc_chan_state[x].allocated)
+	for (x = 0; x != sc->sc_host_ch_max; x++) {
+		if (sc->sc_chan_state[x].allocated != 0)
 			continue;
+		/* check if channel is still enabled */
 		if (sc->sc_chan_state[x].wait_sof != 0)
 			continue;
 
 		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;
 
 		/* clear interrupts */
 		dwc_otg_clear_hcint(sc, x);
@@ -615,53 +658,44 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 		sc->sc_active_rx_ep |= (1 << x);
 
 		/* set channel */
-		td->channel = x;
+		td->channel[which] = x;
 
 		return (0);	/* allocated */
 	}
+	/* wait a bit */
+	dwc_otg_enable_sof_irq(sc);
 	return (1);	/* busy */
 }
 
 static void
-dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x)
-{
-	uint32_t hcchar;
-	if (sc->sc_chan_state[x].wait_sof != 0)
-		return;
-	hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
-	if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) {
-		/* disable channel */
-		DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
-		    HCCHAR_CHENA | HCCHAR_CHDIS);
-		/* don't re-use channel until next SOF is transmitted */
-		sc->sc_chan_state[x].wait_sof = 2;
-		/* enable SOF interrupt */
-		dwc_otg_enable_sof_irq(sc);
-	}
-}
-
-static void
-dwc_otg_host_channel_free(struct dwc_otg_td *td)
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which)
 {
-	struct dwc_otg_softc *sc;
 	uint8_t x;
 
-	if (td->channel >= DWC_OTG_MAX_CHANNELS)
+	if (td->channel[which] >= DWC_OTG_MAX_CHANNELS)
 		return;		/* already freed */
 
 	/* free channel */
-	x = td->channel;
-	td->channel = DWC_OTG_MAX_CHANNELS;
+	x = td->channel[which];
+	td->channel[which] = DWC_OTG_MAX_CHANNELS;
 
 	DPRINTF("CH=%d\n", x);
 
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
-
-	dwc_otg_host_channel_disable(sc, 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:
+	 */
+	if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
+		/* 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;
-	sc->sc_chan_state[x].suspended = 0;
 
 	/* ack any pending messages */
 	if (sc->sc_last_rx_status != 0 &&
@@ -674,136 +708,131 @@ dwc_otg_host_channel_free(struct dwc_otg
 }
 
 static uint8_t
-dwc_otg_host_setup_tx(struct dwc_otg_td *td)
+dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
 	struct usb_device_request req __aligned(4);
-	struct dwc_otg_softc *sc;
 	uint32_t hcint;
 	uint32_t hcchar;
+	uint8_t delta;
 
-	if (dwc_otg_host_channel_alloc(td))
-		return (1);		/* busy */
-
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
+	if (td->channel[0] < DWC_OTG_MAX_CHANNELS) {
+		hcint = sc->sc_chan_state[td->channel[0]].hcint;
 
-	hcint = sc->sc_chan_state[td->channel].hcint;
-
-	DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-	    td->channel, td->state, hcint,
-	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+		DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+		    td->channel[0], td->state, hcint,
+		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])),
+		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0])));
+	} else {
+		hcint = 0;
+		goto check_state;
+	}
 
 	if (hcint & (HCINT_RETRY |
 	    HCINT_ACK | HCINT_NYET)) {
 		/* give success bits priority over failure bits */
 	} else if (hcint & HCINT_STALL) {
-		DPRINTF("CH=%d STALL\n", td->channel);
+		DPRINTF("CH=%d STALL\n", td->channel[0]);
 		td->error_stall = 1;
 		td->error_any = 1;
-		return (0);		/* complete */
+		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;
-			return (0);		/* complete */
+			goto complete;
 		}
 	}
 
-	/* channel must be disabled before we can complete the transfer */
-
 	if (hcint & (HCINT_ERRORS | HCINT_RETRY |
 	    HCINT_ACK | HCINT_NYET)) {
-
-		dwc_otg_host_channel_disable(sc, td->channel);
-
 		if (!(hcint & HCINT_ERRORS))
 			td->errcnt = 0;
 	}
 
+check_state:
 	switch (td->state) {
 	case DWC_CHAN_ST_START:
 		goto send_pkt;
 
 	case DWC_CHAN_ST_WAIT_ANE:
 		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			if (!dwc_otg_host_channel_wait(td))
-				break;
-			td->did_nak = 1;
+			td->did_nak++;
+			td->tt_scheduled = 0;
 			goto send_pkt;
-		}
-		if (hcint & (HCINT_ACK | HCINT_NYET)) {
-			if (!dwc_otg_host_channel_wait(td))
-				break;
+		} else if (hcint & (HCINT_ACK | HCINT_NYET)) {
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle = 1;
-			return (0);	/* complete */
+			td->tt_scheduled = 0;
+			goto 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->did_nak++;
+			td->tt_scheduled = 0;
 			goto send_pkt;
-		}
-		if (hcint & (HCINT_ACK | HCINT_NYET)) {
-			if (!dwc_otg_host_channel_wait(td))
-				break;
+		} else if (hcint & (HCINT_ACK | HCINT_NYET)) {
 			goto send_cpkt;
 		}
 		break;
+
 	case DWC_CHAN_ST_WAIT_C_ANE:
 		if (hcint & HCINT_NYET) {
-			if (!dwc_otg_host_channel_wait(td))
-				break;
 			goto send_cpkt;
-		}
-		if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
-			if (!dwc_otg_host_channel_wait(td))
-				break;
-			td->did_nak = 1;
+		} else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+			td->did_nak++;
+			td->tt_scheduled = 0;
 			goto send_pkt;
-		}
-		if (hcint & HCINT_ACK) {
-			if (!dwc_otg_host_channel_wait(td))
-				break;
+		} else if (hcint & HCINT_ACK) {
 			td->offset += td->tx_bytes;
 			td->remainder -= td->tx_bytes;
 			td->toggle = 1;
-			return (0);	/* complete */
+			goto complete;
 		}
 		break;
-	case DWC_CHAN_ST_TX_PKT_SYNC:
-		goto send_pkt_sync;
+
+	case DWC_CHAN_ST_WAIT_C_PKT:
+		goto send_cpkt;
+
 	default:
 		break;
 	}
-	return (1);		/* busy */
+	goto busy;
 
 send_pkt:
+	/* free existing channel, if any */
+	dwc_otg_host_channel_free(sc, td, 0);
+
 	if (sizeof(req) != td->remainder) {
 		td->error_any = 1;
-		return (0);		/* complete */
+		goto 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 */
+		delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+		if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+			td->state = DWC_CHAN_ST_START;
+			goto busy;
+		}
+		delta = sc->sc_last_frame_num - td->tt_start_slot;
+		if (delta > 5) {
+			/* missed it */
+			td->tt_scheduled = 0;
+			td->state = DWC_CHAN_ST_START;
+			goto busy;
 		}
+	}
 
+	/* allocate a new channel */
+	if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) {
+		td->state = DWC_CHAN_ST_START;
+		goto busy;
+	}
+
+	if (td->hcsplt != 0) {
 		td->hcsplt &= ~HCSPLT_COMPSPLT;
 		td->state = DWC_CHAN_ST_WAIT_S_ANE;
 	} else {
@@ -812,57 +841,83 @@ send_pkt_sync:
 
 	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 &= ~(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);
 
 	/* store number of bytes transmitted */
 	td->tx_bytes = sizeof(req);
-
-	return (1);	/* busy */
+	goto busy;
 
 send_cpkt:
+	/* free existing channel, if any */
+	dwc_otg_host_channel_free(sc, td, 0);
+
+	delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+	if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+		td->state = DWC_CHAN_ST_WAIT_C_PKT;
+		goto busy;
+	}
+	delta = sc->sc_last_frame_num - td->tt_start_slot;
+	if (delta > DWC_OTG_TT_SLOT_MAX) {
+		/* we missed the service interval */
+		if (td->ep_type != UE_ISOCHRONOUS)
+			td->error_any = 1;
+		goto complete;
+	}
+	/* allocate a new channel */
+	if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) {
+		td->state = DWC_CHAN_ST_WAIT_C_PKT;
+		goto busy;
+	}
+
+	/* wait until next slot before trying again */
+	td->tt_complete_slot++;
+
 	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 &= ~(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 */
+
+complete:
+	dwc_otg_host_channel_free(sc, td, 0);
+	return (0);	/* complete */
 }
 
 static uint8_t
-dwc_otg_setup_rx(struct dwc_otg_td *td)
+dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
-	struct dwc_otg_softc *sc;
 	struct usb_device_request req __aligned(4);
 	uint32_t temp;
 	uint16_t count;
 
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
-
 	/* check endpoint status */
 
 	if (sc->sc_last_rx_status == 0)
@@ -984,39 +1039,42 @@ not_complete:
 }
 
 static uint8_t
-dwc_otg_host_rate_check(struct dwc_otg_td *td)
+dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
-	struct dwc_otg_softc *sc;
-	uint8_t ep_type;
+	uint8_t delta;
 
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
+	delta = sc->sc_tmr_val - td->tmr_val;
+	if (delta >= 128)
+		return (1);	/* busy */
 
-	ep_type = ((td->hcchar &
-	    HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
+	td->tmr_val = sc->sc_tmr_val + td->tmr_res;
 
-	if (sc->sc_chan_state[td->channel].suspended)
-		goto busy;
+	/* set toggle, if any */
+	if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+	return (0);
+}
 
-	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;
+static uint8_t
+dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+	if (td->ep_type == UE_ISOCHRONOUS) {
+		/* non TT isochronous traffic */
+		if ((td->tmr_val != 0) ||
+		    (sc->sc_last_frame_num & (td->tmr_res - 1))) {
+			goto busy;
+		}
+		td->tmr_val = 1;	/* executed */
+		td->toggle = 0;
 
-		delta = sc->sc_tmr_val - td->tmr_val;
-		if (delta >= 128)
+	} else if (td->ep_type == UE_INTERRUPT) {
+		if (!td->tt_scheduled)
 			goto busy;
-		td->tmr_val = sc->sc_tmr_val + td->tmr_res;
-	} else if (td->did_nak != 0) {
+		td->tt_scheduled = 0;
+	} else if (td->did_nak >= DWC_OTG_NAK_MAX) {
 		goto busy;
-	} 
-
-	if (ep_type == UE_ISOCHRONOUS) {
-		td->toggle = 0;
 	} else if (td->set_toggle) {
 		td->set_toggle = 0;
 		td->toggle = 1;
@@ -1027,29 +1085,27 @@ busy:
 }
 
 static uint8_t
-dwc_otg_host_data_rx(struct dwc_otg_td *td)
+dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
-	struct dwc_otg_softc *sc;
 	uint32_t hcint;
 	uint32_t hcchar;
 	uint32_t count;
-	uint8_t ep_type;
-
-	if (dwc_otg_host_channel_alloc(td))
-		return (1);		/* busy */
-
-	/* get pointer to softc */
-	sc = DWC_OTG_PC2SC(td->pc);
+	uint8_t delta;
+	uint8_t channel;
 
-	ep_type = ((td->hcchar &
-	    HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
+	channel = td->channel[td->tt_channel_tog];
 
-	hcint = sc->sc_chan_state[td->channel].hcint;
+	if (channel < DWC_OTG_MAX_CHANNELS) {
+		hcint = sc->sc_chan_state[channel].hcint;
 
-	DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-	    td->channel, td->state, hcint,
-	    DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-	    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+		DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+		    channel, td->state, hcint,
+		    DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+		    DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
+	} else {
+		hcint = 0;
+		goto check_state;
+	}
 
 	/* check interrupt bits */
 
@@ -1057,35 +1113,26 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 	    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", channel);
 		td->error_stall = 1;
 		td->error_any = 1;
-		return (0);		/* complete */
+		goto complete;
 	} else if (hcint & HCINT_ERRORS) {
-		DPRINTF("CH=%d ERROR\n", td->channel);
+		DPRINTF("CH=%d ERROR\n", channel);
 		td->errcnt++;
 		if (td->hcsplt != 0 || td->errcnt >= 3) {
-			td->error_any = 1;
-			return (0);		/* complete */
+			if (td->ep_type != UE_ISOCHRONOUS) {
+				td->error_any = 1;
+				goto complete;
+			}
 		}
 	}
 
-	/* channel must be disabled before we can complete the transfer */
-
-	if (hcint & (HCINT_ERRORS | HCINT_RETRY |
-	    HCINT_ACK | HCINT_NYET)) {
-
-		dwc_otg_host_channel_disable(sc, td->channel);
-
-		if (!(hcint & HCINT_ERRORS))
-			td->errcnt = 0;
-	}
-
 	/* check endpoint status */
 	if (sc->sc_last_rx_status == 0)
 		goto check_state;
 
-	if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->channel)
+	if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel)
 		goto check_state;
 
 	switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
@@ -1103,25 +1150,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 (td->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);
+					goto complete;
+				}
 			}
+			td->toggle ^= 1;
+			td->tt_scheduled = 0;
 		}
 
 		/* verify the packet byte count */
@@ -1131,7 +1195,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 
 			/* release FIFO */
 			dwc_otg_common_rx_ack(sc);
-			return (0);		/* we are complete */
+			goto complete;
 		}
 
 		usbd_copy_in(td->pc, td->offset,

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



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