[PATCH 08/22] usb: musb: do not enable TX and RX DMA at the same time

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

 



From: Anand Gadiyar <gadiyar@xxxxxx>

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>
Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx>
---
 drivers/usb/musb/musbhsdma.c |   32 ++++++++++++++++++++++++++++++++
 drivers/usb/musb/musbhsdma.h |    2 ++
 2 files changed, 34 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index a237550..927abf0 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -151,6 +151,11 @@ static void configure_channel(struct dma_channel *channel,
 				? (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 dma_channel *channel,
 				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 dma_channel *channel,
 	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_channel *channel)
 		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(int irq, void *private_data)
 
 				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)
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
index 1299d92..df87863 100644
--- a/drivers/usb/musb/musbhsdma.h
+++ b/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;
 };
-- 
1.6.6.rc0

--
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