[PATCH v3 RFC/RFT] MUSB: Do not enable TX and RX DMA at the same time

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



MUSB: Do not enable TX and RX DMA at the same time

This is a workaround for OMAP3 errata 1.130.

This bug causes an occassional DMA controller hang when
two DMA channels are simultaneously enabled in RX and TX
directions.

With this workaround, in the best case, we effectively use
DMA in both directions, and in the worst case, we use PIO
mode in one direction. The average throughput is better
than using PIO mode in one direction all the time.

Signed-off-by: Anand Gadiyar <gadiyar@xxxxxx>
Signed-off-by: Nilkesh Patra <Nilkesh.patra@xxxxxx>
Signed-off-by: Ajay Kumar Gupta <ajay.gupta@xxxxxx>
---
(v3. Fix compilation, and test before sending out.
Today's just not my day :( )

- To reproduce the hang condition, use g_ether and start
pings in both directions - higher ping sizes reproduce the
failure faster.

- Needs another patch [1] from me to work reliably.

[1] <http://marc.info/?l=linux-usb&m=125897074717960&w=2>


- An alternative workaround for this bug involves using
the SystemDMA to do a non-synchronized DMA transfer to unload
the RX FIFO, and use the MUSB DMA for all TX channels.
If someone wants to use that patch, I can post it as well.

 drivers/usb/musb/musbhsdma.c |   32 ++++++++++++++++++++++++++++++++
 drivers/usb/musb/musbhsdma.h |    2 ++
 2 files changed, 34 insertions(+)

Index: linux-omap-2.6/drivers/usb/musb/musbhsdma.c
===================================================================
--- linux-omap-2.6.orig/drivers/usb/musb/musbhsdma.c
+++ linux-omap-2.6/drivers/usb/musb/musbhsdma.c
@@ -151,6 +151,11 @@ static void configure_channel(struct dma
 				? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)
 				: 0);
 
+	if (musb_channel->transmit)
+		controller->tx_active |= (1 << bchannel);
+	else
+		controller->rx_active |= (1 << bchannel);
+
 	/* address/count */
 	musb_write_hsdma_addr(mbase, bchannel, dma_addr);
 	musb_write_hsdma_count(mbase, bchannel, len);
@@ -166,6 +171,8 @@ static int dma_channel_program(struct dm
 				dma_addr_t dma_addr, u32 len)
 {
 	struct musb_dma_channel *musb_channel = channel->private_data;
+	struct musb_dma_controller *controller = musb_channel->controller;
+	struct musb *musb = controller->private_data;
 
 	DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n",
 		musb_channel->epnum,
@@ -175,6 +182,19 @@ static int dma_channel_program(struct dm
 	BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
 		channel->status == MUSB_DMA_STATUS_BUSY);
 
+	/* In version 1.4, if two DMA channels are simultaneously
+	 * enabled in opposite directions, there is a chance that
+	 * the DMA controller will hang. However, it is safe to
+	 * have multiple DMA channels enabled in the same direction
+	 * at the same time.
+	 */
+	if (musb->hwvers == MUSB_HWVERS_1400) {
+		if (musb_channel->transmit && controller->rx_active)
+			return false;
+		else if	(!musb_channel->transmit && controller->tx_active)
+			return false;
+	}
+
 	channel->actual_len = 0;
 	musb_channel->start_addr = dma_addr;
 	musb_channel->len = len;
@@ -229,6 +249,11 @@ static int dma_channel_abort(struct dma_
 		musb_write_hsdma_addr(mbase, bchannel, 0);
 		musb_write_hsdma_count(mbase, bchannel, 0);
 		channel->status = MUSB_DMA_STATUS_FREE;
+
+		if (musb_channel->transmit)
+			musb_channel->controller->tx_active &= ~(1 << bchannel);
+		else
+			musb_channel->controller->rx_active &= ~(1 << bchannel);
 	}
 
 	return 0;
@@ -297,6 +322,13 @@ static irqreturn_t dma_controller_irq(in
 
 				channel->status = MUSB_DMA_STATUS_FREE;
 
+				if (musb_channel->transmit)
+					controller->tx_active &=
+							~(1 << bchannel);
+				else
+					controller->rx_active &=
+							~(1 << bchannel);
+
 				/* completed */
 				if ((devctl & MUSB_DEVCTL_HM)
 					&& (musb_channel->transmit)
Index: linux-omap-2.6/drivers/usb/musb/musbhsdma.h
===================================================================
--- linux-omap-2.6.orig/drivers/usb/musb/musbhsdma.h
+++ linux-omap-2.6/drivers/usb/musb/musbhsdma.h
@@ -146,4 +146,6 @@ struct musb_dma_controller {
 	u8				channel_count;
 	u8				used_channels;
 	u8				irq;
+	u8				tx_active;
+	u8				rx_active;
 };
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux