mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 17:48:58 +00:00
c56ae22db0
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
96 lines
3.6 KiB
Diff
96 lines
3.6 KiB
Diff
From af6743f045159970b95f6426de13c0fb82678e67 Mon Sep 17 00:00:00 2001
|
|
From: Jonathan Bell <jonathan@raspberrypi.org>
|
|
Date: Wed, 8 Jan 2020 12:48:09 +0000
|
|
Subject: [PATCH] dwc_otg: fiq_fsm: pause when cancelling split
|
|
transactions
|
|
|
|
Non-periodic splits will DMA to/from the driver-provided transfer_buffer,
|
|
which may be freed immediately after the dequeue call returns. Block until
|
|
we know the transfer is complete.
|
|
|
|
A similar delay is needed when cleaning up disconnects, as the FIQ could
|
|
have started a periodic transfer in the previous microframe to the one
|
|
that triggered a disconnect.
|
|
|
|
Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
|
|
---
|
|
drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 33 +++++++++++++++++++++--
|
|
drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 1 +
|
|
2 files changed, 32 insertions(+), 2 deletions(-)
|
|
|
|
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
|
|
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
|
|
@@ -175,6 +175,7 @@ static void kill_urbs_in_qh_list(dwc_otg
|
|
dwc_list_link_t *qh_item, *qh_tmp;
|
|
dwc_otg_qh_t *qh;
|
|
dwc_otg_qtd_t *qtd, *qtd_tmp;
|
|
+ int quiesced = 0;
|
|
|
|
DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
|
|
qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
|
|
@@ -198,8 +199,17 @@ static void kill_urbs_in_qh_list(dwc_otg
|
|
qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
|
|
qh->channel->halt_pending = 1;
|
|
if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
|
|
- hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
|
|
+ hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
|
|
hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
|
|
+ /* We're called from disconnect callback or in the middle of freeing the HCD here,
|
|
+ * so FIQ is disabled, top-level interrupts masked and we're holding the spinlock.
|
|
+ * No further URBs will be submitted, but wait 1 microframe for any previously
|
|
+ * submitted periodic DMA to finish.
|
|
+ */
|
|
+ if (!quiesced) {
|
|
+ udelay(125);
|
|
+ quiesced = 1;
|
|
+ }
|
|
} else {
|
|
dwc_otg_hc_halt(hcd->core_if, qh->channel,
|
|
DWC_OTG_HC_XFER_URB_DEQUEUE);
|
|
@@ -600,15 +610,34 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
|
|
/* In FIQ FSM mode, we need to shut down carefully.
|
|
* The FIQ may attempt to restart a disabled channel */
|
|
if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) {
|
|
+ int retries = 3;
|
|
+ int running = 0;
|
|
+ enum fiq_fsm_state state;
|
|
+
|
|
local_fiq_disable();
|
|
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
|
|
qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
|
|
qh->channel->halt_pending = 1;
|
|
if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
|
|
- hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
|
|
+ hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
|
|
hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
|
|
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
|
|
local_fiq_enable();
|
|
+
|
|
+ if (dwc_qh_is_non_per(qh)) {
|
|
+ do {
|
|
+ state = READ_ONCE(hcd->fiq_state->channel[n].fsm);
|
|
+ running = (state != FIQ_NP_SPLIT_DONE) &&
|
|
+ (state != FIQ_NP_SPLIT_LS_ABORTED) &&
|
|
+ (state != FIQ_NP_SPLIT_HS_ABORTED);
|
|
+ if (!running)
|
|
+ break;
|
|
+ udelay(125);
|
|
+ } while(--retries);
|
|
+ if (!retries)
|
|
+ DWC_WARN("Timed out waiting for FSM NP transfer to complete on %d",
|
|
+ qh->channel->hc_num);
|
|
+ }
|
|
} else {
|
|
dwc_otg_hc_halt(hcd->core_if, qh->channel,
|
|
DWC_OTG_HC_XFER_URB_DEQUEUE);
|
|
--- a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
|
|
+++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
|
|
@@ -27,6 +27,7 @@
|
|
#include <linux/workqueue.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/pci.h>
|
|
+#include <linux/compiler.h>
|
|
|
|
#include <linux/version.h>
|
|
|