diff options
Diffstat (limited to 'drivers/usb/dwc2/drd.c')
-rw-r--r-- | drivers/usb/dwc2/drd.c | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c index aa6eb76f64dd..1b39c4776369 100644 --- a/drivers/usb/dwc2/drd.c +++ b/drivers/usb/dwc2/drd.c @@ -13,6 +13,10 @@ #include <linux/usb/role.h> #include "core.h" +#define dwc2_ovr_gotgctl(gotgctl) \ + ((gotgctl) |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN | \ + GOTGCTL_DBNCE_FLTR_BYPASS) + static void dwc2_ovr_init(struct dwc2_hsotg *hsotg) { unsigned long flags; @@ -21,9 +25,12 @@ static void dwc2_ovr_init(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); gotgctl = dwc2_readl(hsotg, GOTGCTL); - gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN; - gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; + dwc2_ovr_gotgctl(gotgctl); gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST) + gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL; + else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL) + gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL; dwc2_writel(hsotg, gotgctl, GOTGCTL); spin_unlock_irqrestore(&hsotg->lock, flags); @@ -40,6 +47,9 @@ static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid) (!valid && !(gotgctl & GOTGCTL_ASESVLD))) return -EALREADY; + /* Always enable overrides to handle the resume case */ + dwc2_ovr_gotgctl(gotgctl); + gotgctl &= ~GOTGCTL_BVALOVAL; if (valid) gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL; @@ -59,6 +69,9 @@ static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid) (!valid && !(gotgctl & GOTGCTL_BSESVLD))) return -EALREADY; + /* Always enable overrides to handle the resume case */ + dwc2_ovr_gotgctl(gotgctl); + gotgctl &= ~GOTGCTL_AVALOVAL; if (valid) gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL; @@ -105,6 +118,14 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) spin_lock_irqsave(&hsotg->lock, flags); + if (role == USB_ROLE_NONE) { + /* default operation mode when usb role is USB_ROLE_NONE */ + if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST) + role = USB_ROLE_HOST; + else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL) + role = USB_ROLE_DEVICE; + } + if (role == USB_ROLE_HOST) { already = dwc2_ovr_avalid(hsotg, true); } else if (role == USB_ROLE_DEVICE) { @@ -146,6 +167,7 @@ int dwc2_drd_init(struct dwc2_hsotg *hsotg) if (!device_property_read_bool(hsotg->dev, "usb-role-switch")) return 0; + hsotg->role_sw_default_mode = usb_get_role_switch_default_mode(hsotg->dev); role_sw_desc.driver_data = hsotg; role_sw_desc.fwnode = dev_fwnode(hsotg->dev); role_sw_desc.set = dwc2_drd_role_sw_set; @@ -183,6 +205,31 @@ void dwc2_drd_suspend(struct dwc2_hsotg *hsotg) void dwc2_drd_resume(struct dwc2_hsotg *hsotg) { u32 gintsts, gintmsk; + enum usb_role role; + + if (hsotg->role_sw) { + /* get last known role (as the get ops isn't implemented by this driver) */ + role = usb_role_switch_get_role(hsotg->role_sw); + + if (role == USB_ROLE_NONE) { + if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST) + role = USB_ROLE_HOST; + else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL) + role = USB_ROLE_DEVICE; + } + + /* restore last role that may have been lost */ + if (role == USB_ROLE_HOST) + dwc2_ovr_avalid(hsotg, true); + else if (role == USB_ROLE_DEVICE) + dwc2_ovr_bvalid(hsotg, true); + + dwc2_force_mode(hsotg, role == USB_ROLE_HOST); + + dev_dbg(hsotg->dev, "resuming %s-session valid\n", + role == USB_ROLE_NONE ? "No" : + role == USB_ROLE_HOST ? "A" : "B"); + } if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) { gintsts = dwc2_readl(hsotg, GINTSTS); |