Merge tag 'usb-4.20-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 22 Nov 2018 16:39:29 +0000 (08:39 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 22 Nov 2018 16:39:29 +0000 (08:39 -0800)
Pull USB fixes from Greg KH:
 "Here are a number of small USB fixes for 4.20-rc4.

  There's the usual xhci and dwc2/3 fixes as well as a few minor other
  issues resolved for problems that have been reported. Full details are
  in the shortlog.

  All have been in linux-next for a while with no reported issues"

* tag 'usb-4.20-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
  usb: cdc-acm: add entry for Hiro (Conexant) modem
  usb: xhci: Prevent bus suspend if a port connect change or polling state is detected
  usb: core: Fix hub port connection events lost
  usb: dwc3: gadget: fix ISOC TRB type on unaligned transfers
  Revert "usb: gadget: ffs: Fix BUG when userland exits with submitted AIO transfers"
  usb: dwc2: pci: Fix an error code in probe
  usb: dwc3: Fix NULL pointer exception in dwc3_pci_remove()
  xhci: Add quirk to workaround the errata seen on Cavium Thunder-X2 Soc
  usb: xhci: fix timeout for transition from RExit to U0
  usb: xhci: fix uninitialized completion when USB3 port got wrong status
  xhci: Add check for invalid byte size error when UAS devices are connected.
  xhci: handle port status events for removed USB3 hcd
  xhci: Fix leaking USB3 shared_hcd at xhci removal
  USB: misc: appledisplay: add 20" Apple Cinema Display
  USB: quirks: Add no-lpm quirk for Raydium touchscreens
  usb: quirks: Add delay-init quirk for Corsair K70 LUX RGB
  USB: Wait for extra delay time after USB_PORT_FEAT_RESET for quirky hub
  usb: dwc3: gadget: Properly check last unaligned/zero chain TRB
  usb: dwc3: core: Clean up ULPI device

20 files changed:
Documentation/admin-guide/kernel-parameters.txt
drivers/usb/class/cdc-acm.c
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
drivers/usb/dwc2/pci.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/function/f_fs.c
drivers/usb/host/xhci-histb.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mtk.c
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci-tegra.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/appledisplay.c
include/linux/usb/quirks.h

index 81d1d5a7472804e50a663480ef92dc67abc71ab5..19f4423e70d913ea2bf0801e4e14b57dc74e320f 100644 (file)
                                        prevent spurious wakeup);
                                n = USB_QUIRK_DELAY_CTRL_MSG (Device needs a
                                        pause after every control message);
+                               o = USB_QUIRK_HUB_SLOW_RESET (Hub needs extra
+                                       delay after resetting its port);
                        Example: quirks=0781:5580:bk,0a5c:5834:gij
 
        usbhid.mousepoll=
index 47d75c20c211c6f70cad75fcae4ae6b75c458c09..1b68fed464cb96456fcd566f409608bfda34b4f1 100644 (file)
@@ -1696,6 +1696,9 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
+       { USB_DEVICE(0x0572, 0x1349), /* Hiro (Conexant) USB MODEM H50228 */
+       .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+       },
        { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
        .driver_info = QUIRK_CONTROL_LINE_STATE, },
        { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
index c6077d582d2966a4fbeffd092092c1bf58429db1..0f9381b69a3b563bc63d8010a639bb270f0fc2c8 100644 (file)
@@ -2794,6 +2794,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
        int i, status;
        u16 portchange, portstatus;
        struct usb_port *port_dev = hub->ports[port1 - 1];
+       int reset_recovery_time;
 
        if (!hub_is_superspeed(hub->hdev)) {
                if (warm) {
@@ -2849,7 +2850,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                        USB_PORT_FEAT_C_BH_PORT_RESET);
                        usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
-                       usb_clear_port_feature(hub->hdev, port1,
+
+                       if (udev)
+                               usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_CONNECTION);
 
                        /*
@@ -2885,11 +2888,18 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 
 done:
        if (status == 0) {
-               /* TRSTRCY = 10 ms; plus some extra */
                if (port_dev->quirks & USB_PORT_QUIRK_FAST_ENUM)
                        usleep_range(10000, 12000);
-               else
-                       msleep(10 + 40);
+               else {
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       reset_recovery_time = 10 + 40;
+
+                       /* Hub needs extra delay after resetting its port. */
+                       if (hub->hdev->quirks & USB_QUIRK_HUB_SLOW_RESET)
+                               reset_recovery_time += 100;
+
+                       msleep(reset_recovery_time);
+               }
 
                if (udev) {
                        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
index 178d6c6063c0280a06c6dc3dc6611394d8233479..f9ff03e6af93e7e41acfbcdcb2154dc3a81bbe03 100644 (file)
@@ -128,6 +128,9 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp)
                        case 'n':
                                flags |= USB_QUIRK_DELAY_CTRL_MSG;
                                break;
+                       case 'o':
+                               flags |= USB_QUIRK_HUB_SLOW_RESET;
+                               break;
                        /* Ignore unrecognized flag characters */
                        }
                }
@@ -380,6 +383,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x1a0a, 0x0200), .driver_info =
                        USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
 
+       /* Terminus Technology Inc. Hub */
+       { USB_DEVICE(0x1a40, 0x0101), .driver_info = USB_QUIRK_HUB_SLOW_RESET },
+
        /* Corsair K70 RGB */
        { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
 
@@ -391,6 +397,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT |
          USB_QUIRK_DELAY_CTRL_MSG },
 
+       /* Corsair K70 LUX RGB */
+       { USB_DEVICE(0x1b1c, 0x1b33), .driver_info = USB_QUIRK_DELAY_INIT },
+
        /* Corsair K70 LUX */
        { USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
 
@@ -411,6 +420,11 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x2040, 0x7200), .driver_info =
                        USB_QUIRK_CONFIG_INTF_STRINGS },
 
+       /* Raydium Touchscreen */
+       { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
+
+       { USB_DEVICE(0x2386, 0x3119), .driver_info = USB_QUIRK_NO_LPM },
+
        /* DJI CineSSD */
        { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },
 
index d257c541e51ba4e51530e5d8c3d1048794f5f013..7afc10872f1f031370727a277180f664fe8e4c56 100644 (file)
@@ -120,6 +120,7 @@ static int dwc2_pci_probe(struct pci_dev *pci,
        dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
        if (!dwc2) {
                dev_err(dev, "couldn't allocate dwc2 device\n");
+               ret = -ENOMEM;
                goto err;
        }
 
index becfbb87f791dad714b5ab23049ede45f9dd63e6..2f2048aa5fde13dde439e52236620f9bab603118 100644 (file)
@@ -1499,6 +1499,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
 err5:
        dwc3_event_buffers_cleanup(dwc);
+       dwc3_ulpi_exit(dwc);
 
 err4:
        dwc3_free_scratch_buffers(dwc);
index 1286076a8890308a66d6ef494b85169ae92d7798..842795856bf49e5093459777b252b33d67e89a96 100644 (file)
@@ -283,8 +283,10 @@ err:
 static void dwc3_pci_remove(struct pci_dev *pci)
 {
        struct dwc3_pci         *dwc = pci_get_drvdata(pci);
+       struct pci_dev          *pdev = dwc->pci;
 
-       gpiod_remove_lookup_table(&platform_bytcr_gpios);
+       if (pdev->device == PCI_DEVICE_ID_INTEL_BYT)
+               gpiod_remove_lookup_table(&platform_bytcr_gpios);
 #ifdef CONFIG_PM
        cancel_work_sync(&dwc->wakeup_work);
 #endif
index 679c12e145225899c1ac5ff3f4f8489d8552a928..9faad896b3a135e86a6d4aebae3678fbc20994d7 100644 (file)
@@ -1081,7 +1081,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
                        /* Now prepare one extra TRB to align transfer size */
                        trb = &dep->trb_pool[dep->trb_enqueue];
                        __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
-                                       maxp - rem, false, 0,
+                                       maxp - rem, false, 1,
                                        req->request.stream_id,
                                        req->request.short_not_ok,
                                        req->request.no_interrupt);
@@ -1125,7 +1125,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                /* Now prepare one extra TRB to align transfer size */
                trb = &dep->trb_pool[dep->trb_enqueue];
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
-                               false, 0, req->request.stream_id,
+                               false, 1, req->request.stream_id,
                                req->request.short_not_ok,
                                req->request.no_interrupt);
        } else if (req->request.zero && req->request.length &&
@@ -1141,7 +1141,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                /* Now prepare one extra TRB to handle ZLP */
                trb = &dep->trb_pool[dep->trb_enqueue];
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
-                               false, 0, req->request.stream_id,
+                               false, 1, req->request.stream_id,
                                req->request.short_not_ok,
                                req->request.no_interrupt);
        } else {
@@ -2259,7 +2259,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
         * with one TRB pending in the ring. We need to manually clear HWO bit
         * from that TRB.
         */
-       if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+       if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
                trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
                return 1;
        }
index 3ada83d81bda8d2810ab04d154b65658f57e0ba2..31e8bf3578c891303a9194c6c23df987cf59f38f 100644 (file)
@@ -215,7 +215,6 @@ struct ffs_io_data {
 
        struct mm_struct *mm;
        struct work_struct work;
-       struct work_struct cancellation_work;
 
        struct usb_ep *ep;
        struct usb_request *req;
@@ -1073,31 +1072,22 @@ ffs_epfile_open(struct inode *inode, struct file *file)
        return 0;
 }
 
-static void ffs_aio_cancel_worker(struct work_struct *work)
-{
-       struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
-                                                  cancellation_work);
-
-       ENTER();
-
-       usb_ep_dequeue(io_data->ep, io_data->req);
-}
-
 static int ffs_aio_cancel(struct kiocb *kiocb)
 {
        struct ffs_io_data *io_data = kiocb->private;
-       struct ffs_data *ffs = io_data->ffs;
+       struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
        int value;
 
        ENTER();
 
-       if (likely(io_data && io_data->ep && io_data->req)) {
-               INIT_WORK(&io_data->cancellation_work, ffs_aio_cancel_worker);
-               queue_work(ffs->io_completion_wq, &io_data->cancellation_work);
-               value = -EINPROGRESS;
-       } else {
+       spin_lock_irq(&epfile->ffs->eps_lock);
+
+       if (likely(io_data && io_data->ep && io_data->req))
+               value = usb_ep_dequeue(io_data->ep, io_data->req);
+       else
                value = -EINVAL;
-       }
+
+       spin_unlock_irq(&epfile->ffs->eps_lock);
 
        return value;
 }
index 27f00160332e2186327da464d7be327d82bc3170..3c4abb5a1c3fc6bdb86e749ab62cf256381c1f75 100644 (file)
@@ -325,14 +325,16 @@ static int xhci_histb_remove(struct platform_device *dev)
        struct xhci_hcd_histb *histb = platform_get_drvdata(dev);
        struct usb_hcd *hcd = histb->hcd;
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct usb_hcd *shared_hcd = xhci->shared_hcd;
 
        xhci->xhc_state |= XHCI_STATE_REMOVING;
 
-       usb_remove_hcd(xhci->shared_hcd);
+       usb_remove_hcd(shared_hcd);
+       xhci->shared_hcd = NULL;
        device_wakeup_disable(&dev->dev);
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(xhci->shared_hcd);
+       usb_put_hcd(shared_hcd);
 
        xhci_histb_host_disable(histb);
        usb_put_hcd(hcd);
index 12eea73d9f20b00701e22da8debdcaa1d9283c92..94aca1b5ac8a228b6ecb690f84441f37c976fbdb 100644 (file)
@@ -876,7 +876,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        status |= USB_PORT_STAT_SUSPEND;
        }
        if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
-               !DEV_SUPERSPEED_ANY(raw_port_status)) {
+               !DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) {
                if ((raw_port_status & PORT_RESET) ||
                                !(raw_port_status & PORT_PE))
                        return 0xffffffff;
@@ -921,7 +921,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        time_left = wait_for_completion_timeout(
                                        &bus_state->rexit_done[wIndex],
                                        msecs_to_jiffies(
-                                               XHCI_MAX_REXIT_TIMEOUT));
+                                               XHCI_MAX_REXIT_TIMEOUT_MS));
                        spin_lock_irqsave(&xhci->lock, flags);
 
                        if (time_left) {
@@ -935,7 +935,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        } else {
                                int port_status = readl(port->addr);
                                xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n",
-                                               XHCI_MAX_REXIT_TIMEOUT,
+                                               XHCI_MAX_REXIT_TIMEOUT_MS,
                                                port_status);
                                status |= USB_PORT_STAT_SUSPEND;
                                clear_bit(wIndex, &bus_state->rexit_ports);
@@ -1474,15 +1474,18 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
        unsigned long flags;
        struct xhci_hub *rhub;
        struct xhci_port **ports;
+       u32 portsc_buf[USB_MAXCHILDREN];
+       bool wake_enabled;
 
        rhub = xhci_get_rhub(hcd);
        ports = rhub->ports;
        max_ports = rhub->num_ports;
        bus_state = &xhci->bus_state[hcd_index(hcd)];
+       wake_enabled = hcd->self.root_hub->do_remote_wakeup;
 
        spin_lock_irqsave(&xhci->lock, flags);
 
-       if (hcd->self.root_hub->do_remote_wakeup) {
+       if (wake_enabled) {
                if (bus_state->resuming_ports ||        /* USB2 */
                    bus_state->port_remote_wakeup) {    /* USB3 */
                        spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1490,26 +1493,36 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                        return -EBUSY;
                }
        }
-
-       port_index = max_ports;
+       /*
+        * Prepare ports for suspend, but don't write anything before all ports
+        * are checked and we know bus suspend can proceed
+        */
        bus_state->bus_suspended = 0;
+       port_index = max_ports;
        while (port_index--) {
-               /* suspend the port if the port is not suspended */
                u32 t1, t2;
-               int slot_id;
 
                t1 = readl(ports[port_index]->addr);
                t2 = xhci_port_state_to_neutral(t1);
+               portsc_buf[port_index] = 0;
 
-               if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
-                       xhci_dbg(xhci, "port %d not suspended\n", port_index);
-                       slot_id = xhci_find_slot_id_by_port(hcd, xhci,
-                                       port_index + 1);
-                       if (slot_id) {
+               /* Bail out if a USB3 port has a new device in link training */
+               if ((t1 & PORT_PLS_MASK) == XDEV_POLLING) {
+                       bus_state->bus_suspended = 0;
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       xhci_dbg(xhci, "Bus suspend bailout, port in polling\n");
+                       return -EBUSY;
+               }
+
+               /* suspend ports in U0, or bail out for new connect changes */
+               if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
+                       if ((t1 & PORT_CSC) && wake_enabled) {
+                               bus_state->bus_suspended = 0;
                                spin_unlock_irqrestore(&xhci->lock, flags);
-                               xhci_stop_device(xhci, slot_id, 1);
-                               spin_lock_irqsave(&xhci->lock, flags);
+                               xhci_dbg(xhci, "Bus suspend bailout, port connect change\n");
+                               return -EBUSY;
                        }
+                       xhci_dbg(xhci, "port %d not suspended\n", port_index);
                        t2 &= ~PORT_PLS_MASK;
                        t2 |= PORT_LINK_STROBE | XDEV_U3;
                        set_bit(port_index, &bus_state->bus_suspended);
@@ -1518,7 +1531,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                 * including the USB 3.0 roothub, but only if CONFIG_PM
                 * is enabled, so also enable remote wake here.
                 */
-               if (hcd->self.root_hub->do_remote_wakeup) {
+               if (wake_enabled) {
                        if (t1 & PORT_CONNECT) {
                                t2 |= PORT_WKOC_E | PORT_WKDISC_E;
                                t2 &= ~PORT_WKCONN_E;
@@ -1538,7 +1551,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
 
                t1 = xhci_port_state_to_neutral(t1);
                if (t1 != t2)
-                       writel(t2, ports[port_index]->addr);
+                       portsc_buf[port_index] = t2;
+       }
+
+       /* write port settings, stopping and suspending ports if needed */
+       port_index = max_ports;
+       while (port_index--) {
+               if (!portsc_buf[port_index])
+                       continue;
+               if (test_bit(port_index, &bus_state->bus_suspended)) {
+                       int slot_id;
+
+                       slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+                                                           port_index + 1);
+                       if (slot_id) {
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+                               xhci_stop_device(xhci, slot_id, 1);
+                               spin_lock_irqsave(&xhci->lock, flags);
+                       }
+               }
+               writel(portsc_buf[port_index], ports[port_index]->addr);
        }
        hcd->state = HC_STATE_SUSPENDED;
        bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
index 71d0d33c3286254b0327646720161df6ba5cc1e9..60987c787e44f457d918659140a0671ed7a717e8 100644 (file)
@@ -590,12 +590,14 @@ static int xhci_mtk_remove(struct platform_device *dev)
        struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev);
        struct usb_hcd  *hcd = mtk->hcd;
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct usb_hcd  *shared_hcd = xhci->shared_hcd;
 
-       usb_remove_hcd(xhci->shared_hcd);
+       usb_remove_hcd(shared_hcd);
+       xhci->shared_hcd = NULL;
        device_init_wakeup(&dev->dev, false);
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(xhci->shared_hcd);
+       usb_put_hcd(shared_hcd);
        usb_put_hcd(hcd);
        xhci_mtk_sch_exit(mtk);
        xhci_mtk_clks_disable(mtk);
index 01c57055c0c5bf6b85b8fd45ce2d09c1a0ad3d15..a9515265db4dba4392cc1a834876a5546d8490b2 100644 (file)
@@ -248,6 +248,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
        if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241)
                xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7;
 
+       if ((pdev->vendor == PCI_VENDOR_ID_BROADCOM ||
+            pdev->vendor == PCI_VENDOR_ID_CAVIUM) &&
+            pdev->device == 0x9026)
+               xhci->quirks |= XHCI_RESET_PLL_ON_DISCONNECT;
+
        if (xhci->quirks & XHCI_RESET_ON_RESUME)
                xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
                                "QUIRK: Resetting on resume");
@@ -380,6 +385,7 @@ static void xhci_pci_remove(struct pci_dev *dev)
        if (xhci->shared_hcd) {
                usb_remove_hcd(xhci->shared_hcd);
                usb_put_hcd(xhci->shared_hcd);
+               xhci->shared_hcd = NULL;
        }
 
        /* Workaround for spurious wakeups at shutdown with HSW */
index 32b5574ad5c56403eb876a70b492f88182096903..ef09cb06212fd367da3593092a31dd5c32d95f58 100644 (file)
@@ -362,14 +362,16 @@ static int xhci_plat_remove(struct platform_device *dev)
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        struct clk *clk = xhci->clk;
        struct clk *reg_clk = xhci->reg_clk;
+       struct usb_hcd *shared_hcd = xhci->shared_hcd;
 
        xhci->xhc_state |= XHCI_STATE_REMOVING;
 
-       usb_remove_hcd(xhci->shared_hcd);
+       usb_remove_hcd(shared_hcd);
+       xhci->shared_hcd = NULL;
        usb_phy_shutdown(hcd->usb_phy);
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(xhci->shared_hcd);
+       usb_put_hcd(shared_hcd);
 
        clk_disable_unprepare(clk);
        clk_disable_unprepare(reg_clk);
index a8d92c90fb58755ad4359d94d4e52f50b949b2a0..65750582133f6a20eae8949e8a06c2c92c913881 100644 (file)
@@ -1521,6 +1521,35 @@ static void handle_device_notification(struct xhci_hcd *xhci,
                usb_wakeup_notification(udev->parent, udev->portnum);
 }
 
+/*
+ * Quirk hanlder for errata seen on Cavium ThunderX2 processor XHCI
+ * Controller.
+ * As per ThunderX2errata-129 USB 2 device may come up as USB 1
+ * If a connection to a USB 1 device is followed by another connection
+ * to a USB 2 device.
+ *
+ * Reset the PHY after the USB device is disconnected if device speed
+ * is less than HCD_USB3.
+ * Retry the reset sequence max of 4 times checking the PLL lock status.
+ *
+ */
+static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
+{
+       struct usb_hcd *hcd = xhci_to_hcd(xhci);
+       u32 pll_lock_check;
+       u32 retry_count = 4;
+
+       do {
+               /* Assert PHY reset */
+               writel(0x6F, hcd->regs + 0x1048);
+               udelay(10);
+               /* De-assert the PHY reset */
+               writel(0x7F, hcd->regs + 0x1048);
+               udelay(200);
+               pll_lock_check = readl(hcd->regs + 0x1070);
+       } while (!(pll_lock_check & 0x1) && --retry_count);
+}
+
 static void handle_port_status(struct xhci_hcd *xhci,
                union xhci_trb *event)
 {
@@ -1556,6 +1585,13 @@ static void handle_port_status(struct xhci_hcd *xhci,
                goto cleanup;
        }
 
+       /* We might get interrupts after shared_hcd is removed */
+       if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) {
+               xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n");
+               bogus_port_status = true;
+               goto cleanup;
+       }
+
        hcd = port->rhub->hcd;
        bus_state = &xhci->bus_state[hcd_index(hcd)];
        hcd_portnum = port->hcd_portnum;
@@ -1639,7 +1675,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
         * RExit to a disconnect state).  If so, let the the driver know it's
         * out of the RExit state.
         */
-       if (!DEV_SUPERSPEED_ANY(portsc) &&
+       if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 &&
                        test_and_clear_bit(hcd_portnum,
                                &bus_state->rexit_ports)) {
                complete(&bus_state->rexit_done[hcd_portnum]);
@@ -1647,8 +1683,12 @@ static void handle_port_status(struct xhci_hcd *xhci,
                goto cleanup;
        }
 
-       if (hcd->speed < HCD_USB3)
+       if (hcd->speed < HCD_USB3) {
                xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+               if ((xhci->quirks & XHCI_RESET_PLL_ON_DISCONNECT) &&
+                   (portsc & PORT_CSC) && !(portsc & PORT_CONNECT))
+                       xhci_cavium_reset_phy_quirk(xhci);
+       }
 
 cleanup:
        /* Update event ring dequeue pointer before dropping the lock */
@@ -2266,6 +2306,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                        goto cleanup;
                case COMP_RING_UNDERRUN:
                case COMP_RING_OVERRUN:
+               case COMP_STOPPED_LENGTH_INVALID:
                        goto cleanup;
                default:
                        xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
index 6b5db344de3011df7154f698333e97c5f62f8f69..938ff06c034959f445dc8ad764d2e8a97887c15b 100644 (file)
@@ -1303,6 +1303,7 @@ static int tegra_xusb_remove(struct platform_device *pdev)
 
        usb_remove_hcd(xhci->shared_hcd);
        usb_put_hcd(xhci->shared_hcd);
+       xhci->shared_hcd = NULL;
        usb_remove_hcd(tegra->hcd);
        usb_put_hcd(tegra->hcd);
 
index 0420eefa647a15cb5321dfa5fd95556a5a5432e5..c928dbbff8811206fd3f668564b3ffe627cdd2aa 100644 (file)
@@ -719,8 +719,6 @@ static void xhci_stop(struct usb_hcd *hcd)
 
        /* Only halt host and free memory after both hcds are removed */
        if (!usb_hcd_is_primary_hcd(hcd)) {
-               /* usb core will free this hcd shortly, unset pointer */
-               xhci->shared_hcd = NULL;
                mutex_unlock(&xhci->mutex);
                return;
        }
index bf0b3692dc9a118d30664963e88d6757c7a5f31e..260b259b72bcb6abaf85db2b32c591a64b23dae5 100644 (file)
@@ -1680,7 +1680,7 @@ struct xhci_bus_state {
  * It can take up to 20 ms to transition from RExit to U0 on the
  * Intel Lynx Point LP xHCI host.
  */
-#define        XHCI_MAX_REXIT_TIMEOUT  (20 * 1000)
+#define        XHCI_MAX_REXIT_TIMEOUT_MS       20
 
 static inline unsigned int hcd_index(struct usb_hcd *hcd)
 {
@@ -1849,6 +1849,7 @@ struct xhci_hcd {
 #define XHCI_INTEL_USB_ROLE_SW BIT_ULL(31)
 #define XHCI_ZERO_64B_REGS     BIT_ULL(32)
 #define XHCI_DEFAULT_PM_RUNTIME_ALLOW  BIT_ULL(33)
+#define XHCI_RESET_PLL_ON_DISCONNECT   BIT_ULL(34)
 
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
index bd539f3058bcaa3d754e07bdb7034d4f2786e8a7..85b48c6ddc7e681e7aadb8ddb86d53dbffba85a9 100644 (file)
@@ -50,6 +50,7 @@ static const struct usb_device_id appledisplay_table[] = {
        { APPLEDISPLAY_DEVICE(0x9219) },
        { APPLEDISPLAY_DEVICE(0x921c) },
        { APPLEDISPLAY_DEVICE(0x921d) },
+       { APPLEDISPLAY_DEVICE(0x9222) },
        { APPLEDISPLAY_DEVICE(0x9236) },
 
        /* Terminating entry */
index b7a99ce56bc9ad6cd4c8ff2c5681d6fbb781b875..a1be64c9940fb4ad4e2365f226419f050d6addba 100644 (file)
@@ -66,4 +66,7 @@
 /* Device needs a pause after every control message. */
 #define USB_QUIRK_DELAY_CTRL_MSG               BIT(13)
 
+/* Hub needs extra delay after resetting its port. */
+#define USB_QUIRK_HUB_SLOW_RESET               BIT(14)
+
 #endif /* __LINUX_USB_QUIRKS_H */