Skip to content

Commit ba6e518

Browse files
lategoodbyegregkh
authored andcommitted
usb: dwc2: Implement recovery after PM domain off
According to the dt-bindings there are some platforms, which have a dedicated USB power domain for DWC2 IP core supply. If the power domain is switched off during system suspend then all USB register will lose their settings. Use GUSBCFG_TOUTCAL as a canary to detect that the power domain has been powered off during suspend. Since the GOTGCTL_CURMODE_HOST doesn't match on all platform with the current mode, additionally backup GINTSTS. This works reliable to decide which registers should be restored. Signed-off-by: Stefan Wahren <wahrenst@gmx.net> Reviewed-by: Douglas Anderson <dianders@chromium.org> Link: https://lore.kernel.org/r/20250217134132.36786-4-wahrenst@gmx.net Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8b7a1b3 commit ba6e518

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

drivers/usb/dwc2/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
4343
/* Backup global regs */
4444
gr = &hsotg->gr_backup;
4545

46+
gr->gintsts = dwc2_readl(hsotg, GINTSTS);
4647
gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
4748
gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
4849
gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);

drivers/usb/dwc2/core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ struct dwc2_hw_params {
667667
/**
668668
* struct dwc2_gregs_backup - Holds global registers state before
669669
* entering partial power down
670+
* @gintsts: Backup of GINTSTS register
670671
* @gotgctl: Backup of GOTGCTL register
671672
* @gintmsk: Backup of GINTMSK register
672673
* @gahbcfg: Backup of GAHBCFG register
@@ -683,6 +684,7 @@ struct dwc2_hw_params {
683684
* @valid: True if registers values backuped.
684685
*/
685686
struct dwc2_gregs_backup {
687+
u32 gintsts;
686688
u32 gotgctl;
687689
u32 gintmsk;
688690
u32 gahbcfg;

drivers/usb/dwc2/platform.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
685685
regulator_disable(dwc2->usb33d);
686686
}
687687

688+
if (is_device_mode)
689+
ret = dwc2_gadget_backup_critical_registers(dwc2);
690+
else
691+
ret = dwc2_host_backup_critical_registers(dwc2);
692+
693+
if (ret)
694+
return ret;
695+
688696
if (dwc2->ll_hw_enabled &&
689697
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
690698
ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -694,6 +702,24 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
694702
return ret;
695703
}
696704

705+
static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg)
706+
{
707+
struct dwc2_gregs_backup *gr;
708+
709+
gr = &hsotg->gr_backup;
710+
711+
if (!gr->valid) {
712+
dev_err(hsotg->dev, "No valid register backup, failed to restore\n");
713+
return -EINVAL;
714+
}
715+
716+
if (gr->gintsts & GINTSTS_CURMODE_HOST)
717+
return dwc2_host_restore_critical_registers(hsotg);
718+
719+
return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
720+
DWC2_RESTORE_DCFG);
721+
}
722+
697723
static int __maybe_unused dwc2_resume(struct device *dev)
698724
{
699725
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
@@ -706,6 +732,18 @@ static int __maybe_unused dwc2_resume(struct device *dev)
706732
}
707733
dwc2->phy_off_for_suspend = false;
708734

735+
/*
736+
* During suspend it's possible that the power domain for the
737+
* DWC2 controller is disabled and all register values get lost.
738+
* In case the GUSBCFG register is not initialized, it's clear the
739+
* registers must be restored.
740+
*/
741+
if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) {
742+
ret = dwc2_restore_critical_registers(dwc2);
743+
if (ret)
744+
return ret;
745+
}
746+
709747
if (dwc2->params.activate_stm_id_vb_detection) {
710748
unsigned long flags;
711749
u32 ggpio, gotgctl;

0 commit comments

Comments
 (0)