From: Xu Yang <xu.yang_2@oss.nxp.com>
To: Vinod Koul <vkoul@kernel.org>,
Neil Armstrong <neil.armstrong@linaro.org>,
Frank Li <Frank.Li@nxp.com>,
Sascha Hauer <s.hauer@pengutronix.de>,
Pengutronix Kernel Team <kernel@pengutronix.de>,
Fabio Estevam <festevam@gmail.com>, Jun Li <jun.li@nxp.com>
Cc: linux-phy@lists.infradead.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Xu Yang <xu.yang_2@nxp.com>
Subject: [PATCH v6 3/6] phy: fsl-imx8mq-usb: add runtime PM support
Date: Wed, 15 Jul 2026 19:33:58 +0800 [thread overview]
Message-ID: <20260715-imx8mp-usb-phy-improvement-v6-3-00d95e270e4c@nxp.com> (raw)
In-Reply-To: <20260715-imx8mp-usb-phy-improvement-v6-0-00d95e270e4c@nxp.com>
From: Xu Yang <xu.yang_2@nxp.com>
Add runtime PM to ensure the PHY is properly powered and clocked during
register access, preventing potential system hangs.
It guards register access in the following scenarios:
- PHY operations: init() and power_on/off() callbacks are guarded by
phy core
- Type-C orientation switching when PHY/Controller are suspended which
needs explicitly care
- Future PHY control port register regmap debugfs access
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v6:
- use devm_pm_runtime_enable() to disable runtime PM when probe fails
- simply pm_runtime_get_sync/disable/put_noidle() to pm_runtime_resume()
Changes in v5:
- use non-devm PM runtime callback to correctly enable/disable clocks
when unbind the device
Changes in v4:
- replace guard() with PM_RUNTIME_ACQUIRE()
Changes in v3:
- new patch
---
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 67 +++++++++++++++++++++---------
1 file changed, 48 insertions(+), 19 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index 3a5788c609e1..bc804d2b5aee 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -9,6 +9,7 @@
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/typec_mux.h>
@@ -136,17 +137,15 @@ static int tca_blk_typec_switch_set(struct typec_switch_dev *sw,
{
struct imx8mq_usb_phy *imx_phy = typec_switch_get_drvdata(sw);
struct tca_blk *tca = imx_phy->tca;
- int ret;
if (tca->orientation == orientation)
return 0;
- ret = clk_prepare_enable(imx_phy->clk);
- if (ret)
- return ret;
+ PM_RUNTIME_ACQUIRE(&imx_phy->phy->dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
tca_blk_orientation_set(tca, orientation);
- clk_disable_unprepare(imx_phy->clk);
return 0;
}
@@ -620,16 +619,6 @@ static int imx8mq_phy_power_on(struct phy *phy)
if (ret)
return ret;
- ret = clk_prepare_enable(imx_phy->clk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(imx_phy->alt_clk);
- if (ret) {
- clk_disable_unprepare(imx_phy->clk);
- return ret;
- }
-
/* Disable rx term override */
value = readl(imx_phy->base + PHY_CTRL6);
value &= ~PHY_CTRL6_RXTERM_OVERRIDE_SEL;
@@ -648,8 +637,6 @@ static int imx8mq_phy_power_off(struct phy *phy)
value |= PHY_CTRL6_RXTERM_OVERRIDE_SEL;
writel(value, imx_phy->base + PHY_CTRL6);
- clk_disable_unprepare(imx_phy->alt_clk);
- clk_disable_unprepare(imx_phy->clk);
regulator_disable(imx_phy->vbus);
return 0;
@@ -686,6 +673,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct imx8mq_usb_phy *imx_phy;
const struct phy_ops *phy_ops;
+ int ret;
imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
if (!imx_phy)
@@ -693,13 +681,13 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, imx_phy);
- imx_phy->clk = devm_clk_get(dev, "phy");
+ imx_phy->clk = devm_clk_get_enabled(dev, "phy");
if (IS_ERR(imx_phy->clk)) {
dev_err(dev, "failed to get imx8mq usb phy clock\n");
return PTR_ERR(imx_phy->clk);
}
- imx_phy->alt_clk = devm_clk_get_optional(dev, "alt");
+ imx_phy->alt_clk = devm_clk_get_optional_enabled(dev, "alt");
if (IS_ERR(imx_phy->alt_clk))
return dev_err_probe(dev, PTR_ERR(imx_phy->alt_clk),
"Failed to get alt clk\n");
@@ -708,6 +696,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable runtime PM\n");
+
phy_ops = of_device_get_match_data(dev);
if (!phy_ops)
return -EINVAL;
@@ -737,15 +730,51 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
static void imx8mq_usb_phy_remove(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+
+ /* To allow devm-managed resources to be released cleanly */
+ if (pm_runtime_suspended(dev))
+ pm_runtime_resume(dev);
+}
+
+static int imx8mq_usb_phy_runtime_suspend(struct device *dev)
+{
+ struct imx8mq_usb_phy *imx_phy = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(imx_phy->alt_clk);
+ clk_disable_unprepare(imx_phy->clk);
+ return 0;
}
+static int imx8mq_usb_phy_runtime_resume(struct device *dev)
+{
+ struct imx8mq_usb_phy *imx_phy = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(imx_phy->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(imx_phy->alt_clk);
+ if (ret) {
+ clk_disable_unprepare(imx_phy->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx8mq_usb_phy_pm_ops, imx8mq_usb_phy_runtime_suspend,
+ imx8mq_usb_phy_runtime_resume, NULL);
+
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
.remove = imx8mq_usb_phy_remove,
.driver = {
.name = "imx8mq-usb-phy",
.of_match_table = imx8mq_usb_phy_of_match,
+ .pm = pm_ptr(&imx8mq_usb_phy_pm_ops),
.suppress_bind_attrs = true,
}
};
--
2.34.1
next prev parent reply other threads:[~2026-07-15 11:30 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-15 11:33 [PATCH v6 0/6] phy: fsl-imx8mq-usb: few improvements Xu Yang
2026-07-15 11:33 ` [PATCH v6 1/6] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path Xu Yang
2026-07-15 11:33 ` [PATCH v6 2/6] phy: fsl-imx8mq-usb: set usb phy to be wakeup capable Xu Yang
2026-07-15 11:33 ` Xu Yang [this message]
2026-07-15 15:28 ` [PATCH v6 3/6] phy: fsl-imx8mq-usb: add runtime PM support Frank Li
2026-07-16 11:32 ` Xu Yang
2026-07-16 12:41 ` Frank Li
2026-07-15 11:33 ` [PATCH v6 4/6] phy: fsl-imx8mq-usb: add control register regmap Xu Yang
2026-07-15 11:34 ` [PATCH v6 5/6] phy: fsl-imx8mq-usb: introduce per-variant driver data structure Xu Yang
2026-07-15 15:29 ` Frank Li
2026-07-15 11:34 ` [PATCH v6 6/6] phy: fsl-imx8mq-usb: keep PHY power domain runtime always-on for i.MX8MP Xu Yang
2026-07-15 15:32 ` Frank Li
2026-07-16 10:42 ` Xu Yang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260715-imx8mp-usb-phy-improvement-v6-3-00d95e270e4c@nxp.com \
--to=xu.yang_2@oss.nxp.com \
--cc=Frank.Li@nxp.com \
--cc=festevam@gmail.com \
--cc=imx@lists.linux.dev \
--cc=jun.li@nxp.com \
--cc=kernel@pengutronix.de \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-phy@lists.infradead.org \
--cc=neil.armstrong@linaro.org \
--cc=s.hauer@pengutronix.de \
--cc=vkoul@kernel.org \
--cc=xu.yang_2@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox