From dfc39f9caf3c1a5e56a6c2b65b3bee83736e29d8 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 30 Dec 2025 22:06:03 +0100 Subject: net: add Microsemi/Microchip MDIO driver Add Microsemi/Microchip MDIO driver for interfaces found in their network switches. Driver is based on the Linux version. Signed-off-by: Robert Marko Acked-by: Jerome Forissier --- 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 (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 13e631c55dc..0089f74c69d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1069,6 +1069,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 +#include +#include +#include +#include +#include + +#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), +}; -- cgit v1.2.3 From 2ee6bf4c65d906205aa9e92b96b35cbe75936afc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 1 Jan 2026 17:51:44 +0100 Subject: net: phy: marvell10g: Fix PHY mode bitmap handling Replace PHY interface mode bitmap handling with comparison test to match U-Boot PHY subsystem behavior. U-Boot currently implements only single PHY interface mode for each PHY. Linux currently uses bitmap of PHY interface modes for each PHY. The reason why in Linux uses bitmap of supported interface modes is so that Linux can select the best serdes mode switching behavior for the PHY. For example if the host only supports 10gbase-r serdes mode, then the PHY must always talk to the host in 10gbase-r mode, even if the RJ-45 copper speed was autonegotiated to lower speed (i.e. 1Gbps). If the host supports both 10gbase-r and sgmii serdes modes, we want the PHY to switch to sgmii if the RJ-45 speed is 1000/100/10, and to switch to 10gbase-r if the RJ-45 speed is 10000. U-Boot does not implement this functionality yet, therefore remove modes which cannot be currently supported and switch mv_test_bit() to plain mode comparison. Fixes: b6fcab0728cb ("net: phy: marvell10g: Adapt Marvell 10G PHY driver from Linux") Signed-off-by: Marek Vasut --- drivers/net/phy/marvell10g.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 8c95bcbb9ad..d6115eea025 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -342,8 +342,7 @@ static int mv2110_select_mactype(struct phy_device *phydev) { if (phydev->interface == PHY_INTERFACE_MODE_USXGMII) return MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII; - else if (phydev->interface == PHY_INTERFACE_MODE_SGMII && - !(phydev->interface == PHY_INTERFACE_MODE_10GBASER)) + else if (phydev->interface == PHY_INTERFACE_MODE_SGMII) return MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER; else if (phydev->interface == PHY_INTERFACE_MODE_10GBASER) return MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH; @@ -381,15 +380,6 @@ static int mv3310_select_mactype(struct phy_device *phydev) { if (phydev->interface == PHY_INTERFACE_MODE_USXGMII) return MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII; - else if (phydev->interface == PHY_INTERFACE_MODE_SGMII && - phydev->interface == PHY_INTERFACE_MODE_10GBASER) - return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER; - else if (phydev->interface == PHY_INTERFACE_MODE_SGMII && - phydev->interface == PHY_INTERFACE_MODE_RXAUI) - return MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI; - else if (phydev->interface == PHY_INTERFACE_MODE_SGMII && - phydev->interface == PHY_INTERFACE_MODE_XAUI) - return MV_V2_3310_PORT_CTRL_MACTYPE_XAUI; else if (phydev->interface == PHY_INTERFACE_MODE_10GBASER) return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH; else if (phydev->interface == PHY_INTERFACE_MODE_RXAUI) @@ -542,7 +532,7 @@ static bool mv3310_has_downshift(struct phy_device *phydev) } #define mv_test_bit(iface, phydev) \ - ({ if ((phydev)->interface & (iface)) return 0; }) + ({ if ((phydev)->interface == (iface)) return 0; }) static int mv3310_mv3340_test_supported_interfaces(struct phy_device *phydev) { -- cgit v1.2.3 From 60545cf032f097bd096be9d8285320704fc61088 Mon Sep 17 00:00:00 2001 From: Markus Niebel Date: Tue, 2 Dec 2025 09:13:42 +0100 Subject: net: phy: micrel_ksz90x1: disable asymmetric pause for KSZ9031 and KSZ9021 Disable the support due to chip errata and call genphy_config_aneg instead of genphy_config. For a complete describtion look at the KSZ9031 errata sheets: DS80000691D or DS80000692D. Micrel KSZ9021 has no errata, but has the same issue with Asymmetric Pause. This patch apply the same workaround as the one for KSZ9031. This follows linux implementation in commits 3aed3e2a143c ("net: phy: micrel: add Asym Pause workaround") 407d8098cb1a ("net: phy: micrel: add Asym Pause workaround for KSZ9021") Signed-off-by: Markus Niebel Signed-off-by: Max Merchel --- drivers/net/phy/micrel_ksz90x1.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index a02dbe900b8..a669a5789b9 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -217,6 +217,31 @@ static int ksz9031_center_flp_timing(struct phy_device *phydev) return ret; } +static void ksz90x1_workaround_asymmetric_pause(struct phy_device *phydev) +{ + u32 features = phydev->drv->features; + + /* Silicon Errata Sheet (DS80000691D or DS80000692D): + * Whenever the device's Asymmetric Pause capability is set to 1, + * link-up may fail after a link-up to link-down transition. + * + * The Errata Sheet is for ksz9031, but ksz9021 has the same issue + * + * Workaround: + * Do not enable the Asymmetric Pause capability bit. + */ + features &= ~ADVERTISE_PAUSE_ASYM; + + /* We force setting the Pause capability as the core will force the + * Asymmetric Pause capability to 1 otherwise. + */ + features |= ADVERTISE_PAUSE_CAP; + + /* update feature support and forward to advertised features */ + phydev->supported = features; + phydev->advertising = phydev->supported; +} + /* * KSZ9021 */ @@ -260,6 +285,8 @@ static int ksz9021_config(struct phy_device *phydev) if (ret) return ret; + ksz90x1_workaround_asymmetric_pause(phydev); + if (env_get("disable_giga")) features &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); @@ -345,6 +372,8 @@ static int ksz9031_config(struct phy_device *phydev) if (ret) return ret; + ksz90x1_workaround_asymmetric_pause(phydev); + /* add an option to disable the gigabit feature of this PHY */ if (env_get("disable_giga")) { unsigned features; -- cgit v1.2.3 From b61d7d95cc62525060f0d05881bdaaf994a55b11 Mon Sep 17 00:00:00 2001 From: Markus Niebel Date: Tue, 2 Dec 2025 09:13:43 +0100 Subject: net: phy: micrel_ksz90x1: support forced GIGE master for KSZ9031 The micrel KSZ9031 phy has a optional clock pin (CLK125_NDO) which can be used as reference clock for the MAC unit. The clock signal must meet the RGMII requirements to ensure the correct data transmission between the MAC and the PHY. The KSZ9031 phy does not fulfill the duty cycle requirement if the phy is configured as slave. For a complete describtion look at the errata sheets: DS80000691D or DS80000692D. The errata sheet recommends to force the phy into master mode whenever there is a 1000Base-T link-up as work around. Only set the "micrel,force-master" property if you use the phy reference clock provided by CLK125_NDO pin as MAC reference clock in your application. Attention: this workaround is only usable if the link partner can be configured to slave mode for 1000Base-T. This follows linux implementation in commit e1b505a60366 ("net: phy: micrel: add 125MHz reference clock workaround") Signed-off-by: Markus Niebel Signed-off-by: Max Merchel --- drivers/net/phy/micrel_ksz90x1.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index a669a5789b9..f357e0f1c77 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -189,7 +189,10 @@ static int ksz9031_of_config(struct phy_device *phydev) { MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW, 2, ksz90x1_txd_grp, 4 }, { MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, 2, ksz9031_clk_grp, 2 }, }; + const unsigned int master = CTRL1000_CONFIG_MASTER | CTRL1000_MANUAL_CONFIG; + struct udevice *dev = phydev->dev; int i, ret = 0; + ofnode node; for (i = 0; i < ARRAY_SIZE(ofcfg); i++) { ret = ksz90x1_of_config_group(phydev, &ofcfg[i], @@ -198,7 +201,39 @@ static int ksz9031_of_config(struct phy_device *phydev) return ret; } - return 0; + node = phydev->node; + + /* Look for a PHY node under the Ethernet node */ + if (!ofnode_valid(node)) + node = dev_read_subnode(dev, "ethernet-phy"); + + /* No node found, look in the Ethernet node */ + if (!ofnode_valid(node)) + node = dev_ofnode(dev); + + /* Silicon Errata Sheet (DS80000691D or DS80000692D): + * When the device links in the 1000BASE-T slave mode only, + * the optional 125MHz reference output clock (CLK125_NDO) + * has wide duty cycle variation. + * + * The optional CLK125_NDO clock does not meet the RGMII + * 45/55 percent (min/max) duty cycle requirement and therefore + * cannot be used directly by the MAC side for clocking + * applications that have setup/hold time requirements on + * rising and falling clock edges. + * + * Workaround: + * Force the phy to be the master to receive a stable clock + * which meets the duty cycle requirement. + */ + if (ofnode_read_bool(node, "micrel,force-master")) { + ret = phy_modify(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, + master | CTRL1000_PREFER_MASTER, master); + if (ret < 0) + pr_err("KSZ9031: error applying 'micrel,force-master'\n"); + } + + return ret; } static int ksz9031_center_flp_timing(struct phy_device *phydev) -- cgit v1.2.3