Merge tag 'for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux...
[deliverable/linux.git] / drivers / usb / chipidea / host.c
index 48731d0bab357a75232fdbd7b4063fe29af3ad4e..21fe1a31431356d5056d4c443eb982a15ff25534 100644 (file)
@@ -33,6 +33,7 @@
 #include "host.h"
 
 static struct hc_driver __read_mostly ci_ehci_hc_driver;
+static int (*orig_bus_suspend)(struct usb_hcd *hcd);
 
 struct ehci_ci_priv {
        struct regulator *reg_vbus;
@@ -43,11 +44,10 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
        struct device *dev = hcd->self.controller;
-       struct ci_hdrc *ci = dev_get_drvdata(dev);
        int ret = 0;
        int port = HCS_N_PORTS(ehci->hcs_params);
 
-       if (priv->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
+       if (priv->reg_vbus) {
                if (port > 1) {
                        dev_warn(dev,
                                "Not support multi-port regulator control\n");
@@ -113,12 +113,23 @@ static int host_start(struct ci_hdrc *ci)
        priv = (struct ehci_ci_priv *)ehci->priv;
        priv->reg_vbus = NULL;
 
-       if (ci->platdata->reg_vbus)
-               priv->reg_vbus = ci->platdata->reg_vbus;
+       if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
+               if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
+                       ret = regulator_enable(ci->platdata->reg_vbus);
+                       if (ret) {
+                               dev_err(ci->dev,
+                               "Failed to enable vbus regulator, ret=%d\n",
+                                                                       ret);
+                               goto put_hcd;
+                       }
+               } else {
+                       priv->reg_vbus = ci->platdata->reg_vbus;
+               }
+       }
 
        ret = usb_add_hcd(hcd, 0, 0);
        if (ret) {
-               goto put_hcd;
+               goto disable_reg;
        } else {
                struct usb_otg *otg = &ci->otg;
 
@@ -133,8 +144,15 @@ static int host_start(struct ci_hdrc *ci)
        if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
                hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
 
+       if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED)
+               hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
+
        return ret;
 
+disable_reg:
+       if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
+                       (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
+               regulator_disable(ci->platdata->reg_vbus);
 put_hcd:
        usb_put_hcd(hcd);
 
@@ -148,6 +166,9 @@ static void host_stop(struct ci_hdrc *ci)
        if (hcd) {
                usb_remove_hcd(hcd);
                usb_put_hcd(hcd);
+               if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
+                       (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
+                               regulator_disable(ci->platdata->reg_vbus);
        }
 }
 
@@ -158,6 +179,47 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci)
                host_stop(ci);
 }
 
+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 paths consumes too much
+                        * time (~24ms), in that case, the SOF will not
+                        * send out within 3ms after resume ends, then the
+                        * high speed device will enter full speed mode.
+                        */
+
+                       tmp = ehci_readl(ehci, &ehci->regs->command);
+                       tmp |= CMD_RUN;
+                       ehci_writel(ehci, tmp, &ehci->regs->command);
+                       /*
+                        * It needs a short delay between set RS bit and PHCD.
+                        */
+                       usleep_range(150, 200);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 int ci_hdrc_host_init(struct ci_hdrc *ci)
 {
        struct ci_role_driver *rdrv;
@@ -176,6 +238,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
        ci->roles[CI_ROLE_HOST] = rdrv;
 
        ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
+       orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
+       ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
 
        return 0;
 }
This page took 0.028416 seconds and 5 git commands to generate.