diff mbox

DM6446 using multiple USB devices

Message ID 51373508.9050005@cogentembedded.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Sergei Shtylyov March 6, 2013, 12:22 p.m. UTC
Hello.

On 06-03-2013 8:45, B, Ravi wrote:

>>      All bulk endpoint transfers are sheduled using the single MUSB EP 1,
>> AFAIR.

>>>> 2.  Is there a software work around ("Interrupt endpoint scheduling")
>>>> that works with devices that need Interrupt EPs?  If so what kernel
>>>> version do I need?

>>> "Interrupt endpoint scheduling" was implemented for very early v2.6.10
>>> based MV kernels that TI used to ship. Recently Ravi up-ported that to
>>> v3.3 but was only tested it on DA850 as per my knowledge. Here is the
>>> link:
>>> http://arago-project.org/git/projects/?p=linux-davinci.git;a=commit;h=0795c14aa91650d778a27fe7b2ef23e2d9ff8c89

>>      This code seems to have the same mistake I fixed for 2.6.18 MV release
>> --
>> it tries to schedule several URBs concurrently on the same endpoint. It
>> seems
>> TI engineers have learned nothing from my work. :-(

> Is there different approach to this ? Can you explain in detail ?

    You can't execute several URBs to one endpoint in parallel, trying to 
"interleave" them -- that's totally incorrect. Please find the sources of 
2.6.18 based TI PSP and compare with your 3.3 code. Although, here's the 
changes I did to the original interrupt scheduling patch (extracted from so 
called patch #47) back then:


> --
> Ravi B

WBR, Sergei
diff mbox

Patch

diff -u linux-2.6.18/drivers/usb/musb/musb_host.c 
linux-2.6.18/drivers/usb/musb/musb_host.c
--- linux-2.6.18/drivers/usb/musb/musb_host.c
+++ linux-2.6.18/drivers/usb/musb/musb_host.c
@@ -269,6 +269,11 @@ 
  				else
  					musb->intr_hold = 1;
  			}
+
+			qh->interval = urb->interval;
+			if ((musb->port1_status & USB_PORT_STAT_HIGH_SPEED) &&
+			    urb->dev->speed != USB_SPEED_HIGH)
+				qh->interval *= 8;
  		}
  		/* FALLTHROUGH */
  #endif	/* CONFIG_MUSB_SCHEDULE_INTR_EP */
@@ -341,8 +346,6 @@ 
  __releases(musb->lock)
  __acquires(musb->lock)
  {
-	struct musb_qh	*qh = urb->hcpriv;
-
  	if ((urb->transfer_flags & URB_SHORT_NOT_OK)
  			&& (urb->actual_length < urb->transfer_buffer_length)
  			&& status == 0
@@ -350,10 +353,8 @@ 
  		status = -EREMOTEIO;

  	spin_lock(&urb->lock);
-
  	urb->hcpriv = NULL;
-	if (urb->status == -EINPROGRESS ||
-	    (musb->intr_ep == qh->hw_ep && urb->status == -EPROTO))
+	if (urb->status == -EINPROGRESS)
  		urb->status = status;
  	spin_unlock(&urb->lock);

@@ -507,9 +508,10 @@ 
  static void musb_host_intr_schedule(struct musb *musb)
  {
  	struct musb_hw_ep	*hw_ep = musb->intr_ep;
+	void __iomem		*epio = hw_ep->regs;
  	struct urb		*purb, *hurb = NULL;
  	struct musb_qh		*pqh, *hqh = NULL;
-	u16 			csr = 0;
+	u16 			csr;

  	/*
  	 * Hold the current interrupt request until the IN token is sent to
@@ -517,50 +519,44 @@ 
  	 * for scheduling other device's interrupt requests.
  	 */
  	if (musb->intr_hold != 0 && --musb->intr_hold == 0) {
-		csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
-
+		csr = musb_readw(epio, MUSB_RXCSR);
  		csr &= ~(MUSB_RXCSR_H_ERROR | MUSB_RXCSR_DATAERROR |
  			 MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_REQPKT);

  		/* Avoid clearing RXPKTRDY */
-		musb_writew(hw_ep->regs, MUSB_RXCSR, csr | MUSB_RXCSR_RXPKTRDY);
+		musb_writew(epio, MUSB_RXCSR, csr | MUSB_RXCSR_RXPKTRDY);
  	}

-	list_for_each_entry(pqh, &musb->in_intr, ring)
-		list_for_each_entry(purb, &pqh->hep->urb_list, urb_list) {
-
-			if (purb->number_of_packets)
-				purb->number_of_packets--;
-			/*
-			 * If a contention occurs in the same frame period
-			 * between several Interrupt requests expiring
-			 * then look for speed as the primary yardstick.
-			 * If they are of the same speed then look for the
-			 * lesser polling interval request.
-			 */
-			if (purb->number_of_packets <= 0 && !musb->intr_hold &&
-			    purb->status != -EPROTO) {
-				if (hurb) {
-					if (hurb->dev->speed ==
-					    purb->dev->speed) {
-						if (hurb->interval <=
-						    purb->interval)
-							continue;
-					} else if (hurb->dev->speed >
-						   purb->dev->speed)
+	list_for_each_entry(pqh, &musb->in_intr, ring) {
+		if (pqh->interval)
+			pqh->interval--;
+		/*
+		 * If a contention occurs in the same frame period
+		 * between several Interrupt requests expiring
+		 * then look for speed as the primary yardstick.
+		 * If they are of the same speed then look for the
+		 * lesser polling interval request.
+		 */
+		if (pqh->interval <= 0 && musb->intr_hold == 0) {
+			purb = next_urb(pqh);
+			if (hurb) {
+				if (hurb->dev->speed == purb->dev->speed) {
+					if (hurb->interval <= purb->interval)
  						continue;
-				}
-				hurb = purb;
-				hqh = pqh;
+				} else if (hurb->dev->speed > purb->dev->speed)
+					continue;
  			}
+			hurb = purb;
+			hqh  = pqh;
  		}
+	}

  	/*
  	 * If a request is chosen to be scheduled, check to see if RXPKTRDY
  	 * is set.  If so, delay until this can be processed by the driver.
  	 */
  	if (hqh && hurb) {
-		csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
+		csr = musb_readw(epio, MUSB_RXCSR);

  		if (csr & MUSB_RXCSR_RXPKTRDY)
  			return;
@@ -569,14 +565,6 @@ 
  		if (pqh)
  			musb_save_toggle(pqh, 1, next_urb(pqh));

-		if (hurb->urb_list.prev != &hqh->hep->urb_list)
-			list_move(&hurb->urb_list, &hqh->hep->urb_list);
-
-		hurb->number_of_packets = hurb->interval;
-		if ((musb->port1_status & USB_PORT_STAT_HIGH_SPEED) &&
-		    hurb->dev->speed != USB_SPEED_HIGH)
-			hurb->number_of_packets *= 8;
-
  		hw_ep->rx_reinit = 1;
  		musb_start_urb(musb, 1, hqh);
  	}
@@ -840,7 +828,6 @@ 
  	void __iomem		*epio = hw_ep->regs;
  	struct musb_qh		*qh = musb_ep_get_qh(hw_ep, !is_out);
  	u16			packet_sz = qh->maxpacket;
-	int			need_dma = 1;

  	DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s "
  				"h_addr%02x h_port%02x bytes %d\n",
@@ -850,16 +837,17 @@ 
  			qh->h_addr_reg, qh->h_port_reg,
  			len);

-#ifdef CONFIG_MUSB_SCHEDULE_INTR_EP
-	if (qh->type == USB_ENDPOINT_XFER_INT)
-		need_dma = 0;
-#endif
-
  	musb_ep_select(mbase, epnum);

  	/* candidate for DMA? */
  	dma_controller = musb->dma_controller;
-	if (is_dma_capable() && epnum && dma_controller && need_dma) {
+#ifdef CONFIG_MUSB_SCHEDULE_INTR_EP
+	if (qh->type == USB_ENDPOINT_XFER_INT)
+		/* Interrupt EP scheduling fails at least with CPPI DMA */
+		dma_channel = NULL;
+	else
+#endif
+	if (is_dma_capable() && epnum && dma_controller) {
  		dma_channel = is_out ? hw_ep->tx_channel : hw_ep->rx_channel;
  		if (!dma_channel) {
  			dma_channel = dma_controller->channel_alloc(
@@ -1902,14 +1890,6 @@ 
  finish:
  	urb->actual_length += xfer_len;
  	qh->offset += xfer_len;
-
-#ifdef CONFIG_MUSB_SCHEDULE_INTR_EP
-	if (hw_ep == musb->intr_ep && status == -EPROTO) {
-		urb->status = status;
-		return;
-	}
-#endif
-
  	if (done) {
  		if (urb->status == -EINPROGRESS)
  			urb->status = status;
@@ -2071,15 +2051,6 @@ 
  	if (!is_host_active(musb) || !musb->is_active)
  		return -ENODEV;

-#ifdef CONFIG_MUSB_SCHEDULE_INTR_EP
-	if (usb_pipeint(urb->pipe) && usb_pipein(urb->pipe)) {
-		urb->number_of_packets = urb->interval;
-		if ((musb->port1_status & USB_PORT_STAT_HIGH_SPEED) &&
-		    urb->dev->speed != USB_SPEED_HIGH)
-			urb->number_of_packets *= 8;
-	}
-#endif
-
  	/* DMA mapping was already done, if needed, and this urb is on
  	 * hep->urb_list ... so there's little to do unless hep wasn't
  	 * yet scheduled onto a live qh.
diff -u linux-2.6.18/drivers/usb/musb/musb_host.h 
linux-2.6.18/drivers/usb/musb/musb_host.h
--- linux-2.6.18/drivers/usb/musb/musb_host.h
+++ linux-2.6.18/drivers/usb/musb/musb_host.h
@@ -81,6 +81,9 @@ 
  	u16			maxpacket;
  	u16			frame;		/* for periodic schedule */
  	unsigned		iso_idx;	/* in urb->iso_frame_desc[] */
+#ifdef	CONFIG_MUSB_SCHEDULE_INTR_EP
+	int			interval;
+#endif
  };

  /* map from control or bulk queue head to the first qh on that ring */