aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-4.19/950-0789-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-4.19/950-0789-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch')
-rw-r--r--target/linux/bcm27xx/patches-4.19/950-0789-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch95
1 files changed, 95 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-4.19/950-0789-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch b/target/linux/bcm27xx/patches-4.19/950-0789-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
new file mode 100644
index 0000000000..53c93a5836
--- /dev/null
+++ b/target/linux/bcm27xx/patches-4.19/950-0789-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
@@ -0,0 +1,95 @@
+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>
+