Commit 09c96980 authored by John Youn's avatar John Youn Committed by Felipe Balbi

usb: dwc2: Add functions to set and clear force mode

Added functions to set force mode for host and device. These functions
will check the current mode and only force if needed thus avoiding
unnecessary force mode delays. However clearing the mode is currently
done unconditionally and with the delay in place. This is needed during
the connector ID status change interrupt in order to ensure that the
mode has changed properly. This preserves the old behavior only for this
case. The warning comment about this is moved into the clear mode
condition.
Signed-off-by: default avatarJohn Youn <johnyoun@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 263b7fb5
...@@ -520,44 +520,129 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) ...@@ -520,44 +520,129 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
} }
/* /*
* Do core a soft reset of the core. Be careful with this because it * Force the mode of the controller.
* resets all the internal state machines of the core.
* *
* Additionally this will apply force mode as per the hsotg->dr_mode * Forcing the mode is needed for two cases:
* parameter. *
* 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
* controller to stay in a particular mode regardless of ID pin
* changes. We do this usually after a core reset.
*
* 2) During probe we want to read reset values of the hw
* configuration registers that are only available in either host or
* device mode. We may need to force the mode if the current mode does
* not allow us to access the register in the mode that we want.
*
* In either case it only makes sense to force the mode if the
* controller hardware is OTG capable.
*
* Checks are done in this function to determine whether doing a force
* would be valid or not.
*
* If a force is done, it requires a 25ms delay to take effect.
*
* Returns true if the mode was forced.
*/ */
int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg) static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{ {
int retval;
u32 gusbcfg; u32 gusbcfg;
u32 set;
u32 clear;
retval = dwc2_core_reset(hsotg); dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
if (retval)
return retval; /*
* Force mode has no effect if the hardware is not OTG.
*/
if (!dwc2_hw_is_otg(hsotg))
return false;
/*
* If dr_mode is either peripheral or host only, there is no
* need to ever force the mode to the opposite mode.
*/
if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
return false;
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
return false;
if (hsotg->dr_mode == USB_DR_MODE_HOST) {
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
} else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg |= GUSBCFG_FORCEDEVMODE; set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
/*
* If the force mode bit is already set, don't set it.
*/
if ((gusbcfg & set) && !(gusbcfg & clear))
return false;
gusbcfg &= ~clear;
gusbcfg |= set;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
} else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
msleep(25);
return true;
}
/*
* Clears the force mode bits.
*/
static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~GUSBCFG_FORCEDEVMODE; gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
}
/* /*
* NOTE: This long sleep is _very_ important, otherwise the core will * NOTE: This long sleep is _very_ important, otherwise the core will
* not stay in host mode after a connector ID change! * not stay in host mode after a connector ID change!
*/ */
usleep_range(150000, 160000); usleep_range(150000, 160000);
}
/*
* Sets or clears force mode based on the dr_mode parameter.
*/
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
dwc2_force_mode(hsotg, true);
break;
case USB_DR_MODE_PERIPHERAL:
dwc2_force_mode(hsotg, false);
break;
case USB_DR_MODE_OTG:
dwc2_clear_force_mode(hsotg);
break;
default:
dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
__func__, hsotg->dr_mode);
break;
}
}
/*
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*
* Additionally this will apply force mode as per the hsotg->dr_mode
* parameter.
*/
int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
{
int retval;
retval = dwc2_core_reset(hsotg);
if (retval)
return retval;
dwc2_force_dr_mode(hsotg);
return 0; return 0;
} }
...@@ -3117,6 +3202,22 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, ...@@ -3117,6 +3202,22 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
dwc2_set_param_hibernation(hsotg, params->hibernation); dwc2_set_param_hibernation(hsotg, params->hibernation);
} }
/*
* Forces either host or device mode if the controller is not
* currently in that mode.
*
* Returns true if the mode was forced.
*/
static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
{
if (host && dwc2_is_host_mode(hsotg))
return false;
else if (!host && dwc2_is_device_mode(hsotg))
return false;
return dwc2_force_mode(hsotg, host);
}
/** /**
* During device initialization, read various hardware configuration * During device initialization, read various hardware configuration
* registers and interpret the contents. * registers and interpret the contents.
......
...@@ -886,6 +886,8 @@ extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg); ...@@ -886,6 +886,8 @@ extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
/* /*
* Host core Functions. * Host core Functions.
* The following functions support managing the DWC_otg controller in host * The following functions support managing the DWC_otg controller in host
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment