diff options
author | Pali Rohár <pali@kernel.org> | 2021-02-07 14:50:08 +0100 |
---|---|---|
committer | Marek Vasut <marex@denx.de> | 2021-03-03 04:12:46 +0100 |
commit | ff77bb301eb77a49409a8c113b02b0048f9f809d (patch) | |
tree | 967a4b3cab7fbf1dba860de47ab50fd7874d5368 /drivers | |
parent | ea7125c4c6d25bbaf7927efc753357e64612bffb (diff) | |
download | u-boot-ff77bb301eb77a49409a8c113b02b0048f9f809d.tar.gz |
usb: musb: Fix receiving of bigger buffers
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/musb/musb_udc.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c index 28719cc3f6..41fff94df4 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; +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); |