Skip to content

Commit

Permalink
usb: dwc3: Set SUSPENDENABLE soon after phy init
Browse files Browse the repository at this point in the history
After phy initialization, some phy operations can only be executed while
in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and
GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid blocking
phy ops.

Previously the SUSPENDENABLE bits are only set after the controller
initialization, which may not happen right away if there's no gadget
driver or xhci driver bound. Revise this to clear SUSPENDENABLE bits
only when there's mode switching (change in GCTL.PRTCAPDIR).

Fixes: 6d73572 ("usb: dwc3: core: Prevent phy suspend during init")
Cc: stable <stable@kernel.org>
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/633aef0afee7d56d2316f7cc3e1b2a6d518a8cc9.1738280911.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Thinh Nguyen authored and Greg Kroah-Hartman committed Mar 4, 2025
1 parent 2b66ef8 commit cc5bfc4
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 30 deletions.
69 changes: 42 additions & 27 deletions drivers/usb/dwc3/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,24 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
}
}

void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
{
unsigned int hw_mode;
u32 reg;

reg = dwc3_readl(dwc->regs, DWC3_GCTL);

/*
* For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
* GUSB2PHYCFG.SUSPHY should be cleared during mode switching,
* and they can be set after core initialization.
*/
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !ignore_susphy) {
if (DWC3_GCTL_PRTCAP(reg) != mode)
dwc3_enable_susphy(dwc, false);
}

reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
Expand Down Expand Up @@ -216,7 +229,7 @@ static void __dwc3_set_mode(struct work_struct *work)

spin_lock_irqsave(&dwc->lock, flags);

dwc3_set_prtcap(dwc, desired_dr_role);
dwc3_set_prtcap(dwc, desired_dr_role, false);

spin_unlock_irqrestore(&dwc->lock, flags);

Expand Down Expand Up @@ -658,16 +671,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
*/
reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;

/*
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration.
* So default value will be '0' when the core is reset. Application
* needs to set it to '1' after the core initialization is completed.
*
* Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be
* cleared after power-on reset, and it can be set after core
* initialization.
*/
/* Ensure the GUSB3PIPECTL.SUSPENDENABLE is cleared prior to phy init. */
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;

if (dwc->u2ss_inp3_quirk)
Expand Down Expand Up @@ -747,15 +751,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
break;
}

/*
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration.
* So default value will be '0' when the core is reset. Application
* needs to set it to '1' after the core initialization is completed.
*
* Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared
* after power-on reset, and it can be set after core initialization.
*/
/* Ensure the GUSB2PHYCFG.SUSPHY is cleared prior to phy init. */
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;

if (dwc->dis_enblslpm_quirk)
Expand Down Expand Up @@ -830,6 +826,25 @@ static int dwc3_phy_init(struct dwc3 *dwc)
goto err_exit_usb3_phy;
}

/*
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB3PIPECTL_SUSPHY and DWC3_GUSB2PHYCFG_SUSPHY to '0' during
* coreConsultant configuration. So default value will be '0' when the
* core is reset. Application needs to set it to '1' after the core
* initialization is completed.
*
* Certain phy requires to be in P0 power state during initialization.
* Make sure GUSB3PIPECTL.SUSPENDENABLE and GUSB2PHYCFG.SUSPHY are clear
* prior to phy init to maintain in the P0 state.
*
* After phy initialization, some phy operations can only be executed
* while in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and
* GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid
* blocking phy ops.
*/
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
dwc3_enable_susphy(dwc, true);

return 0;

err_exit_usb3_phy:
Expand Down Expand Up @@ -1588,7 +1603,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)

switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false);

if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
Expand All @@ -1600,7 +1615,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
return dev_err_probe(dev, ret, "failed to initialize gadget\n");
break;
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false);

if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
Expand Down Expand Up @@ -1645,7 +1660,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
}

/* de-assert DRVVBUS for HOST and OTG mode */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
}

static void dwc3_get_software_properties(struct dwc3 *dwc)
Expand Down Expand Up @@ -2453,15 +2468,15 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;

dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
dwc3_gadget_resume(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true);
break;
}
/* Restore GUSB2PHYCFG bits that were modified in suspend */
Expand Down Expand Up @@ -2490,7 +2505,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;

dwc3_set_prtcap(dwc, dwc->current_dr_role);
dwc3_set_prtcap(dwc, dwc->current_dr_role, true);

dwc3_otg_init(dwc);
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1558,7 +1558,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)

/* prototypes */
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);

Expand Down
4 changes: 2 additions & 2 deletions drivers/usb/dwc3/drd.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void dwc3_otg_init(struct dwc3 *dwc)
* block "Initialize GCTL for OTG operation".
*/
/* GCTL.PrtCapDir=2'b11 */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
/* GUSB2PHYCFG0.SusPHY=0 */
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
Expand Down Expand Up @@ -556,7 +556,7 @@ int dwc3_drd_init(struct dwc3 *dwc)

dwc3_drd_update(dwc);
} else {
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);

/* use OTG block to get ID event */
irq = dwc3_otg_get_irq(dwc);
Expand Down

0 comments on commit cc5bfc4

Please sign in to comment.