Re: [net-next PATCH v4 2/4] net: pcs: xpcs: Allow for multiple addr in creation

Jakub Kicinski <[email protected]> Mon, 3 Aug 2026 18:48:30 -0700
Newsgroups gmane.linux.network,gmane.linux.kernel
Message-ID <[email protected]>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
--- Patch [2]: [net-next PATCH v4 2/4] net: pcs: xpcs: Allow for multiple addr in creation ---
commit 659d2ab0068099d84cd67aa41fc9d8519508054c
Author: Mike Marciniszyn (Meta) <[email protected]>

net: pcs: xpcs: Allow for multiple addr in creation

This extends xpcs_create_data(), xpcs_create() and xpcs_free_data() to take
an array of mdio devices, adds a new exported xpcs_create_pcs_mdiodevs()
entry point, and switches fbnic over to registering two MDIO addresses.  A
new xpcs->channels field records how many mdio devices were provisioned.

A few questions below on the new multi-channel plumbing and the
kernel-doc/changelog wording.

The changelog says:

    Add xpcs_create_mdiodevs() and xpcs_create_pcs_mdiodevs calls so that a
    driver using the above part can tell xpcs the addr values in one call.

xpcs_create_mdiodevs() is added as static, with no EXPORT_SYMBOL_GPL and no
declaration in include/linux/pcs/pcs-xpcs.h, so a driver that needs the
struct dw_xpcs handle (the xpcs_create_mdiodev() users) still cannot pass
more than one address.  Should the changelog only mention
xpcs_create_pcs_mdiodevs() as the driver-facing addition?

> diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c
> index 09c5225111be3..88fc78d55c7a5 100644
> --- a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c
> +++ b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c
> @@ -200,9 +200,11 @@ int fbnic_phylink_create(struct net_device *netdev)
>  	struct fbnic_dev *fbd = fbn->fbd;
>  	struct phylink_pcs *pcs;
>  	struct phylink *phylink;
> +	/* See fbnic_mdio.c addr usage */
> +	int addrs[] = { 0, 1 };
>  	int err;
>  
> -	pcs = xpcs_create_pcs_mdiodev(fbd->mdio_bus, 0);
> +	pcs = xpcs_create_pcs_mdiodevs(fbd->mdio_bus, addrs, ARRAY_SIZE(addrs));
>  	if (IS_ERR(pcs)) {
>  		err = PTR_ERR(pcs);
>  		dev_err(fbd->dev, "Failed to create PCS device: %d\n", err);

> diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
> index 6a645df58f9b8..a6b993745f3be 100644
> --- a/drivers/net/pcs/pcs-xpcs.c
> +++ b/drivers/net/pcs/pcs-xpcs.c
> @@ -214,9 +214,18 @@ int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
>  	return mdiodev_c45_read(xpcs->mdiodev[0], dev, reg);
>  }
>  
> +static int
> +xpcs_mdev_write_ch(struct dw_xpcs *xpcs, int ch, int dev, u32 reg, u16 val)
> +{
> +	if (ch >= xpcs->channels)
> +		return -ENXIO;
> +
> +	return mdiodev_c45_write(xpcs->mdiodev[ch], dev, reg, val);
> +}
> +
>  int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
>  {
> -	return mdiodev_c45_write(xpcs->mdiodev[0], dev, reg, val);
> +	return xpcs_mdev_write_ch(xpcs, 0, dev, reg, val);
>  }
>  
>  int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set)

[Severity: Medium]
[Finding: main-1-1]
[Sources: opus-5]
Only the write path becomes channel aware here.  xpcs_read(),
xpcs_modify(), xpcs_modify_changed() and the
phylink_mii_c45_pcs_get_state(xpcs->mdiodev[0], ...) call in
xpcs_get_state_c73() stay pinned to mdiodev[0], and no per-channel read or
read-modify-write helper appears later in the series either (checked at
7b16dc0f9f78, where xpcs_write_pcs_ch() and xpcs_mdev_write_ch() are the
only channel-aware accessors).

For fbnic, addr 1 is a distinct register page:

drivers/net/ethernet/meta/fbnic/fbnic_mdio.c maps addr 0/1 onto
FBNIC_PCS_PAGE(addr), described as allowing access to both halves of the
PCS for the 50R2 config.

So in the two-instance modes (PHY_INTERFACE_MODE_LAUI and
PHY_INTERFACE_MODE_100GBASEP, both with .channels = 2), instance 1 gets the
vendor writes from xpcs_config_rsfec_pma() later in the series, but:

xpcs_pre_config() -> xpcs_soft_reset() -> xpcs_modify() -> mdiodev[0]
xpcs_get_state_c73() -> xpcs_read(MDIO_STAT1/STAT2) -> mdiodev[0]

Does instance 1 need its own soft reset for those vendor writes to latch,
and should its fault/alignment status be folded into the state reported to
phylink?  As written, a faulted or unreset second half would not be visible.

> @@ -1507,16 +1516,21 @@ static int xpcs_identify(struct dw_xpcs *xpcs)

[ ... ]

> @@ -1610,25 +1627,24 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
>  	return ERR_PTR(ret);
>  }
>  
> -/**
> - * xpcs_create_mdiodev() - create a DW xPCS instance with the MDIO @addr
> - * @bus: pointer to the MDIO-bus descriptor for the device to be looked at
> - * @addr: device MDIO-bus ID
> - *
> - * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if
> - * the PCS device couldn't be found on the bus and other negative errno related
> - * to the data allocation and MDIO-bus communications.
> - */
> -struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)
> +static struct dw_xpcs *
> +xpcs_create_mdiodevs(struct mii_bus *bus, const int *addrs, int channels)
>  {
> -	struct mdio_device *mdiodev;
> +	struct mdio_device *mdiodev[DW_XPCS_MAX_CHANNELS], *rval;
>  	struct dw_xpcs *xpcs;
> +	int i;
> +
> +	if (!addrs || channels <= 0 || channels > DW_XPCS_MAX_CHANNELS)
> +		return ERR_PTR(-EINVAL);

[ ... ]

> +/**
> + * xpcs_create_pcs_mdiodevs() - create a DW xPCS instance with multiple @addrs
> + * @bus: pointer to the embedded phylink PCS instance

[Severity: Low]
[Finding: main-1-4]
[Sources: opus-5]
Should @bus be described as the MDIO-bus descriptor, as the existing
xpcs_create_mdiodev() kernel-doc does?  It is a struct mii_bus * that gets
handed to mdio_device_create(bus, addrs[i]); the embedded phylink PCS is
what the function returns.

> + * @addrs: an array of int
> + * @channels: the number of addrs items or channels
> + *
> + * Return: a pointer to the phylink PCS if successful, otherwise -ENODEV if
> + * the PCS device couldn't be found on the bus and other negative errno related
> + * to the data allocation and MDIO-bus communications.
> + */

[Severity: Low]
[Finding: main-1-4]
[Sources: opus-5]
Could the Return: section also mention -EINVAL?  The new check in
xpcs_create_mdiodevs():

	if (!addrs || channels <= 0 || channels > DW_XPCS_MAX_CHANNELS)
		return ERR_PTR(-EINVAL);

is reachable through this exported wrapper, but only -ENODEV and
allocation/MDIO errors are listed.  Both wording issues are still present at
the end of the series (7b16dc0f9f78).

> +struct phylink_pcs *
> +xpcs_create_pcs_mdiodevs(struct mii_bus *bus, const int *addrs, int channels)
> +{
> +	struct dw_xpcs *xpcs;
> +
> +	xpcs = xpcs_create_mdiodevs(bus, addrs, channels);
> +	if (IS_ERR(xpcs))
> +		return ERR_CAST(xpcs);
> +
> +	return &xpcs->pcs;
> +}
> +EXPORT_SYMBOL_GPL(xpcs_create_pcs_mdiodevs);

[ ... ]

> diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h
> index 5c804cfd47d3c..36f4906e689a8 100644
> --- a/drivers/net/pcs/pcs-xpcs.h
> +++ b/drivers/net/pcs/pcs-xpcs.h
> @@ -114,6 +114,7 @@ struct dw_xpcs {
>  	struct mdio_device *mdiodev[DW_XPCS_MAX_CHANNELS];
>  	struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
>  	struct phylink_pcs pcs;
> +	int channels;

[Severity: Low]
[Finding: main-1-2]
[Sources: opus-5]
This isn't a bug, but the name channels now carries two different meanings
in this driver.  Here it counts the MDIO devices a driver provisioned, while
struct pma_pcs_values::channels in pcs-xpcs.c counts the PCS instances the
active link mode uses (.channels = 1 for 25GBASER/50GBASER, .channels = 2
for LAUI/100GBASEP).

fbnic_phylink_create() passes addrs[] = { 0, 1 } before any interface is
selected, so xpcs->channels is 2 even in the single-instance modes fbnic
also advertises.  The bound in xpcs_mdev_write_ch():

	if (ch >= xpcs->channels)
		return -ENXIO;

then checks against the provisioned count rather than the active mode's
count.  Would a name like n_mdiodevs or num_addrs make that invariant
clearer?

The fbnic side also repeats the fbnic_mdio.c address layout as bare numbers
tied to it only by the "/* See fbnic_mdio.c addr usage */" comment; could
those addresses come from a shared define?

>  	phy_interface_t interface;
>  	bool need_reset;
>  	u8 eee_mult_fact;