usb: chipidea: host: add portpower override
authorMichael Grzeschik <m.grzeschik@pengutronix.de>
Mon, 13 Oct 2014 01:53:04 +0000 (09:53 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 3 Nov 2014 23:34:00 +0000 (15:34 -0800)
This patch adds an external portpower override callback, only single
port regulator control is supported now.

Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/chipidea/host.c

index ebde7b6ce687040dda25dbca4802b9daa13e9cfa..33027abb53491fffce5677f44ce25c9e7d58f574 100644 (file)
 
 static struct hc_driver __read_mostly ci_ehci_hc_driver;
 
+struct ehci_ci_priv {
+       struct regulator *reg_vbus;
+};
+
+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 (port > 1) {
+                       dev_warn(dev,
+                               "Not support multi-port regulator control\n");
+                       return 0;
+               }
+               if (enable)
+                       ret = regulator_enable(priv->reg_vbus);
+               else
+                       ret = regulator_disable(priv->reg_vbus);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed to %s vbus regulator, ret=%d\n",
+                               enable ? "enable" : "disable", ret);
+                       return ret;
+               }
+       }
+       return 0;
+};
+
+static const struct ehci_driver_overrides ehci_ci_overrides = {
+       .extra_priv_size = sizeof(struct ehci_ci_priv),
+       .port_power      = ehci_ci_portpower,
+};
+
 static irqreturn_t host_irq(struct ci_hdrc *ci)
 {
        return usb_hcd_irq(ci->irq, ci->hcd);
@@ -43,6 +81,7 @@ static int host_start(struct ci_hdrc *ci)
 {
        struct usb_hcd *hcd;
        struct ehci_hcd *ehci;
+       struct ehci_ci_priv *priv;
        int ret;
 
        if (usb_disabled())
@@ -68,23 +107,15 @@ static int host_start(struct ci_hdrc *ci)
        ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
        ehci->imx28_write_fix = ci->imx28_write_fix;
 
-       /*
-        * vbus is always on if host is not in OTG FSM mode,
-        * otherwise should be controlled by OTG FSM
-        */
-       if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
-               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;
-               }
-       }
+       priv = (struct ehci_ci_priv *)ehci->priv;
+       priv->reg_vbus = NULL;
+
+       if (ci->platdata->reg_vbus)
+               priv->reg_vbus = ci->platdata->reg_vbus;
 
        ret = usb_add_hcd(hcd, 0, 0);
        if (ret) {
-               goto disable_reg;
+               goto put_hcd;
        } else {
                struct usb_otg *otg = ci->transceiver->otg;
 
@@ -100,10 +131,6 @@ static int host_start(struct ci_hdrc *ci)
 
        return ret;
 
-disable_reg:
-       if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
-               regulator_disable(ci->platdata->reg_vbus);
-
 put_hcd:
        usb_put_hcd(hcd);
 
@@ -117,8 +144,6 @@ 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))
-                       regulator_disable(ci->platdata->reg_vbus);
        }
 }
 
@@ -146,7 +171,7 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
        rdrv->name      = "host";
        ci->roles[CI_ROLE_HOST] = rdrv;
 
-       ehci_init_driver(&ci_ehci_hc_driver, NULL);
+       ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
 
        return 0;
 }