[PATCH] phy: renesas: phy-rzg3e-usb3: Fix runtime PM underflow during suspend
Ovidiu Panait
ovidiu.panait.rb at renesas.com
Mon Apr 27 12:47:41 PDT 2026
On the Renesas RZ/V2H platform, if the xhcd driver is unbound and the
system is suspended afterwards, a PM underflow error will occur:
# echo 15850000.usb > /sys/bus/platform/drivers/xhci-renesas-hcd/unbind
# systemctl suspend
15870000.usb-phy: PM: dpm_run_callback(): genpd_resume_noirq returns -13
15870000.usb-phy: PM: failed to resume noirq: error -13
15870000.usb-phy: Runtime PM usage count underflow!
Since the PHY framework is managing the runtime PM of the PHY via
phy_power_on()/phy_power_off(), there is no need for the PHY driver to
manipulate the runtime PM state during suspend.
To fix this, remove the runtime PM calls from the suspend/resume paths
and add a get/put pair inside rzg3e_phy_usb3_init_helper() to make sure
the clock is enabled during init, even when there is no consumer for
the PHY.
Also, change the suspend ops from NOIRQ_SYSTEM_SLEEP_PM_OPS to
SYSTEM_SLEEP_PM_OPS because runtime PM is disabled during the noirq phase
and pm_runtime_resume_and_get() would not actually enable the device clock.
Fixes: ee5f1a3f90a4 ("phy: renesas: Add Renesas RZ/G3E USB3.0 PHY driver")
Signed-off-by: Ovidiu Panait <ovidiu.panait.rb at renesas.com>
---
drivers/phy/renesas/phy-rzg3e-usb3.c | 31 ++++++++++++++++------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/drivers/phy/renesas/phy-rzg3e-usb3.c b/drivers/phy/renesas/phy-rzg3e-usb3.c
index 6b3453ea0004..055775e1a0f7 100644
--- a/drivers/phy/renesas/phy-rzg3e-usb3.c
+++ b/drivers/phy/renesas/phy-rzg3e-usb3.c
@@ -64,6 +64,7 @@
#define USB3_TEST_LANECONFIG0_DEFAULT (0xd)
struct rz_usb3 {
+ struct device *dev;
void __iomem *base;
struct reset_control *rstc;
bool skip_reinit;
@@ -130,11 +131,21 @@ static int rzg3e_phy_usb3test_phy_init(void __iomem *base)
return 0;
}
-static int rzg3e_phy_usb3_init_helper(void __iomem *base)
+static int rzg3e_phy_usb3_init_helper(struct rz_usb3 *r)
{
- rzg3e_phy_usb2test_phy_init(base);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(r->dev);
+ if (ret)
+ return ret;
+
+ rzg3e_phy_usb2test_phy_init(r->base);
- return rzg3e_phy_usb3test_phy_init(base);
+ ret = rzg3e_phy_usb3test_phy_init(r->base);
+
+ pm_runtime_put_sync(r->dev);
+
+ return ret;
}
static int rzg3e_phy_usb3_init(struct phy *p)
@@ -143,7 +154,7 @@ static int rzg3e_phy_usb3_init(struct phy *p)
int ret = 0;
if (!r->skip_reinit)
- ret = rzg3e_phy_usb3_init_helper(r->base);
+ ret = rzg3e_phy_usb3_init_helper(r);
return ret;
}
@@ -187,6 +198,7 @@ static int rzg3e_phy_usb3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, r);
phy_set_drvdata(phy, r);
+ r->dev = dev;
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
@@ -199,7 +211,6 @@ static int rzg3e_phy_usb3_suspend(struct device *dev)
{
struct rz_usb3 *r = dev_get_drvdata(dev);
- pm_runtime_put(dev);
reset_control_assert(r->rstc);
r->skip_reinit = false;
@@ -215,27 +226,21 @@ static int rzg3e_phy_usb3_resume(struct device *dev)
if (ret)
return ret;
- ret = pm_runtime_resume_and_get(dev);
+ ret = rzg3e_phy_usb3_init_helper(r);
if (ret)
goto reset_assert;
- ret = rzg3e_phy_usb3_init_helper(r->base);
- if (ret)
- goto pm_put;
-
r->skip_reinit = true;
return 0;
-pm_put:
- pm_runtime_put(dev);
reset_assert:
reset_control_assert(r->rstc);
return ret;
}
static const struct dev_pm_ops rzg3e_phy_usb3_pm = {
- NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3e_phy_usb3_suspend, rzg3e_phy_usb3_resume)
+ SYSTEM_SLEEP_PM_OPS(rzg3e_phy_usb3_suspend, rzg3e_phy_usb3_resume)
};
static const struct of_device_id rzg3e_phy_usb3_match_table[] = {
--
2.34.1
More information about the linux-phy
mailing list