[PATCH net-next v3 1/4] net: pcs: xpcs: Expand mdiodev member as an array

From: mike . marciniszyn

Date: Tue May 26 2026 - 20:13:28 EST


From: "Mike Marciniszyn (Meta)" <mike.marciniszyn@xxxxxxxxx>

This allows for support of 50GR2 and 100GR2 by extending the
current singleton mdiodev to an array.

DW XPCS can support 25GR1 and 50GR1 using just a single group
channel. To operate at 50R2 or 100R2 we need both halves
of the channel combined into one group channel device.

Adjust current use of singleton mdiodev to use the 0th element.

Reviewed-by: Alexander Duyck <alexanderduyck@xxxxxx>
Signed-off-by: Mike Marciniszyn (Meta) <mike.marciniszyn@xxxxxxxxx>
---
v3:
- new to series
- Added DW XPCS details to commit message
- change mdiodev to an array in dw_xpcs
- adjust singleton use case to use mdiodev[0]

drivers/net/pcs/pcs-xpcs-wx.c | 4 ++--
drivers/net/pcs/pcs-xpcs.c | 42 +++++++++++++++++------------------
drivers/net/pcs/pcs-xpcs.h | 5 ++++-
3 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/drivers/net/pcs/pcs-xpcs-wx.c b/drivers/net/pcs/pcs-xpcs-wx.c
index fc52f7aa5f59..38c980f974b4 100644
--- a/drivers/net/pcs/pcs-xpcs-wx.c
+++ b/drivers/net/pcs/pcs-xpcs-wx.c
@@ -123,7 +123,7 @@ static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs)
10000, 1000000, false,
xpcs, DW_VR_XS_PCS_DIG_STS);
if (ret < 0)
- dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n");
+ dev_err(&xpcs->mdiodev[0]->dev, "xpcs power-up timeout\n");

return ret;
}
@@ -139,7 +139,7 @@ static int txgbe_pma_init_done(struct dw_xpcs *xpcs)
100000, 10000000, false,
xpcs, DW_VR_XS_PCS_DIG_CTRL1);
if (ret < 0)
- dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n");
+ dev_err(&xpcs->mdiodev[0]->dev, "xpcs pma initialization timeout\n");

return ret;
}
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index e69fa2f0a0e8..3c11c9552b60 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -211,23 +211,23 @@ static bool __xpcs_linkmode_supported(const struct dw_xpcs_compat *compat,

int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
{
- return mdiodev_c45_read(xpcs->mdiodev, dev, reg);
+ return mdiodev_c45_read(xpcs->mdiodev[0], dev, reg);
}

int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
{
- return mdiodev_c45_write(xpcs->mdiodev, dev, reg, val);
+ return mdiodev_c45_write(xpcs->mdiodev[0], dev, reg, val);
}

int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set)
{
- return mdiodev_c45_modify(xpcs->mdiodev, dev, reg, mask, set);
+ return mdiodev_c45_modify(xpcs->mdiodev[0], dev, reg, mask, set);
}

static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg,
u16 mask, u16 set)
{
- return mdiodev_c45_modify_changed(xpcs->mdiodev, dev, reg, mask, set);
+ return mdiodev_c45_modify_changed(xpcs->mdiodev[0], dev, reg, mask, set);
}

static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg)
@@ -304,7 +304,7 @@ static int xpcs_soft_reset(struct dw_xpcs *xpcs,
#define xpcs_warn(__xpcs, __state, __args...) \
({ \
if ((__state)->link) \
- dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \
+ dev_warn(&(__xpcs)->mdiodev[0]->dev, ##__args); \
})

static int xpcs_read_fault_c73(struct dw_xpcs *xpcs,
@@ -400,7 +400,7 @@ static void xpcs_link_up_usxgmii(struct dw_xpcs *xpcs, int speed)
return;

out:
- dev_err(&xpcs->mdiodev->dev, "%s: XPCS access returned %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "%s: XPCS access returned %pe\n",
__func__, ERR_PTR(ret));
}

@@ -726,7 +726,7 @@ static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)

ret = xpcs_switch_interface_mode(xpcs, interface);
if (ret)
- dev_err(&xpcs->mdiodev->dev, "switch interface failed: %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "switch interface failed: %pe\n",
ERR_PTR(ret));

if (!xpcs->need_reset)
@@ -734,14 +734,14 @@ static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)

compat = xpcs_find_compat(xpcs, interface);
if (!compat) {
- dev_err(&xpcs->mdiodev->dev, "unsupported interface %s\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "unsupported interface %s\n",
phy_modes(interface));
return;
}

ret = xpcs_soft_reset(xpcs, compat);
if (ret)
- dev_err(&xpcs->mdiodev->dev, "soft reset failed: %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "soft reset failed: %pe\n",
ERR_PTR(ret));

xpcs->need_reset = false;
@@ -1189,30 +1189,30 @@ static void xpcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,

switch (compat->an_mode) {
case DW_10GBASER:
- phylink_mii_c45_pcs_get_state(xpcs->mdiodev, state);
+ phylink_mii_c45_pcs_get_state(xpcs->mdiodev[0], state);
break;
case DW_AN_C73:
ret = xpcs_get_state_c73(xpcs, state, compat);
if (ret)
- dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "%s returned %pe\n",
"xpcs_get_state_c73", ERR_PTR(ret));
break;
case DW_AN_C37_SGMII:
ret = xpcs_get_state_c37_sgmii(xpcs, state);
if (ret)
- dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "%s returned %pe\n",
"xpcs_get_state_c37_sgmii", ERR_PTR(ret));
break;
case DW_AN_C37_1000BASEX:
ret = xpcs_get_state_c37_1000basex(xpcs, neg_mode, state);
if (ret)
- dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "%s returned %pe\n",
"xpcs_get_state_c37_1000basex", ERR_PTR(ret));
break;
case DW_2500BASEX:
ret = xpcs_get_state_2500basex(xpcs, state);
if (ret)
- dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "%s returned %pe\n",
"xpcs_get_state_2500basex", ERR_PTR(ret));
break;
default:
@@ -1232,14 +1232,14 @@ static void xpcs_link_up_sgmii_1000basex(struct dw_xpcs *xpcs,

if (interface == PHY_INTERFACE_MODE_1000BASEX) {
if (speed != SPEED_1000) {
- dev_err(&xpcs->mdiodev->dev,
+ dev_err(&xpcs->mdiodev[0]->dev,
"%s: speed %dMbps not supported\n",
__func__, speed);
return;
}

if (duplex != DUPLEX_FULL)
- dev_err(&xpcs->mdiodev->dev,
+ dev_err(&xpcs->mdiodev[0]->dev,
"%s: half duplex not supported\n",
__func__);
}
@@ -1247,7 +1247,7 @@ static void xpcs_link_up_sgmii_1000basex(struct dw_xpcs *xpcs,
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
mii_bmcr_encode_fixed(speed, duplex));
if (ret)
- dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n",
+ dev_err(&xpcs->mdiodev[0]->dev, "%s: xpcs_write returned %pe\n",
__func__, ERR_PTR(ret));
}

@@ -1533,7 +1533,7 @@ static struct dw_xpcs *xpcs_create_data(struct mdio_device *mdiodev)
return ERR_PTR(-ENOMEM);

mdio_device_get(mdiodev);
- xpcs->mdiodev = mdiodev;
+ xpcs->mdiodev[0] = mdiodev;
xpcs->pcs.ops = &xpcs_phylink_ops;
xpcs->pcs.poll = true;

@@ -1542,7 +1542,7 @@ static struct dw_xpcs *xpcs_create_data(struct mdio_device *mdiodev)

static void xpcs_free_data(struct dw_xpcs *xpcs)
{
- mdio_device_put(xpcs->mdiodev);
+ mdio_device_put(xpcs->mdiodev[0]);
kfree(xpcs);
}

@@ -1552,7 +1552,7 @@ static int xpcs_init_clks(struct dw_xpcs *xpcs)
[DW_XPCS_CORE_CLK] = "core",
[DW_XPCS_PAD_CLK] = "pad",
};
- struct device *dev = &xpcs->mdiodev->dev;
+ struct device *dev = &xpcs->mdiodev[0]->dev;
int ret, i;

for (i = 0; i < DW_XPCS_NUM_CLKS; ++i)
@@ -1580,7 +1580,7 @@ static int xpcs_init_id(struct dw_xpcs *xpcs)
{
const struct dw_xpcs_info *info;

- info = dev_get_platdata(&xpcs->mdiodev->dev);
+ info = dev_get_platdata(&xpcs->mdiodev[0]->dev);
if (!info) {
xpcs->info.pcs = DW_XPCS_ID_NATIVE;
xpcs->info.pma = DW_XPCS_PMA_ID_NATIVE;
diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h
index 929fa238445e..5c804cfd47d3 100644
--- a/drivers/net/pcs/pcs-xpcs.h
+++ b/drivers/net/pcs/pcs-xpcs.h
@@ -105,10 +105,13 @@ enum dw_xpcs_clock {
DW_XPCS_NUM_CLKS,
};

+/* 2 channels per group DW PCS devices */
+#define DW_XPCS_MAX_CHANNELS 2
+
struct dw_xpcs {
struct dw_xpcs_info info;
const struct dw_xpcs_desc *desc;
- struct mdio_device *mdiodev;
+ struct mdio_device *mdiodev[DW_XPCS_MAX_CHANNELS];
struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
struct phylink_pcs pcs;
phy_interface_t interface;
--
2.43.0