Re: [PATCH RESEND 05/17] phy: spacemit: add Innosilicon DP TX PHY driver
Cody Kang <[email protected]>
| Newsgroups | dev.linux.lists.sashiko-reviews,org.freedesktop.lists.dri-devel,org.infradead.lists.linux-phy,org.kernel.vger.linux-devicetree |
|---|---|
| Message-ID | <gsv53gzsbs4selyf5ws3f77ff7odhf6xsbf62qb6tcftns7ibc@dpop7iwzwvzb> |
On Sat, Jul 25, 2026 at 05:02:54AM +0000, [email protected] wrote: > Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: > - [High] PHY provider is registered before probe finishes, exposing it to early concurrent access and potential Use-After-Free (UAF) on probe failure. > - [High] Lack of driver-level synchronization between CCF and PHY frameworks leads to concurrent hardware state corruption. > - [Medium] The MPLL (main link PLL) is powered on during PHY initialization but is never powered off in the power down sequence. > - [Medium] Missing hardware state cleanup on error paths in `k3_inno_dp_phy_power_on` leaves the PHY powered on. > - [Medium] Rate configuration functions unconditionally power on the PLLs, overriding the logical PHY power state. > - [Medium] PLL programming functions do not poll for hardware lock before proceeding. > -- > > --- Patch [5]: [PATCH RESEND 05/17] phy: spacemit: add Innosilicon DP TX PHY driver --- > commit 31db74eae7dc59ddc61373dbe3e2c6c1e6966ccd > Author: Cody Kang <[email protected]> > Subject: phy: spacemit: add Innosilicon DP TX PHY driver > > This commit introduces the SpacemiT K3 Innosilicon DisplayPort PHY driver. > It integrates the PHY as a child of the DP controller, registering a PHY > provider and exposing the pixel PLL as a clock provider. > > > diff --git a/drivers/phy/spacemit/phy-k3-inno-dp.c b/drivers/phy/spacemit/phy-k3-inno-dp.c > > new file mode 100644 > > index 0000000000000..9dd62edb7a5af > > --- /dev/null > > +++ b/drivers/phy/spacemit/phy-k3-inno-dp.c > > @@ -0,0 +1,948 @@ > > [ ... ] > > > +static int k3_inno_dp_phy_power_on(struct phy *phy) > > +{ > > + struct k3_inno_dp_phy *p = phy_get_drvdata(phy); > > + u32 lane_en; > > + u32 val; > > + int ret; > > [ ... ] > > > + ret = regmap_read_poll_timeout(p->regmap, DPTX_ANA_MPLL, val, > > + val & DPTX_ANA_MPLL_LOCKED, > > + 2000, 10 * 1000); > > + if (ret) { > > + dev_err(p->dev, "DP PHY core PLL lock timed out\n"); > > + return ret; > > + } > > [Severity: Medium] > If the MPLL or PREPLL fails to lock within 10ms, this immediately returns an > error without restoring the power down bits or XMIT_EN. > > Since the generic PHY framework does not automatically call phy_power_off() > when phy_power_on() fails, could this leave the hardware running in an > inconsistent state and leak power? Real; will fix in v2: the lock-timeout paths will park the lanes and the pixel PLL, the same set power_off parks. > > > + > > + ret = regmap_read_poll_timeout(p->regmap, DPTX_ANA_PREPLL, val, > > + val & DPTX_ANA_PREPLL_LOCKED, > > + 2000, 10 * 1000); > > + if (ret) > > + dev_err(p->dev, "DP PHY pixel PLL lock timed out\n"); > > + > > + return ret; > > +} > > + > > +static int k3_inno_dp_phy_power_off(struct phy *phy) > > +{ > > + struct k3_inno_dp_phy *p = phy_get_drvdata(phy); > > + > > + regmap_write_bits(p->regmap, DPTX_PHY_CTRL, DPTX_PHY_CTRL_XMIT_EN, 0); > > + usleep_range(2000, 4000); > > + > > + regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PD, > > + DPTX_ANA_PREPLL_PD); > > + usleep_range(2000, 4000); > > + > > + return 0; > > +} > > [Severity: Medium] > Does this power down sequence omit the MPLL? > > The k3_inno_dp_phy_power_on() function powers up the MPLL by clearing > DPTX_ANA_MPLL_PD, but this power_off function does not appear to re-assert > it. Could this leave the main link PLL running indefinitely when the DP > controller is suspended or torn down? Intentional, and it took a board round to prove it: the MPLL also feeds the 16 MHz AUX reference, and the controller parks the PHY through power_off on every link drop while AUX still has to serve EDID, DPCD and panel detection. Parking the MPLL here kills AUX from bind onward. Will add a comment in v2 saying so. > > [ ... ] > > > +static void k3_inno_dp_phy_program_mpll(struct k3_inno_dp_phy *p, > > + const struct k3_inno_dp_mpll_cfg *cfg) > > +{ > > + struct regmap *rm = p->regmap; > > [ ... ] > > > + regmap_write_bits(rm, DPTX_ANA_PREPLL_CTRL, DPTX_ANA_PREPLL_LOCK_BYPEN, > > + FIELD_PREP(DPTX_ANA_PREPLL_LOCK_BYPEN, 1)); > > + > > + regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_PD, > > + FIELD_PREP(DPTX_ANA_MPLL_PD, 0)); > > [Severity: Medium] > Does unconditionally writing 0 to DPTX_ANA_MPLL_PD override the logical > power state of the PHY? > > If a DRM driver configures the link rate (calling phy_configure()) while the > PHY is logically powered off, this will force the PLL to run and > desynchronize the hardware from the PHY framework's power state. Not reachable: the PHY has a single consumer, the parent DP controller, and it only configures rates from the enable path, never while the PHY is logically off. > > > + usleep_range(2000, 4000); > > +} > > [Severity: Medium] > Is it safe to rely on a fixed 2-4ms sleep here instead of polling > DPTX_ANA_MPLL_LOCKED? > > The power_on() function polls the locked bit for up to 10ms. A fixed delay > here might proceed with an unstable clock if the hardware takes longer to > lock, potentially causing display failures or link training issues. power_on polls both PLL lock bits after programming; that is the lock gate for the whole bring-up sequence. > > [ ... ] > > > +static void k3_inno_dp_program_prepll(struct k3_inno_dp_phy *p, > > + const struct k3_inno_dp_prepll_cfg *cfg) > > +{ > > + regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PD, > > + FIELD_PREP(DPTX_ANA_PREPLL_PD, 1)); > > + usleep_range(2000, 4000); > > [Severity: High] > Is a driver-level lock needed to synchronize access to the PHY registers > between the CCF and PHY frameworks? > > Since this driver exposes both a clock (via CCF) and a PHY that share the > same hardware registers, concurrent calls to clk_set_rate() and > phy_power_on() can interleave. > > If clk_set_rate() begins this multi-step programming sequence, a concurrent > phy_power_on() could clear the PD bit while the clock is still being > configured, corrupting the hardware state. No concurrent callers exist: the single consumer serializes all clk_set_rate() and phy_power_on/off() calls through its own modeset path. > > [ ... ] > > > + regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, > > + DPTX_ANA_PREPLL_PCLK_NORMAL, > > + FIELD_PREP(DPTX_ANA_PREPLL_PCLK_NORMAL, 1)); > > + usleep_range(2000, 4000); > > + > > + regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PD, > > + FIELD_PREP(DPTX_ANA_PREPLL_PD, 0)); > > [Severity: Medium] > Similar to the MPLL configuration, does unconditionally clearing > DPTX_ANA_PREPLL_PD here override the PHY power state if the clock rate is > set while the PHY is logically off? Same as the MPLL case above: rates are only set from the enable path. > > > + usleep_range(2000, 4000); > > +} > > [Severity: Medium] > Is it safe to rely on a fixed delay here instead of polling the > DPTX_ANA_PREPLL_LOCKED bit, which could lead to proceeding with an unstable > pixel clock? Same: power_on polls DPTX_ANA_PREPLL_LOCKED before the sequence completes. > > [ ... ] > > > +static int k3_inno_dp_phy_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct k3_inno_dp_phy *p; > > + struct phy_provider *provider; > > + struct clk_init_data init = {}; > > + int ret; > > [ ... ] > > > + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > > + if (IS_ERR(provider)) > > + return dev_err_probe(dev, PTR_ERR(provider), > > + "failed to register PHY provider\n"); > > + > > + /* PHY takes a 24 MHz reference clock */ > > + p->ref_clk_khz = 24000; > > + > > + /* The APMU pixel-clock mux references this clock by name. */ > > + of_property_read_string(dev->of_node, "clock-output-names", &init.name); > > + if (!init.name) > > + init.name = dev_name(dev); > > + init.ops = &k3_inno_dp_pxclk_ops; > > + init.parent_names = NULL; > > + init.num_parents = 0; > > + init.flags = 0; > > + p->pxclk_hw.init = &init; > > + > > + ret = devm_clk_hw_register(dev, &p->pxclk_hw); > > + if (ret) > > + return dev_err_probe(dev, ret, > > + "failed to register pixel clock\n"); > > [Severity: High] > Can the PHY provider be accessed by consumers before probe completes > successfully? > > The PHY provider is registered via devm_of_phy_provider_register() before > the pixel clock is registered. If devm_clk_hw_register() fails, probe will > return an error and devres will free the driver context struct. > > If a consumer driver acquires a reference to the PHY during this race > window, it could retain the reference after probe fails, leading to a > use-after-free when it attempts PHY operations on the freed pointer. Taken: will register the PHY and clock providers last in probe in v2. Cody