From: Mike Marciniszyn (Meta) The DW channel 10g-100g IP builds the PCS out of multiple addr mdio addresses. 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_data(), xpcs_create(), and xpcs_free_data() are extended to accept a variable number of mdio devices. Existing singleton interfaces are re-cast as a having an array pointing to a single mdio device. Signed-off-by: Mike Marciniszyn (Meta) Signed-off-by: Alexander Duyck --- drivers/net/ethernet/meta/fbnic/fbnic_phylink.c | 4 + drivers/net/pcs/pcs-xpcs.c | 106 ++++++++++++++++++----- drivers/net/pcs/pcs-xpcs.h | 1 include/linux/pcs/pcs-xpcs.h | 2 4 files changed, 88 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c index 09c5225111be..88fc78d55c7a 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 22246abec948..2e236ff1ad85 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) @@ -1525,16 +1534,21 @@ static int xpcs_identify(struct dw_xpcs *xpcs) return -ENODEV; } -static struct dw_xpcs *xpcs_create_data(struct mdio_device *mdiodev) +static struct dw_xpcs * +xpcs_create_data(struct mdio_device **mdiodev, int channels) { struct dw_xpcs *xpcs; + int i; xpcs = kzalloc_obj(*xpcs); if (!xpcs) return ERR_PTR(-ENOMEM); - mdio_device_get(mdiodev); - xpcs->mdiodev[0] = mdiodev; + xpcs->channels = channels; + for (i = 0; i < channels; i++) { + mdio_device_get(mdiodev[i]); + xpcs->mdiodev[i] = mdiodev[i]; + } xpcs->pcs.ops = &xpcs_phylink_ops; xpcs->pcs.poll = true; @@ -1543,7 +1557,10 @@ 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[0]); + int i; + + for (i = 0; i < xpcs->channels; i++) + mdio_device_put(xpcs->mdiodev[i]); kfree(xpcs); } @@ -1592,12 +1609,12 @@ static int xpcs_init_id(struct dw_xpcs *xpcs) return xpcs_identify(xpcs); } -static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) +static struct dw_xpcs *xpcs_create(struct mdio_device **mdiodev, int channels) { struct dw_xpcs *xpcs; int ret; - xpcs = xpcs_create_data(mdiodev); + xpcs = xpcs_create_data(mdiodev, channels); if (IS_ERR(xpcs)) return xpcs; @@ -1628,25 +1645,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); - mdiodev = mdio_device_create(bus, addr); - if (IS_ERR(mdiodev)) - return ERR_CAST(mdiodev); + for (i = 0; i < channels; i++) { + rval = mdio_device_create(bus, addrs[i]); + if (IS_ERR(rval)) + goto out_nomem; + mdiodev[i] = rval; + } - xpcs = xpcs_create(mdiodev); + xpcs = xpcs_create(mdiodev, channels); /* xpcs_create() has taken a refcount on the mdiodev if it was * successful. If xpcs_create() fails, this will free the mdio @@ -1654,9 +1670,51 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) * anymore, and putting it here will allow mdio_device_put() in * xpcs_destroy() to automatically free the mdio device. */ - mdio_device_put(mdiodev); + for (i = 0; i < channels; i++) + mdio_device_put(mdiodev[i]); return xpcs; +out_nomem: + while (i--) + mdio_device_free(mdiodev[i]); + return ERR_CAST(rval); +} + +/** + * xpcs_create_pcs_mdiodevs() - create a DW xPCS instance with multiple @addrs + * @bus: pointer to the embedded phylink PCS instance + * @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. + */ +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); + +/** + * 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) +{ + return xpcs_create_mdiodevs(bus, &addr, 1); } EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); @@ -1694,7 +1752,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode) if (!mdiodev) return ERR_PTR(-EPROBE_DEFER); - xpcs = xpcs_create(mdiodev); + xpcs = xpcs_create(&mdiodev, 1); /* xpcs_create() has taken a refcount on the mdiodev if it was * successful. If xpcs_create() fails, this will free the mdio diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h index 5c804cfd47d3..36f4906e689a 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; phy_interface_t interface; bool need_reset; u8 eee_mult_fact; diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index 36073f7b6bb4..d36904ae7cc9 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -57,6 +57,8 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr); struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode); void xpcs_destroy(struct dw_xpcs *xpcs); +struct phylink_pcs * +xpcs_create_pcs_mdiodevs(struct mii_bus *bus, const int *addrs, int channels); struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr); void xpcs_destroy_pcs(struct phylink_pcs *pcs);