This patch adds an hub_control override to toggle the PORT_POWER by the registered regulator. That is needed when an external GPIO is used instead of the default PORT_POWER bit. Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx> --- drivers/usb/chipidea/host.c | 102 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index ebde7b6..ea84cc8 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -34,6 +34,76 @@ static struct hc_driver __read_mostly ci_ehci_hc_driver; +struct ehci_ci_priv { + struct regulator *reg_vbus; + bool vbus_enabled; +}; + +static const struct ehci_driver_overrides ehci_ci_overrides = { + .extra_priv_size = sizeof(struct ehci_ci_priv), +}; + +static int ehci_ci_portpower(struct usb_hcd *hcd, int 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; + int ret = 0; + + if (IS_ERR_OR_NULL(priv->reg_vbus)) + return 0; + + if (enable && !priv->vbus_enabled) + ret = regulator_enable(priv->reg_vbus); + else if (!enable && priv->vbus_enabled) + ret = regulator_disable(priv->reg_vbus); + + if (ret < 0) { + dev_err(dev, + "Failed to %s vbus regulator, ret=%d\n", + enable ? "enable" : "disable", ret); + return ret; + } + + priv->vbus_enabled = enable; + + return 0; +} + +static int ci_ehci_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int ports = HCS_N_PORTS(ehci->hcs_params); + struct device *dev = hcd->self.controller; + struct ci_hdrc *ci = dev_get_drvdata(dev); + int ret = 0; + + switch (typeReq) { + case SetPortFeature: + case ClearPortFeature: + if (!wIndex || wIndex > ports) + return -EPIPE; + + if (wValue != USB_PORT_FEAT_POWER) + break; + + if (!ci_otg_is_fsm_mode(ci)) + ret = ehci_ci_portpower(hcd, typeReq == SetPortFeature); + if (ret) + return ret; + break; + } + + return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); +} + static irqreturn_t host_irq(struct ci_hdrc *ci) { return usb_hcd_irq(ci->irq, ci->hcd); @@ -43,6 +113,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 +139,16 @@ 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; + priv->vbus_enabled = 0; + + 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 +164,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); @@ -115,10 +175,9 @@ static void host_stop(struct ci_hdrc *ci) struct usb_hcd *hcd = ci->hcd; if (hcd) { + ehci_ci_portpower(hcd, 0); 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 +205,8 @@ 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); + ci_ehci_hc_driver.hub_control = ci_ehci_hub_control; return 0; } -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html