[PATCH] net: add Microsemi/Microchip MDIO driver

Robert Marko robert.marko at sartura.hr
Tue Dec 30 22:06:03 CET 2025


Add Microsemi/Microchip MDIO driver for interfaces found in their network
switches.

Driver is based on the Linux version.

Signed-off-by: Robert Marko <robert.marko at sartura.hr>
---
 drivers/net/Kconfig          |   8 +++
 drivers/net/Makefile         |   1 +
 drivers/net/mdio-mscc-miim.c | 136 +++++++++++++++++++++++++++++++++++
 3 files changed, 145 insertions(+)
 create mode 100644 drivers/net/mdio-mscc-miim.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 544e302d600..ec1c6329e5c 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1065,6 +1065,14 @@ config ASPEED_MDIO
 	  This driver supports the MDIO bus of Aspeed AST2600 SOC.  The driver
 	  currently supports Clause 22.
 
+config MDIO_MSCC_MIIM
+	bool "Microsemi MIIM interface support"
+	depends on DM_MDIO
+	select REGMAP
+	help
+		This driver supports MDIO interface found in Microsemi and Microchip
+		network switches.
+
 config MDIO_MUX_MMIOREG
 	bool "MDIO MUX accessed as a MMIO register access"
 	depends on DM_MDIO_MUX
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a3c3420898c..5bb40480d88 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_LITEETH) += liteeth.o
 obj-$(CONFIG_MACB) += macb.o
 obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
 obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
+obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
 obj-$(CONFIG_MDIO_GPIO_BITBANG) += mdio_gpio.o
 obj-$(CONFIG_MDIO_MT7531_MMIO) += mdio-mt7531-mmio.o
 obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o
diff --git a/drivers/net/mdio-mscc-miim.c b/drivers/net/mdio-mscc-miim.c
new file mode 100644
index 00000000000..5700b872586
--- /dev/null
+++ b/drivers/net/mdio-mscc-miim.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <asm/io.h>
+#include <dm.h>
+#include <time.h>
+#include <regmap.h>
+#include <miiphy.h>
+#include <linux/bitfield.h>
+
+#define MSCC_MIIM_REG_STATUS		0x0
+#define		MSCC_MIIM_STATUS_STAT_PENDING	BIT(2)
+#define		MSCC_MIIM_STATUS_STAT_BUSY	BIT(3)
+#define MSCC_MIIM_REG_CMD		0x8
+#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 MSCC_MIIM_REG_DATA		0xC
+#define		MSCC_MIIM_DATA_ERROR		(BIT(16) | BIT(17))
+#define		MSCC_MIIM_DATA_MASK		GENMASK(15, 0)
+#define MSCC_MIIM_REG_CFG		0x10
+#define		MSCC_MIIM_CFG_PRESCALE_MASK	GENMASK(7, 0)
+/* 01 = Clause 22, 00 = Clause 45 */
+#define		MSCC_MIIM_CFG_ST_CFG_MASK	GENMASK(10, 9)
+#define		MSCC_MIIM_C22			1
+#define		MSCC_MIIM_C45			0
+
+#define MSCC_MDIO_TIMEOUT    10000
+#define MSCC_MDIO_SLEEP      50
+
+struct mscc_mdio_priv {
+	struct regmap *map;
+};
+
+static int mscc_mdio_wait_busy(struct mscc_mdio_priv *priv)
+{
+	u32 busy;
+
+	return regmap_read_poll_timeout(priv->map, MSCC_MIIM_REG_STATUS, busy,
+				       (busy & MSCC_MIIM_STATUS_STAT_BUSY) == 0,
+				       MSCC_MDIO_SLEEP,
+				       MSCC_MDIO_TIMEOUT);
+}
+
+static int mscc_mdio_read(struct udevice *dev, int addr, int devad, int reg)
+{
+	struct mscc_mdio_priv *priv = dev_get_priv(dev);
+	u32 val;
+	int ret;
+
+	if (mscc_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	ret = regmap_write(priv->map, MSCC_MIIM_REG_CMD,
+			   MSCC_MIIM_CMD_VLD |
+			   (addr << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+			   (reg << MSCC_MIIM_CMD_REGAD_SHIFT) |
+			   MSCC_MIIM_CMD_OPR_READ);
+	if (ret)
+		return ret;
+
+	if (mscc_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	regmap_read(priv->map, MSCC_MIIM_REG_DATA, &val);
+	if (val & MSCC_MIIM_DATA_ERROR)
+		return -EIO;
+
+	return FIELD_GET(MSCC_MIIM_DATA_MASK, val);
+}
+
+int mscc_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val)
+{
+	struct mscc_mdio_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (mscc_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	ret = regmap_write(priv->map, MSCC_MIIM_REG_CMD,
+			   MSCC_MIIM_CMD_VLD |
+			   (addr << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+			   (reg << MSCC_MIIM_CMD_REGAD_SHIFT) |
+			   (val << MSCC_MIIM_CMD_WRDATA_SHIFT) |
+			   MSCC_MIIM_CMD_OPR_WRITE);
+
+	return ret;
+}
+
+static const struct mdio_ops mscc_mdio_ops = {
+	.read = mscc_mdio_read,
+	.write = mscc_mdio_write,
+};
+
+static int mscc_mdio_bind(struct udevice *dev)
+{
+	if (ofnode_valid(dev_ofnode(dev)))
+		device_set_name(dev, ofnode_get_name(dev_ofnode(dev)));
+
+	return 0;
+}
+
+static int mscc_mdio_probe(struct udevice *dev)
+{
+	struct mscc_mdio_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->map);
+	if (ret)
+		return -EINVAL;
+
+	/* Enter Clause 22 mode */
+	ret = regmap_update_bits(priv->map, MSCC_MIIM_REG_CFG,
+				 MSCC_MIIM_CFG_ST_CFG_MASK,
+				 FIELD_PREP(MSCC_MIIM_CFG_ST_CFG_MASK,
+					    MSCC_MIIM_C22));
+
+	return ret;
+}
+
+static const struct udevice_id mscc_mdio_ids[] = {
+	{ .compatible = "mscc,ocelot-miim", },
+	{ }
+};
+
+U_BOOT_DRIVER(mscc_mdio) = {
+	.name           = "mscc_mdio",
+	.id             = UCLASS_MDIO,
+	.of_match       = mscc_mdio_ids,
+	.bind           = mscc_mdio_bind,
+	.probe          = mscc_mdio_probe,
+	.ops            = &mscc_mdio_ops,
+	.priv_auto	  = sizeof(struct mscc_mdio_priv),
+};
-- 
2.52.0



More information about the U-Boot mailing list