Re: [PATCH v1 net-next 3/3] net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect mdio access

From: Colin Foster
Date: Mon Nov 22 2021 - 12:21:17 EST


On Sun, Nov 21, 2021 at 05:44:13PM +0000, Vladimir Oltean wrote:
> On Fri, Nov 19, 2021 at 01:39:18PM -0800, Colin Foster wrote:
> > Switch to a shared MDIO access implementation now provided by
> > drivers/net/mdio/mdio-mscc-miim.c
> >
> > Signed-off-by: Colin Foster <colin.foster@xxxxxxxxxxxxxxxx>
> > ---
> > drivers/net/dsa/ocelot/Kconfig | 1 +
> > drivers/net/dsa/ocelot/Makefile | 1 +
> > drivers/net/dsa/ocelot/felix_mdio.c | 54 ++++++++++++
> > drivers/net/dsa/ocelot/felix_mdio.h | 13 +++
> > drivers/net/dsa/ocelot/seville_vsc9953.c | 108 ++---------------------
> > drivers/net/mdio/mdio-mscc-miim.c | 37 ++++++--
> > include/linux/mdio/mdio-mscc-miim.h | 19 ++++
> > include/soc/mscc/ocelot.h | 1 +
> > 8 files changed, 125 insertions(+), 109 deletions(-)
> > create mode 100644 drivers/net/dsa/ocelot/felix_mdio.c
> > create mode 100644 drivers/net/dsa/ocelot/felix_mdio.h
> > create mode 100644 include/linux/mdio/mdio-mscc-miim.h
> >
> > diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig
> > index 9948544ba1c4..220b0b027b55 100644
> > --- a/drivers/net/dsa/ocelot/Kconfig
> > +++ b/drivers/net/dsa/ocelot/Kconfig
> > @@ -21,6 +21,7 @@ config NET_DSA_MSCC_SEVILLE
> > depends on NET_VENDOR_MICROSEMI
> > depends on HAS_IOMEM
> > depends on PTP_1588_CLOCK_OPTIONAL
> > + select MDIO_MSCC_MIIM
> > select MSCC_OCELOT_SWITCH_LIB
> > select NET_DSA_TAG_OCELOT_8021Q
> > select NET_DSA_TAG_OCELOT
> > diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile
> > index f6dd131e7491..34b9b128efb8 100644
> > --- a/drivers/net/dsa/ocelot/Makefile
> > +++ b/drivers/net/dsa/ocelot/Makefile
> > @@ -8,4 +8,5 @@ mscc_felix-objs := \
> >
> > mscc_seville-objs := \
> > felix.o \
> > + felix_mdio.o \
> > seville_vsc9953.o
> > diff --git a/drivers/net/dsa/ocelot/felix_mdio.c b/drivers/net/dsa/ocelot/felix_mdio.c
> > new file mode 100644
> > index 000000000000..34375285756b
> > --- /dev/null
> > +++ b/drivers/net/dsa/ocelot/felix_mdio.c
> > @@ -0,0 +1,54 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > +/* Distributed Switch Architecture VSC9953 driver
> > + * Copyright (C) 2020, Maxim Kochetkov <fido_max@xxxxxxxx>
> > + * Copyright (C) 2021 Innovative Advantage
> > + */
> > +#include <linux/of_mdio.h>
> > +#include <linux/types.h>
> > +#include <soc/mscc/ocelot.h>
> > +#include <linux/dsa/ocelot.h>
> > +#include <linux/mdio/mdio-mscc-miim.h>
> > +#include "felix.h"
> > +#include "felix_mdio.h"
> > +
> > +int felix_of_mdio_register(struct ocelot *ocelot, struct device_node *np)
> > +{
> > + struct felix *felix = ocelot_to_felix(ocelot);
> > + struct device *dev = ocelot->dev;
> > + int rc;
> > +
> > + /* Needed in order to initialize the bus mutex lock */
> > + rc = of_mdiobus_register(felix->imdio, np);
> > + if (rc < 0) {
> > + dev_err(dev, "failed to register MDIO bus\n");
> > + felix->imdio = NULL;
> > + }
> > +
> > + return rc;
> > +}
> > +
> > +int felix_mdio_bus_alloc(struct ocelot *ocelot)
> > +{
> > + struct felix *felix = ocelot_to_felix(ocelot);
> > + struct device *dev = ocelot->dev;
> > + struct mii_bus *bus;
> > + int err;
> > +
> > + err = mscc_miim_setup(dev, &bus, ocelot->targets[GCB],
> > + ocelot->map[GCB][GCB_MIIM_MII_STATUS & REG_MASK],
> > + ocelot->targets[GCB],
> > + ocelot->map[GCB][GCB_PHY_PHY_CFG & REG_MASK]);
> > +
> > + if (!err)
> > + felix->imdio = bus;
> > +
> > + return err;
> > +}
>
> I am a little doubtful of the value added by felix_mdio.c, since very
> little is actually common. For example, the T1040 SoC has dpaa-eth
> standalone ports (including the DSA master) and an embedded Seville
> (VSC9953) switch. To access external PHYs connected to the switch, the
> dpaa-eth MDIO bus is used (drivers/net/ethernet/freescale/xgmac_mdio.c).
> The Microsemi MIIM MDIO controller from the Seville switch is used to
> access only the NXP Lynx PCS, which is MDIO-mapped. That's why we put it
> in felix->imdio (i == internal).

I do think some value was lost from felix_mdio.c once I was made aware
of the mscc_mdio_miim.[ch] drivers. Initially I had just pulled out
everything from vsc_9953_mdio_{read,write} into a common place... but
later it just wrapped into mscc_mdio_miim.

>
> Whereas in your case, the Microsemi MIIM MDIO controller is used to
> actually access the external PHYs. So it won't go in felix->imdio, since
> it's not internal.

It is both actually. What is currently functional is using imdio to
access the internal copper phys to the VSC7512 on ports 0-3. My
understanding is that same bus can be used to access the phys addressed
at 4-7 on the VSC8514.

>
> (yes, I know that NXP's integration of Vitesse switches is strange, but
> it is what it is).
>
> So unless I'm missing something, I guess you would be better off leaving
> this code in Seville, and just duplicating minor portions of it (the
> call to mscc_miim_setup) in your vsc7514 driver. What do you think?

I think Seville could be updated to use mscc_mdio_miim, which was done
by way of felix_mdio. Maybe that's a separate patch for Seville alone.
But I have no issue with removing this file and hooking into mdio_miim
directly from the ocelot_spi driver.

>
> > +
> > +void felix_mdio_bus_free(struct ocelot *ocelot)
> > +{
> > + struct felix *felix = ocelot_to_felix(ocelot);
> > +
> > + if (felix->imdio)
> > + mdiobus_unregister(felix->imdio);
> > +}
> > diff --git a/drivers/net/dsa/ocelot/felix_mdio.h b/drivers/net/dsa/ocelot/felix_mdio.h
> > new file mode 100644
> > index 000000000000..93286f598c3b
> > --- /dev/null
> > +++ b/drivers/net/dsa/ocelot/felix_mdio.h
> > @@ -0,0 +1,13 @@
> > +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> > +/* Shared code for indirect MDIO access for Felix drivers
> > + *
> > + * Author: Colin Foster <colin.foster@xxxxxxxxxxxxxxxx>
> > + * Copyright (C) 2021 Innovative Advantage
> > + */
> > +#include <linux/of.h>
> > +#include <linux/types.h>
> > +#include <soc/mscc/ocelot.h>
> > +
> > +int felix_mdio_bus_alloc(struct ocelot *ocelot);
> > +int felix_of_mdio_register(struct ocelot *ocelot, struct device_node *np);
> > +void felix_mdio_bus_free(struct ocelot *ocelot);
> > diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
> > index db124922c374..dd7ae6a1d843 100644
> > --- a/drivers/net/dsa/ocelot/seville_vsc9953.c
> > +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
> > @@ -10,15 +10,9 @@
> > #include <linux/pcs-lynx.h>
> > #include <linux/dsa/ocelot.h>
> > #include <linux/iopoll.h>
> > -#include <linux/of_mdio.h>
> > #include "felix.h"
> > +#include "felix_mdio.h"
> >
> > -#define MSCC_MIIM_CMD_OPR_WRITE BIT(1)
> > -#define MSCC_MIIM_CMD_OPR_READ BIT(2)
> > -#define MSCC_MIIM_CMD_WRDATA_SHIFT 4
> > -#define MSCC_MIIM_CMD_REGAD_SHIFT 20
> > -#define MSCC_MIIM_CMD_PHYAD_SHIFT 25
> > -#define MSCC_MIIM_CMD_VLD BIT(31)
> > #define VSC9953_VCAP_POLICER_BASE 11
> > #define VSC9953_VCAP_POLICER_MAX 31
> > #define VSC9953_VCAP_POLICER_BASE2 120
> > @@ -862,7 +856,6 @@ static struct vcap_props vsc9953_vcap_props[] = {
> > #define VSC9953_INIT_TIMEOUT 50000
> > #define VSC9953_GCB_RST_SLEEP 100
> > #define VSC9953_SYS_RAMINIT_SLEEP 80
> > -#define VCS9953_MII_TIMEOUT 10000
> >
> > static int vsc9953_gcb_soft_rst_status(struct ocelot *ocelot)
> > {
> > @@ -882,82 +875,6 @@ static int vsc9953_sys_ram_init_status(struct ocelot *ocelot)
> > return val;
> > }
> >
> > -static int vsc9953_gcb_miim_pending_status(struct ocelot *ocelot)
> > -{
> > - int val;
> > -
> > - ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_PENDING, &val);
> > -
> > - return val;
> > -}
> > -
> > -static int vsc9953_gcb_miim_busy_status(struct ocelot *ocelot)
> > -{
> > - int val;
> > -
> > - ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_BUSY, &val);
> > -
> > - return val;
> > -}
> > -
> > -static int vsc9953_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
> > - u16 value)
> > -{
> > - struct ocelot *ocelot = bus->priv;
> > - int err, cmd, val;
> > -
> > - /* Wait while MIIM controller becomes idle */
> > - err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot,
> > - val, !val, 10, VCS9953_MII_TIMEOUT);
> > - if (err) {
> > - dev_err(ocelot->dev, "MDIO write: pending timeout\n");
> > - goto out;
> > - }
> > -
> > - cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
> > - (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
> > - (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
> > - MSCC_MIIM_CMD_OPR_WRITE;
> > -
> > - ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD);
> > -
> > -out:
> > - return err;
> > -}
> > -
> > -static int vsc9953_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
> > -{
> > - struct ocelot *ocelot = bus->priv;
> > - int err, cmd, val;
> > -
> > - /* Wait until MIIM controller becomes idle */
> > - err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot,
> > - val, !val, 10, VCS9953_MII_TIMEOUT);
> > - if (err) {
> > - dev_err(ocelot->dev, "MDIO read: pending timeout\n");
> > - goto out;
> > - }
> > -
> > - /* Write the MIIM COMMAND register */
> > - cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
> > - (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ;
> > -
> > - ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD);
> > -
> > - /* Wait while read operation via the MIIM controller is in progress */
> > - err = readx_poll_timeout(vsc9953_gcb_miim_busy_status, ocelot,
> > - val, !val, 10, VCS9953_MII_TIMEOUT);
> > - if (err) {
> > - dev_err(ocelot->dev, "MDIO read: busy timeout\n");
> > - goto out;
> > - }
> > -
> > - val = ocelot_read(ocelot, GCB_MIIM_MII_DATA);
> > -
> > - err = val & 0xFFFF;
> > -out:
> > - return err;
> > -}
> >
> > /* CORE_ENA is in SYS:SYSTEM:RESET_CFG
> > * MEM_INIT is in SYS:SYSTEM:RESET_CFG
> > @@ -1089,7 +1006,6 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
> > {
> > struct felix *felix = ocelot_to_felix(ocelot);
> > struct device *dev = ocelot->dev;
> > - struct mii_bus *bus;
> > int port;
> > int rc;
> >
> > @@ -1101,26 +1017,18 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
> > return -ENOMEM;
> > }
> >
> > - bus = devm_mdiobus_alloc(dev);
> > - if (!bus)
> > - return -ENOMEM;
> > -
> > - bus->name = "VSC9953 internal MDIO bus";
> > - bus->read = vsc9953_mdio_read;
> > - bus->write = vsc9953_mdio_write;
> > - bus->parent = dev;
> > - bus->priv = ocelot;
> > - snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
> > + rc = felix_mdio_bus_alloc(ocelot);
> > + if (rc < 0) {
> > + dev_err(dev, "failed to allocate MDIO bus\n");
> > + return rc;
> > + }
> >
> > - /* Needed in order to initialize the bus mutex lock */
> > - rc = of_mdiobus_register(bus, NULL);
> > + rc = felix_of_mdio_register(ocelot, NULL);
> > if (rc < 0) {
> > dev_err(dev, "failed to register MDIO bus\n");
> > return rc;
> > }
> >
> > - felix->imdio = bus;
> > -
> > for (port = 0; port < felix->info->num_ports; port++) {
> > struct ocelot_port *ocelot_port = ocelot->ports[port];
> > int addr = port + 4;
> > @@ -1165,7 +1073,7 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
> > mdio_device_free(pcs->mdio);
> > lynx_pcs_destroy(pcs);
> > }
> > - mdiobus_unregister(felix->imdio);
> > + felix_mdio_bus_free(ocelot);
> > }
> >
> > static const struct felix_info seville_info_vsc9953 = {
> > diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c
> > index f55ad20c28d5..cf3fa7a4459c 100644
> > --- a/drivers/net/mdio/mdio-mscc-miim.c
> > +++ b/drivers/net/mdio/mdio-mscc-miim.c
> > @@ -10,6 +10,7 @@
> > #include <linux/io.h>
> > #include <linux/iopoll.h>
> > #include <linux/kernel.h>
> > +#include <linux/mdio/mdio-mscc-miim.h>
> > #include <linux/module.h>
> > #include <linux/of_mdio.h>
> > #include <linux/phy.h>
> > @@ -37,7 +38,9 @@
> >
> > struct mscc_miim_dev {
> > struct regmap *regs;
> > + int mii_status_offset;
> > struct regmap *phy_regs;
> > + int phy_reset_offset;
> > };
> >
> > /* When high resolution timers aren't built-in: we can't use usleep_range() as
> > @@ -56,7 +59,8 @@ static int mscc_miim_status(struct mii_bus *bus)
> > struct mscc_miim_dev *miim = bus->priv;
> > int val, err;
> >
> > - err = regmap_read(miim->regs, MSCC_MIIM_REG_STATUS, &val);
> > + err = regmap_read(miim->regs,
> > + MSCC_MIIM_REG_STATUS + miim->mii_status_offset, &val);
> > if (err < 0)
> > WARN_ONCE(1, "mscc miim status read error %d\n", err);
> >
> > @@ -91,7 +95,9 @@ static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
> > if (ret)
> > goto out;
> >
> > - err = regmap_write(miim->regs, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD |
> > + err = regmap_write(miim->regs,
> > + MSCC_MIIM_REG_CMD + miim->mii_status_offset,
> > + MSCC_MIIM_CMD_VLD |
> > (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
> > (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
> > MSCC_MIIM_CMD_OPR_READ);
> > @@ -103,7 +109,8 @@ static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
> > if (ret)
> > goto out;
> >
> > - err = regmap_read(miim->regs, MSCC_MIIM_REG_DATA, &val);
> > + err = regmap_read(miim->regs,
> > + MSCC_MIIM_REG_DATA + miim->mii_status_offset, &val);
> >
> > if (err < 0)
> > WARN_ONCE(1, "mscc miim read data reg error %d\n", err);
> > @@ -128,7 +135,9 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
> > if (ret < 0)
> > goto out;
> >
> > - err = regmap_write(miim->regs, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD |
> > + err = regmap_write(miim->regs,
> > + MSCC_MIIM_REG_CMD + miim->mii_status_offset,
> > + MSCC_MIIM_CMD_VLD |
> > (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
> > (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
> > (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
> > @@ -143,14 +152,17 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
> > static int mscc_miim_reset(struct mii_bus *bus)
> > {
> > struct mscc_miim_dev *miim = bus->priv;
> > + int offset = miim->phy_reset_offset;
> > int err;
> >
> > if (miim->phy_regs) {
> > - err = regmap_write(miim->phy_regs, MSCC_PHY_REG_PHY_CFG, 0);
> > + err = regmap_write(miim->phy_regs,
> > + MSCC_PHY_REG_PHY_CFG + offset, 0);
> > if (err < 0)
> > WARN_ONCE(1, "mscc reset set error %d\n", err);
> >
> > - err = regmap_write(miim->phy_regs, MSCC_PHY_REG_PHY_CFG, 0x1ff);
> > + err = regmap_write(miim->phy_regs,
> > + MSCC_PHY_REG_PHY_CFG + offset, 0x1ff);
> > if (err < 0)
> > WARN_ONCE(1, "mscc reset clear error %d\n", err);
> >
> > @@ -166,10 +178,12 @@ static const struct regmap_config mscc_miim_regmap_config = {
> > .reg_stride = 4,
> > };
> >
> > -static int mscc_miim_setup(struct device *dev, struct mii_bus *bus,
> > - struct regmap *mii_regmap, struct regmap *phy_regmap)
> > +int mscc_miim_setup(struct device *dev, struct mii_bus **pbus,
> > + struct regmap *mii_regmap, int status_offset,
> > + struct regmap *phy_regmap, int reset_offset)
> > {
> > struct mscc_miim_dev *miim;
> > + struct mii_bus *bus;
> >
> > bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
> > if (!bus)
> > @@ -185,10 +199,15 @@ static int mscc_miim_setup(struct device *dev, struct mii_bus *bus,
> > miim = bus->priv;
> >
> > miim->regs = mii_regmap;
> > + miim->mii_status_offset = status_offset;
> > miim->phy_regs = phy_regmap;
> > + miim->phy_reset_offset = reset_offset;
> > +
> > + *pbus = bus;
> >
> > return 0;
> > }
> > +EXPORT_SYMBOL(mscc_miim_setup);
> >
> > static int mscc_miim_probe(struct platform_device *pdev)
> > {
> > @@ -225,7 +244,7 @@ static int mscc_miim_probe(struct platform_device *pdev)
> > return PTR_ERR(dev->phy_regs);
> > }
> >
> > - mscc_miim_setup(&pdev->dev, bus, mii_regmap, phy_regmap);
> > + mscc_miim_setup(&pdev->dev, &bus, mii_regmap, 0, phy_regmap, 0);
> >
> > ret = of_mdiobus_register(bus, pdev->dev.of_node);
> > if (ret < 0) {
> > diff --git a/include/linux/mdio/mdio-mscc-miim.h b/include/linux/mdio/mdio-mscc-miim.h
> > new file mode 100644
> > index 000000000000..3ceab7b6ffc1
> > --- /dev/null
> > +++ b/include/linux/mdio/mdio-mscc-miim.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> > +/*
> > + * Driver for the MDIO interface of Microsemi network switches.
> > + *
> > + * Author: Colin Foster <colin.foster@xxxxxxxxxxxxxxxx>
> > + * Copyright (C) 2021 Innovative Advantage
> > + */
> > +#ifndef MDIO_MSCC_MIIM_H
> > +#define MDIO_MSCC_MIIM_H
> > +
> > +#include <linux/device.h>
> > +#include <linux/phy.h>
> > +#include <linux/regmap.h>
> > +
> > +int mscc_miim_setup(struct device *device, struct mii_bus **bus,
> > + struct regmap *mii_regmap, int status_offset,
> > + struct regmap *phy_regmap, int reset_offset);
> > +
> > +#endif
> > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> > index 89d17629efe5..9d6fe8ce9dd1 100644
> > --- a/include/soc/mscc/ocelot.h
> > +++ b/include/soc/mscc/ocelot.h
> > @@ -398,6 +398,7 @@ enum ocelot_reg {
> > GCB_MIIM_MII_STATUS,
> > GCB_MIIM_MII_CMD,
> > GCB_MIIM_MII_DATA,
> > + GCB_PHY_PHY_CFG,
> > DEV_CLOCK_CFG = DEV_GMII << TARGET_OFFSET,
> > DEV_PORT_MISC,
> > DEV_EVENTS,
> > --
> > 2.25.1
> >