usb: musb: Fix receiving of bigger buffers
authorPali Rohár <pali@kernel.org>
Sun, 7 Feb 2021 13:50:08 +0000 (14:50 +0100)
committerMarek Vasut <marex@denx.de>
Wed, 3 Mar 2021 03:12:46 +0000 (04:12 +0100)
If musb_peri_rx_ep() was called to process received HW buffer but U-Boot
cannot read it yet (e.g. because U-Boot SW buffer is full) then interrupt
was marked as processed also when HW buffer stayed unprocessed.

U-Boot tried to process this buffer again when it received interrupt again,
but it can receive it only when sender (host) sends a new data. As sender
(host) is not going to send a new data until U-Boot process current data
this issue caused a deadlock in case sender (host) is emitting data faster
than U-Boot can process it.

Reading musb intrrx register automatically clears this register and marks
interrupt as processed. So to prevent marking interrupt in U-Boot as
processed, adds a new variable pending_intrrx which would contain
unprocessed bits of intrrx register.

For a second step, every time when musb_peri_rx_ep() is called and there
are waiting data to be processed (signaled by MUSB_RXCSR_RXPKTRDY) either
acknowledge sender (via musb_peri_rx_ack()) that whole HW buffer was
processed or set corresponding bit in pending_intrrx that HW buffer was not
fully processed yet and next iteration is required after U-Boot allocates
space for reading HW buffer.

This patch fixes receiving large usb buffers, e.g. file transfer via Kermit
protocol implemented by 'loadb' U-Boot command over usbtty serial console.

Signed-off-by: Pali Rohár <pali@kernel.org>
Reviewed-by: Lukasz Majewski <lukma@denx.de>
Acked-by: Pavel Machek <pavel@ucw.cz>
drivers/usb/musb/musb_udc.c

index 28719cc..41fff94 100644 (file)
@@ -104,6 +104,8 @@ struct usb_endpoint_instance *ep0_endpoint;
 static struct usb_device_instance *udc_device;
 static int enabled;
 
+static u16 pending_intrrx;
+
 #ifdef MUSB_DEBUG
 static void musb_db_regs(void)
 {
@@ -664,7 +666,10 @@ static void musb_peri_rx_ep(unsigned int ep)
                                /* The common musb fifo reader */
                                read_fifo(ep, length, data);
 
-                               musb_peri_rx_ack(ep);
+                               if (length == peri_rxcount)
+                                       musb_peri_rx_ack(ep);
+                               else
+                                       pending_intrrx |= (1 << ep);
 
                                /*
                                 * urb's actual_length is updated in
@@ -677,18 +682,24 @@ static void musb_peri_rx_ep(unsigned int ep)
                                        serial_printf("ERROR : %s %d no space "
                                                      "in rcv buffer\n",
                                                      __PRETTY_FUNCTION__, ep);
+
+                               pending_intrrx |= (1 << ep);
                        }
                } else {
                        if (debug_level > 0)
                                serial_printf("ERROR : %s %d problem with "
                                              "endpoint\n",
                                              __PRETTY_FUNCTION__, ep);
+
+                       pending_intrrx |= (1 << ep);
                }
 
        } else {
                if (debug_level > 0)
                        serial_printf("ERROR : %s %d with nothing to do\n",
                                      __PRETTY_FUNCTION__, ep);
+
+               musb_peri_rx_ack(ep);
        }
 }
 
@@ -770,6 +781,9 @@ void udc_irq(void)
                        intrrx = readw(&musbr->intrrx);
                        intrtx = readw(&musbr->intrtx);
 
+                       intrrx |= pending_intrrx;
+                       pending_intrrx = 0;
+
                        if (intrrx)
                                musb_peri_rx(intrrx);