usb: dwc3-msm: Do not powerdown SSPHY if auto suspend is disabled

On certain platforms, due to a HW issue, the SSPHY keeps
transitioning in and out of p3 whenever we perform any HS
transfers. This leads to a storm of PWR_EVNT irgs.
Fix this by disabling SSPHY autosuspend on such platforms.
Autosuspend will be enabled as part of dwc3_msm_prepare_suspend
so that SSPHY gets a chance to go into P3.

Change-Id: I4e674f780a72a8c4abcb3e6c9f083f23b1faa196
Signed-off-by: Aniket Randive <arandive@codeaurora.org>
This commit is contained in:
Aniket Randive 2020-12-10 11:20:52 +05:30 • committed by Gerrit - the friendly Code Review server
commit bebc6b630d

View file

@ -2671,57 +2671,71 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)
}
static void dwc3_msm_dp_ssphy_autosuspend(struct dwc3_msm *mdwc)
/*
* Suspend SSPHY0/SSPHY1 based on the index. 'idx' argument will choose
* appropriate PWR_EVT_IRQ_STAT and GUSBPIPECTL registers.
*
* The return value is a boolean flag denoting if SSPHY was suspended or not.
*/
static bool dwc3_msm_ssphy_autosuspend(struct dwc3_msm *mdwc, int idx)
{
unsigned long timeout;
u32 reg = 0, reg1 = 0;
u32 reg = 0, stat_reg;
bool suspended = false;
idx = !!idx;
stat_reg = idx ? PWR_EVNT_IRQ_STAT_REG1 : PWR_EVNT_IRQ_STAT_REG;
/* pwr_evt_irq not configured for dual port; clear in_p3 explicitly */
atomic_set(&mdwc->in_p3, 0);
/* Clear previous P3 events */
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG,
PWR_EVNT_POWERDOWN_IN_P3_MASK | PWR_EVNT_POWERDOWN_OUT_P3_MASK);
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1,
dwc3_msm_write_reg(mdwc->base, stat_reg,
PWR_EVNT_POWERDOWN_IN_P3_MASK | PWR_EVNT_POWERDOWN_OUT_P3_MASK);
/* Prepare SSPHYs for suspend */
reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(0));
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(0),
reg | DWC3_GUSB3PIPECTL_SUSPHY);
reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(1));
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(1),
/* Prepare SSPHY for suspend */
reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(idx));
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(idx),
reg | DWC3_GUSB3PIPECTL_SUSPHY);
/* Wait for PHYs to go into P3 */
/* Wait for SSPHY to go into P3 */
timeout = jiffies + msecs_to_jiffies(5);
while (!time_after(jiffies, timeout)) {
reg = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG);
reg1 = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1);
if ((reg & PWR_EVNT_POWERDOWN_IN_P3_MASK) &&
(reg1 & PWR_EVNT_POWERDOWN_IN_P3_MASK)) {
atomic_set(&mdwc->in_p3, 1);
reg = dwc3_msm_read_reg(mdwc->base, stat_reg);
if (reg & PWR_EVNT_POWERDOWN_IN_P3_MASK) {
suspended = true;
break;
}
}
/* Clear P3 event bit */
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG,
PWR_EVNT_POWERDOWN_IN_P3_MASK);
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1,
PWR_EVNT_POWERDOWN_IN_P3_MASK);
dwc3_msm_write_reg(mdwc->base, stat_reg, PWR_EVNT_POWERDOWN_IN_P3_MASK);
return suspended;
}
static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc, bool ignore_p3_state)
{
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
unsigned long timeout;
u32 reg = 0;
bool ssphy0_sus, ssphy1_sus;
/* Allow SSPHYs to go to P3 for dual port controllers */
if (mdwc->dual_port)
dwc3_msm_dp_ssphy_autosuspend(mdwc);
/* Allow SSPHY(s) to go to P3 state if SSPHY autosuspend is disabled */
if (dwc->dis_u3_susphy_quirk) {
/* Clear in_p3 and allow SSPHY suspend explicitly */
atomic_set(&mdwc->in_p3, 0);
ssphy0_sus = dwc3_msm_ssphy_autosuspend(mdwc, 0);
if (mdwc->ss_phy1)
ssphy1_sus = dwc3_msm_ssphy_autosuspend(mdwc, 1);
if (!mdwc->dual_port)
atomic_set(&mdwc->in_p3, ssphy0_sus);
else
atomic_set(&mdwc->in_p3, ssphy0_sus & ssphy1_sus);
}
/* SSPHY(s) should be in P3 to detect port events */
if (!ignore_p3_state && ((mdwc->in_host_mode || mdwc->in_device_mode)
&& (dwc3_msm_is_superspeed(mdwc) || mdwc->dual_port) &&
&& (dwc3_msm_is_superspeed(mdwc) || dwc->dis_u3_susphy_quirk) &&
!mdwc->in_restart)) {
if (!atomic_read(&mdwc->in_p3)) {
dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
@ -4989,7 +5003,7 @@ static int dwc3_msm_host_notifier(struct notifier_block *nb,
* platforms, power leakage is not a concern. Also, as a part of USB
* suspend sequence, we will enable autosuspend for SSPHYs to go to P3.
*/
if (mdwc->dual_port)
if (dwc->dis_u3_susphy_quirk)
return NOTIFY_DONE;
/*