
On Fri, 5 Feb 2021 20:12:04 +0100 Pali Rohár pali@kernel.org wrote:
If musb_peri_rx_ep() was called to processed 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 but HW buffer stayed unprocessed.
U-Boot tried to process this buffer again when it receive interrupt again, but it can receive it only when sender (host) send 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 mark interrupt as processed. So to prevent marking interrupt in U-Boot as processed and a new variable pending_intrrx which would contain unprocessed bits of intrrx register.
And as 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 allocate 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.
Reviewed-by: Lukasz Majewski lukma@denx.de
Signed-off-by: Pali Rohár pali@kernel.org
drivers/usb/musb/musb_udc.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c index 28719cc3f6..7c74422623 100644 --- a/drivers/usb/musb/musb_udc.c +++ b/drivers/usb/musb/musb_udc.c @@ -104,6 +104,8 @@ struct usb_endpoint_instance *ep0_endpoint; static struct usb_device_instance *udc_device; static int enabled;
+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); +
} else { if (debug_level > 0) serial_printf("ERROR : %s %d problempending_intrrx |= (1 << ep); }
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);
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de