diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 97a6868c4e07..abf9aeca77b0 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -4219,6 +4219,21 @@ static inline const char *usb_role_string(enum usb_role role) return "Invalid"; } +static bool dwc3_msm_role_allowed(struct dwc3_msm *mdwc, enum usb_role role) +{ + dev_dbg(mdwc->dev, "%s: dr_mode=%s role_requested=%s\n", + __func__, usb_dr_modes[mdwc->dr_mode], + usb_role_string(role)); + + if (role == USB_ROLE_HOST && mdwc->dr_mode == USB_DR_MODE_PERIPHERAL) + return false; + + if (role == USB_ROLE_DEVICE && mdwc->dr_mode == USB_DR_MODE_HOST) + return false; + + return true; +} + static enum usb_role dwc3_msm_usb_get_role(struct device *dev) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); @@ -4242,6 +4257,9 @@ static int dwc3_msm_usb_set_role(struct device *dev, enum usb_role role) struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); enum usb_role cur_role = USB_ROLE_NONE; + if (!dwc3_msm_role_allowed(mdwc, role)) + return -EINVAL; + cur_role = dwc3_msm_usb_get_role(dev); switch (role) { @@ -4342,14 +4360,13 @@ static ssize_t mode_store(struct device *dev, struct device_attribute *attr, struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (sysfs_streq(buf, "peripheral")) { - if (mdwc->dr_mode == USB_DR_MODE_HOST) { - dev_err(dev, "Core supports host mode only.\n"); + if (!dwc3_msm_role_allowed(mdwc, USB_ROLE_DEVICE)) return -EINVAL; - } - mdwc->vbus_active = true; mdwc->id_state = DWC3_ID_FLOAT; } else if (sysfs_streq(buf, "host")) { + if (!dwc3_msm_role_allowed(mdwc, USB_ROLE_HOST)) + return -EINVAL; mdwc->vbus_active = false; mdwc->id_state = DWC3_ID_GROUND; } else {