Support VBUS power control using regulator framework. Enables the regulator while the port is in host mode. The "vbus-supply" property can be provided using a usb-connector child node and standard devicetree bindings. Signed-off-by: Mike Looijmans <mike.looijmans@xxxxxxxx> --- v2: Add missing devm_regulator_get call which got lost during rebase v3: Remove devicetree binding, use standard usb-connector Fix probe fail when vbus-supply is not present drivers/usb/dwc3/core.c | 38 ++++++++++++++++++++++++++++++-------- drivers/usb/dwc3/core.h | 4 ++++ drivers/usb/dwc3/drd.c | 6 ++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index edc17155cb2b..42e804ede1b3 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -25,6 +25,7 @@ #include <linux/of.h> #include <linux/acpi.h> #include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/usb/ch9.h> @@ -112,6 +113,23 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) dwc->current_dr_role = mode; } +void dwc3_set_vbus(struct dwc3 *dwc, bool enable) +{ + int ret; + + if (enable != dwc->vbus_reg_enabled) { + if (enable) + ret = regulator_enable(dwc->vbus_reg); + else + ret = regulator_disable(dwc->vbus_reg); + if (!ret) + dwc->vbus_reg_enabled = enable; + } + + if (dwc->usb2_phy) + otg_set_vbus(dwc->usb2_phy->otg, enable); +} + static void __dwc3_set_mode(struct work_struct *work) { struct dwc3 *dwc = work_to_dwc(work); @@ -164,8 +182,7 @@ static void __dwc3_set_mode(struct work_struct *work) if (ret) { dev_err(dwc->dev, "failed to initialize host\n"); } else { - if (dwc->usb2_phy) - otg_set_vbus(dwc->usb2_phy->otg, true); + dwc3_set_vbus(dwc, true); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); } @@ -173,8 +190,7 @@ static void __dwc3_set_mode(struct work_struct *work) case DWC3_GCTL_PRTCAP_DEVICE: dwc3_event_buffers_setup(dwc); - if (dwc->usb2_phy) - otg_set_vbus(dwc->usb2_phy->otg, false); + dwc3_set_vbus(dwc, false); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); @@ -1183,8 +1199,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) case USB_DR_MODE_PERIPHERAL: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); - if (dwc->usb2_phy) - otg_set_vbus(dwc->usb2_phy->otg, false); + dwc3_set_vbus(dwc, false); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); @@ -1198,8 +1213,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) case USB_DR_MODE_HOST: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); - if (dwc->usb2_phy) - otg_set_vbus(dwc->usb2_phy->otg, true); + dwc3_set_vbus(dwc, true); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); @@ -1470,6 +1484,14 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_get_properties(dwc); + dwc->vbus_reg = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(dwc->vbus_reg)) { + if (PTR_ERR(dwc->vbus_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dwc->vbus_reg = NULL; + } + dwc->reset = devm_reset_control_array_get(dev, true, true); if (IS_ERR(dwc->reset)) return PTR_ERR(dwc->reset); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4c171a8e215f..cee2574d7bf4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1085,6 +1085,9 @@ struct dwc3 { bool phys_ready; + struct regulator *vbus_reg; + bool vbus_reg_enabled; + struct ulpi *ulpi; bool ulpi_ready; @@ -1397,6 +1400,7 @@ struct dwc3_gadget_ep_cmd_params { /* prototypes */ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); +void dwc3_set_vbus(struct dwc3 *dwc, bool enable); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 7db1ffc92bbd..45fdec2d128d 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -384,8 +384,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) if (ret) { dev_err(dwc->dev, "failed to initialize host\n"); } else { - if (dwc->usb2_phy) - otg_set_vbus(dwc->usb2_phy->otg, true); + dwc3_set_vbus(dwc, true); if (dwc->usb2_generic_phy) phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); @@ -398,8 +397,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) dwc3_event_buffers_setup(dwc); spin_unlock_irqrestore(&dwc->lock, flags); - if (dwc->usb2_phy) - otg_set_vbus(dwc->usb2_phy->otg, false); + dwc3_set_vbus(dwc, false); if (dwc->usb2_generic_phy) phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); -- 2.17.1