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
