From 3e51f3b3f36e36221e74635e7ff7a543eea87d77 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Tue, 5 May 2026 19:54:34 +0530 Subject: [PATCH 1/2] WORKAROUND: clk: qcom: gdsc: Add gdsc_synced_poweroff_disable callback USB and PCIe GDSCs must remain enabled during system suspend for USB host mode and PCIe non-D3cold use cases. These GDSCs currently use PWRSTS_RET_ON in the GDSC driver, which prevents the gdsc driver from disabling them in hardware after the first enable. Consequently, they remain enabled indefinitely, even after their consumers become inactive, preventing the GDSCs from being powered off when no longer needed. Instead, use the GenPDs synced_poweroff flag to control whether a GDSC is disabled during suspend. USB and PCIe drivers can leave the flag unset when the GDSC must remain enabled, and set it when the GDSC should be powered off. This allows consumer drivers to have more dynamic control over the GDSC power state based on their current use case. The existing gdsc_gx_gdsc() callback also disables the GDSC based on synced_poweroff flag but it polls for GDSC status during disable if synced poweroff flag is set. But few PCIE GDSCs have a VOTABLE flag and should not be polled for status during disable. Hence introduce a new gdsc_synced_poweroff_disable() callback that disables the GDSC based on GenPD synced_poweroff flag but won't poll for status if GDSC has VOTABLE flag. Signed-off-by: Jagadeesh Kona --- drivers/clk/qcom/gdsc.c | 29 +++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 3 ++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index f419a28f616b6..2f067d1eb9f19 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -730,3 +730,32 @@ int gdsc_gx_disable(struct generic_pm_domain *domain) return 0; } EXPORT_SYMBOL_GPL(gdsc_gx_disable); + +/* + * USB and PCIE GDSCs require GDSC to be kept ON even during system system + * for USB host mode and PCIE non-D3 cold usecases. The below callback can + * be used for such GDSCs where the consumer drivers can set GenPD's + * synced_poweroff flag using dev_pm_genpd_synced_poweroff() before suspend + * to disable the GDSC and can avoid setting the flag to keep the GDSC ON + * during suspend. + */ +int gdsc_synced_poweroff_disable(struct generic_pm_domain *domain) +{ + struct gdsc *sc = domain_to_gdsc(domain); + + /* + * For GDSCs with VOTABLE flag(e.g. PCIE), status must not be polled + * during disable. However, gdsc_disable() polls for status when + * synced_poweroff is set. Use gdsc_toggle_logic() with wait = false + * to skip status polling for VOTABLE GDSCs during power-off. + */ + if (domain->synced_poweroff) + return gdsc_toggle_logic(sc, GDSC_OFF, false); + + /* Remove parent-supply placed in enable */ + if (sc->rsupply) + return regulator_disable(sc->rsupply); + + return 0; +} +EXPORT_SYMBOL_GPL(gdsc_synced_poweroff_disable); diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index 1acb5c97ccd96..d62f4b63172f8 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -94,7 +94,7 @@ int gdsc_register(struct gdsc_desc *desc, struct reset_controller_dev *, void gdsc_unregister(struct gdsc_desc *desc); int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain); int gdsc_gx_disable(struct generic_pm_domain *domain); -#define gdsc_synced_poweroff_disable gdsc_gx_disable +int gdsc_synced_poweroff_disable(struct generic_pm_domain *domain); #else static inline int gdsc_register(struct gdsc_desc *desc, struct reset_controller_dev *rcdev, @@ -104,5 +104,6 @@ static inline int gdsc_register(struct gdsc_desc *desc, } static inline void gdsc_unregister(struct gdsc_desc *desc) {}; +static int gdsc_synced_poweroff_disable(struct generic_pm_domain *domain) {}; #endif /* CONFIG_QCOM_GDSC */ #endif /* __QCOM_GDSC_H__ */ From d2679cd748cbc8859cf14d286994cedf96586c63 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 24 Sep 2026 15:40:36 +0530 Subject: [PATCH 2/2] WORKAROUND: clk: qcom: gcc-glymur: Use synced poweroff for USB GDSC's Use the gdsc_synced_poweroff_disable() as power_off callback for USB GDSCs on Glymur platform. This allows the GDSCs to be disabled during suspend only when the consumer driver explicitly calls dev_pm_genpd_synced_poweroff() before suspend. Consumer drivers can therefore keep the GDSCs powered on during system suspend by not calling dev_pm_genpd_synced_poweroff() before suspend. Signed-off-by: Jagadeesh Kona --- drivers/clk/qcom/gcc-glymur.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/qcom/gcc-glymur.c b/drivers/clk/qcom/gcc-glymur.c index 6925c6865089c..df7f8cff9bd9d 100644 --- a/drivers/clk/qcom/gcc-glymur.c +++ b/drivers/clk/qcom/gcc-glymur.c @@ -7784,6 +7784,7 @@ static struct gdsc gcc_usb20_prim_gdsc = { .clk_dis_wait_val = 0xf, .pd = { .name = "gcc_usb20_prim_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7796,6 +7797,7 @@ static struct gdsc gcc_usb30_mp_gdsc = { .clk_dis_wait_val = 0xf, .pd = { .name = "gcc_usb30_mp_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7808,6 +7810,7 @@ static struct gdsc gcc_usb30_prim_gdsc = { .clk_dis_wait_val = 0xf, .pd = { .name = "gcc_usb30_prim_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7820,6 +7823,7 @@ static struct gdsc gcc_usb30_sec_gdsc = { .clk_dis_wait_val = 0xf, .pd = { .name = "gcc_usb30_sec_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7832,6 +7836,7 @@ static struct gdsc gcc_usb30_tert_gdsc = { .clk_dis_wait_val = 0xf, .pd = { .name = "gcc_usb30_tert_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7844,6 +7849,7 @@ static struct gdsc gcc_usb3_mp_ss0_phy_gdsc = { .clk_dis_wait_val = 0x2, .pd = { .name = "gcc_usb3_mp_ss0_phy_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7856,6 +7862,7 @@ static struct gdsc gcc_usb3_mp_ss1_phy_gdsc = { .clk_dis_wait_val = 0x2, .pd = { .name = "gcc_usb3_mp_ss1_phy_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7904,6 +7911,7 @@ static struct gdsc gcc_usb_0_phy_gdsc = { .clk_dis_wait_val = 0x2, .pd = { .name = "gcc_usb_0_phy_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7916,6 +7924,7 @@ static struct gdsc gcc_usb_1_phy_gdsc = { .clk_dis_wait_val = 0x2, .pd = { .name = "gcc_usb_1_phy_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, @@ -7928,6 +7937,7 @@ static struct gdsc gcc_usb_2_phy_gdsc = { .clk_dis_wait_val = 0x2, .pd = { .name = "gcc_usb_2_phy_gdsc", + .power_off = gdsc_synced_poweroff_disable, }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE,