/* Check if A-Session is already in the right state */ if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
(!valid && !(gotgctl & GOTGCTL_ASESVLD))) return -EALREADY;
/* Always enable overrides to handle the resume case */
dwc2_ovr_gotgctl(gotgctl);
/* Check if B-Session is already in the right state */ if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
(!valid && !(gotgctl & GOTGCTL_BSESVLD))) return -EALREADY;
/* Always enable overrides to handle the resume case */
dwc2_ovr_gotgctl(gotgctl);
/* Skip session not in line with dr_mode */ if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
(role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)) return -EINVAL;
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) /* Skip session if core is in test mode */ if (role == USB_ROLE_NONE && hsotg->test_mode) {
dev_dbg(hsotg->dev, "Core is in test mode\n"); return -EBUSY;
} #endif
/* * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of * the probe and enabled on udc_start. * If role-switch set is called before the udc_start, we need to enable * the clock to read/write GOTGCTL and GUSBCFG registers to override * mode and sessions. It is the case if cable is plugged at boot.
*/ if (!hsotg->ll_hw_enabled && hsotg->clk) { int ret = clk_prepare_enable(hsotg->clk);
if (ret) return ret;
}
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; elseif (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
role = USB_ROLE_DEVICE;
}
if (role == USB_ROLE_HOST) {
already = dwc2_ovr_avalid(hsotg, true);
} elseif (role == USB_ROLE_DEVICE) {
already = dwc2_ovr_bvalid(hsotg, true); if (dwc2_is_device_enabled(hsotg)) { /* This clear DCTL.SFTDISCON bit */
dwc2_hsotg_core_connect(hsotg);
}
} else { if (dwc2_is_device_mode(hsotg)) { if (!dwc2_ovr_bvalid(hsotg, false)) /* This set DCTL.SFTDISCON bit */
dwc2_hsotg_core_disconnect(hsotg);
} else {
dwc2_ovr_avalid(hsotg, false);
}
}
spin_unlock_irqrestore(&hsotg->lock, flags);
if (!already && hsotg->dr_mode == USB_DR_MODE_OTG) /* This will raise a Connector ID Status Change Interrupt */
dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
if (!hsotg->ll_hw_enabled && hsotg->clk)
clk_disable_unprepare(hsotg->clk);
dev_dbg(hsotg->dev, "%s-session valid\n",
role == USB_ROLE_NONE ? "No" :
role == USB_ROLE_HOST ? "A" : "B");
return 0;
}
int dwc2_drd_init(struct dwc2_hsotg *hsotg)
{ struct usb_role_switch_desc role_sw_desc = {0}; struct usb_role_switch *role_sw; int ret;
if (!device_property_read_bool(hsotg->dev, "usb-role-switch")) return 0;
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; elseif (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); elseif (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");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.