diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-10-28 17:29:40 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-10-28 17:29:41 -0700 |
| commit | 33893757d5f40af69e9a1f80ffffee00a2eb0221 (patch) | |
| tree | 50d80b953a22d018a1a82395ae12b1e472638121 /drivers | |
| parent | 8146be04908d3efbe3c6e130f551edbfd7a9dab8 (diff) | |
| parent | 87b88b658b9b983b119f59396c08e4df1a211d59 (diff) | |
Merge branch 'net-pcs-xpcs-fix-runtime-pm-and-clock-lifecycle'
Coia Prant says:
====================
net: pcs: xpcs: fix runtime PM and clock lifecycle
This series fixes the runtime PM and CSR clock lifecycle in the DW XPCS
platform driver, a misleading error message in the Rockchip glue driver,
and the MAINTAINERS coverage for the platform driver and its binding.
The issue was found while using pcs-xpcs-plat.c as a reference for the
Rockchip XPCS glue driver (pcs-xpcs-rk.c). On Rockchip the CSR clock
(PCLK_XPCS) is required for register access, which exposed two problems
in the generic platform driver:
- it calls pm_runtime_set_active() before runtime PM is enabled and
before the clock is prepared/enabled, so a later suspend tries to
disable a clock that was never enabled;
- with CONFIG_PM disabled, the runtime resume path is compiled out
and the CSR clock is never enabled at all.
Patch 1 fixes the initial runtime PM status and registers a devm action
that force-suspends the device on teardown, so the clock does not leak
when the driver is removed while runtime-active.
Patch 2 handles !CONFIG_PM builds by preparing/enabling the CSR clock
manually and disabling it in the cleanup action. It also switches to
RUNTIME_PM_OPS() and pm_ptr(), which are no-ops when CONFIG_PM is
enabled, so that the runtime callbacks stay referenced and .pm is NULL
in !CONFIG_PM builds. Because the CSR clock is optional and both cases
are handled in the driver, Kconfig is left untouched and no PM
dependency is added.
Patch 3 rewords the cleanup action error message in the Rockchip glue
driver, which was merged into mainline today and currently mentions only
the EEE clock even though the action also tears down runtime PM.
Patch 4 lists pcs-xpcs-plat.c and its DT binding under the DW XPCS
entry. pcs-xpcs-plat.c currently only matches the ETHERNET PHY LIBRARY
entry, and the binding is only matched by the generic DT and networking
rules, so the entry does not cover the whole driver.
No upstream platform currently enables pcs-xpcs-plat with a real CSR
clock dependency, so neither runtime issue is reachable in mainline
today. Because of that, no Fixes: or Cc: stable tags are included, as
discussed in the v1 thread.
The patches touch code that is already in mainline: the platform driver
has been there since f6bb3e9d98c2, and the Rockchip glue driver was
merged today. They are fixes to existing code, not new drivers or new
features.
====================
Link: https://patch.msgid.link/20261023075214.1793922-2-coiaprant@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/pcs/pcs-xpcs-plat.c | 62 | ||||
| -rw-r--r-- | drivers/net/pcs/pcs-xpcs-rk.c | 2 |
2 files changed, 54 insertions, 10 deletions
diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c index e669a7b248ac..c4404500bac6 100644 --- a/drivers/net/pcs/pcs-xpcs-plat.c +++ b/drivers/net/pcs/pcs-xpcs-plat.c @@ -275,6 +275,15 @@ static int xpcs_plat_init_res(struct dw_xpcs_plat *pxpcs) return 0; } +#ifdef CONFIG_PM +static void xpcs_plat_exit_clk(void *data) +{ + struct dw_xpcs_plat *pxpcs = data; + struct device *dev = &pxpcs->pdev->dev; + + pm_runtime_force_suspend(dev); +} + static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs) { struct device *dev = &pxpcs->pdev->dev; @@ -285,15 +294,50 @@ static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs) return dev_err_probe(dev, PTR_ERR(pxpcs->cclk), "Failed to get CSR clock\n"); - pm_runtime_set_active(dev); - ret = devm_pm_runtime_enable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + ret = devm_add_action_or_reset(dev, xpcs_plat_exit_clk, pxpcs); + if (ret) { + dev_err(dev, "Failed to register devm for xpcs_plat_exit_clk: %d\n", ret); + return ret; + } + + return 0; +} +#else +static void xpcs_plat_exit_clk(void *data) +{ + struct dw_xpcs_plat *pxpcs = data; + + clk_disable_unprepare(pxpcs->cclk); +} + +static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs) +{ + struct device *dev = &pxpcs->pdev->dev; + int ret; + + pxpcs->cclk = devm_clk_get_optional(dev, "csr"); + if (IS_ERR(pxpcs->cclk)) + return dev_err_probe(dev, PTR_ERR(pxpcs->cclk), + "Failed to get CSR clock\n"); + + ret = clk_prepare_enable(pxpcs->cclk); + if (ret) { + dev_err(dev, "Failed to enable CSR clock: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, xpcs_plat_exit_clk, pxpcs); if (ret) { - dev_err(dev, "Failed to enable runtime-PM\n"); + dev_err(dev, "Failed to register devm for xpcs_plat_exit_clk: %d\n", ret); return ret; } return 0; } +#endif static int xpcs_plat_init_bus(struct dw_xpcs_plat *pxpcs) { @@ -398,7 +442,7 @@ static int xpcs_plat_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused xpcs_plat_pm_runtime_suspend(struct device *dev) +static int xpcs_plat_pm_runtime_suspend(struct device *dev) { struct dw_xpcs_plat *pxpcs = dev_get_drvdata(dev); @@ -407,7 +451,7 @@ static int __maybe_unused xpcs_plat_pm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused xpcs_plat_pm_runtime_resume(struct device *dev) +static int xpcs_plat_pm_runtime_resume(struct device *dev) { struct dw_xpcs_plat *pxpcs = dev_get_drvdata(dev); @@ -415,9 +459,9 @@ static int __maybe_unused xpcs_plat_pm_runtime_resume(struct device *dev) } static const struct dev_pm_ops xpcs_plat_pm_ops = { - SET_RUNTIME_PM_OPS(xpcs_plat_pm_runtime_suspend, - xpcs_plat_pm_runtime_resume, - NULL) + RUNTIME_PM_OPS(xpcs_plat_pm_runtime_suspend, + xpcs_plat_pm_runtime_resume, + NULL) }; DW_XPCS_INFO_DECLARE(xpcs_generic, DW_XPCS_ID_NATIVE, DW_XPCS_PMA_ID_NATIVE); @@ -446,7 +490,7 @@ static struct platform_driver xpcs_plat_driver = { .probe = xpcs_plat_probe, .driver = { .name = "dwxpcs", - .pm = &xpcs_plat_pm_ops, + .pm = pm_ptr(&xpcs_plat_pm_ops), .of_match_table = xpcs_of_ids, }, }; diff --git a/drivers/net/pcs/pcs-xpcs-rk.c b/drivers/net/pcs/pcs-xpcs-rk.c index 698a43b75334..6f0a110e32d2 100644 --- a/drivers/net/pcs/pcs-xpcs-rk.c +++ b/drivers/net/pcs/pcs-xpcs-rk.c @@ -365,7 +365,7 @@ static int xpcs_rk_init_clk(struct dw_xpcs_rk *pxpcs) ret = devm_add_action_or_reset(dev, xpcs_rk_exit_clk, pxpcs); if (ret) { - dev_err(dev, "Failed to register devm for EEE clock: %d\n", ret); + dev_err(dev, "Failed to register devm for xpcs_rk_exit_clk: %d\n", ret); return ret; } |
