From e39637e1b816d841e94f2a32f9bf18e0ee89269e Mon Sep 17 00:00:00 2001
From: Min Xu <min.xu@min-info.net>
Date: Sat, 25 Oct 2014 20:39:28 +0200
Subject: pcd_enumerate: Avoid disabling interrupt during endpoint refill

The old implementation has a big local_irq_save() / restore() around the
endpoint re-fill routine.  This disables interrupts for a long time,
psossibly causing lost interrupts.

The new implementation works around this by disabling the USB (UDP)
interrupt only, rather than disabling all interrupts on the system.
---
 firmware/src/os/pcd_enumerate.c | 53 ++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 25 deletions(-)

diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c
index 65ae9d9..62f5809 100644
--- a/firmware/src/os/pcd_enumerate.c
+++ b/firmware/src/os/pcd_enumerate.c
@@ -139,7 +139,7 @@ void udp_unthrottle(void)
 	pUDP->UDP_IER = AT91C_UDP_EPINT1;
 }
 
-static int __udp_refill_ep(int ep)
+int udp_refill_ep(int ep)
 {
 	u_int16_t i;
 	AT91PS_UDP pUDP = upcd.pUdp;
@@ -154,9 +154,11 @@ static int __udp_refill_ep(int ep)
 	
 	/* If there are already two packets in transit, the DPR of
 	 * the SAM7 UDC doesn't have space for more data */
-	if (atomic_read(&upcd.ep[ep].pkts_in_transit) == 2) {
+	if (atomic_read(&upcd.ep[ep].pkts_in_transit) == 2)
 		return -EBUSY;
-	}
+
+	/* disable endpoint interrup */
+	pUDP->UDP_IDR |= 1 << ep;
 
 	/* If we have an incompletely-transmitted req_ctx (>EP size),
 	 * we need to transmit the rest and finish the transaction */
@@ -167,8 +169,23 @@ static int __udp_refill_ep(int ep)
 		/* get pending rctx and start transmitting from zero */
 		rctx = req_ctx_find_get(0, epstate[ep].state_pending, 
 					epstate[ep].state_busy);
-		if (!rctx)
+		if (!rctx) {
+			/* re-enable endpoint interrupt */
+			pUDP->UDP_IER |= 1 << ep;
+			return 0;
+		}
+		if (rctx->tot_len == 0) {
+			/* re-enable endpoint interrupt */
+			pUDP->UDP_IER |= 1 << ep;
+			req_ctx_put(rctx);
 			return 0;
+		}
+		DEBUGPCR("USBTx %08X, Len = %04u, Head 4/Tail 4 bytes: %02X %02X %02X %02X / %02X %02X %02X %02X",
+			 rctx->data, rctx->tot_len,
+			 rctx->data[4], rctx->data[5], rctx->data[6], rctx->data[7],
+			 rctx->data[rctx->tot_len - 4], rctx->data[rctx->tot_len - 3],
+			 rctx->data[rctx->tot_len - 2], rctx->data[rctx->tot_len - 1]);
+
 		start = 0;
 
 		upcd.ep[ep].incomplete.bytes_sent = 0;
@@ -180,8 +197,6 @@ static int __udp_refill_ep(int ep)
 		end = start + AT91C_EP_IN_SIZE;
 
 	/* fill FIFO/DPR */
-	DEBUGII("RCTX_tx(ep=%u,ctx=%u):%u ", ep, req_ctx_num(rctx),
-		end - start);
 	for (i = start; i < end; i++) 
 		pUDP->UDP_FDR[ep] = rctx->data[i];
 
@@ -190,15 +205,13 @@ static int __udp_refill_ep(int ep)
 		pUDP->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
 	}
 
-	if ((end - start < AT91C_EP_OUT_SIZE) ||
-	    (((end - start) == 0) && end && (rctx->tot_len % AT91C_EP_OUT_SIZE) == 0)) {
+	if (end == rctx->tot_len) {
 		/* CASE 1: return context to pool, if
 		 * - packet transfer < AT91C_EP_OUT_SIZE
 		 * - after ZLP of transfer == AT91C_EP_OUT_SIZE
 		 * - after ZLP of transfer % AT91C_EP_OUT_SIZE == 0
 		 * - after last packet of transfer % AT91C_EP_OUT_SIZE != 0
 		 */
-		DEBUGII("RCTX(ep=%u,ctx=%u)_tx_done ", ep, req_ctx_num(rctx));
 		upcd.ep[ep].incomplete.rctx = NULL;
 		req_ctx_put(rctx);
 	} else {
@@ -206,25 +219,15 @@ static int __udp_refill_ep(int ep)
 		 * - after data of transfer == AT91C_EP_OUT_SIZE
 		 * - after data of transfer > AT91C_EP_OUT_SIZE
 		 * - after last packet of transfer % AT91C_EP_OUT_SIZE == 0
-		 */
+	         */
 		upcd.ep[ep].incomplete.rctx = rctx;
 		upcd.ep[ep].incomplete.bytes_sent += end - start;
-		DEBUGII("RCTX(ep=%u)_tx_cont ", ep);
 	}
 
-	return 1;
-}
+	/* re-enable endpoint interrupt */
+	pUDP->UDP_IER |= 1 << ep;
 
-int udp_refill_ep(int ep)
-{
-	unsigned long flags;
-	int ret;
-
-	local_irq_save(flags);
-	ret = __udp_refill_ep(ep);
-	local_irq_restore(flags);
-
-	return ret;
+	return 1;
 }
 
 static void udp_irq(void)
@@ -361,7 +364,7 @@ cont_ep2:
 			if (atomic_dec_return(&upcd.ep[2].pkts_in_transit) == 1)
 				pUDP->UDP_CSR[2] |= AT91C_UDP_TXPKTRDY;
 
-			__udp_refill_ep(2);
+			udp_refill_ep(2);
 		}
 	}
 	if (isr & AT91C_UDP_EPINT3) {
@@ -376,7 +379,7 @@ cont_ep2:
 			if (atomic_dec_return(&upcd.ep[3].pkts_in_transit) == 1)
 				pUDP->UDP_CSR[3] |= AT91C_UDP_TXPKTRDY;
 
-			__udp_refill_ep(3);
+			udp_refill_ep(3);
 		}
 	}
 	if (isr & AT91C_UDP_RXSUSP) {
-- 
cgit v1.2.3