[rfc/rft/patch v2 17/19] usb: musb: fix behavior with g_zero and g_ether

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

 



From: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx>

g_zero was broken, because it started a TX which never
completed, then nuked the EP, generating a spurious
TX IRQ. This confused the previous driver.

Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx>
Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx>
---
 drivers/usb/musb/musb_core.c   |   20 ++++++++++++++------
 drivers/usb/musb/musb_gadget.c |   40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 19d6185..893d7a5 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1519,7 +1519,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
 irqreturn_t musb_interrupt(struct musb *musb)
 {
 	irqreturn_t	retval = IRQ_NONE;
-	u8		devctl, power;
+	u8		devctl, power, int_usb;
 	int		ep_num;
 	u32		reg;
 
@@ -1527,26 +1527,32 @@ irqreturn_t musb_interrupt(struct musb *musb)
 	power = musb_readb(musb->mregs, MUSB_POWER);
 
 	DBG(4, "** IRQ %s usb%04x tx%04x rx%04x\n",
-		(devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
-		musb->int_usb, musb->int_tx, musb->int_rx);
+			(devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
+			musb->int_usb, musb->int_tx, musb->int_rx);
 
 	if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
 		if (!musb->gadget_driver) {
 			DBG(5, "No gadget driver loaded\n");
+			musb->int_usb = 0;
+			musb->int_tx = 0;
+			musb->int_rx = 0;
 			return IRQ_HANDLED;
 		}
 
 	/* the core can interrupt us for multiple reasons; docs have
 	 * a generic interrupt flowchart to follow
 	 */
-	if (musb->int_usb & STAGE0_MASK)
-		retval |= musb_stage0_irq(musb, musb->int_usb,
-				devctl, power);
 
+	int_usb = musb->int_usb;
+	musb->int_usb = 0;
+	int_usb &= ~MUSB_INTR_SOF;
+	if (int_usb)
+		retval |= musb_stage0_irq(musb, int_usb, devctl, power);
 	/* "stage 1" is handling endpoint irqs */
 
 	/* handle endpoint 0 first */
 	if (musb->int_tx & 1) {
+		musb->int_tx &= ~1;
 		if (devctl & MUSB_DEVCTL_HM)
 			retval |= musb_h_ep0_irq(musb);
 		else
@@ -1555,6 +1561,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
 
 	/* RX on endpoints 1-15 */
 	reg = musb->int_rx >> 1;
+	musb->int_rx = 0;
 	ep_num = 1;
 	while (reg) {
 		if (reg & 1) {
@@ -1576,6 +1583,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
 
 	/* TX on endpoints 1-15 */
 	reg = musb->int_tx >> 1;
+	musb->int_rx = 0;
 	ep_num = 1;
 	while (reg) {
 		if (reg & 1) {
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index a12cc55..1afd01f 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -333,6 +333,9 @@ static void nuke(struct musb_ep *ep, const int status)
 		stop_dma(musb, ep, req);
 
 		if (ep->is_in) {
+			u16 csr;
+
+			csr = musb_readw(epio, MUSB_TXCSR);
 			/*
 			 * The programming guide says that we must not clear
 			 * the DMAMODE bit before DMAENAB, so we only
@@ -342,6 +345,19 @@ static void nuke(struct musb_ep *ep, const int status)
 				    MUSB_TXCSR_DMAMODE | MUSB_TXCSR_FLUSHFIFO);
 			musb_writew(epio, MUSB_TXCSR,
 					0 | MUSB_TXCSR_FLUSHFIFO);
+
+			if (csr & MUSB_TXCSR_TXPKTRDY) {
+				/* If TxPktRdy was set, an extra IRQ was just
+				 * generated. This IRQ will confuse things if
+				 * a we don't handle it before a new TX request
+				 * is started. So we clear it here, in a bit
+				 * unsafe fashion (if nuke() is called outside
+				 * musb_interrupt(), we might have a delay in
+				 * handling other TX EPs.) */
+				musb->int_tx |= musb_readw(musb->mregs,
+						MUSB_INTRTX);
+				musb->int_tx &= ~(1 << ep->current_epnum);
+			}
 		} else {
 			musb_writew(epio, MUSB_RXCSR,
 				    MUSB_RXCSR_DMAMODE | MUSB_RXCSR_FLUSHFIFO);
@@ -349,6 +365,12 @@ static void nuke(struct musb_ep *ep, const int status)
 					0 | MUSB_RXCSR_FLUSHFIFO);
 		}
 	}
+
+	if (ep->is_in)
+		musb_writew(epio, MUSB_TXCSR, 0);
+	else
+		musb_writew(epio, MUSB_RXCSR, 0);
+
 	ep->rx_pending = false;
 
 	while (!list_empty(&(ep->req_list))) {
@@ -464,6 +486,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
 	struct musb_ep		*musb_ep = &musb->endpoints[epnum].ep_in;
 	void __iomem		*epio = musb->endpoints[epnum].regs;
 	struct dma_channel	*dma;
+	int			count;
 
 	musb_ep_select(mbase, epnum);
 	request = next_request(musb_ep);
@@ -508,6 +531,23 @@ void musb_g_tx(struct musb *musb, u8 epnum)
 		DBG(2, "underrun on ep%d, req %p\n", epnum, request);
 	}
 
+
+	/* The interrupt is generated when this bit gets cleared,
+	 * if we fall here while TXPKTRDY is still set, then that's
+	 * a really messed up case. One such case seems to be due to
+	 * the HW -- sometimes the IRQ is generated early.
+	 */
+	count = 0;
+	while (csr & MUSB_TXCSR_TXPKTRDY) {
+		count++;
+		if (count == 1000) {
+			DBG(1, "TX IRQ while TxPktRdy still set "
+					"(CSR %04x)\n", csr);
+			return;
+		}
+		csr = musb_readw(epio, MUSB_TXCSR);
+	}
+
 	if (dma != NULL && dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
 		/* SHOULD NOT HAPPEN ... has with cppi though, after
 		 * changing SENDSTALL (and other cases); harmless?
-- 
1.6.4.2.253.g0b1fac

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