@@ -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
@@ -8,4 +8,5 @@ mscc_felix-objs := \
mscc_seville-objs := \
felix.o \
+ felix_mdio.o \
seville_vsc9953.o
new file mode 100644
@@ -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;
+}
+
+void felix_mdio_bus_free(struct ocelot *ocelot)
+{
+ struct felix *felix = ocelot_to_felix(ocelot);
+
+ if (felix->imdio)
+ mdiobus_unregister(felix->imdio);
+}
new file mode 100644
@@ -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);
@@ -11,13 +11,7 @@
#include <linux/dsa/ocelot.h>
#include <linux/iopoll.h>
#include "felix.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)
+#include "felix_mdio.h"
static const u32 vsc9953_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x00b500),
@@ -857,7 +851,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)
{
@@ -877,82 +870,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
@@ -1084,7 +1001,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;
@@ -1096,26 +1012,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;
@@ -1160,7 +1068,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 = {
@@ -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,12 +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;
- int ret;
bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
if (!bus)
@@ -187,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)
{
@@ -227,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) {
new file mode 100644
@@ -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);
+
+#endif
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 ++++ 7 files changed, 123 insertions(+), 110 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