This adds an MDIO driver for PIC64-HPSC/HX. The hardware supports C22
and C45 but only C22 is implemented in this commit.

This MDIO hardware is based on a Microsemi design supported in U-Boot by
mdio-mscc-miim.c. However, The register interface is completely
different with pic64hpsc, hence the need for a separate driver.

The documentation recommends an input clock of 156.25MHz and a prescaler
of 39, which yields an MDIO clock of 1.95MHz.

The hardware supports an interrupt pin or a "TRIGGER" bit that can be
polled to signal transaction completion. This commit uses polling.

This was tested on Microchip HB1301 evalkit with a VSC8574 and a
VSC8541.

A Linux driver is also available for this hardware. See these links:
https://elixir.bootlin.com/linux/v7.2.10/source/drivers/net/mdio/mdio-pic64hpsc.c
https://patch.msgid.link/[email protected]

Signed-off-by: Charles Perry <[email protected]>
---
 drivers/net/Kconfig          |   8 ++
 drivers/net/Makefile         |   1 +
 drivers/net/mdio-pic64hpsc.c | 178 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 187 insertions(+)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 797c8c24d5d4..50d3834d2ba1 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1108,6 +1108,14 @@ config MDIO_MSCC_MIIM
          This driver supports MDIO interface found in Microsemi and Microchip
          network switches.
 
+config MDIO_PIC64HPSC
+       bool "PIC64-HPSC/HX MDIO interface support"
+       depends on DM_MDIO
+       select CLK
+       help
+         This driver supports the MDIO interface found on the PIC64-HPSC/HX
+         SoCs.
+
 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 6a351018a880..dd643b9c4c7b 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
 obj-$(CONFIG_MDIO_GPIO_BITBANG) += mdio_gpio.o
 obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
 obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
+obj-$(CONFIG_MDIO_PIC64HPSC) += mdio-pic64hpsc.o
 obj-$(CONFIG_MDIO_MT7531_MMIO) += mdio-mt7531-mmio.o
 obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o
 obj-$(CONFIG_MDIO_MUX_MESON_G12A) += mdio_mux_meson_g12a.o
diff --git a/drivers/net/mdio-pic64hpsc.c b/drivers/net/mdio-pic64hpsc.c
new file mode 100644
index 000000000000..4bb34bf96492
--- /dev/null
+++ b/drivers/net/mdio-pic64hpsc.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2026 Microchip
+ */
+
+#include <clk.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/kernel.h>
+#include <miiphy.h>
+#include <wait_bit.h>
+
+#define P64H_MDIO_REG_PRESCALER 0x20
+#define P64H_MDIO_CFG_PRESCALE_MASK GENMASK(7, 0)
+
+#define P64H_MDIO_REG_FRAME_CFG_1 0x24
+#define P64H_MDIO_WDATA_MASK GENMASK(15, 0)
+
+#define P64H_MDIO_REG_FRAME_CFG_2 0x28
+#define P64H_MDIO_TRIGGER_BIT BIT(31)
+#define P64H_MDIO_REG_DEV_ADDR_MASK GENMASK(20, 16)
+#define P64H_MDIO_PHY_PRT_ADDR_MASK GENMASK(8, 4)
+#define P64H_MDIO_OPERATION_MASK GENMASK(3, 2)
+#define P64H_MDIO_START_OF_FRAME_MASK GENMASK(1, 0)
+
+#define P64H_MDIO_OPERATION_WRITE BIT(0)
+#define P64H_MDIO_OPERATION_READ BIT(1)
+
+#define P64H_MDIO_REG_FRAME_STATUS 0x2C
+#define P64H_MDIO_READOK_BIT BIT(24)
+#define P64H_MDIO_RDATA_MASK GENMASK(15, 0)
+
+struct pic64hpsc_mdio_priv {
+       void *regs_base;
+};
+
+static int pic64hpsc_mdio_wait_trigger(struct udevice *dev)
+{
+       struct pic64hpsc_mdio_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = wait_for_bit_le32(priv->regs_base + P64H_MDIO_REG_FRAME_CFG_2,
+                               P64H_MDIO_TRIGGER_BIT, false, 250, false);
+
+       if (ret < 0) {
+               dev_err(dev, "TRIGGER bit timeout: %x\n",
+                       in_le32(priv->regs_base + P64H_MDIO_REG_FRAME_CFG_2));
+       }
+
+       return ret;
+}
+
+static int pic64hpsc_mdio_read(struct udevice *dev, int addr, int devad,
+                              int reg)
+{
+       struct pic64hpsc_mdio_priv *priv = dev_get_priv(dev);
+       u32 val;
+       int ret;
+
+       if (devad != MDIO_DEVAD_NONE)
+               return -EOPNOTSUPP;
+
+       ret = pic64hpsc_mdio_wait_trigger(dev);
+       if (ret)
+               return ret;
+
+       out_le32(priv->regs_base + P64H_MDIO_REG_FRAME_CFG_2,
+                P64H_MDIO_TRIGGER_BIT |
+                        FIELD_PREP(P64H_MDIO_REG_DEV_ADDR_MASK, reg) |
+                        FIELD_PREP(P64H_MDIO_PHY_PRT_ADDR_MASK, addr) |
+                        FIELD_PREP(P64H_MDIO_OPERATION_MASK,
+                                   P64H_MDIO_OPERATION_READ) |
+                        FIELD_PREP(P64H_MDIO_START_OF_FRAME_MASK, 1));
+
+       ret = pic64hpsc_mdio_wait_trigger(dev);
+       if (ret)
+               return ret;
+
+       val = in_le32(priv->regs_base + P64H_MDIO_REG_FRAME_STATUS);
+
+       if (!FIELD_GET(P64H_MDIO_READOK_BIT, val))
+               return -ENODEV;
+
+       return FIELD_GET(P64H_MDIO_RDATA_MASK, val);
+}
+
+static int pic64hpsc_mdio_write(struct udevice *dev, int addr, int devad,
+                               int reg, u16 val)
+{
+       struct pic64hpsc_mdio_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       if (devad != MDIO_DEVAD_NONE)
+               return -EOPNOTSUPP;
+
+       ret = pic64hpsc_mdio_wait_trigger(dev);
+       if (ret < 0)
+               return ret;
+
+       out_le32(priv->regs_base + P64H_MDIO_REG_FRAME_CFG_1,
+                FIELD_PREP(P64H_MDIO_WDATA_MASK, val));
+
+       out_le32(priv->regs_base + P64H_MDIO_REG_FRAME_CFG_2,
+                P64H_MDIO_TRIGGER_BIT |
+                        FIELD_PREP(P64H_MDIO_REG_DEV_ADDR_MASK, reg) |
+                        FIELD_PREP(P64H_MDIO_PHY_PRT_ADDR_MASK, addr) |
+                        FIELD_PREP(P64H_MDIO_OPERATION_MASK,
+                                   P64H_MDIO_OPERATION_WRITE) |
+                        FIELD_PREP(P64H_MDIO_START_OF_FRAME_MASK, 1));
+
+       return 0;
+}
+
+static const struct mdio_ops pic64hpsc_mdio_ops = {
+       .read = pic64hpsc_mdio_read,
+       .write = pic64hpsc_mdio_write,
+};
+
+static int pic64hpsc_mdio_probe(struct udevice *dev)
+{
+       struct pic64hpsc_mdio_priv *priv = dev_get_priv(dev);
+       u32 div, bus_freq;
+       ulong clk_rate;
+       struct clk clk;
+       int ret;
+
+       priv->regs_base = dev_read_addr_ptr(dev);
+       if (!priv->regs_base)
+               return -ENODEV;
+
+       ret = clk_get_by_index(dev, 0, &clk);
+       if (ret) {
+               dev_err(dev, "Missing clock\n");
+               return ret;
+       }
+
+       ret = clk_enable(&clk);
+       if (ret) {
+               dev_err(dev, "Cannot enable clock\n");
+               return ret;
+       }
+
+       clk_rate = clk_get_rate(&clk);
+       if (!clk_rate) {
+               dev_err(dev, "Cannot get clock rate\n");
+               return -EINVAL;
+       }
+
+       bus_freq = ofnode_read_u32_default(dev_ofnode(dev), "clock-frequency",
+                                          2500000);
+
+       div = DIV_ROUND_UP(clk_rate, 2 * bus_freq) - 1;
+       if (div == 0 || div & ~P64H_MDIO_CFG_PRESCALE_MASK) {
+               dev_err(dev, "MDIO clock-frequency out of range\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(dev, "rate=%lu bus_freq=%u real_bus_freq=%lu div=%u\n",
+               clk_rate, bus_freq, clk_rate / (2 * (1 + div)), div);
+       out_le32(priv->regs_base + P64H_MDIO_REG_PRESCALER, div);
+
+       return 0;
+}
+
+static const struct udevice_id pic64hpsc_mdio_of_ids[] = {
+       { .compatible = "microchip,pic64hpsc-mdio" },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(pic64hpsc_mdio) = {
+       .name = "pic64hpsc_mdio",
+       .id = UCLASS_MDIO,
+       .of_match = pic64hpsc_mdio_of_ids,
+       .probe = pic64hpsc_mdio_probe,
+       .ops = &pic64hpsc_mdio_ops,
+       .priv_auto = sizeof(struct pic64hpsc_mdio_priv),
+};

---
base-commit: 65a7532545336e4b73e049a69436e255da7df961
change-id: 20261016-mdio-pic64hpsc-ff42ea1ec08b

Best regards,
-- 
Thanks,
Charles

Reply via email to