Linux-PHY Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 5/5] phy: qcom: snps-femto-v2: Fix possible NULL-deref on early runtime suspend
From: Loic Poulain @ 2026-02-05 16:02 UTC (permalink / raw)
  To: vkoul, kishon
  Cc: linux-arm-msm, linux-phy, dmitry.baryshkov, neil.armstrong,
	konrad.dybcio, Loic Poulain, Abel Vesa
In-Reply-To: <20260205160240.748371-1-loic.poulain@oss.qualcomm.com>

Enabling runtime PM before attaching the hsphy instance as driver data
can lead to a NULL pointer dereference in runtime PM callbacks that
expect valid driver data. There is a small window where the suspend
callback may run after PM runtime enabling and before runtime forbid.

Attach the hsphy instance as driver data before enabling runtime PM to
prevent NULL pointer dereference in runtime PM callbacks.

Reorder pm_runtime_enable() and pm_runtime_forbid() to prevent a
short window where an unnecessary runtime suspend can occur.

Use the devres-managed version to ensure PM runtime is symmetrically
disabled during driver removal for proper cleanup.

Fixes: 0d75f508a9d5 ("phy: qcom-snps: Add runtime suspend and resume handlers")
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
 drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
index eb0b0f61d98e..d1288a6c202e 100644
--- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
+++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
@@ -599,13 +599,17 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
 		return dev_err_probe(dev, ret,
 				     "failed to get regulator supplies\n");
 
-	pm_runtime_set_active(dev);
-	pm_runtime_enable(dev);
+	dev_set_drvdata(dev, hsphy);
+
 	/*
-	 * Prevent runtime pm from being ON by default. Users can enable
-	 * it using power/control in sysfs.
+	 * Enable runtime PM support, but forbid it by default.
+	 * Users can allow it again via the power/control attribute in sysfs.
 	 */
+	pm_runtime_set_active(dev);
 	pm_runtime_forbid(dev);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
 
 	generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);
 	if (IS_ERR(generic_phy)) {
@@ -615,15 +619,12 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
 	}
 	hsphy->phy = generic_phy;
 
-	dev_set_drvdata(dev, hsphy);
 	phy_set_drvdata(generic_phy, hsphy);
 	qcom_snps_hsphy_read_override_param_seq(dev);
 
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 	if (!IS_ERR(phy_provider))
 		dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
-	else
-		pm_runtime_disable(dev);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* Re: Lynx 10G SerDes Driver on my kernel
From: Tanjeff Moos @ 2026-02-05 16:47 UTC (permalink / raw)
  To: Vladimir Oltean; +Cc: Sean Anderson, linux-phy@lists.infradead.org
In-Reply-To: <20251205124423.gddjli3jtqlvkrkv@skbuf>

Hello Vladimir,

I wish you a great year 2026!

Meanwhile I ported some of your code and I got my networking partially 
running. Details follow, but first I have a question.


== Question ==

The lynx-10g driver disables the managed lanes in lynx_10g_init(), and 
they are never enabled afterwards. Probably I didn't port the associated 
code, but can't find where it is.

I modified lynx_10g_init() to leave the lane enabled, which solves the 
problem, but this is a dirty hack.

Question: How is the lane supposed to be enabled?


== Status of my work ==

In case you are interested I provide some details here.

I ported only part of your code (tag lf-6.12.49-2.2.0), mainly the 
lynx-10g driver plus some changes you did in pcs-lynx.c, phy-core.c, 
guts.c, the dpaa drivers plus their associated header files. I 
deliberately omitted code dealing with c72 autonegotiation (for 
backplane), as I don't need it. I'm still working with kernel 6.6.119 
plus OpenWRT patches and cannot easily upgrade.

So far, I have a 2.5 GbE PHY and can run the following modes:
- 100M : SGMII
- 1G:    SGMII
- 2.5G:  2500BaseX

Switching forth and back the modes works seamlessly (with my dirty hack 
to power on the lane!). But I didn't test RCW rewriting as it is not 
needed in this scenario.

Kind regards so far, Tanjeff



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 3/4 v2] phy: s32g: Add serdes xpcs subsystem
From: Vincent Guittot @ 2026-02-05 17:02 UTC (permalink / raw)
  To: Russell King (Oracle)
  Cc: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
	s32, p.zabel, ghennadi.procopciuc, Ionut.Vicovan, linux-phy,
	devicetree, linux-kernel, linux-arm-kernel, netdev, horms,
	Frank.li
In-Reply-To: <aYNl3yQXNa7SkpH9@shell.armlinux.org.uk>

On Wed, 4 Feb 2026 at 16:29, Russell King (Oracle)
<linux@armlinux.org.uk> wrote:
>
> Sorry, I don't have time to finish this review, nor cut down the context
> as I normally would do... I'm being bothered on company Slack, which now
> has the really bloody annoying feature of popping up a window rather than
> using KDE's notification subsystem, and that steals keyboard focus away
> from whatever one is trying to do at the time.
>
> On Tue, Feb 03, 2026 at 05:19:16PM +0100, Vincent Guittot wrote:
> > +static bool s32g_xpcs_poll_timeout(struct s32g_xpcs *xpcs, xpcs_poll_func_t func,
> > +                                ktime_t timeout)
> > +{
> > +     ktime_t cur = ktime_get();
> > +
> > +     return func(xpcs) || ktime_after(cur, timeout);
> > +}
> > +
> > +static int s32g_xpcs_wait(struct s32g_xpcs *xpcs, xpcs_poll_func_t func)
> > +{
> > +     ktime_t timeout = ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS);
> > +
> > +     spin_until_cond(s32g_xpcs_poll_timeout(xpcs, func, timeout));
> > +     if (!func(xpcs))
> > +             return -ETIMEDOUT;
>
> XPCS_TIMEOUT_MS is 300ms. spin_until_cond() spins until the condition is
> true. Do you need to tie up this CPU for up to 300ms? That seems
> excessive. What is the reason that read_poll_timeout() or similar
> couldn't be used?

This needs additional tests because some instabilities have been
reported when using read_poll_timeout in some places

>
> The advantage of read_poll_timeout() is that it will correctly handle
> the timeout vs condition being satisfied witout need for special code.
>
> > +
> > +     return 0;
> > +}
> > +
> > +static int s32g_xpcs_wait_bits(struct s32g_xpcs *xpcs, unsigned int reg,
> > +                            unsigned int mask, unsigned int bits)
> > +{
> > +     ktime_t cur;
> > +     ktime_t timeout = ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS);
> > +
> > +     spin_until_cond((cur = ktime_get(),
> > +                      (s32g_xpcs_read(xpcs, reg) & mask) == bits ||
> > +                      ktime_after(cur, timeout)));
> > +     if ((s32g_xpcs_read(xpcs, reg) & mask) != bits)
> > +             return -ETIMEDOUT;
>
> Same here:
>
>         return read_poll_timeout(s32g_xpcs_read, val, (val & mask) == bits,
>                                  0, XPCS_TIMEOUT_MS, false,
>                                  xpcs, reg);
>
> > +
> > +     return 0;
> > +}
> > +
> > +static unsigned int s32g_xpcs_digital_status(struct s32g_xpcs *xpcs)
> > +{
> > +     return s32g_xpcs_read(xpcs, VR_MII_DIG_STS);
> > +}
> > +
> > +static int s32g_xpcs_wait_power_good_state(struct s32g_xpcs *xpcs)
> > +{
> > +     unsigned int val;
> > +
> > +     return read_poll_timeout(s32g_xpcs_digital_status, val,
> > +                              FIELD_GET(PSEQ_STATE_MASK, val) == POWER_GOOD_STATE,
> > +                              0,
> > +                              XPCS_TIMEOUT_MS, false, xpcs);
>
> This could be:
>         return read_poll_timeout(s32g_xpcs_read, val,
>                                  FIELD_GET(PSEQ_STATE_MASK, val) == POWER_GOOD_STATE,
>                                  0, XPCS_TIMEOUT_MS, false,
>                                  xpcs, VR_MII_DIG_STS);
>
> eliminating the need for s32g_xpcs_digital_status().

fair enough

>
> > +}
> > +
> > +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs)
> > +{
> > +     /* Step 19 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, VR_RST, VR_RST);
> > +}
> > +
> > +static bool s32g_xpcs_is_not_in_reset(struct s32g_xpcs *xpcs)
> > +{
> > +     unsigned int val;
> > +
> > +     val = s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1);
> > +
> > +     return !(val & VR_RST);
> > +}
> > +
> > +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs)
> > +{
> > +     int ret;
> > +
> > +     /* Step 20 */
> > +     ret = s32g_xpcs_wait(xpcs, s32g_xpcs_is_not_in_reset);
> > +     if (ret)
> > +             dev_err(xpcs->dev, "XPCS%d is in reset\n", xpcs->id);
> > +
> > +     return ret;
> > +}
> > +
> > +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs)
> > +{
> > +     int ret = 0;
> > +
> > +     ret = s32g_xpcs_wait_power_good_state(xpcs);
> > +     if (ret) {
> > +             dev_err(xpcs->dev, "Failed to enter in PGOOD state after vendor reset\n");
> > +             return ret;
> > +     }
> > +
> > +     /* Step 21 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL1,
> > +                          RX_RST_0, RX_RST_0);
> > +
> > +     /* Step 22 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL1,
> > +                          RX_RST_0, 0);
> > +
> > +     /* Step 23 */
> > +     /* Wait until SR_MII_STS[LINK_STS] = 1 */
> > +
> > +     return ret;
> > +}
> > +
> > +static int s32g_xpcs_ref_clk_sel(struct s32g_xpcs *xpcs,
> > +                              enum s32g_xpcs_pll ref_pll)
> > +{
> > +     switch (ref_pll) {
> > +     case XPCS_PLLA:
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL,
> > +                                  MPLLB_SEL_0, 0);
> > +             xpcs->ref = XPCS_PLLA;
> > +             break;
> > +     case XPCS_PLLB:
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL,
> > +                                  MPLLB_SEL_0, MPLLB_SEL_0);
> > +             xpcs->ref = XPCS_PLLB;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void s32g_xpcs_electrical_configure(struct s32g_xpcs *xpcs)
> > +{
> > +     /* Step 2 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_EQ_CTRL0,
> > +                          TX_EQ_MAIN_MASK, FIELD_PREP(TX_EQ_MAIN_MASK, 0xC));
>
> Prefer hex numbers to be lower case.

ok

>
> > +
> > +     /* Step 3 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_CONSUMER_10G_TX_TERM_CTRL,
> > +                          TX0_TERM_MASK, FIELD_PREP(TX0_TERM_MASK, 0x4));
> > +}
> > +
> > +static int s32g_xpcs_vco_cfg(struct s32g_xpcs *xpcs, enum s32g_xpcs_pll vco_pll)
> > +{
> > +     unsigned int vco_ld = 0;
> > +     unsigned int vco_ref = 0;
> > +     unsigned int rx_baud = 0;
> > +     unsigned int tx_baud = 0;
> > +
> > +     switch (vco_pll) {
> > +     case XPCS_PLLA:
> > +             if (xpcs->mhz125) {
> > +                     vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1360);
> > +                     vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 17);
> > +             } else {
> > +                     vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1350);
> > +                     vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 27);
> > +             }
> > +
> > +             rx_baud = FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_8);
> > +             tx_baud = FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_4);
> > +             break;
> > +     case XPCS_PLLB:
> > +             if (xpcs->mhz125) {
> > +                     vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1350);
> > +                     vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 27);
> > +             } else {
> > +                     vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1344);
> > +                     vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 43);
> > +             }
> > +
> > +             rx_baud = FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_2);
> > +             tx_baud = FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_1);
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_VCO_CAL_LD0,
> > +                          VCO_LD_VAL_0_MASK, vco_ld);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_VCO_CAL_REF0,
> > +                          VCO_REF_LD_0_MASK, vco_ref);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_RATE_CTRL,
> > +                          TX0_RATE_MASK, tx_baud);
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_RATE_CTRL,
> > +                          RX0_RATE_MASK, rx_baud);
> > +
> > +     if (vco_pll == XPCS_PLLB) {
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL,
> > +                                  VCO_LOW_FREQ_0, VCO_LOW_FREQ_0);
> > +     } else {
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL,
> > +                                  VCO_LOW_FREQ_0, 0);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int s32g_xpcs_init_mplla(struct s32g_xpcs *xpcs)
> > +{
> > +     unsigned int val;
> > +
> > +     if (!xpcs)
> > +             return -EINVAL;
> > +
> > +     /* Step 7 */
> > +     val = 0;
> > +     if (xpcs->ext_clk)
> > +             val |= REF_USE_PAD;
> > +
> > +     if (xpcs->mhz125) {
> > +             val |= REF_MPLLA_DIV2;
> > +             val |= REF_CLK_DIV2;
> > +             val |= FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ);
> > +     } else {
> > +             val |= FIELD_PREP(REF_RANGE_MASK, RANGE_26_53_MHZ);
> > +     }
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_REF_CLK_CTRL,
> > +                          REF_MPLLA_DIV2 | REF_USE_PAD | REF_RANGE_MASK |
> > +                          REF_CLK_DIV2, val);
> > +
> > +     /* Step 8 */
> > +     if (xpcs->mhz125)
> > +             val = FIELD_PREP(MLLA_MULTIPLIER_MASK, 80);
> > +     else
> > +             val = FIELD_PREP(MLLA_MULTIPLIER_MASK, 25);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLA_CTRL0,
> > +                          MPLLA_CAL_DISABLE | MLLA_MULTIPLIER_MASK,
> > +                          val);
> > +
> > +     /* Step 9 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLA_CTRL1,
> > +                          MPLLA_FRACN_CTRL_MASK, 0);
> > +
> > +     /* Step 10 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLA_CTRL2,
> > +                          MPLLA_TX_CLK_DIV_MASK | MPLLA_DIV10_CLK_EN,
> > +                          FIELD_PREP(MPLLA_TX_CLK_DIV_MASK, 1) | MPLLA_DIV10_CLK_EN);
> > +
> > +     /* Step 11 */
> > +     if (xpcs->mhz125)
> > +             val = FIELD_PREP(MPLLA_BANDWIDTH_MASK, 43);
> > +     else
> > +             val = FIELD_PREP(MPLLA_BANDWIDTH_MASK, 357);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLA_CTRL3,
> > +                          MPLLA_BANDWIDTH_MASK, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int s32g_xpcs_init_mpllb(struct s32g_xpcs *xpcs)
> > +{
> > +     unsigned int val;
> > +
> > +     if (!xpcs)
> > +             return -EINVAL;
> > +
> > +     /* Step 7 */
> > +     val = 0;
> > +     if (xpcs->ext_clk)
> > +             val |= REF_USE_PAD;
> > +
> > +     if (xpcs->mhz125) {
> > +             val |= REF_MPLLB_DIV2;
> > +             val |= REF_CLK_DIV2;
> > +             val |= FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ);
> > +     } else {
> > +             val |= FIELD_PREP(REF_RANGE_MASK, RANGE_26_53_MHZ);
> > +     }
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_REF_CLK_CTRL,
> > +                          REF_MPLLB_DIV2 | REF_USE_PAD | REF_RANGE_MASK |
> > +                          REF_CLK_DIV2, val);
> > +
> > +     /* Step 8 */
> > +     if (xpcs->mhz125)
> > +             val = 125 << MLLB_MULTIPLIER_OFF;
> > +     else
> > +             val = 39 << MLLB_MULTIPLIER_OFF;
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLB_CTRL0,
> > +                          MPLLB_CAL_DISABLE | MLLB_MULTIPLIER_MASK,
> > +                          val);
> > +
> > +     /* Step 9 */
> > +     if (xpcs->mhz125)
> > +             val = FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 0);
> > +     else
> > +             val = FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 1044);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLB_CTRL1,
> > +                          MPLLB_FRACN_CTRL_MASK, val);
> > +
> > +     /* Step 10 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLB_CTRL2,
> > +                          MPLLB_TX_CLK_DIV_MASK | MPLLB_DIV10_CLK_EN,
> > +                          FIELD_PREP(MPLLB_TX_CLK_DIV_MASK, 5) | MPLLB_DIV10_CLK_EN);
> > +
> > +     /* Step 11 */
> > +     if (xpcs->mhz125)
> > +             val = FIELD_PREP(MPLLB_BANDWIDTH_MASK, 68);
> > +     else
> > +             val = FIELD_PREP(MPLLB_BANDWIDTH_MASK, 102);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLB_CTRL3,
> > +                          MPLLB_BANDWIDTH_MASK, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static void s32g_serdes_pma_high_freq_recovery(struct s32g_xpcs *xpcs)
> > +{
> > +     /* PCS signal protection, PLL railout recovery */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_DBG_CTRL, SUPPRESS_LOS_DET | RX_DT_EN_CTL,
> > +                          SUPPRESS_LOS_DET | RX_DT_EN_CTL);
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MISC_CTRL0,
> > +                          PLL_CTRL, PLL_CTRL);
> > +}
> > +
> > +static void s32g_serdes_pma_configure_tx_eq_post(struct s32g_xpcs *xpcs)
> > +{
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_EQ_CTRL1,
> > +                          TX_EQ_OVR_RIDE, TX_EQ_OVR_RIDE);
> > +}
> > +
> > +static int s32g_serdes_bifurcation_pll_transit(struct s32g_xpcs *xpcs,
> > +                                            enum s32g_xpcs_pll target_pll)
> > +{
> > +     int ret = 0;
> > +     struct device *dev = xpcs->dev;
> > +
> > +     /* Configure XPCS speed and VCO */
> > +     if (target_pll == XPCS_PLLA) {
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, EN_2_5G_MODE, 0);
> > +             s32g_xpcs_vco_cfg(xpcs, XPCS_PLLA);
> > +     } else {
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> > +                                  EN_2_5G_MODE, EN_2_5G_MODE);
> > +             s32g_xpcs_vco_cfg(xpcs, XPCS_PLLB);
> > +     }
>
> I am really not happy with this driver being
> PHY_INTERFACE_MODE_SGMII-only but supporting running that at 2.5Gbps.
> In the kernel, PHY_INTERFACE_MODE_SGMII is strictly _Cisco_ SGMII only,
> which means the version of it clocked at 1.25GHz, not 3.125GHz.
>
> OCSGMII or whatever random name you call it tends to be only supported
> without inband AN, and we have pushed everyone to adopt
> PHY_INTERFACE_MODE_2500BASEX for that. Please do the same.
>
> Should this SerDes be connected to a SFP cage, it will need to support
> dynamically switching between Cisco SGMII and 2500BASE-X.

okay, this needs to be checked with SoC Team

>
> > +
> > +     /* Signal that clock are not available */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1,
> > +                          TX_CLK_RDY_0, 0);
> > +
> > +     /* Select PLL reference */
> > +     if (target_pll == XPCS_PLLA)
> > +             s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLA);
> > +     else
> > +             s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLB);
> > +
> > +     /* Initiate transmitter TX reconfiguration request */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL2,
> > +                          TX_REQ_0, TX_REQ_0);
> > +
> > +     /* Wait for transmitter to reconfigure */
> > +     ret = s32g_xpcs_wait_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL2,
> > +                               TX_REQ_0, 0);
> > +     if (ret) {
> > +             dev_err(dev, "Switch to TX_REQ_0 failed\n");
> > +             return ret;
> > +     }
> > +
> > +     /* Initiate transmitter RX reconfiguration request */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL2,
> > +                          RX_REQ_0, RX_REQ_0);
> > +
> > +     /* Wait for receiver to reconfigure */
> > +     ret = s32g_xpcs_wait_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL2,
> > +                               RX_REQ_0, 0);
> > +     if (ret) {
> > +             dev_err(dev, "Switch to RX_REQ_0 failed\n");
> > +             return ret;
> > +     }
> > +
> > +     /* Signal that clock are available */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1,
> > +                          TX_CLK_RDY_0, TX_CLK_RDY_0);
> > +
> > +     /* Flush internal logic */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, INIT, INIT);
> > +
> > +     /* Wait for init */
> > +     ret = s32g_xpcs_wait_bits(xpcs, VR_MII_DIG_CTRL1, INIT, 0);
> > +     if (ret) {
> > +             dev_err(dev, "XPCS INIT failed\n");
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +/*
> > + * phylink_pcs_ops
> > + */
> > +
> > +static unsigned int s32cc_phylink_pcs_inband_caps(struct phylink_pcs *pcs,
> > +                                               phy_interface_t interface)
> > +{
> > +     switch (interface) {
> > +     case PHY_INTERFACE_MODE_SGMII:
> > +             return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
> > +
> > +     default:
> > +             return 0;
> > +     }
> > +}
> > +
> > +static int s32cc_phylink_pcs_config(struct phylink_pcs *pcs,
> > +                                 unsigned int neg_mode,
> > +                                 phy_interface_t interface,
> > +                                 const unsigned long *advertising,
> > +                                 bool permit_pause_to_mac)
> > +{
> > +     struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > +
> > +     /* Step 1: Disable SGMII AN */
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > +
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > +                          MII_AN_INTR_EN,
> > +                          0);
> > +
> > +     if (!(neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED))
> > +             return 0;
> > +
> > +     /* Step 2  */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > +                          PCS_MODE_MASK,
> > +                          FIELD_PREP(PCS_MODE_MASK, PCS_MODE_SGMII));
> > +
> > +     /* Step 3 */
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL,
> > +                          SS13 | SS6,
> > +                          SS6);
> > +
> > +     /* Step 4  */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > +                          MII_CTRL,
> > +                          0);
> > +     /* Step 5 and 8 */
> > +     if (xpcs->pcie_shared == PCIE_XPCS_2G5) {
> > +             s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x2faf);
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> > +                                  MAC_AUTO_SW, MAC_AUTO_SW);
> > +     } else {
> > +             s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x7a1);
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, MAC_AUTO_SW, 0);
> > +     }
> > +
> > +     /* Step 6 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> > +                          CL37_TMR_OVRRIDE, CL37_TMR_OVRRIDE);
> > +
> > +     /* Step 7 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > +                          MII_AN_INTR_EN,
> > +                          MII_AN_INTR_EN);
> > +
> > +     /* Step 9: Enable SGMII AN */
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, AN_ENABLE);
> > +
> > +     return 0;
> > +}
> > +
> > +static void s32cc_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
> > +                                     struct phylink_link_state *state)
> > +{
> > +     struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > +     bool ss6, ss13, an_enabled;
> > +     struct device *dev = xpcs->dev;
> > +     unsigned int val, ss;
> > +
> > +     an_enabled = (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED);
> > +
> > +     if (an_enabled) {
> > +             state->link = 0;
> > +             val = s32g_xpcs_read(xpcs, VR_MII_AN_INTR_STS);
> > +
> > +             /* Interrupt is raised with each SGMII AN that is in cases
> > +              * Link down - Every SGMII link timer expire
> > +              * Link up - Once before link goes up
> > +              * So either linkup or raised interrupt mean AN was completed
> > +              */
> > +             if ((val & CL37_ANCMPLT_INTR) || (val & CL37_ANSGM_STS_LINK)) {
> > +                     state->an_complete = 1;
> > +                     if (val & CL37_ANSGM_STS_LINK)
> > +                             state->link = 1;
> > +                     else
> > +                             return;
> > +                     if (val & CL37_ANSGM_STS_DUPLEX)
> > +                             state->duplex = DUPLEX_FULL;
> > +                     else
> > +                             state->duplex = DUPLEX_HALF;
> > +                     ss = FIELD_GET(CL37_ANSGM_STS_SPEED_MASK, val);
> > +             } else {
> > +                     return;
> > +             }
> > +
> > +     } else {
> > +             val = s32g_xpcs_read(xpcs, SR_MII_STS);
> > +             state->link = !!(val & LINK_STS);
> > +             state->an_complete = 0;
> > +             state->pause = MLO_PAUSE_NONE;
> > +
> > +             val = s32g_xpcs_read(xpcs, SR_MII_CTRL);
> > +             if (val & DUPLEX_MODE)
> > +                     state->duplex = DUPLEX_FULL;
> > +             else
> > +                     state->duplex = DUPLEX_HALF;
> > +
> > +             /*
> > +              * Build similar value as CL37_ANSGM_STS_SPEED with
> > +              * SS6 and SS13 of SR_MII_CTRL:
> > +              *   - 0 for 10 Mbps
> > +              *   - 1 for 100 Mbps
> > +              *   - 2 for 1000 Mbps
> > +              */
> > +             ss6 = !!(val & SS6);
> > +             ss13 = !!(val & SS13);
> > +             ss = ss6 << 1 | ss13;
> > +     }
> > +
> > +     switch (ss) {
> > +     case CL37_ANSGM_10MBPS:
> > +             state->speed = SPEED_10;
> > +             break;
> > +     case CL37_ANSGM_100MBPS:
> > +             state->speed = SPEED_100;
> > +             break;
> > +     case CL37_ANSGM_1000MBPS:
> > +             state->speed = SPEED_1000;
> > +             break;
> > +     default:
> > +             dev_err(dev, "Failed to interpret the value of SR_MII_CTRL\n");
> > +             break;
> > +     }
> > +
> > +     val = s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1);
> > +     if ((val & EN_2_5G_MODE) && state->speed == SPEED_1000)
> > +             state->speed = SPEED_2500;
> > +
> > +     /* Cover SGMII AN inability to distigunish between 1G and 2.5G */
> > +     if ((val & EN_2_5G_MODE) &&
> > +         state->speed != SPEED_2500 && an_enabled) {
> > +             dev_err(dev, "Speed not supported in SGMII AN mode\n");
> > +     }
> > +}
> > +
> > +static void s32cc_phylink_pcs_link_up(struct phylink_pcs *pcs,
> > +                                   unsigned int neg_mode,
> > +                                   phy_interface_t interface, int speed,
> > +                                   int duplex)
> > +{
> > +     struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > +     struct device *dev = xpcs->dev;
> > +     bool an_enabled = (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED);
> > +     unsigned int val;
> > +     int ret;
> > +
> > +     dev_dbg(dev, "xpcs_%d: speed=%u duplex=%d an=%d\n", xpcs->id,
> > +             speed, duplex, an_enabled);
> > +
> > +     if (an_enabled)
> > +             return;
> > +
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_AN_INTR_EN, 0);
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_CTRL, 0);
>
> Haven't you already disabled AN in .pcs_config() ? This method doesn't
> change the AN enable state, the only time that happens is when
> .pcs_config() will be called. All other cases of passing neg_mode are
> merely informational.

Fair enough, this is probably not needed anymore with the changes in .pcs_config

>
> > +
> > +     if (duplex == DUPLEX_FULL)
> > +             val = DUPLEX_MODE;
> > +     else
> > +             val = 0;
> > +
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, val);
> > +
> > +     switch (speed) {
> > +     case SPEED_10:
> > +             val = 0;
> > +             break;
> > +     case SPEED_100:
> > +             val = SS13;
> > +             break;
> > +     case SPEED_1000:
> > +             val = SS6;
> > +             break;
> > +     case SPEED_2500:
> > +             val = SS6;
> > +             break;
> > +     default:
> > +             dev_err(dev, "Speed not supported\n");
> > +             break;
> > +     }
> > +
> > +     if (speed == SPEED_2500) {
> > +             ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB);
> > +             if (ret)
> > +                     dev_err(dev, "Switch to PLLB failed\n");
> > +     } else {
> > +             ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLA);
> > +             if (ret)
> > +                     dev_err(dev, "Switch to PLLA failed\n");
> > +     }
>
> This is a protocol transition, and isn't something that can be handled
> here. Cisco SGMII (PHY_INTERFACE_MODE_SGMII) does not support 2500Mbps
> and phylink will not allow it.
>
> See my comments for s32g_serdes_bifurcation_pll_transit().
>
> > +
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, SS6 | SS13, val);
> > +}
> > +
> > +static const struct phylink_pcs_ops s32cc_phylink_pcs_ops = {
> > +     .pcs_inband_caps = s32cc_phylink_pcs_inband_caps,
> > +     .pcs_get_state = s32cc_phylink_pcs_get_state,
> > +     .pcs_config = s32cc_phylink_pcs_config,
> > +     .pcs_link_up = s32cc_phylink_pcs_link_up,
> > +};
> > +
> > +/*
> > + * Serdes functions for initializing/configuring/releasing the xpcs
> > + */
> > +
> > +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs)
> > +{
> > +     int ret;
> > +
> > +     /* Enable voltage boost */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1, VBOOST_EN_0,
> > +                          VBOOST_EN_0);
> > +
> > +     /* TX rate baud  */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_RATE_CTRL, 0x7, 0x0U);
> > +
> > +     /* Rx rate baud/2 */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_RATE_CTRL, 0x3U, 0x1U);
> > +
> > +     /* Set low-frequency operating band */
> > +     s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL, CDR_SSC_EN_0,
> > +                          VCO_LOW_FREQ_0);
> > +
> > +     ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB);
> > +     if (ret)
> > +             dev_err(xpcs->dev, "Switch to PLLB failed\n");
> > +
> > +     return ret;
> > +}
> > +
> > +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs)
> > +{
> > +     int ret;
> > +     struct device *dev = xpcs->dev;
> > +
> > +     if (!xpcs->ext_clk) {
> > +             /* Step 1 */
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, BYP_PWRUP, BYP_PWRUP);
> > +     } else if (xpcs->pcie_shared == NOT_SHARED) {
> > +             ret = s32g_xpcs_wait_power_good_state(xpcs);
> > +             if (ret)
> > +                     return ret;
> > +     } else if (xpcs->pcie_shared == PCIE_XPCS_2G5) {
> > +             ret = s32g_xpcs_wait_power_good_state(xpcs);
> > +             if (ret)
> > +                     return ret;
> > +             /* Configure equalization */
> > +             s32g_serdes_pma_configure_tx_eq_post(xpcs);
> > +             s32g_xpcs_electrical_configure(xpcs);
> > +
> > +             /* Enable receiver recover */
> > +             s32g_serdes_pma_high_freq_recovery(xpcs);
> > +             return 0;
> > +     }
> > +
> > +     s32g_xpcs_electrical_configure(xpcs);
> > +
> > +     s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLA);
> > +     ret = s32g_xpcs_init_mplla(xpcs);
> > +     if (ret) {
> > +             dev_err(dev, "Failed to initialize PLLA\n");
> > +             return ret;
> > +     }
> > +     ret = s32g_xpcs_init_mpllb(xpcs);
> > +     if (ret) {
> > +             dev_err(dev, "Failed to initialize PLLB\n");
> > +             return ret;
> > +     }
> > +     s32g_xpcs_vco_cfg(xpcs, XPCS_PLLA);
> > +
> > +     /* Step 18 */
> > +     if (!xpcs->ext_clk)
> > +             s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, BYP_PWRUP, 0);
> > +
> > +     /* Will be cleared by Step 19 Vreset */
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE);
> > +
> > +     return ret;
> > +}
> > +
> > +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs)
> > +{
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE);
> > +     s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > +}
>
> Sorry, but why? You should never override phylink's requests.

Serdes sometimes needs to be reset and the AN is enabled by default
after the reset and even before pcs_config has been called. This is
called during the inti of serdes after an ip reset.
That being said this might not be needed anymore after the change in
pcs_config that disables AN


>
> > +
> > +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev,
> > +                unsigned char id, void __iomem *base, bool ext_clk,
> > +                unsigned long rate, enum s32g_xpcs_shared pcie_shared)
> > +{
> > +     struct regmap_config conf;
> > +
> > +     if (rate != (125 * HZ_PER_MHZ) && rate != (100 * HZ_PER_MHZ)) {
> > +             dev_err(dev, "XPCS cannot operate @%lu HZ\n", rate);
> > +             return -EINVAL;
> > +     }
> > +
> > +     xpcs->base = base;
> > +     xpcs->ext_clk = ext_clk;
> > +     xpcs->id = id;
> > +     xpcs->dev = dev;
> > +     xpcs->pcie_shared = pcie_shared;
> > +
> > +     if (rate == (125 * HZ_PER_MHZ))
> > +             xpcs->mhz125 = true;
> > +     else
> > +             xpcs->mhz125 = false;
> > +
> > +     conf = s32g_xpcs_regmap_config;
> > +
> > +     if (!id)
> > +             conf.name = "xpcs0";
> > +     else
> > +             conf.name = "xpcs1";
> > +
> > +     xpcs->regmap = devm_regmap_init(dev, NULL, xpcs, &conf);
> > +     if (IS_ERR(xpcs->regmap))
> > +             return dev_err_probe(dev, PTR_ERR(xpcs->regmap),
> > +                                  "Failed to init register amp\n");
> > +
> > +     /* Phylink PCS */
> > +     xpcs->pcs.ops = &s32cc_phylink_pcs_ops;
> > +     xpcs->pcs.poll = true;
> > +     __set_bit(PHY_INTERFACE_MODE_SGMII, xpcs->pcs.supported_interfaces);
> > +
> > +     return 0;
> > +}
> > diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> > index 45184a3cdd69..bb7f59897faf 100644
> > --- a/drivers/phy/freescale/Kconfig
> > +++ b/drivers/phy/freescale/Kconfig
> > @@ -66,6 +66,7 @@ config PHY_S32G_SERDES
> >       tristate "NXP S32G SERDES support"
> >       depends on ARCH_S32 || COMPILE_TEST
> >       select GENERIC_PHY
> > +     select REGMAP
> >       help
> >         This option enables support for S23G SerDes PHY used for
> >         PCIe & Ethernet
> > diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> > index 321a80c02be5..f2f7eb5aa327 100644
> > --- a/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> > +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> > @@ -12,12 +12,14 @@
> >  #include <linux/module.h>
> >  #include <linux/of_platform.h>
> >  #include <linux/of_address.h>
> > +#include <linux/pcs/pcs-nxp-s32g-xpcs.h>
> >  #include <linux/phy/phy.h>
> >  #include <linux/platform_device.h>
> >  #include <linux/processor.h>
> >  #include <linux/reset.h>
> >  #include <linux/units.h>
> >
> > +#define S32G_SERDES_XPCS_MAX                 2
> >  #define S32G_SERDES_MODE_MAX                 5
> >
> >  #define EXTERNAL_CLK_NAME                    "ext"
> > @@ -32,6 +34,52 @@
> >  #define S32G_PCIE_PHY_MPLLA_CTRL             0x10
> >  #define  MPLL_STATE                          BIT(30)
> >
> > +#define S32G_PCIE_PHY_MPLLB_CTRL             0x14
> > +#define  MPLLB_SSC_EN                                BIT(1)
> > +
> > +#define S32G_PCIE_PHY_EXT_CTRL_SEL           0x18
> > +#define  EXT_PHY_CTRL_SEL                    BIT(0)
> > +
> > +#define S32G_PCIE_PHY_EXT_BS_CTRL            0x1C
> > +#define  EXT_BS_TX_LOWSWING                  BIT(6)
> > +#define  EXT_BS_RX_BIGSWING                  BIT(5)
> > +#define  EXT_BS_RX_LEVEL_MASK                        GENMASK(4, 0)
> > +
> > +#define S32G_PCIE_PHY_REF_CLK_CTRL           0x20
> > +#define  EXT_REF_RANGE_MASK                  GENMASK(5, 3)
> > +#define  REF_CLK_DIV2_EN                     BIT(2)
> > +#define  REF_CLK_MPLLB_DIV2_EN                       BIT(1)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLA_CTRL_1               0x30
> > +#define  EXT_MPLLA_BANDWIDTH_MASK            GENMASK(15, 0)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_1               0x40
> > +#define  EXT_MPLLB_DIV_MULTIPLIER_MASK               GENMASK(31, 24)
> > +#define  EXT_MPLLB_DIV_CLK_EN                        BIT(19)
> > +#define  EXT_MPLLB_DIV8_CLK_EN                       BIT(18)
> > +#define  EXT_MPLLB_DIV10_CLK_EN                      BIT(16)
> > +#define  EXT_MPLLB_BANDWIDTH_MASK            GENMASK(15, 0)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_2               0x44
> > +#define  EXT_MPLLB_FRACN_CTRL_MASK           GENMASK(22, 12)
> > +#define  MPLLB_MULTIPLIER_MASK                       GENMASK(8, 0)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_3               0x48
> > +#define  EXT_MPLLB_WORD_DIV2_EN                      BIT(31)
> > +#define  EXT_MPLLB_TX_CLK_DIV_MASK           GENMASK(30, 28)
> > +
> > +#define S32G_PCIE_PHY_EXT_MISC_CTRL_1                0xA0
> > +#define  EXT_RX_LOS_THRESHOLD_MASK           GENMASK(7, 1)
> > +#define  EXT_RX_VREF_CTRL_MASK                       GENMASK(28, 24)
> > +
> > +#define S32G_PCIE_PHY_EXT_MISC_CTRL_2                0xA4
> > +#define  EXT_TX_VBOOST_LVL_MASK                      GENMASK(18, 16)
> > +#define  EXT_TX_TERM_CTRL_MASK                       GENMASK(26, 24)
> > +
> > +#define S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL     0xD0
> > +#define  XPCS1_RX_VCO_LD_VAL_MASK            GENMASK(28, 16)
> > +#define  XPCS1_RX_REF_LD_VAL_MASK            GENMASK(14, 8)
> > +
> >  #define S32G_SS_RW_REG_0                     0xF0
> >  #define  SUBMODE_MASK                                GENMASK(3, 0)
> >  #define  CLKEN_MASK                          BIT(23)
> > @@ -44,6 +92,9 @@
> >
> >  #define S32G_PHY_REG_DATA                    0x4
> >
> > +#define S32G_PHY_RST_CTRL                    0x8
> > +#define  WARM_RST                            BIT(1)
> > +
> >  #define RAWLANE0_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN   0x3019
> >  #define RAWLANE1_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN   0x3119
> >
> > @@ -76,16 +127,33 @@ struct s32g_pcie_ctrl {
> >       bool powered_on;
> >  };
> >
> > +struct s32g_xpcs_ctrl {
> > +     struct s32g_xpcs *phys[2];
> > +     void __iomem *base0, *base1;
> > +};
> > +
> >  struct s32g_serdes {
> >       struct s32g_serdes_ctrl ctrl;
> >       struct s32g_pcie_ctrl pcie;
> > +     struct s32g_xpcs_ctrl xpcs;
> >       struct device *dev;
> > +     u8 lanes_status;
> >  };
> >
> >  /* PCIe phy subsystem */
> >
> >  #define S32G_SERDES_PCIE_FREQ                        (100 * HZ_PER_MHZ)
> >
> > +static void s32g_pcie_phy_reset(struct s32g_serdes *serdes)
> > +{
> > +     u32 val;
> > +
> > +     val = readl(serdes->pcie.phy_base + S32G_PHY_RST_CTRL);
> > +     writel(val | WARM_RST, serdes->pcie.phy_base + S32G_PHY_RST_CTRL);
> > +     usleep_range(1000, 1100);
> > +     writel(val, serdes->pcie.phy_base + S32G_PHY_RST_CTRL);
> > +}
> > +
> >  static int s32g_pcie_check_clk(struct s32g_serdes *serdes)
> >  {
> >       struct s32g_serdes_ctrl *sctrl = &serdes->ctrl;
> > @@ -277,6 +345,192 @@ static struct phy *s32g_serdes_phy_xlate(struct device *dev,
> >       return phy;
> >  }
> >
> > +/* XPCS subsystem */
> > +
> > +static int s32g_serdes_xpcs_init(struct s32g_serdes *serdes, int id)
> > +{
> > +     struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> > +     struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> > +     enum s32g_xpcs_shared shared = NOT_SHARED;
> > +     unsigned long rate = ctrl->ref_clk_rate;
> > +     struct device *dev = serdes->dev;
> > +     void __iomem *base;
> > +
> > +     if (!id)
> > +             base = xpcs->base0;
> > +     else
> > +             base = xpcs->base1;
> > +
> > +     if (ctrl->ss_mode == 1 || ctrl->ss_mode == 2)
> > +             shared = PCIE_XPCS_1G;
> > +     else if (ctrl->ss_mode == 5)
> > +             shared = PCIE_XPCS_2G5;
> > +
> > +     return s32g_xpcs_init(xpcs->phys[id], dev, id, base,
> > +                            ctrl->ext_clk, rate, shared);
> > +}
> > +
> > +static void s32g_serdes_prepare_pma_mode5(struct s32g_serdes *serdes)
> > +{
> > +     u32 val;
> > +     /* Configure TX_VBOOST_LVL and TX_TERM_CTRL */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2);
> > +     val &= ~(EXT_TX_VBOOST_LVL_MASK | EXT_TX_TERM_CTRL_MASK);
> > +     val |= FIELD_PREP(EXT_TX_VBOOST_LVL_MASK, 0x3) |
> > +             FIELD_PREP(EXT_TX_TERM_CTRL_MASK, 0x4);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2);
> > +
> > +     /* Enable phy external control */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL);
> > +     val |= EXT_PHY_CTRL_SEL;
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL);
> > +
> > +     /* Configure ref range, disable PLLB/ref div2 */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL);
> > +     val &= ~(REF_CLK_DIV2_EN | REF_CLK_MPLLB_DIV2_EN | EXT_REF_RANGE_MASK);
> > +     val |= FIELD_PREP(EXT_REF_RANGE_MASK, 0x3);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL);
> > +
> > +     /* Configure multiplier */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2);
> > +     val &= ~(MPLLB_MULTIPLIER_MASK | EXT_MPLLB_FRACN_CTRL_MASK | BIT(24) | BIT(28));
> > +     val |= FIELD_PREP(MPLLB_MULTIPLIER_MASK, 0x27U) |
> > +             FIELD_PREP(EXT_MPLLB_FRACN_CTRL_MASK, 0x414);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2);
> > +
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL);
> > +     val &= ~MPLLB_SSC_EN;
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL);
> > +
> > +     /* Configure tx lane division, disable word clock div2*/
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3);
> > +     val &= ~(EXT_MPLLB_WORD_DIV2_EN | EXT_MPLLB_TX_CLK_DIV_MASK);
> > +     val |= FIELD_PREP(EXT_MPLLB_TX_CLK_DIV_MASK, 0x5);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3);
> > +
> > +     /* Configure bandwidth for filtering and div10*/
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1);
> > +     val &= ~(EXT_MPLLB_BANDWIDTH_MASK | EXT_MPLLB_DIV_CLK_EN |
> > +              EXT_MPLLB_DIV8_CLK_EN | EXT_MPLLB_DIV_MULTIPLIER_MASK);
> > +     val |= FIELD_PREP(EXT_MPLLB_BANDWIDTH_MASK, 0x5f) | EXT_MPLLB_DIV10_CLK_EN;
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1);
> > +
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1);
> > +     val &= ~(EXT_MPLLA_BANDWIDTH_MASK);
> > +     val |= FIELD_PREP(EXT_MPLLA_BANDWIDTH_MASK, 0xc5);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1);
> > +
> > +     /* Configure VCO */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL);
> > +     val &= ~(XPCS1_RX_VCO_LD_VAL_MASK | XPCS1_RX_REF_LD_VAL_MASK);
> > +     val |= FIELD_PREP(XPCS1_RX_VCO_LD_VAL_MASK, 0x540) |
> > +             FIELD_PREP(XPCS1_RX_REF_LD_VAL_MASK, 0x2b);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL);
> > +
> > +     /* Boundary scan control */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL);
> > +     val &= ~(EXT_BS_RX_LEVEL_MASK | EXT_BS_TX_LOWSWING);
> > +     val |= FIELD_PREP(EXT_BS_RX_LEVEL_MASK, 0xB) | EXT_BS_RX_BIGSWING;
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL);
> > +
> > +     /* Rx loss threshold */
> > +     val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1);
> > +     val &= ~(EXT_RX_LOS_THRESHOLD_MASK | EXT_RX_VREF_CTRL_MASK);
> > +     val |= FIELD_PREP(EXT_RX_LOS_THRESHOLD_MASK, 0x3U) |
> > +             FIELD_PREP(EXT_RX_VREF_CTRL_MASK, 0x11U);
> > +     writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1);
> > +}
> > +
> > +static int s32g_serdes_enable_mode5(struct s32g_serdes *serdes, struct s32g_xpcs *xpcs)
> > +{
> > +     int ret;
> > +
> > +     s32g_serdes_prepare_pma_mode5(serdes);
> > +
> > +     ret = s32g_xpcs_pre_pcie_2g5(xpcs);
> > +     if (ret) {
> > +             dev_err(serdes->dev,
> > +                     "Failed to prepare SerDes for PCIE & XPCS @ 2G5 mode\n");
> > +             return ret;
> > +     }
> > +
> > +     s32g_pcie_phy_reset(serdes);
> > +
> > +     return 0;
> > +}
> > +
> > +static int s32g_serdes_init_clks(struct s32g_serdes *serdes)
> > +{
> > +     struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> > +     struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> > +     struct s32g_xpcs *order[2];
> > +     size_t i;
> > +     int ret;
> > +
> > +     switch (ctrl->ss_mode) {
> > +     case 0:
> > +             return 0;
> > +     case 1:
> > +             order[0] = xpcs->phys[0];
> > +             order[1] = NULL;
> > +             break;
> > +     case 2:
> > +     case 5:
> > +             order[0] = xpcs->phys[1];
> > +             order[1] = NULL;
> > +             break;
> > +     case 3:
> > +             order[0] = xpcs->phys[1];
> > +             order[1] = xpcs->phys[0];
> > +             break;
> > +     case 4:
> > +             order[0] = xpcs->phys[0];
> > +             order[1] = xpcs->phys[1];
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     for (i = 0; i < ARRAY_SIZE(order); i++) {
> > +             if (!order[i])
> > +                     continue;
> > +
> > +             ret = s32g_xpcs_init_plls(order[i]);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     for (i = 0; i < ARRAY_SIZE(order); i++) {
> > +             if (!order[i])
> > +                     continue;
> > +
> > +             if (ctrl->ss_mode == 5) {
> > +                     ret = s32g_serdes_enable_mode5(serdes, order[i]);
> > +                     if (ret)
> > +                             return ret;
> > +             } else {
> > +                     s32g_xpcs_vreset(order[i]);
> > +             }
> > +     }
> > +
> > +     for (i = 0; i < ARRAY_SIZE(order); i++) {
> > +             if (!order[i])
> > +                     continue;
> > +
> > +             ret = s32g_xpcs_wait_vreset(order[i]);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             ret = s32g_xpcs_reset_rx(order[i]);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             s32g_xpcs_disable_an(order[i]);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> >  /* Serdes subsystem */
> >
> >  static int s32g_serdes_assert_reset(struct s32g_serdes *serdes)
> > @@ -331,6 +585,10 @@ static int s32g_serdes_init(struct s32g_serdes *serdes)
> >               return ret;
> >       }
> >
> > +     /*
> > +      * We have a tight timing for the init sequence and any delay linked to
> > +      * printk as an example can fail the init after reset
> > +      */
> >       ret = s32g_serdes_assert_reset(serdes);
> >       if (ret)
> >               goto disable_clks;
> > @@ -363,7 +621,13 @@ static int s32g_serdes_init(struct s32g_serdes *serdes)
> >       dev_info(serdes->dev, "Using mode %d for SerDes subsystem\n",
> >                ctrl->ss_mode);
> >
> > -     return 0;
> > +     ret = s32g_serdes_init_clks(serdes);
> > +     if (ret) {
> > +             dev_err(serdes->dev, "XPCS init failed\n");
> > +             goto disable_clks;
> > +     }
> > +
> > +     return ret;
> >
> >  disable_clks:
> >       clk_bulk_disable_unprepare(serdes->ctrl.nclks,
> > @@ -449,12 +713,32 @@ static int s32g_serdes_get_pcie_resources(struct platform_device *pdev, struct s
> >       return 0;
> >  }
> >
> > +static int s32g_serdes_get_xpcs_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
> > +{
> > +     struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> > +     struct device *dev = &pdev->dev;
> > +
> > +     xpcs->base0 = devm_platform_ioremap_resource_byname(pdev, "xpcs0");
> > +     if (IS_ERR(xpcs->base0)) {
> > +             dev_err(dev, "Failed to map 'xpcs0'\n");
> > +             return PTR_ERR(xpcs->base0);
> > +     }
> > +
> > +     xpcs->base1 = devm_platform_ioremap_resource_byname(pdev, "xpcs1");
> > +     if (IS_ERR(xpcs->base1)) {
> > +             dev_err(dev, "Failed to map 'xpcs1'\n");
> > +             return PTR_ERR(xpcs->base1);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> >  static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct device_node *child_node)
> >  {
> >       struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> >       struct phy_provider *phy_provider;
> >       struct device *dev = serdes->dev;
> > -     int ss_mode = ctrl->ss_mode;
> > +     int ret, ss_mode = ctrl->ss_mode;
> >       struct phy *phy;
> >
> >       if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-pcie-phy")) {
> > @@ -476,6 +760,37 @@ static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct device_nod
> >               if (IS_ERR(phy_provider))
> >                       return PTR_ERR(phy_provider);
> >
> > +     } else if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-xpcs")) {
> > +             struct s32g_xpcs_ctrl *xpcs_ctrl = &serdes->xpcs;
> > +             struct s32g_xpcs *xpcs;
> > +             int port;
> > +
> > +             /* no Ethernet phy lane */
> > +             if (ss_mode == 0)
> > +                     return 0;
> > +
> > +             /* Get XPCS port number connected to the lane */
> > +             if (of_property_read_u32(child_node, "reg", &port))
> > +                     return -EINVAL;
> > +
> > +             /* XPCS1 is not used */
> > +             if (ss_mode == 1 && port == 1)
> > +                     return -EINVAL;
> > +
> > +             /* XPCS0 is not used */
> > +             if (ss_mode == 2 && port == 0)
> > +                     return -EINVAL;
> > +
> > +             xpcs = devm_kmalloc(dev, sizeof(*xpcs), GFP_KERNEL);
> > +             if (!xpcs)
> > +                     return -ENOMEM;
> > +
> > +             xpcs_ctrl->phys[port] = xpcs;
> > +
> > +             ret = s32g_serdes_xpcs_init(serdes, port);
> > +             if (ret)
> > +                     return ret;
> > +
> >       } else {
> >               dev_warn(dev, "Skipping unknown child node %pOFn\n", child_node);
> >       }
> > @@ -517,6 +832,10 @@ static int s32g_serdes_probe(struct platform_device *pdev)
> >       if (ret)
> >               return ret;
> >
> > +     ret = s32g_serdes_get_xpcs_resources(pdev, serdes);
> > +     if (ret)
> > +             return ret;
> > +
> >       ret = s32g_serdes_parse_lanes(dev, serdes);
> >       if (ret)
> >               return ret;
> > @@ -555,6 +874,57 @@ static int __maybe_unused s32g_serdes_resume(struct device *device)
> >       return ret;
> >  }
> >
> > +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_node *np)
> > +{
> > +     struct platform_device *pdev;
> > +     struct device_node *pcs_np;
> > +     struct s32g_serdes *serdes;
> > +     u32 port;
> > +
> > +     if (of_property_read_u32(np, "reg", &port))
> > +             return ERR_PTR(-EINVAL);
> > +
> > +     if (port >= S32G_SERDES_XPCS_MAX)
> > +             return ERR_PTR(-EINVAL);
> > +
> > +     /* The PCS pdev is attached to the parent node */
> > +     pcs_np = of_get_parent(np);
> > +     if (!pcs_np)
> > +             return ERR_PTR(-ENODEV);
> > +
> > +     if (!of_device_is_available(pcs_np)) {
> > +             of_node_put(pcs_np);
> > +             return ERR_PTR(-ENODEV);
> > +     }
> > +
> > +     pdev = of_find_device_by_node(pcs_np);
> > +     of_node_put(pcs_np);
> > +     if (!pdev)
> > +             return ERR_PTR(-EPROBE_DEFER);
> > +
> > +     serdes = platform_get_drvdata(pdev);
> > +     if (!serdes) {
> > +             put_device(&pdev->dev);
> > +             return ERR_PTR(-EPROBE_DEFER);
> > +     }
> > +
> > +     if (!serdes->xpcs.phys[port]) {
> > +             put_device(&pdev->dev);
> > +             return ERR_PTR(-EPROBE_DEFER);
> > +     }
> > +
> > +     return &serdes->xpcs.phys[port]->pcs;
> > +}
> > +EXPORT_SYMBOL(s32g_serdes_pcs_create);
> > +
> > +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs)
> > +{
> > +     struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > +
> > +     put_device(xpcs->dev);
> > +}
> > +EXPORT_SYMBOL(s32g_serdes_pcs_destroy);
> > +
> >  static const struct of_device_id s32g_serdes_match[] = {
> >       {
> >               .compatible = "nxp,s32g2-serdes",
> > diff --git a/include/linux/pcs/pcs-nxp-s32g-xpcs.h b/include/linux/pcs/pcs-nxp-s32g-xpcs.h
> > new file mode 100644
> > index 000000000000..96a0049b93a6
> > --- /dev/null
> > +++ b/include/linux/pcs/pcs-nxp-s32g-xpcs.h
> > @@ -0,0 +1,50 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/**
> > + * Copyright 2021-2026 NXP
> > + */
> > +#ifndef PCS_NXP_S32G_XPCS_H
> > +#define PCS_NXP_S32G_XPCS_H
> > +
> > +#include <linux/phylink.h>
> > +
> > +enum s32g_xpcs_shared {
> > +     NOT_SHARED,
> > +     PCIE_XPCS_1G,
> > +     PCIE_XPCS_2G5,
> > +};
> > +
> > +enum s32g_xpcs_pll {
> > +     XPCS_PLLA,      /* Slow PLL */
> > +     XPCS_PLLB,      /* Fast PLL */
> > +};
> > +
> > +struct s32g_xpcs {
> > +     void __iomem *base;
> > +     struct device *dev;
> > +     unsigned char id;
> > +     struct regmap *regmap;
> > +     enum s32g_xpcs_pll ref;
> > +     bool ext_clk;
> > +     bool mhz125;
> > +     enum s32g_xpcs_shared pcie_shared;
> > +     struct phylink_pcs pcs;
> > +};
> > +
> > +#define phylink_pcs_to_s32g_xpcs(pl_pcs) \
> > +     container_of((pl_pcs), struct s32g_xpcs, pcs)
> > +
> > +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev,
> > +                unsigned char id, void __iomem *base, bool ext_clk,
> > +                unsigned long rate, enum s32g_xpcs_shared pcie_shared);
> > +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs);
> > +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs);
> > +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs);
> > +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs);
> > +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs);
> > +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs);
> > +
> > +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_node *np);
> > +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs);
> > +
> > +#endif /* PCS_NXP_S32G_XPCS_H */
> > +
> > --
> > 2.43.0
> >
> >
>
>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [PATCH v2 0/4] Add eMMC PHY support for Axiado AX3000 SoC
From: Tzu-Hao Wei @ 2026-02-06  8:22 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
	Tzu-Hao Wei

Axiado AX3000 SoC contains Arasan PHY which provides the interface to the
HS200 eMMC controller.

This series includes:
1. Add bindings for Axiado AX3000 eMMC PHY
2. Add Axiado AX3000 eMMC phy driver
3. Update MAINTAINERS for the new driver
4. Update Axiado AX3000 device tree

Changes in v2:
- Fix dt-binding format
- Fix compilation error in m68k
- Use readl_poll_timeout instead of read_poll_timeout
- Link to v1: https://lore.kernel.org/r/20260109-axiado-ax3000-add-emmc-phy-driver-support-v1-0-dd43459dbfea@axiado.com

Changes: (The previous version was mixed with Host driver, so I separate
the PHY driver as a new thread)
- Fix property order in required section to match properties section
- Fixed example to use lowercase hex and proper node naming
- Removed wrapper functions, use readl/writel directly
- Replaced manual polling loops with read_poll_timeout macro
- Used devm_platform_ioremap_resource instead of separate calls
- Removed unnecessary of_match_node check
- Used dev_err_probe for error reporting
- Added proper Kconfig dependencies (ARCH_AXIADO || COMPILE_TEST)
- Fixed various coding style issues
- Link to previous patches: https://lore.kernel.org/all/20251222-axiado-ax3000-add-emmc-host-driver-support-v1-0-5457d0ebcdb4@axiado.com/

Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
SriNavmani A (3):
      dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY
      phy: axiado: add Axiado eMMC PHY driver
      arm64: dts: axiado: Add eMMC PHY node

Tzu-Hao Wei (1):
      MAINTAINERS: Add Axiado AX3000 eMMC PHY driver

 .../bindings/phy/axiado,ax3000-emmc-phy.yaml       |  37 ++++
 MAINTAINERS                                        |  10 +
 arch/arm64/boot/dts/axiado/ax3000.dtsi             |   7 +
 drivers/phy/Kconfig                                |   1 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/axiado/Kconfig                         |  11 +
 drivers/phy/axiado/Makefile                        |   1 +
 drivers/phy/axiado/phy-axiado-emmc.c               | 221 +++++++++++++++++++++
 8 files changed, 289 insertions(+)
---
base-commit: 63804fed149a6750ffd28610c5c1c98cce6bd377
change-id: 20260108-axiado-ax3000-add-emmc-phy-driver-support-d61aead8f622

Best regards,
-- 
Tzu-Hao Wei <twei@axiado.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [PATCH v2 1/4] dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY
From: Tzu-Hao Wei @ 2026-02-06  8:22 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
	Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>

From: SriNavmani A <srinavmani@axiado.com>

Axiado AX3000 SoC contains Arasan PHY which provides the interface to the
HS200 eMMC host controller.

Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
 .../bindings/phy/axiado,ax3000-emmc-phy.yaml       | 37 ++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml b/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..61700b80e93f7185e16ca9eab0922fe6bb29fe86
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/axiado,ax3000-emmc-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Axiado AX3000 Arasan eMMC PHY
+
+maintainers:
+  - SriNavmani A <srinavmani@axiado.com>
+  - Tzu-Hao Wei <twei@axiado.com>
+  - Prasad Bolisetty <pbolisetty@axiado.com>
+
+properties:
+  compatible:
+    const: axiado,ax3000-emmc-phy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    phy@80801c00 {
+        compatible = "axiado,ax3000-emmc-phy";
+        reg = <0x80801c00 0x1000>;
+        #phy-cells = <0>;
+    };

-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v2 2/4] phy: axiado: add Axiado eMMC PHY driver
From: Tzu-Hao Wei @ 2026-02-06  8:22 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
	Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>

From: SriNavmani A <srinavmani@axiado.com>

It provides the required configurations for Axiado eMMC PHY driver for
HS200 mode.

Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Co-developed-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
 drivers/phy/Kconfig                  |   1 +
 drivers/phy/Makefile                 |   1 +
 drivers/phy/axiado/Kconfig           |  11 ++
 drivers/phy/axiado/Makefile          |   1 +
 drivers/phy/axiado/phy-axiado-emmc.c | 221 +++++++++++++++++++++++++++++++++++
 5 files changed, 235 insertions(+)

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 678dd0452f0aa0597773433f04d2a9ba77474d2a..b802274ea45a84bd36d7c0b7fb90e368a5c018b4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -103,6 +103,7 @@ config PHY_NXP_PTN3222
 
 source "drivers/phy/allwinner/Kconfig"
 source "drivers/phy/amlogic/Kconfig"
+source "drivers/phy/axiado/Kconfig"
 source "drivers/phy/broadcom/Kconfig"
 source "drivers/phy/cadence/Kconfig"
 source "drivers/phy/freescale/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index bfb27fb5a494283d7fd05dd670ebd1b12df8b1a1..f1b9e4a8673bcde3fdc0fdc06a3deddb5785ced1 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE)		+= phy-airoha-pcie.o
 obj-$(CONFIG_PHY_NXP_PTN3222)		+= phy-nxp-ptn3222.o
 obj-y					+= allwinner/	\
 					   amlogic/	\
+					   axiado/	\
 					   broadcom/	\
 					   cadence/	\
 					   freescale/	\
diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..d159e0345345987c7f48dcd12d3237997735d2b5
--- /dev/null
+++ b/drivers/phy/axiado/Kconfig
@@ -0,0 +1,11 @@
+#
+# PHY drivers for Axiado platforms
+#
+
+config PHY_AX3000_EMMC
+	tristate "Axiado eMMC PHY driver"
+	depends on OF && (ARCH_AXIADO || COMPILE_TEST)
+	select GENERIC_PHY
+	help
+	  Enables this to support for the AX3000 EMMC PHY driver.
+	  If unsure, say N.
diff --git a/drivers/phy/axiado/Makefile b/drivers/phy/axiado/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..1e2b1ba016092eaffdbd7acbd9cdc8577d79b35c
--- /dev/null
+++ b/drivers/phy/axiado/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PHY_AX3000_EMMC)		+= phy-axiado-emmc.o
diff --git a/drivers/phy/axiado/phy-axiado-emmc.c b/drivers/phy/axiado/phy-axiado-emmc.c
new file mode 100644
index 0000000000000000000000000000000000000000..28d2a30c3b35ee7dba917487959e226941e8ea4b
--- /dev/null
+++ b/drivers/phy/axiado/phy-axiado-emmc.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Axiado eMMC PHY driver
+ *
+ * Copyright (C) 2017 Arasan Chip Systems Inc.
+ * Copyright (C) 2022-2025 Axiado Corporation (or its affiliates).
+ *
+ * Based on Arasan Driver (sdhci-pci-arasan.c)
+ * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with integrated phy.
+ */
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/* Arasan eMMC 5.1 - PHY configuration registers */
+#define CAP_REG_IN_S1_LSB		0x00
+#define CAP_REG_IN_S1_MSB		0x04
+#define PHY_CTRL_1			0x38
+#define PHY_CTRL_2			0x3C
+#define PHY_CTRL_3			0x40
+#define STATUS				0x50
+
+#define DLL_ENBL	BIT(26)
+#define RTRIM_EN	BIT(21)
+#define PDB_ENBL	BIT(23)
+#define RETB_ENBL	BIT(1)
+
+#define REN_STRB	BIT(27)
+#define REN_CMD		BIT(12)
+#define REN_DAT0	BIT(13)
+#define REN_DAT1	BIT(14)
+#define REN_DAT2	BIT(15)
+#define REN_DAT3	BIT(16)
+#define REN_DAT4	BIT(17)
+#define REN_DAT5	BIT(18)
+#define REN_DAT6	BIT(19)
+#define REN_DAT7	BIT(20)
+#define REN_CMD_EN	(REN_CMD | REN_DAT0 | REN_DAT1 | REN_DAT2 | \
+		REN_DAT3 | REN_DAT4 | REN_DAT5 | REN_DAT6 | REN_DAT7)
+
+/* Pull-UP Enable on CMD Line */
+#define PU_CMD		BIT(3)
+#define PU_DAT0		BIT(4)
+#define PU_DAT1		BIT(5)
+#define PU_DAT2		BIT(6)
+#define PU_DAT3		BIT(7)
+#define PU_DAT4		BIT(8)
+#define PU_DAT5		BIT(9)
+#define PU_DAT6		BIT(10)
+#define PU_DAT7		BIT(11)
+#define PU_CMD_EN (PU_CMD | PU_DAT0 | PU_DAT1 | PU_DAT2 | PU_DAT3 | \
+		PU_DAT4 | PU_DAT5 | PU_DAT6 | PU_DAT7)
+
+/* Selection value for the optimum delay from 1-32 output tap lines */
+#define OTAP_DLY	0x02
+/* DLL charge pump current trim default [1000] */
+#define DLL_TRM_ICP	0x08
+/* Select the frequency range of DLL Operation */
+#define FRQ_SEL	0x01
+
+#define OTAP_SEL_MASK		GENMASK(10, 7)
+#define DLL_TRM_MASK		GENMASK(25, 22)
+#define DLL_FRQSEL_MASK		GENMASK(27, 25)
+
+#define OTAP_SEL(x)		(FIELD_PREP(OTAP_SEL_MASK, x) | OTAPDLY_EN)
+#define DLL_TRM(x)		(FIELD_PREP(DLL_TRM_MASK, x) | DLL_ENBL)
+#define DLL_FRQSEL(x)	FIELD_PREP(DLL_FRQSEL_MASK, x)
+
+#define OTAPDLY_EN	BIT(11)
+
+#define SEL_DLY_RXCLK	BIT(18)
+#define SEL_DLY_TXCLK	BIT(19)
+
+#define CALDONE_MASK	0x40
+#define DLL_RDY_MASK	0x1
+#define MAX_CLK_BUF0	BIT(20)
+#define MAX_CLK_BUF1	BIT(21)
+#define MAX_CLK_BUF2	BIT(22)
+
+#define CLK_MULTIPLIER	0xC008E
+#define POLL_TIMEOUT_MS	3000
+#define POLL_DELAY_US	100
+
+struct axiado_emmc_phy {
+	void __iomem *reg_base;
+	struct device *dev;
+};
+
+static int axiado_emmc_phy_init(struct phy *phy)
+{
+	struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
+	struct device *dev = ax_phy->dev;
+	u32 val;
+	int ret;
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_1);
+	writel(val | RETB_ENBL | RTRIM_EN, ax_phy->reg_base + PHY_CTRL_1);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_3);
+	writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3);
+
+	ret = readl_poll_timeout(ax_phy->reg_base + STATUS, val,
+				 val & CALDONE_MASK, POLL_DELAY_US,
+				 POLL_TIMEOUT_MS * 1000);
+	if (ret) {
+		dev_err(dev, "PHY calibration timeout\n");
+		return ret;
+	}
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_1);
+	writel(val | REN_CMD_EN | PU_CMD_EN, ax_phy->reg_base + PHY_CTRL_1);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_2);
+	writel(val | REN_STRB, ax_phy->reg_base + PHY_CTRL_2);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_3);
+	writel(val | MAX_CLK_BUF0 | MAX_CLK_BUF1 | MAX_CLK_BUF2,
+	       ax_phy->reg_base + PHY_CTRL_3);
+
+	writel(CLK_MULTIPLIER, ax_phy->reg_base + CAP_REG_IN_S1_MSB);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_3);
+	writel(val | SEL_DLY_RXCLK | SEL_DLY_TXCLK,
+	       ax_phy->reg_base + PHY_CTRL_3);
+
+	return 0;
+}
+
+static int axiado_emmc_phy_power_on(struct phy *phy)
+{
+	struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
+	struct device *dev = ax_phy->dev;
+	u32 val;
+	int ret;
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_1);
+	writel(val | RETB_ENBL, ax_phy->reg_base + PHY_CTRL_1);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_3);
+	writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_2);
+	writel(val | OTAP_SEL(OTAP_DLY), ax_phy->reg_base + PHY_CTRL_2);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_1);
+	writel(val | DLL_TRM(DLL_TRM_ICP), ax_phy->reg_base + PHY_CTRL_1);
+
+	val = readl(ax_phy->reg_base + PHY_CTRL_3);
+	writel(val | DLL_FRQSEL(FRQ_SEL), ax_phy->reg_base + PHY_CTRL_3);
+
+	ret = read_poll_timeout(readl, val, val & DLL_RDY_MASK, POLL_DELAY_US,
+				POLL_TIMEOUT_MS * 1000, false,
+				ax_phy->reg_base + STATUS);
+	if (ret) {
+		dev_err(dev, "DLL ready timeout\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct phy_ops axiado_emmc_phy_ops = {
+	.init = axiado_emmc_phy_init,
+	.power_on = axiado_emmc_phy_power_on,
+	.owner = THIS_MODULE,
+};
+
+static const struct of_device_id axiado_emmc_phy_of_match[] = {
+	{ .compatible = "axiado,ax3000-emmc-phy" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, axiado_emmc_phy_of_match);
+
+static int axiado_emmc_phy_probe(struct platform_device *pdev)
+{
+	struct axiado_emmc_phy *ax_phy;
+	struct phy_provider *phy_provider;
+	struct device *dev = &pdev->dev;
+	struct phy *generic_phy;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL);
+	if (!ax_phy)
+		return -ENOMEM;
+
+	ax_phy->reg_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(ax_phy->reg_base))
+		return PTR_ERR(ax_phy->reg_base);
+
+	ax_phy->dev = dev;
+
+	generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops);
+	if (IS_ERR(generic_phy))
+		return dev_err_probe(dev, PTR_ERR(generic_phy),
+				     "failed to create PHY\n");
+
+	phy_set_drvdata(generic_phy, ax_phy);
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver axiado_emmc_phy_driver = {
+	.probe = axiado_emmc_phy_probe,
+	.driver = {
+		.name = "axiado-emmc-phy",
+		.of_match_table = axiado_emmc_phy_of_match,
+	},
+};
+module_platform_driver(axiado_emmc_phy_driver);
+
+MODULE_DESCRIPTION("AX3000 eMMC PHY Driver");
+MODULE_AUTHOR("Axiado Corporation");
+MODULE_LICENSE("GPL");

-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v2 3/4] MAINTAINERS: Add Axiado AX3000 eMMC PHY driver
From: Tzu-Hao Wei @ 2026-02-06  8:22 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
	Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>

Add SriNavmani, Prasad and me as maintainers for Axiado AX3000 eMMC PHY
driver

Acked-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 67db88b04537b431c927b73624993233eef43e3f..c33b0aa94de81c89b674e44d4813c4e3b95b7b2d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4254,6 +4254,16 @@ W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml
 F:	drivers/hwmon/axi-fan-control.c
 
+AXIADO EMMC PHY DRIVER
+M:	SriNavmani A <srinavmani@axiado.com>
+M:	Tzu-Hao Wei <twei@axiado.com>
+M:	Prasad Bolisetty <pbolisetty@axiado.com>
+L:	linux-phy@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
+F:	drivers/phy/axiado/Kconfig
+F:	drivers/phy/axiado/phy-axiado-emmc.c
+
 AXI SPI ENGINE
 M:	Michael Hennerich <michael.hennerich@analog.com>
 M:	Nuno Sá <nuno.sa@analog.com>

-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v2 4/4] arm64: dts: axiado: Add eMMC PHY node
From: Tzu-Hao Wei @ 2026-02-06  8:22 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
	Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>

From: SriNavmani A <srinavmani@axiado.com>

Add the eMMC PHY device tree node to the AX3000 SoC DTSI.
AX3000 has one eMMC PHY interface.

Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
 arch/arm64/boot/dts/axiado/ax3000.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/axiado/ax3000.dtsi b/arch/arm64/boot/dts/axiado/ax3000.dtsi
index 792f52e0c7dd42cbc54b0eb47e25b0fbf1a706b8..ccc8088bd8258cfb666268b14a3b0716a9ca69f4 100644
--- a/arch/arm64/boot/dts/axiado/ax3000.dtsi
+++ b/arch/arm64/boot/dts/axiado/ax3000.dtsi
@@ -507,6 +507,13 @@ uart3: serial@80520800 {
 			clocks = <&refclk &refclk>;
 			status = "disabled";
 		};
+
+		emmc_phy: phy@80801c00 {
+			compatible = "axiado,ax3000-emmc-phy";
+			reg = <0x0 0x80801c00 0x0 0x1000>;
+			#phy-cells = <0>;
+			status = "disabled";
+		};
 	};
 
 	timer {

-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v3 0/2] Add Axiado AX3000 eMMC Host Controller Support
From: Tzu-Hao Wei @ 2026-02-06  8:23 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Adrian Hunter, Michal Simek
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
	openbmc, Tzu-Hao Wei

Axiado AX3000 SoC eMMC controller is based on Arasan eMMC controller.

This series includes:
1. Add bindings for AX3000 SoC eMMC controller
2. Add arasan sdhci support for eMMC in Axiado AX3000

For platform device tree change, we will send a different series of
patches.

It has been verified on AX3000 platform.

---
Changes in v3:
- Enable SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN quirk
- Remove dependencies in b4
- Link to v2: https://lore.kernel.org/r/20260109-axiado-ax3000-add-emmc-host-driver-support-v2-0-934f1a61f7c0@axiado.com

Changes in v2:
- Keep host controller changes in this patchset
- Use pdata instead of mix compatible string
- Fix coding style
- Link to v1: https://lore.kernel.org/r/20251222-axiado-ax3000-add-emmc-host-driver-support-v1-0-5457d0ebcdb4@axiado.com

Signed-off-by: Tzu-Hao Wei <twei@axiado.com>

---
SriNavmani A (2):
      dt-bindings: mmc: arasan,sdhci: Add Axiado AX3000 SoC
      mmc: sdhci-of-arasan: add support on Axiado AX3000 SoC

 Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml |  3 +++
 drivers/mmc/host/sdhci-of-arasan.c                      | 15 +++++++++++++++
 2 files changed, 18 insertions(+)
---
base-commit: 63804fed149a6750ffd28610c5c1c98cce6bd377
change-id: 20251222-axiado-ax3000-add-emmc-host-driver-support-2cc84a8f889a

Best regards,
-- 
Tzu-Hao Wei <twei@axiado.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [PATCH v3 1/2] dt-bindings: mmc: arasan,sdhci: Add Axiado AX3000 SoC
From: Tzu-Hao Wei @ 2026-02-06  8:23 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Adrian Hunter, Michal Simek
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
	openbmc, Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-host-driver-support-v3-0-ef83b09325be@axiado.com>

From: SriNavmani A <srinavmani@axiado.com>

Add compatible strings for Axiado AX3000 SoC eMMC controller which
is based on Arasan eMMC controller.

Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
 Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml
index 8e79de97b242a698a2c555b0b94e2975b1761710..17dfe220503a6722b9ab446cb6e1aeed56099caf 100644
--- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml
+++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml
@@ -106,6 +106,9 @@ properties:
         description:
           For this device it is strongly suggested to include
           arasan,soc-ctl-syscon.
+      - items:
+          - const: axiado,ax3000-sdhci-5.1-emmc  # Axiado AX3000 eMMC controller
+          - const: arasan,sdhci-5.1
 
   reg:
     maxItems: 1

-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v3 2/2] mmc: sdhci-of-arasan: add support on Axiado AX3000 SoC
From: Tzu-Hao Wei @ 2026-02-06  8:23 UTC (permalink / raw)
  To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Adrian Hunter, Michal Simek
  Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
	openbmc, Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-host-driver-support-v3-0-ef83b09325be@axiado.com>

From: SriNavmani A <srinavmani@axiado.com>

Axiado AX3000 SoC eMMC controller is based on Arasan eMMC 5.1 host
controller IP.

Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
 drivers/mmc/host/sdhci-of-arasan.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index ab7f0ffe7b4f007a58eb0a26868b08b0b02b40f3..caf97238a58b487312d6cc2b7a868913ace60f22 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -1512,6 +1512,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
 	.clk_ops = &arasan_clk_ops,
 };
 
+static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = {
+	.ops = &sdhci_arasan_ops,
+	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+			SDHCI_QUIRK_BROKEN_CQE,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = {
+	.pdata = &sdhci_arasan_axiado_pdata,
+	.clk_ops = &arasan_clk_ops,
+};
+
 static const struct of_device_id sdhci_arasan_of_match[] = {
 	/* SoC-specific compatible strings w/ soc_ctl_map */
 	{
@@ -1538,6 +1549,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
 		.compatible = "intel,keembay-sdhci-5.1-sdio",
 		.data = &intel_keembay_sdio_data,
 	},
+	{
+		.compatible = "axiado,ax3000-sdhci-5.1-emmc",
+		.data = &sdhci_arasan_axiado_data,
+	},
 	/* Generic compatible below here */
 	{
 		.compatible = "arasan,sdhci-8.9a",

-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* Re: [PATCH v6 2/8] phy: sort Kconfig and Makefile
From: Luca Ceresoli @ 2026-02-06  8:39 UTC (permalink / raw)
  To: Théo Lebrun, Vladimir Kondratiev, Grégory Clement,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
	Kishon Vijay Abraham I, Michael Turquette, Stephen Boyd,
	Philipp Zabel, Thomas Bogendoerfer, Neil Armstrong
  Cc: linux-mips, devicetree, linux-kernel, linux-phy, linux-clk,
	Benoît Monin, Tawfik Bayouk, Thomas Petazzoni
In-Reply-To: <20260127-macb-phy-v6-2-cdd840588188@bootlin.com>

On Tue Jan 27, 2026 at 6:09 PM CET, Théo Lebrun wrote:
> Neither Kconfig nor Makefile are sorted; reorder them.
>
> $ diff -U100 <(grep ^config drivers/phy/Kconfig) \
>              <(grep ^config drivers/phy/Kconfig | sort)
>
> $ diff -U100 <(grep ^obj-\\$ drivers/phy/Makefile) \
>              <(grep ^obj-\\$ drivers/phy/Makefile | sort)
>
> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>

Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [PATCH 1/2] dt-bindings: phy: spacemit: add regulator support to K1 USB2 PHY
From: Chukun Pan @ 2026-02-06 10:00 UTC (permalink / raw)
  To: Yixun Lan, Vinod Koul, Ze Huang
  Cc: Rob Herring, Chukun Pan, Mark Brown, Conor Dooley, Liam Girdwood,
	Krzysztof Kozlowski, Neil Armstrong, linux-riscv, linux-phy,
	linux-kernel, devicetree, spacemit

Add an optional phy-supply property to describe the regulator
supplying for USB VBUS.

Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
 Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
index 43eaca90d88c..74a1cd5bcdbe 100644
--- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
@@ -19,6 +19,10 @@ properties:
   clocks:
     maxItems: 1
 
+  phy-supply:
+    description:
+      Phandle to a regulator that provides power to VBUS.
+
   "#phy-cells":
     const: 0
 
-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH 2/2] phy: spacemit: add regulator support to K1 USB2 PHY
From: Chukun Pan @ 2026-02-06 10:00 UTC (permalink / raw)
  To: Yixun Lan, Vinod Koul, Ze Huang
  Cc: Rob Herring, Chukun Pan, Mark Brown, Conor Dooley, Liam Girdwood,
	Krzysztof Kozlowski, Neil Armstrong, linux-riscv, linux-phy,
	linux-kernel, devicetree, spacemit
In-Reply-To: <20260206100009.873182-1-amadeus@jmu.edu.cn>

USB VBUS may have a regulator supply. Add optional phy-supply
support to avoid the need for the regulator to be always-on.

Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
 drivers/phy/spacemit/phy-k1-usb2.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c
index 342061380012..c163a29dabf9 100644
--- a/drivers/phy/spacemit/phy-k1-usb2.c
+++ b/drivers/phy/spacemit/phy-k1-usb2.c
@@ -75,6 +75,7 @@ struct spacemit_usb2phy {
 	struct phy *phy;
 	struct clk *clk;
 	struct regmap *regmap_base;
+	struct regulator *regulator;
 };
 
 static const struct regmap_config phy_regmap_config = {
@@ -98,6 +99,12 @@ static int spacemit_usb2phy_init(struct phy *phy)
 		return ret;
 	}
 
+	if (sphy->regulator) {
+		ret = regulator_enable(sphy->regulator);
+		if (ret)
+			return ret;
+	}
+
 	/*
 	 * make sure the usb controller is not under reset process before
 	 * any configuration
@@ -139,6 +146,9 @@ static int spacemit_usb2phy_exit(struct phy *phy)
 
 	clk_disable(sphy->clk);
 
+	if (sphy->regulator)
+		return regulator_disable(sphy->regulator);
+
 	return 0;
 }
 
@@ -171,6 +181,16 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev)
 	if (IS_ERR(sphy->regmap_base))
 		return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n");
 
+	sphy->regulator = devm_regulator_get_optional(dev, "phy");
+	if (IS_ERR(sphy->regulator)) {
+		int ret = PTR_ERR(sphy->regulator);
+
+		if (ret != -ENODEV)
+			return dev_err_probe(dev, ret, "Failed to get regulator\n");
+
+		sphy->regulator = NULL;
+	}
+
 	sphy->phy = devm_phy_create(dev, NULL, &spacemit_usb2phy_ops);
 	if (IS_ERR(sphy->phy))
 		return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n");
-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* Re: [PATCH 1/3] phy: qcom: edp: Correct and clean up eDP/DP combo PHY configuration values
From: Konrad Dybcio @ 2026-02-06 10:47 UTC (permalink / raw)
  To: Yongxing Mou, Vinod Koul, Neil Armstrong
  Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260205-edp_phy-v1-1-231882bbf3f1@oss.qualcomm.com>

On 2/5/26 10:20 AM, Yongxing Mou wrote:
> According to the current HPG settings, most eDP/DP combo PHYs can reuse the
> same configuration values.

Even across the various process nodes?

> DP mode:
> 	-sa8775p/sc7280/sc8280xp/x1e80100
> 	-glymur
> eDP mode(low vdiff):
> 	-glymur/sa8775p/sc8280xp/x1e80100
> 	-sc7280
> The current driver still keeps multiple versions of these tables and
> doesn't fully support every combo PHY mode. This patch removes the
> redundant configs and keeps only the sets we actually use, matching the
> platforms listed above.

I see that e.g. eDP Low-Vdiff swing setting for RBR is:

		hamoa	kodiak
arr[0][1]	0x11	0x12

It may be that this changed later during tuning but it's not reflected
in the docs for kodiak

Konrad

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 2/3] phy: qcom: edp: Add per-version LDO configuration callback
From: Konrad Dybcio @ 2026-02-06 10:52 UTC (permalink / raw)
  To: Yongxing Mou, Vinod Koul, Neil Armstrong, Dmitry Baryshkov
  Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260205-edp_phy-v1-2-231882bbf3f1@oss.qualcomm.com>

On 2/5/26 10:20 AM, Yongxing Mou wrote:
> Introduce the com_ldo_config callback to support per‑PHY LDO
> configuration.
> 
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---

[...]

> +static int qcom_edp_ldo_config_v4(const struct qcom_edp *edp)
> +{
> +	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
> +	u32 ldo_config;
> +
> +	if (!edp->is_edp)
> +		ldo_config = 0x0;
> +	else if (dp_opts->link_rate <= 2700)
> +		ldo_config = 0xC1;

lowercase hex, please

> +	else
> +		ldo_config = 0x81;
> +
> +	writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> +	writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);

tx1 should be dp_ops->lanes ? 2 : ldo_config : 0x00, in all cases,
I believe

Konrad

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 3/3] phy: qcom: edp: Add eDP phy mode switch support
From: Konrad Dybcio @ 2026-02-06 11:02 UTC (permalink / raw)
  To: Yongxing Mou, Vinod Koul, Neil Armstrong
  Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260205-edp_phy-v1-3-231882bbf3f1@oss.qualcomm.com>

On 2/5/26 10:20 AM, Yongxing Mou wrote:
> Add DP/eDP switch support by splitting the PHY swing/pre-emphasis tables
> into separate DP and eDP configurations. This allows the driver to select
> the correct table based on the is_edp flag.
> 
> Add a dedicated table for the SC7280/glymur platforms, as they are not
> compatible with the others.
> 
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---

[...]

> +static const u8 edp_swing_hbr2_hbr3_v3[4][4] = {
> +	{ 0x0b, 0x11, 0x16, 0x1b },
> +	{ 0x0b, 0x19, 0x1f, 0xff },
> +	{ 0x18, 0x1f, 0xff, 0xff },
> +	{ 0x1f, 0xff, 0xff, 0xff }
> +};
> +
> +static const u8 edp_pre_emp_hbr2_hbr3_v3[4][4] = {
> +	{ 0x0c, 0x15, 0x19, 0x1e },
> +	{ 0x09, 0x14, 0x19, 0xff },
> +	{ 0x0f, 0x14, 0xff, 0xff },
> +	{ 0x0d, 0xff, 0xff, 0xff }
> +};

This is not quite in line with docs for kodiak. Now, if you have
better/newer sequences than the HPG, I'm not objecting, but please
cross-check

the rest of this patch I think looks fine

Konrad

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [bug report] phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY config and DP mode support
From: Dan Carpenter @ 2026-02-06 13:39 UTC (permalink / raw)
  To: Xiangxu Yin; +Cc: Neil Armstrong, linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <caa37f28-a2e8-4e0a-a9ce-a365ce805e4b@stanley.mountain>

[ Smatch checking is paused while we raise funding.  #SadFace
  https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]

Hello Xiangxu Yin,

Commit 81791c45c8e0 ("phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY
config and DP mode support") from Dec 15, 2025 (linux-next), leads to
the following Smatch static checker warning:

	drivers/phy/qualcomm/phy-qcom-qmp-usbc.c:803 qmp_v2_configure_dp_swing()
	index hardmax out of bounds '(*cfg->swing_tbl)[v_level]' size=4 max='4' rl='0-4'

drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
    777 static int qmp_v2_configure_dp_swing(struct qmp_usbc *qmp)
    778 {
    779         const struct qmp_phy_cfg *cfg = qmp->cfg;
    780         const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
    781         void __iomem *tx = qmp->dp_tx;
    782         void __iomem *tx2 = qmp->dp_tx2;
    783         unsigned int v_level = 0, p_level = 0;
    784         u8 voltage_swing_cfg, pre_emphasis_cfg;
    785         int i;
    786 
    787         if (dp_opts->lanes > 4) {
    788                 dev_err(qmp->dev, "Invalid lane_num(%d)\n", dp_opts->lanes);
    789                 return -EINVAL;
    790         }
    791 
    792         for (i = 0; i < dp_opts->lanes; i++) {
    793                 v_level = max(v_level, dp_opts->voltage[i]);
    794                 p_level = max(p_level, dp_opts->pre[i]);
    795         }
    796 
    797         if (v_level > 4 || p_level > 4) {

These should be >= 4 instead of >.

    798                 dev_err(qmp->dev, "Invalid v(%d) | p(%d) level)\n",
    799                         v_level, p_level);
    800                 return -EINVAL;
    801         }
    802 
--> 803         voltage_swing_cfg = (*cfg->swing_tbl)[v_level][p_level];
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is a 4x4 array.

    804         pre_emphasis_cfg = (*cfg->pre_emphasis_tbl)[v_level][p_level];
    805 
    806         voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
    807         pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
    808 
    809         if (voltage_swing_cfg == 0xff && pre_emphasis_cfg == 0xff)
    810                 return -EINVAL;
    811 
    812         writel(voltage_swing_cfg, tx + QSERDES_V2_TX_TX_DRV_LVL);
    813         writel(pre_emphasis_cfg, tx + QSERDES_V2_TX_TX_EMP_POST1_LVL);
    814         writel(voltage_swing_cfg, tx2 + QSERDES_V2_TX_TX_DRV_LVL);
    815         writel(pre_emphasis_cfg, tx2 + QSERDES_V2_TX_TX_EMP_POST1_LVL);
    816 
    817         return 0;
    818 }

regards,
dan carpenter

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [bug report] phy: apple: Add Apple Type-C PHY
From: Dan Carpenter @ 2026-02-06 13:40 UTC (permalink / raw)
  To: Sven Peter
  Cc: Neal Gompa, Neil Armstrong, asahi, linux-arm-kernel, linux-phy,
	linux-kernel
In-Reply-To: <caa37f28-a2e8-4e0a-a9ce-a365ce805e4b@stanley.mountain>

[ Smatch checking is paused while we raise funding.  #SadFace
  https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]

Hello Sven Peter,

Commit 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") from Dec 14,
2025 (linux-next), leads to the following Smatch static checker
warning:

	drivers/phy/apple/atc.c:2209 atcphy_map_resources()
	warn: 'resources[i]->addr' isn't an ERR_PTR

drivers/phy/apple/atc.c
    2191 static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
    2192 {
    2193         struct {
    2194                 const char *name;
    2195                 void __iomem **addr;
    2196                 struct resource **res;
    2197         } resources[] = {
    2198                 { "core", &atcphy->regs.core, &atcphy->res.core },
    2199                 { "lpdptx", &atcphy->regs.lpdptx, NULL },
    2200                 { "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
    2201                 { "usb2phy", &atcphy->regs.usb2phy, NULL },
    2202                 { "pipehandler", &atcphy->regs.pipehandler, NULL },
    2203         };
    2204         struct resource *res;
    2205 
    2206         for (int i = 0; i < ARRAY_SIZE(resources); i++) {
    2207                 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
    2208                 *resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
--> 2209                 if (IS_ERR(resources[i].addr))

This is checking the wrong variable.  The * is missing.
if (IS_ERR(*resources[i].addr)) {

    2210                         return dev_err_probe(atcphy->dev, PTR_ERR(resources[i].addr),
    2211                                              "Unable to map %s regs", resources[i].name);
    2212 
    2213                 if (resources[i].res)
    2214                         *resources[i].res = res;
    2215         }
    2216 
    2217         return 0;
    2218 }

regards,
dan carpenter

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [bug report] phy: apple: Add Apple Type-C PHY
From: Janne Grunau @ 2026-02-06 21:47 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Sven Peter, Neal Gompa, Neil Armstrong, asahi, linux-arm-kernel,
	linux-phy, linux-kernel
In-Reply-To: <aYXvX1bYOXtYCgfC@stanley.mountain>

On Fri, Feb 06, 2026 at 04:40:47PM +0300, Dan Carpenter wrote:
> [ Smatch checking is paused while we raise funding.  #SadFace
>   https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]

This is unfortunate, there have been useful bug reports.

> Commit 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") from Dec 14,
> 2025 (linux-next), leads to the following Smatch static checker
> warning:
> 
> 	drivers/phy/apple/atc.c:2209 atcphy_map_resources()
> 	warn: 'resources[i]->addr' isn't an ERR_PTR
> 
> drivers/phy/apple/atc.c
>     2191 static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
>     2192 {
>     2193         struct {
>     2194                 const char *name;
>     2195                 void __iomem **addr;
>     2196                 struct resource **res;
>     2197         } resources[] = {
>     2198                 { "core", &atcphy->regs.core, &atcphy->res.core },
>     2199                 { "lpdptx", &atcphy->regs.lpdptx, NULL },
>     2200                 { "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
>     2201                 { "usb2phy", &atcphy->regs.usb2phy, NULL },
>     2202                 { "pipehandler", &atcphy->regs.pipehandler, NULL },
>     2203         };
>     2204         struct resource *res;
>     2205 
>     2206         for (int i = 0; i < ARRAY_SIZE(resources); i++) {
>     2207                 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
>     2208                 *resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
> --> 2209                 if (IS_ERR(resources[i].addr))
> 
> This is checking the wrong variable.  The * is missing.
> if (IS_ERR(*resources[i].addr)) {

This issue was identified by testing and is fixed in next by commit
7d55b44e2be1 ("phy: apple: atc: Actually check return value of
devm_apple_tunable_parse").

https://lore.kernel.org/all/20260104-atcphy-tunable-fix-v2-1-84e5c2a57aaa@kernel.org/

Thanks for the report

Janne

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [bug report] phy: apple: Add Apple Type-C PHY
From: Sven Peter @ 2026-02-06 21:48 UTC (permalink / raw)
  To: Janne Grunau, Dan Carpenter
  Cc: Neal Gompa, Neil Armstrong, asahi, linux-arm-kernel, linux-phy,
	linux-kernel
In-Reply-To: <20260206214743.GA194367@robin.jannau.net>

On 06.02.26 22:47, Janne Grunau wrote:
> On Fri, Feb 06, 2026 at 04:40:47PM +0300, Dan Carpenter wrote:
>> [ Smatch checking is paused while we raise funding.  #SadFace
>>    https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]
> 
> This is unfortunate, there have been useful bug reports.
> 
>> Commit 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") from Dec 14,
>> 2025 (linux-next), leads to the following Smatch static checker
>> warning:
>>
>> 	drivers/phy/apple/atc.c:2209 atcphy_map_resources()
>> 	warn: 'resources[i]->addr' isn't an ERR_PTR
>>
>> drivers/phy/apple/atc.c
>>      2191 static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
>>      2192 {
>>      2193         struct {
>>      2194                 const char *name;
>>      2195                 void __iomem **addr;
>>      2196                 struct resource **res;
>>      2197         } resources[] = {
>>      2198                 { "core", &atcphy->regs.core, &atcphy->res.core },
>>      2199                 { "lpdptx", &atcphy->regs.lpdptx, NULL },
>>      2200                 { "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
>>      2201                 { "usb2phy", &atcphy->regs.usb2phy, NULL },
>>      2202                 { "pipehandler", &atcphy->regs.pipehandler, NULL },
>>      2203         };
>>      2204         struct resource *res;
>>      2205
>>      2206         for (int i = 0; i < ARRAY_SIZE(resources); i++) {
>>      2207                 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
>>      2208                 *resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
>> --> 2209                 if (IS_ERR(resources[i].addr))
>>
>> This is checking the wrong variable.  The * is missing.
>> if (IS_ERR(*resources[i].addr)) {
> 
> This issue was identified by testing and is fixed in next by commit
> 7d55b44e2be1 ("phy: apple: atc: Actually check return value of
> devm_apple_tunable_parse").
> 
> https://lore.kernel.org/all/20260104-atcphy-tunable-fix-v2-1-84e5c2a57aaa@kernel.org/

I think I actually messed this up *twice*! Once for the tunables and 
once again for the resources here :(


Sven



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 2/3] phy: qcom: edp: Add per-version LDO configuration callback
From: Dmitry Baryshkov @ 2026-02-07 10:17 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Vinod Koul, Neil Armstrong, linux-arm-msm, linux-phy,
	linux-kernel
In-Reply-To: <20260205-edp_phy-v1-2-231882bbf3f1@oss.qualcomm.com>

On Thu, Feb 05, 2026 at 05:20:54PM +0800, Yongxing Mou wrote:
> Introduce the com_ldo_config callback to support per‑PHY LDO
> configuration.

Missing the why part. Is the driver programming incorrect values, or is
it an optimisation? In the former case it needs Fixes, maybe cc:stable,
maybe Reported-by, etc.

> 
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
>  drivers/phy/qualcomm/phy-qcom-edp.c | 86 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 76 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
> index 10cbb7d9a8a0..388226dbad7f 100644
> --- a/drivers/phy/qualcomm/phy-qcom-edp.c
> +++ b/drivers/phy/qualcomm/phy-qcom-edp.c
> @@ -81,6 +81,7 @@ struct phy_ver_ops {
>  	int (*com_clk_fwd_cfg)(const struct qcom_edp *edp);
>  	int (*com_configure_pll)(const struct qcom_edp *edp);
>  	int (*com_configure_ssc)(const struct qcom_edp *edp);
> +	int (*com_ldo_config)(const struct qcom_edp *edp);
>  };
>  
>  struct qcom_edp_phy_cfg {
> @@ -273,7 +274,7 @@ static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configur
>  	const struct qcom_edp_swing_pre_emph_cfg *cfg = edp->cfg->swing_pre_emph_cfg;
>  	unsigned int v_level = 0;
>  	unsigned int p_level = 0;
> -	u8 ldo_config;
> +	int ret;
>  	u8 swing;
>  	u8 emph;
>  	int i;
> @@ -300,13 +301,13 @@ static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configur
>  	if (swing == 0xff || emph == 0xff)
>  		return -EINVAL;
>  
> -	ldo_config = edp->is_edp ? 0x0 : 0x1;
> +	ret = edp->cfg->ver_ops->com_ldo_config(edp);
> +	if (ret)
> +		return ret;
>  
> -	writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
>  	writel(swing, edp->tx0 + TXn_TX_DRV_LVL);
>  	writel(emph, edp->tx0 + TXn_TX_EMP_POST1_LVL);
>  
> -	writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
>  	writel(swing, edp->tx1 + TXn_TX_DRV_LVL);
>  	writel(emph, edp->tx1 + TXn_TX_EMP_POST1_LVL);
>  
> @@ -530,6 +531,52 @@ static int qcom_edp_com_configure_pll_v4(const struct qcom_edp *edp)
>  	return 0;
>  }
>  
> +static int qcom_edp_ldo_config_v3(const struct qcom_edp *edp)
> +{
> +	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
> +	u32 ldo_config;
> +
> +	if (!edp->is_edp)
> +		ldo_config = 0x0;
> +	else if (dp_opts->link_rate <= 2700)
> +		ldo_config = 0x81;
> +	else
> +		ldo_config = 0x41;
> +
> +	writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> +	writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
> +
> +	return 0;
> +}
> +
> +static int qcom_edp_ldo_config_v4(const struct qcom_edp *edp)
> +{
> +	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
> +	u32 ldo_config;
> +
> +	if (!edp->is_edp)
> +		ldo_config = 0x0;
> +	else if (dp_opts->link_rate <= 2700)
> +		ldo_config = 0xC1;

Lowercase hex

> +	else
> +		ldo_config = 0x81;
> +
> +	writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> +	writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ver_ops qcom_edp_phy_ops_v3 = {
> +	.com_power_on		= qcom_edp_phy_power_on_v4,
> +	.com_resetsm_cntrl	= qcom_edp_phy_com_resetsm_cntrl_v4,
> +	.com_bias_en_clkbuflr	= qcom_edp_com_bias_en_clkbuflr_v4,
> +	.com_clk_fwd_cfg	= qcom_edp_com_clk_fwd_cfg_v4,
> +	.com_configure_pll	= qcom_edp_com_configure_pll_v4,
> +	.com_configure_ssc	= qcom_edp_com_configure_ssc_v4,
> +	.com_ldo_config		= qcom_edp_ldo_config_v3,
> +};
> +
>  static const struct phy_ver_ops qcom_edp_phy_ops_v4 = {
>  	.com_power_on		= qcom_edp_phy_power_on_v4,
>  	.com_resetsm_cntrl	= qcom_edp_phy_com_resetsm_cntrl_v4,
> @@ -537,6 +584,7 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v4 = {
>  	.com_clk_fwd_cfg	= qcom_edp_com_clk_fwd_cfg_v4,
>  	.com_configure_pll	= qcom_edp_com_configure_pll_v4,
>  	.com_configure_ssc	= qcom_edp_com_configure_ssc_v4,
> +	.com_ldo_config		= qcom_edp_ldo_config_v4,
>  };
>  
>  static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
> @@ -550,7 +598,7 @@ static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
>  static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = {
>  	.aux_cfg = edp_phy_aux_cfg_v4,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> -	.ver_ops = &qcom_edp_phy_ops_v4,
> +	.ver_ops = &qcom_edp_phy_ops_v3,

This looks like an extra change. Is it intentional in this patch? If so,
mention it in the commit message.

>  };
>  
>  static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = {

-- 
With best wishes
Dmitry

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 3/3] phy: qcom: edp: Add eDP phy mode switch support
From: Dmitry Baryshkov @ 2026-02-07 10:20 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Vinod Koul, Neil Armstrong, linux-arm-msm, linux-phy,
	linux-kernel
In-Reply-To: <20260205-edp_phy-v1-3-231882bbf3f1@oss.qualcomm.com>

On Thu, Feb 05, 2026 at 05:20:55PM +0800, Yongxing Mou wrote:
> Add DP/eDP switch support by splitting the PHY swing/pre-emphasis tables
> into separate DP and eDP configurations. This allows the driver to select
> the correct table based on the is_edp flag.
> 
> Add a dedicated table for the SC7280/glymur platforms, as they are not
> compatible with the others.

Again, describe what is not working / broken now. Is it something that
we were supposedly supporting or not? Fixes, etc.

> 
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
>  drivers/phy/qualcomm/phy-qcom-edp.c | 72 ++++++++++++++++++++++++++++---------
>  1 file changed, 56 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
> index 388226dbad7f..85caa869a8c0 100644
> --- a/drivers/phy/qualcomm/phy-qcom-edp.c
> +++ b/drivers/phy/qualcomm/phy-qcom-edp.c
> @@ -88,7 +88,8 @@ struct qcom_edp_phy_cfg {
>  	bool is_edp;
>  	const u8 *aux_cfg;
>  	const u8 *vco_div_cfg;
> -	const struct qcom_edp_swing_pre_emph_cfg *swing_pre_emph_cfg;
> +	const struct qcom_edp_swing_pre_emph_cfg *dp_swing_pre_emph_cfg;
> +	const struct qcom_edp_swing_pre_emph_cfg *edp_swing_pre_emph_cfg;
>  	const struct phy_ver_ops *ver_ops;
>  };
>  
> @@ -151,6 +152,20 @@ static const struct qcom_edp_swing_pre_emph_cfg dp_phy_swing_pre_emph_cfg = {
>  	.pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
>  };
>  
> +static const u8 dp_pre_emp_hbr_rbr_v8[4][4] = {
> +	{ 0x00, 0x0e, 0x15, 0x1a },
> +	{ 0x00, 0x0e, 0x15, 0xff },
> +	{ 0x00, 0x0e, 0xff, 0xff },
> +	{ 0x00, 0xff, 0xff, 0xff }
> +};
> +
> +static const struct qcom_edp_swing_pre_emph_cfg dp_phy_swing_pre_emph_cfg_v8 = {
> +	.swing_hbr_rbr = &dp_swing_hbr_rbr,
> +	.swing_hbr3_hbr2 = &dp_swing_hbr2_hbr3,
> +	.pre_emphasis_hbr_rbr = &dp_pre_emp_hbr_rbr_v8,
> +	.pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
> +};
> +
>  static const u8 edp_swing_hbr_rbr[4][4] = {
>  	{ 0x07, 0x0f, 0x16, 0x1f },
>  	{ 0x0d, 0x16, 0x1e, 0xff },
> @@ -186,6 +201,27 @@ static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = {
>  	.pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3,
>  };
>  
> +static const u8 edp_swing_hbr2_hbr3_v3[4][4] = {
> +	{ 0x0b, 0x11, 0x16, 0x1b },
> +	{ 0x0b, 0x19, 0x1f, 0xff },
> +	{ 0x18, 0x1f, 0xff, 0xff },
> +	{ 0x1f, 0xff, 0xff, 0xff }
> +};
> +
> +static const u8 edp_pre_emp_hbr2_hbr3_v3[4][4] = {
> +	{ 0x0c, 0x15, 0x19, 0x1e },
> +	{ 0x09, 0x14, 0x19, 0xff },
> +	{ 0x0f, 0x14, 0xff, 0xff },
> +	{ 0x0d, 0xff, 0xff, 0xff }
> +};
> +
> +static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg_v3 = {
> +	.swing_hbr_rbr = &edp_swing_hbr_rbr,
> +	.swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3_v3,
> +	.pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr,
> +	.pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3_v3,
> +};
> +
>  static const u8 edp_phy_aux_cfg_v4[DP_AUX_CFG_SIZE] = {
>  	0x00, 0x13, 0x24, 0x00, 0x0a, 0x26, 0x0a, 0x03, 0x37, 0x03, 0x02, 0x02, 0x00,
>  };
> @@ -242,12 +278,7 @@ static int qcom_edp_phy_init(struct phy *phy)
>  	       DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
>  	       edp->edp + DP_PHY_PD_CTL);
>  
> -	/*
> -	 * TODO: Re-work the conditions around setting the cfg8 value
> -	 * when more information becomes available about why this is
> -	 * even needed.
> -	 */
> -	if (edp->cfg->swing_pre_emph_cfg && !edp->is_edp)
> +	if (!edp->is_edp)
>  		aux_cfg[8] = 0xb7;
>  
>  	writel(0xfc, edp->edp + DP_PHY_MODE);
> @@ -271,7 +302,7 @@ static int qcom_edp_phy_init(struct phy *phy)
>  
>  static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configure_opts_dp *dp_opts)
>  {
> -	const struct qcom_edp_swing_pre_emph_cfg *cfg = edp->cfg->swing_pre_emph_cfg;
> +	const struct qcom_edp_swing_pre_emph_cfg *cfg;
>  	unsigned int v_level = 0;
>  	unsigned int p_level = 0;
>  	int ret;
> @@ -279,12 +310,14 @@ static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configur
>  	u8 emph;
>  	int i;
>  
> +	if (edp->is_edp)
> +		cfg = edp->cfg->edp_swing_pre_emph_cfg;
> +	else
> +		cfg = edp->cfg->dp_swing_pre_emph_cfg;
> +
>  	if (!cfg)
>  		return 0;

Is it not redundant now?

>  
> -	if (edp->is_edp)
> -		cfg = &edp_phy_swing_pre_emph_cfg;
> -
>  	for (i = 0; i < dp_opts->lanes; i++) {
>  		v_level = max(v_level, dp_opts->voltage[i]);
>  		p_level = max(p_level, dp_opts->pre[i]);
> @@ -591,20 +624,24 @@ static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
>  	.is_edp = false,
>  	.aux_cfg = edp_phy_aux_cfg_v5,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> -	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> +	.dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v4,
>  };
>  
>  static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = {
>  	.aux_cfg = edp_phy_aux_cfg_v4,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> +	.dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v3,
>  	.ver_ops = &qcom_edp_phy_ops_v3,
>  };
>  
>  static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = {
>  	.aux_cfg = edp_phy_aux_cfg_v4,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> -	.swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v4,
>  };
>  
> @@ -612,7 +649,8 @@ static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = {
>  	.is_edp = true,
>  	.aux_cfg = edp_phy_aux_cfg_v4,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> -	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> +	.dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v4,
>  };
>  
> @@ -811,7 +849,8 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v6 = {
>  static struct qcom_edp_phy_cfg x1e80100_phy_cfg = {
>  	.aux_cfg = edp_phy_aux_cfg_v4,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> -	.swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> +	.edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v6,
>  };
>  
> @@ -991,7 +1030,8 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v8 = {
>  static struct qcom_edp_phy_cfg glymur_phy_cfg = {
>  	.aux_cfg = edp_phy_aux_cfg_v8,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v8,
> -	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> +	.dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg_v8,
> +	.edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v8,
>  };
>  
> 
> -- 
> 2.43.0
> 

-- 
With best wishes
Dmitry

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 1/3] phy: qcom: edp: Correct and clean up eDP/DP combo PHY configuration values
From: Dmitry Baryshkov @ 2026-02-07 10:22 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Vinod Koul, Neil Armstrong, linux-arm-msm, linux-phy,
	linux-kernel
In-Reply-To: <20260205-edp_phy-v1-1-231882bbf3f1@oss.qualcomm.com>

On Thu, Feb 05, 2026 at 05:20:53PM +0800, Yongxing Mou wrote:
> According to the current HPG settings, most eDP/DP combo PHYs can reuse the
> same configuration values.
> DP mode:
> 	-sa8775p/sc7280/sc8280xp/x1e80100
> 	-glymur
> eDP mode(low vdiff):
> 	-glymur/sa8775p/sc8280xp/x1e80100
> 	-sc7280
> The current driver still keeps multiple versions of these tables and
> doesn't fully support every combo PHY mode. This patch removes the

See Documentation/process/submitting-patches.rst, "This patch".

> redundant configs and keeps only the sets we actually use, matching the
> platforms listed above.

Should it be combined with the 3rd patch? There you sort out all the
tables, it makes more sense to review all programming together.

> 
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
>  drivers/phy/qualcomm/phy-qcom-edp.c | 41 +++++++++----------------------------
>  1 file changed, 10 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
> index 13feab99feec..10cbb7d9a8a0 100644
> --- a/drivers/phy/qualcomm/phy-qcom-edp.c
> +++ b/drivers/phy/qualcomm/phy-qcom-edp.c
> @@ -116,17 +116,17 @@ struct qcom_edp {
>  };
>  
>  static const u8 dp_swing_hbr_rbr[4][4] = {
> -	{ 0x08, 0x0f, 0x16, 0x1f },
> +	{ 0x07, 0x0f, 0x16, 0x1f },
>  	{ 0x11, 0x1e, 0x1f, 0xff },
>  	{ 0x16, 0x1f, 0xff, 0xff },
>  	{ 0x1f, 0xff, 0xff, 0xff }
>  };
>  
>  static const u8 dp_pre_emp_hbr_rbr[4][4] = {
> -	{ 0x00, 0x0d, 0x14, 0x1a },
> +	{ 0x00, 0x0e, 0x15, 0x1a },
>  	{ 0x00, 0x0e, 0x15, 0xff },
>  	{ 0x00, 0x0e, 0xff, 0xff },
> -	{ 0x03, 0xff, 0xff, 0xff }
> +	{ 0x04, 0xff, 0xff, 0xff }
>  };
>  
>  static const u8 dp_swing_hbr2_hbr3[4][4] = {
> @@ -158,7 +158,7 @@ static const u8 edp_swing_hbr_rbr[4][4] = {
>  };
>  
>  static const u8 edp_pre_emp_hbr_rbr[4][4] = {
> -	{ 0x05, 0x12, 0x17, 0x1d },
> +	{ 0x05, 0x11, 0x17, 0x1d },
>  	{ 0x05, 0x11, 0x18, 0xff },
>  	{ 0x06, 0x11, 0xff, 0xff },
>  	{ 0x00, 0xff, 0xff, 0xff }
> @@ -172,10 +172,10 @@ static const u8 edp_swing_hbr2_hbr3[4][4] = {
>  };
>  
>  static const u8 edp_pre_emp_hbr2_hbr3[4][4] = {
> -	{ 0x08, 0x11, 0x17, 0x1b },
> -	{ 0x00, 0x0c, 0x13, 0xff },
> -	{ 0x05, 0x10, 0xff, 0xff },
> -	{ 0x00, 0xff, 0xff, 0xff }
> +	{ 0x0c, 0x15, 0x19, 0x1e },
> +	{ 0x0b, 0x15, 0x19, 0xff },
> +	{ 0x0e, 0x14, 0xff, 0xff },
> +	{ 0x0d, 0xff, 0xff, 0xff }
>  };
>  
>  static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = {
> @@ -193,27 +193,6 @@ static const u8 edp_phy_vco_div_cfg_v4[4] = {
>  	0x01, 0x01, 0x02, 0x00,
>  };
>  
> -static const u8 edp_pre_emp_hbr_rbr_v5[4][4] = {
> -	{ 0x05, 0x11, 0x17, 0x1d },
> -	{ 0x05, 0x11, 0x18, 0xff },
> -	{ 0x06, 0x11, 0xff, 0xff },
> -	{ 0x00, 0xff, 0xff, 0xff }
> -};
> -
> -static const u8 edp_pre_emp_hbr2_hbr3_v5[4][4] = {
> -	{ 0x0c, 0x15, 0x19, 0x1e },
> -	{ 0x0b, 0x15, 0x19, 0xff },
> -	{ 0x0e, 0x14, 0xff, 0xff },
> -	{ 0x0d, 0xff, 0xff, 0xff }
> -};
> -
> -static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg_v5 = {
> -	.swing_hbr_rbr = &edp_swing_hbr_rbr,
> -	.swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3,
> -	.pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr_v5,
> -	.pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3_v5,
> -};
> -
>  static const u8 edp_phy_aux_cfg_v5[DP_AUX_CFG_SIZE] = {
>  	0x00, 0x13, 0xa4, 0x00, 0x0a, 0x26, 0x0a, 0x03, 0x37, 0x03, 0x02, 0x02, 0x00,
>  };
> @@ -564,7 +543,7 @@ static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
>  	.is_edp = false,
>  	.aux_cfg = edp_phy_aux_cfg_v5,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v4,
> -	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v5,
> +	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v4,
>  };
>  
> @@ -944,7 +923,7 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v8 = {
>  static struct qcom_edp_phy_cfg glymur_phy_cfg = {
>  	.aux_cfg = edp_phy_aux_cfg_v8,
>  	.vco_div_cfg = edp_phy_vco_div_cfg_v8,
> -	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v5,
> +	.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
>  	.ver_ops = &qcom_edp_phy_ops_v8,
>  };
>  
> 
> -- 
> 2.43.0
> 

-- 
With best wishes
Dmitry

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: phy: spacemit: add regulator support to K1 USB2 PHY
From: Krzysztof Kozlowski @ 2026-02-07 10:48 UTC (permalink / raw)
  To: Chukun Pan
  Cc: Yixun Lan, Vinod Koul, Ze Huang, Rob Herring, Mark Brown,
	Conor Dooley, Liam Girdwood, Krzysztof Kozlowski, Neil Armstrong,
	linux-riscv, linux-phy, linux-kernel, devicetree, spacemit
In-Reply-To: <20260206100009.873182-1-amadeus@jmu.edu.cn>

On Fri, Feb 06, 2026 at 06:00:08PM +0800, Chukun Pan wrote:
> Add an optional phy-supply property to describe the regulator
> supplying for USB VBUS.

Why wasn't it there before? USB did not have VBUS?

Explanation is so incomplete I suspect you are patching broken things,
so as well this could be completely different hardware (e.g. there is no
regulator for this block but e.g. connector).


> 
> Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
> ---
>  Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> index 43eaca90d88c..74a1cd5bcdbe 100644
> --- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> @@ -19,6 +19,10 @@ properties:
>    clocks:
>      maxItems: 1
>  
> +  phy-supply:
> +    description:
> +      Phandle to a regulator that provides power to VBUS.

Drop redundant part. This cannot be anything else than phandle and
regulator.

"VBUS power supply" for example. But anyway, I don't have certainty that
this is correct hardware representation. It's your task to provide that.

Best regards,
Krzysztof


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox