0a7356ffa260e46ba3db14bc5daf4eff5829eb2d
[oweals/openwrt.git] /
1 From 09648b92a71b03450e9482f0cc5bd22298f78d44 Mon Sep 17 00:00:00 2001
2 From: Jonathan Bell <jonathan@raspberrypi.org>
3 Date: Wed, 8 Jan 2020 12:48:09 +0000
4 Subject: [PATCH] dwc_otg: fiq_fsm: pause when cancelling split
5  transactions
6
7 Non-periodic splits will DMA to/from the driver-provided transfer_buffer,
8 which may be freed immediately after the dequeue call returns. Block until
9 we know the transfer is complete.
10
11 A similar delay is needed when cleaning up disconnects, as the FIQ could
12 have started a periodic transfer in the previous microframe to the one
13 that triggered a disconnect.
14
15 Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
16 ---
17  drivers/usb/host/dwc_otg/dwc_otg_hcd.c    | 33 +++++++++++++++++++++--
18  drivers/usb/host/dwc_otg/dwc_otg_os_dep.h |  1 +
19  2 files changed, 32 insertions(+), 2 deletions(-)
20
21 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
22 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
23 @@ -175,6 +175,7 @@ static void kill_urbs_in_qh_list(dwc_otg
24         dwc_list_link_t *qh_item, *qh_tmp;
25         dwc_otg_qh_t *qh;
26         dwc_otg_qtd_t *qtd, *qtd_tmp;
27 +       int quiesced = 0;
28  
29         DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
30                 qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
31 @@ -198,8 +199,17 @@ static void kill_urbs_in_qh_list(dwc_otg
32                                 qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
33                                 qh->channel->halt_pending = 1;
34                                 if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
35 -                                       hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
36 +                                   hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
37                                         hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
38 +                               /* We're called from disconnect callback or in the middle of freeing the HCD here,
39 +                                * so FIQ is disabled, top-level interrupts masked and we're holding the spinlock.
40 +                                * No further URBs will be submitted, but wait 1 microframe for any previously
41 +                                * submitted periodic DMA to finish.
42 +                                */
43 +                               if (!quiesced) {
44 +                                       udelay(125);
45 +                                       quiesced = 1;
46 +                               }
47                         } else {
48                                 dwc_otg_hc_halt(hcd->core_if, qh->channel,
49                                                 DWC_OTG_HC_XFER_URB_DEQUEUE);
50 @@ -600,15 +610,34 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
51                         /* In FIQ FSM mode, we need to shut down carefully.
52                          * The FIQ may attempt to restart a disabled channel */
53                         if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) {
54 +                               int retries = 3;
55 +                               int running = 0;
56 +                               enum fiq_fsm_state state;
57 +
58                                 local_fiq_disable();
59                                 fiq_fsm_spin_lock(&hcd->fiq_state->lock);
60                                 qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
61                                 qh->channel->halt_pending = 1;
62                                 if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
63 -                                       hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
64 +                                   hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
65                                         hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
66                                 fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
67                                 local_fiq_enable();
68 +
69 +                               if (dwc_qh_is_non_per(qh)) {
70 +                                       do {
71 +                                               state = READ_ONCE(hcd->fiq_state->channel[n].fsm);
72 +                                               running = (state != FIQ_NP_SPLIT_DONE) &&
73 +                                                         (state != FIQ_NP_SPLIT_LS_ABORTED) &&
74 +                                                         (state != FIQ_NP_SPLIT_HS_ABORTED);
75 +                                               if (!running)
76 +                                                       break;
77 +                                               udelay(125);
78 +                                       } while(--retries);
79 +                                       if (!retries)
80 +                                               DWC_WARN("Timed out waiting for FSM NP transfer to complete on %d",
81 +                                                        qh->channel->hc_num);
82 +                               }
83                         } else {
84                                 dwc_otg_hc_halt(hcd->core_if, qh->channel,
85                                                 DWC_OTG_HC_XFER_URB_DEQUEUE);
86 --- a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
87 +++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
88 @@ -27,6 +27,7 @@
89  #include <linux/workqueue.h>
90  #include <linux/stat.h>
91  #include <linux/pci.h>
92 +#include <linux/compiler.h>
93  
94  #include <linux/version.h>
95