USB: Fix ehci infinite suspend-resume loop issue in zhaoxin
authorWeitao Wango <WeitaoWang-oc@zhaoxin.com>
Thu, 24 Mar 2022 12:17:35 +0000 (20:17 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 21 Apr 2022 16:53:53 +0000 (18:53 +0200)
In zhaoxin platform, some ehci projects will latch a wakeup signal
internal when plug in a device on port during system S0. This wakeup
signal will turn on when ehci runtime suspend, which will trigger a
system control interrupt that will resume ehci back to D0. As no
device connect, ehci will be set to runtime suspend and turn on the
internal latched wakeup signal again. It will cause a suspend-resume
loop and generate system control interrupt continuously.

Fixed this issue by clear wakeup signal latched in ehci internal when
ehci resume callback is called.

Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Weitao Wang <WeitaoWang-oc@zhaoxin.com>
Link: https://lore.kernel.org/r/20220324121735.3803-1-WeitaoWang-oc@zhaoxin.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci.h

index 3d82e0b853be521774827f1302fdab616252d797..684164fa97169d3da8f8c50e65c0cc86b089797b 100644 (file)
@@ -1103,6 +1103,26 @@ static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
 
 #ifdef CONFIG_PM
 
+/* Clear wakeup signal locked in zhaoxin platform when device plug in. */
+static void ehci_zx_wakeup_clear(struct ehci_hcd *ehci)
+{
+       u32 __iomem     *reg = &ehci->regs->port_status[4];
+       u32             t1 = ehci_readl(ehci, reg);
+
+       t1 &= (u32)~0xf0000;
+       t1 |= PORT_TEST_FORCE;
+       ehci_writel(ehci, t1, reg);
+       t1 = ehci_readl(ehci, reg);
+       msleep(1);
+       t1 &= (u32)~0xf0000;
+       ehci_writel(ehci, t1, reg);
+       ehci_readl(ehci, reg);
+       msleep(1);
+       t1 = ehci_readl(ehci, reg);
+       ehci_writel(ehci, t1 | PORT_CSC, reg);
+       ehci_readl(ehci, reg);
+}
+
 /* suspend/resume, section 4.3 */
 
 /* These routines handle the generic parts of controller suspend/resume */
@@ -1154,6 +1174,9 @@ int ehci_resume(struct usb_hcd *hcd, bool force_reset)
        if (ehci->shutdown)
                return 0;               /* Controller is dead */
 
+       if (ehci->zx_wakeup_clear_needed)
+               ehci_zx_wakeup_clear(ehci);
+
        /*
         * If CF is still set and reset isn't forced
         * then we maintained suspend power.
index 638f03b8973948171dbf70906a6493e086307d41..9937c5a7efc2d139cf759355fa0530f0dfc0b180 100644 (file)
@@ -231,6 +231,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                        ehci->is_aspeed = 1;
                }
                break;
+       case PCI_VENDOR_ID_ZHAOXIN:
+               if (pdev->device == 0x3104 && (pdev->revision & 0xf0) == 0x90)
+                       ehci->zx_wakeup_clear_needed = 1;
+               break;
        }
 
        /* optional debug port, normally in the first BAR */
index fdd073cc053b89e777701a8e48e04f69be3581ca..ad3f13a3eaf1b729a97e125a736b76b04078cfd3 100644 (file)
@@ -220,6 +220,7 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                imx28_write_fix:1; /* For Freescale i.MX28 */
        unsigned                spurious_oc:1;
        unsigned                is_aspeed:1;
+       unsigned                zx_wakeup_clear_needed:1;
 
        /* required for usb32 quirk */
        #define OHCI_CTRL_HCFS          (3 << 6)