brcm2708: switch to 3.14
[15.05/openwrt.git] / target / linux / brcm2708 / patches-3.10 / 0088-dwc_otg-prevent-crashes-on-host-port-disconnects.patch
1 From 53e5476701371e6f8b85cdeeb6099cdaea3872ce Mon Sep 17 00:00:00 2001
2 From: P33M <P33M@github.com>
3 Date: Mon, 5 Aug 2013 11:47:12 +0100
4 Subject: [PATCH 088/196] dwc_otg: prevent crashes on host port disconnects
5
6 Fix several issues resulting in crashes or inconsistent state
7 if a Model A root port was disconnected.
8
9 - Clean up queue heads properly in kill_urbs_in_qh_list by
10   removing the empty QHs from the schedule lists
11 - Set the halt status properly to prevent IRQ handlers from
12   using freed memory
13 - Add fiq_split related cleanup for saved registers
14 - Make microframe scheduling reclaim host channels if
15   active during a disconnect
16 - Abort URBs with -ESHUTDOWN status response, informing
17   device drivers so they respond in a more correct fashion
18   and don't try to resubmit URBs
19 - Prevent IRQ handlers from attempting to handle channel
20   interrupts if the associated URB was dequeued (and the
21   driver state was cleared)
22 ---
23  drivers/usb/host/dwc_otg/dwc_otg_hcd.c       | 44 ++++++++++++++++++++++++----
24  drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c  |  7 +++++
25  drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c |  3 ++
26  3 files changed, 48 insertions(+), 6 deletions(-)
27
28 diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
29 index c42172f..be1d25b 100644
30 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
31 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
32 @@ -59,6 +59,11 @@ static int last_sel_trans_num_avail_hc_at_end = 0;
33  
34  extern int g_next_sched_frame, g_np_count, g_np_sent;
35  
36 +extern haint_data_t haint_saved;
37 +extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
38 +extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
39 +extern gintsts_data_t ginsts_saved;
40 +
41  dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
42  {
43         return DWC_ALLOC(sizeof(dwc_otg_hcd_t));
44 @@ -168,31 +173,43 @@ static void del_timers(dwc_otg_hcd_t * hcd)
45  
46  /**
47   * Processes all the URBs in a single list of QHs. Completes them with
48 - * -ETIMEDOUT and frees the QTD.
49 + * -ESHUTDOWN and frees the QTD.
50   */
51  static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
52  {
53 -       dwc_list_link_t *qh_item;
54 +       dwc_list_link_t *qh_item, *qh_tmp;
55         dwc_otg_qh_t *qh;
56         dwc_otg_qtd_t *qtd, *qtd_tmp;
57  
58 -       DWC_LIST_FOREACH(qh_item, qh_list) {
59 +       DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
60                 qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
61                 DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp,
62                                          &qh->qtd_list, qtd_list_entry) {
63                         qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
64                         if (qtd->urb != NULL) {
65                                 hcd->fops->complete(hcd, qtd->urb->priv,
66 -                                                   qtd->urb, -DWC_E_TIMEOUT);
67 +                                                   qtd->urb, -DWC_E_SHUTDOWN);
68                                 dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
69                         }
70  
71                 }
72 +               if(qh->channel) {
73 +                       /* Using hcchar.chen == 1 is not a reliable test.
74 +                        * It is possible that the channel has already halted
75 +                        * but not yet been through the IRQ handler.
76 +                        */
77 +                       dwc_otg_hc_halt(hcd->core_if, qh->channel,
78 +                               DWC_OTG_HC_XFER_URB_DEQUEUE);
79 +                       if(microframe_schedule)
80 +                               hcd->available_host_channels++;
81 +                       qh->channel = NULL;
82 +               }
83 +               dwc_otg_hcd_qh_remove(hcd, qh);
84         }
85  }
86  
87  /**
88 - * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
89 + * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic
90   * and periodic schedules. The QTD associated with each URB is removed from
91   * the schedule and freed. This function may be called when a disconnect is
92   * detected or when the HCD is being stopped.
93 @@ -278,7 +295,8 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p)
94          */
95         dwc_otg_hcd->flags.b.port_connect_status_change = 1;
96         dwc_otg_hcd->flags.b.port_connect_status = 0;
97 -
98 +       if(fiq_fix_enable)
99 +               local_fiq_disable();
100         /*
101          * Shutdown any transfers in process by clearing the Tx FIFO Empty
102          * interrupt mask and status bits and disabling subsequent host
103 @@ -374,8 +392,22 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p)
104                                 channel->qh = NULL;
105                         }
106                 }
107 +               if(fiq_split_enable) {
108 +                       for(i=0; i < 128; i++) {
109 +                               dwc_otg_hcd->hub_port[i] = 0;
110 +                       }
111 +                       haint_saved.d32 = 0;
112 +                       for(i=0; i < MAX_EPS_CHANNELS; i++) {
113 +                               hcint_saved[i].d32 = 0;
114 +                               hcintmsk_saved[i].d32 = 0;
115 +                       }
116 +               }
117 +
118         }
119  
120 +       if(fiq_fix_enable)
121 +               local_fiq_enable();
122 +
123         if (dwc_otg_hcd->fops->disconnect) {
124                 dwc_otg_hcd->fops->disconnect(dwc_otg_hcd);
125         }
126 diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
127 index 7d521d9..19abea0 100644
128 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
129 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
130 @@ -2660,6 +2660,13 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
131  
132         hc = dwc_otg_hcd->hc_ptr_array[num];
133         hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
134 +       if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
135 +               /* We are responding to a channel disable. Driver
136 +                * state is cleared - our qtd has gone away.
137 +                */
138 +               release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status);
139 +               return 1;
140 +       }
141         qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
142  
143         hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
144 diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
145 index 80690f9..0d49b50 100644
146 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
147 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
148 @@ -309,6 +309,9 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
149         case -DWC_E_OVERFLOW:
150                 status = -EOVERFLOW;
151                 break;
152 +       case -DWC_E_SHUTDOWN:
153 +               status = -ESHUTDOWN;
154 +               break;
155         default:
156                 if (status) {
157                         DWC_PRINTF("Uknown urb status %d\n", status);
158 -- 
159 1.9.1
160