From: LeoLiu-oc LeoLiu-oc@zhaoxin.com
zhaoxin inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40QDN CVE: NA
----------------------------------------------------------------
If we plug in a LS/FS device on USB2 port of EHCI, it will latch a wakeup signal in EHCI internal. This is a bug of EHCI for Some project of ZhaoXin. If enable EHCI runtime suspend and no device attach. PM core will let EHCI go to D3 to save power. However, once EHCI go to D3, it will release wakeup signal that latched on device connect to port during S0. Which will generate a SCI interrupt and bring EHCI to D0. But without device connect, EHCI will go to D3 again. So, there is suspend-resume loop and generate SCI interrupt Continuously.
In order to fix this issues, we need to clear the wakeup signal latched in EHCI when EHCI suspend function is called.
Signed-off-by: LeoLiu-oc LeoLiu-oc@zhaoxin.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/pci/pci-driver.c | 6 +++++- drivers/usb/host/ehci-hcd.c | 21 +++++++++++++++++++++ drivers/usb/host/ehci-pci.c | 4 ++++ drivers/usb/host/ehci.h | 1 + 4 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 8b587fc97f7b..c33d7e0a60b5 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -518,7 +518,11 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev) }
pci_restore_state(pci_dev); - pci_pme_restore(pci_dev); + if (!((pci_dev->vendor == PCI_VENDOR_ID_ZHAOXIN) && + (pci_dev->device == 0x3104) && + ((pci_dev->revision & 0xf0) == 0x90)) || + !(pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)) + pci_pme_restore(pci_dev); return 0; }
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8aff19ff8e8f..586e2735d6e0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1142,6 +1142,27 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) return -EBUSY; }
+ /*clear wakeup signal locked in S0 state when device plug in*/ + if (ehci->zx_wakeup_clear == 1) { + 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); + usleep_range(1000, 2000); + t1 &= (u32)~0xf0000; + ehci_writel(ehci, t1, reg); + usleep_range(1000, 2000); + t1 = ehci_readl(ehci, reg); + ehci_writel(ehci, t1 | PORT_CSC, reg); + udelay(500); + t1 = ehci_readl(ehci, &ehci->regs->status); + ehci_writel(ehci, t1 & STS_PCD, &ehci->regs->status); + ehci_readl(ehci, &ehci->regs->status); + } + return 0; } EXPORT_SYMBOL_GPL(ehci_suspend); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index e87cf3a00fa4..a5e27deda83a 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -222,6 +222,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci->has_synopsys_hc_bug = 1; } break; + case PCI_VENDOR_ID_ZHAOXIN: + if (pdev->device == 0x3104 && (pdev->revision & 0xf0) == 0x90) + ehci->zx_wakeup_clear = 1; + break; }
/* optional debug port, normally in the first BAR */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 59fd523c55f3..8da080a54668 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -219,6 +219,7 @@ struct ehci_hcd { /* one per controller */ unsigned need_oc_pp_cycle:1; /* MPC834X port power */ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */ unsigned is_aspeed:1; + unsigned zx_wakeup_clear:1;
/* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6)