[PATCH RFC v3 06/12] phy: freescale: s32g: Add SerDes subsystem PHY
sashiko-bot at kernel.org
sashiko-bot at kernel.org
Sat Sep 19 23:54:56 PDT 2026
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] mm: Use-After-Free in s32g_serdes_pcs_create() via lockless drvdata access
- [High] driver: Race condition exposing PHY provider before hardware initialization
- [High] pm: Sleep in atomic context via noirq sleep callbacks
- [Medium] driver: Infinite probe deferral on unconfigured XPCS instance
- [Medium] pm: Missing resource cleanup on resume error path
--
commit 179aba95589906304ef5b4ba6f8b771475155fa2
Author: Vincent Guittot <vincent.guittot at linaro.org>
phy: freescale: s32g: Add SerDes subsystem PHY
The S32G SoC family includes two SerDes subsystems, each made of one
PCIe controller, two XPCS and a shared 2-lane combo PHY. The lanes can
be configured to carry PCIe and/or SGMII.
Add the SerDes subsystem PHY driver: the PCIe PHY, the shared combo-PHY
bring-up, and the setup of the XPCS instances added by the previous
patch.
> diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> new file mode 100644
> index 0000000000000..e81878137f2e1
> --- /dev/null
> +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> @@ -0,0 +1,1057 @@
[ ... ]
> +static int s32g_serdes_init(struct s32g_serdes *serdes)
> +{
> + struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> + u32 reg0;
> + int ret;
> +
> + ret = clk_bulk_prepare_enable(ctrl->nclks, ctrl->clks);
> + if (ret) {
> + dev_err(serdes->dev, "Failed to enable SerDes clocks\n");
> + return ret;
> + }
[ ... ]
> + /* Wait for the selection of working mode (as per the manual specs) */
> + usleep_range(100, 110);
[ ... ]
> +static int s32g_serdes_create_pcie_phy(struct s32g_serdes *serdes,
> + struct device_node *child_node)
> +{
> + struct phy_provider *phy_provider;
> + struct device *dev = serdes->dev;
> + struct phy *phy;
> +
> + phy = devm_phy_create(dev, child_node, &serdes_pcie_ops);
> + if (IS_ERR(phy))
> + return PTR_ERR(phy);
> +
> + phy_set_drvdata(phy, serdes);
> +
> + phy->attrs.mode = PHY_MODE_PCIE;
> + serdes->pcie.phy = phy;
> +
> + phy_provider = devm_of_phy_provider_register(&phy->dev,
> + s32g_serdes_phy_xlate);
[Severity: High]
Does this expose the PHY provider before the hardware is initialized?
s32g_serdes_parse_lanes() registers the PHY provider via
devm_of_phy_provider_register(), making it visible to consumers. The hardware
clocks and resets aren't initialized until s32g_serdes_init() is called later
in s32g_serdes_probe().
If a consumer probes concurrently and calls phy_power_on(), could it access
unclocked registers and cause a synchronous external abort?
> + if (IS_ERR(phy_provider))
> + return PTR_ERR(phy_provider);
> +
> + return 0;
> +}
[ ... ]
> +static int s32g_serdes_probe(struct platform_device *pdev)
> +{
[ ... ]
> + ret = s32g_serdes_parse_lanes(dev, serdes);
> + if (ret)
> + return ret;
[ ... ]
> + ret = s32g_serdes_init(serdes);
> + if (ret)
> + return ret;
[ ... ]
> +static int s32g_serdes_resume(struct device *device)
> +{
> + struct s32g_serdes *serdes = dev_get_drvdata(device);
> + struct s32g_pcie_ctrl *pcie = &serdes->pcie;
> + int ret;
> +
> + ret = s32g_serdes_init(serdes);
> + if (ret) {
> + dev_err(device, "Failed to initialize\n");
> + return ret;
> + }
> +
> + /* Restore PCIe phy power */
> + if (pcie->powered_on) {
> + ret = s32g_pcie_phy_power_on(serdes);
> + if (ret)
> + dev_err(device, "Failed to power-on PCIe phy\n");
> + }
> +
> + return ret;
[Severity: Medium]
Does this leak clock resources on the error path?
If s32g_pcie_phy_power_on() fails, the function returns an error without
undoing the hardware initialization performed earlier in s32g_serdes_init().
The PM core will mark the device as having failed to resume, but the leaked
clock enables might prevent the power domain from fully shutting down.
> +}
[ ... ]
> +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev,
> + struct device_node *np)
> +{
[ ... ]
> + serdes = platform_get_drvdata(pdev);
> + if (!serdes) {
> + put_device(&pdev->dev);
> + return ERR_PTR(-EPROBE_DEFER);
> + }
[Severity: High]
Can this race with driver unbind leading to a use-after-free?
This function is exposed to consumer drivers and reads drvdata locklessly.
If the SerDes driver is unbound concurrently via sysfs,
dev_set_drvdata(dev, NULL) is called and the devm_kzalloc'd serdes
structure is freed by devres.
If platform_get_drvdata() reads the pointer just before the unbind, won't
accessing serdes->xpcs.phys[instance] below dereference freed memory?
> +
> + if (!serdes->xpcs.phys[instance]) {
> + put_device(&pdev->dev);
> + return ERR_PTR(-EPROBE_DEFER);
[Severity: Medium]
Will this cause an infinite probe deferral loop if the instance is unconfigured?
If the SerDes has probed successfully (meaning drvdata is non-NULL) but
intentionally did not populate serdes->xpcs.phys[instance] due to lane
configuration mismatch, this instance will never be populated. Would it be
better to return an error rather than -EPROBE_DEFER in this case?
> + }
[ ... ]
> +/* noirq, like the PCIe controller using this PHY */
> +static const struct dev_pm_ops s32g_serdes_pm_ops = {
> + NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_serdes_suspend,
> + s32g_serdes_resume)
[Severity: High]
Does this call sleepable functions in an atomic context?
The driver assigns s32g_serdes_resume() to the .resume_noirq callback, which
executes with hardware interrupts disabled. However, s32g_serdes_resume() calls
s32g_serdes_init(), which invokes clk_bulk_prepare_enable() (takes a mutex)
and usleep_range() (sleeps via hrtimers).
> +};
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260919-s32g_serdes-v3-0-9d68868c1e89@oss.nxp.com?part=6
More information about the linux-phy
mailing list