]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ENGR00286426-20 usb: chipidea: host: add quirk for ehci operation
authorPeter Chen <peter.chen@freescale.com>
Sun, 29 Sep 2013 03:07:23 +0000 (11:07 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Mon, 16 Jun 2014 16:05:59 +0000 (18:05 +0200)
For chipidea controller, it does not follow ehci spec strictly.
Taking resume signal as an example, it will stop resume signal about
20-21ms later automatically, but standard ehci spec says, the resume
signal is controlled by software (clear portsc.PORT_RESUME).

This operation causes some remote wakeup problems for high speed
devices due to host controller does not send SOF in time since
software can't guarantee set run/stop bit in time (run/stop bit
was cleared at the ehci suspend routine).

When software sets run/stop bit, it needs 1 SoF time to make it effect.
If we close the PHY clock just after setting run/stop bit, it does
not be set in practice, so a software delay is needed.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
drivers/usb/chipidea/host.c

index 177ad484bb9ac37ad16c66678c5670f694b9108c..b5e455b86a3160e146fb122e6ebf56ee7e9fb61c 100644 (file)
 #include "host.h"
 
 static struct hc_driver __read_mostly ci_ehci_hc_driver;
+static int (*orig_bus_suspend)(struct usb_hcd *hcd);
+
+static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
+{
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       int port;
+       u32 tmp;
+
+       int ret = orig_bus_suspend(hcd);
+
+       if (ret)
+               return ret;
+
+       port = HCS_N_PORTS(ehci->hcs_params);
+       while (port--) {
+               u32 __iomem *reg = &ehci->regs->port_status[port];
+               u32 portsc = ehci_readl(ehci, reg);
+
+               if (portsc & PORT_CONNECT) {
+                       /*
+                        * For chipidea, the resume signal will be ended
+                        * automatically, so for remote wakeup case, the
+                        * usbcmd.rs may not be set before the resume has
+                        * ended if other resume path consumes too much
+                        * time (~23ms-24ms), in that case, the SOF will not
+                        * send out within 3ms after resume ends, then the
+                        * device will enter suspend again.
+                        */
+                       if (hcd->self.root_hub->do_remote_wakeup) {
+                               ehci_dbg(ehci,
+                                       "Remote wakeup is enabled, "
+                                       "and device is on the port\n");
+
+                               tmp = ehci_readl(ehci, &ehci->regs->command);
+                               tmp |= CMD_RUN;
+                               ehci_writel(ehci, tmp, &ehci->regs->command);
+                               /*
+                                * It needs a short delay between set RUNSTOP
+                                * and set PHCD.
+                                */
+                               udelay(125);
+                       }
+               }
+       }
+
+       return 0;
+}
 
 static irqreturn_t host_irq(struct ci_hdrc *ci)
 {
@@ -146,5 +193,9 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
 
        ehci_init_driver(&ci_ehci_hc_driver, NULL);
 
+       orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
+
+       ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
+
        return 0;
 }