USB: EHCI: add check for wakeup/suspend race
[firefly-linux-kernel-4.4.55.git] / drivers / usb / host / ehci-hcd.c
index 5d6022f30ebe94b2228c6b38851217af1ee8f477..e8ba4c44223a5c360ec552cbbb4137ca2b4ca3b2 100644 (file)
@@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
 #include "ehci.h"
 #include "pci-quirks.h"
 
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+               struct ehci_tt *tt);
+
 /*
  * The MosChip MCS9990 controller updates its microframe counter
  * a little before the frame counter, and occasionally we will read
@@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd)
        INIT_LIST_HEAD(&ehci->intr_qh_list);
        INIT_LIST_HEAD(&ehci->cached_itd_list);
        INIT_LIST_HEAD(&ehci->cached_sitd_list);
+       INIT_LIST_HEAD(&ehci->tt_list);
 
        if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
                /* periodic schedule size can be smaller than default */
@@ -956,6 +960,7 @@ rescan:
                        goto idle_timeout;
 
                /* BUG_ON(!list_empty(&stream->free_list)); */
+               reserve_release_iso_bandwidth(ehci, stream, -1);
                kfree(stream);
                goto done;
        }
@@ -982,6 +987,8 @@ idle_timeout:
                if (qh->clearing_tt)
                        goto idle_timeout;
                if (list_empty (&qh->qtd_list)) {
+                       if (qh->ps.bw_uperiod)
+                               reserve_release_intr_bandwidth(ehci, qh, -1);
                        qh_destroy(ehci, qh);
                        break;
                }
@@ -1022,7 +1029,6 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
         * the toggle bit in the QH.
         */
        if (qh) {
-               usb_settoggle(qh->dev, epnum, is_out, 0);
                if (!list_empty(&qh->qtd_list)) {
                        WARN_ONCE(1, "clear_halt for a busy endpoint\n");
                } else {
@@ -1030,6 +1036,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
                         * while the QH is active.  Unlink it now;
                         * re-linking will call qh_refresh().
                         */
+                       usb_settoggle(qh->ps.udev, epnum, is_out, 0);
                        qh->exception = 1;
                        if (eptype == USB_ENDPOINT_XFER_BULK)
                                start_unlink_async(ehci, qh);
@@ -1048,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd)
 
 /*-------------------------------------------------------------------------*/
 
+/* Device addition and removal */
+
+static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       spin_lock_irq(&ehci->lock);
+       drop_tt(udev);
+       spin_unlock_irq(&ehci->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
 #ifdef CONFIG_PM
 
 /* suspend/resume, section 4.3 */
@@ -1075,6 +1095,14 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
        spin_unlock_irq(&ehci->lock);
 
+       synchronize_irq(hcd->irq);
+
+       /* Check for race with a wakeup request */
+       if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+               ehci_resume(hcd, false);
+               return -EBUSY;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ehci_suspend);
@@ -1191,6 +1219,11 @@ static const struct hc_driver ehci_hc_driver = {
        .bus_resume =           ehci_bus_resume,
        .relinquish_port =      ehci_relinquish_port,
        .port_handed_over =     ehci_port_handed_over,
+
+       /*
+        * device support
+        */
+       .free_dev =             ehci_remove_device,
 };
 
 void ehci_init_driver(struct hc_driver *drv,
@@ -1238,11 +1271,6 @@ MODULE_LICENSE ("GPL");
 #define XILINX_OF_PLATFORM_DRIVER      ehci_hcd_xilinx_of_driver
 #endif
 
-#ifdef CONFIG_USB_W90X900_EHCI
-#include "ehci-w90x900.c"
-#define        PLATFORM_DRIVER         ehci_hcd_w90x900_driver
-#endif
-
 #ifdef CONFIG_USB_OCTEON_EHCI
 #include "ehci-octeon.c"
 #define PLATFORM_DRIVER                ehci_octeon_driver