Re: [PATCH v1 net-next 3/3] net: dsa: ocelot: felix: switch to mdio-mscc-miim driver for indirect mdio access
From: Colin Foster <colin.foster@in-advantage.com>
Date: 2021-11-22 17:21:16
Also in:
lkml
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:quoted
Switch to a shared MDIO access implementation now provided by drivers/net/mdio/mdio-mscc-miim.c Signed-off-by: Colin Foster <colin.foster@in-advantage.com> --- 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.hdiff --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_OCELOTdiff --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.odiff --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@inbox.ru> + * 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.
quoted
+ +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@in-advantage.com> + * 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@in-advantage.com> + * 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); + +#endifdiff --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