Merge branch '2024-03-28-assorted-net-changes' into next

- A few ncsi PHY fixes, clean up PHY GPIO reset code, support LEDs on
  BCM54210E PHY, fix a signed shift overflow in the PHY code, hifemac
  updates, E1000 i225-IT support, improve DM_MDIO+DM_PHY support and
  enable it on the BeaglePlay platform.
diff --git a/configs/am62x_beagleplay_a53_defconfig b/configs/am62x_beagleplay_a53_defconfig
index 8878450..d9751bc 100644
--- a/configs/am62x_beagleplay_a53_defconfig
+++ b/configs/am62x_beagleplay_a53_defconfig
@@ -88,9 +88,9 @@
 CONFIG_MMC_SDHCI_AM654=y
 CONFIG_PHY_REALTEK=y
 CONFIG_PHY_TI=y
-CONFIG_PHY_FIXED=y
 CONFIG_TI_AM65_CPSW_NUSS=y
 CONFIG_PHY=y
+CONFIG_DM_ETH_PHY=y
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 CONFIG_PINCTRL_SINGLE=y
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c
index 84a2a7c..4e7ba66 100644
--- a/drivers/net/e1000.c
+++ b/drivers/net/e1000.c
@@ -116,6 +116,8 @@
 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_SERDES) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_SERDES_FLASHLESS) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_1000BASEKX) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I225_UNPROGRAMMED) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I225_IT) },
 
 	{}
 };
@@ -1575,6 +1577,8 @@
 	case PCI_DEVICE_ID_INTEL_I210_SERDES:
 	case PCI_DEVICE_ID_INTEL_I210_SERDES_FLASHLESS:
 	case PCI_DEVICE_ID_INTEL_I210_1000BASEKX:
+	case PCI_DEVICE_ID_INTEL_I225_UNPROGRAMMED:
+	case PCI_DEVICE_ID_INTEL_I225_IT:
 		hw->mac_type = e1000_igb;
 		break;
 	default:
@@ -3258,7 +3262,8 @@
 		if (ret_val)
 			return ret_val;
 	} else if (hw->phy_type == e1000_phy_m88 ||
-		hw->phy_type == e1000_phy_igb) {
+		hw->phy_type == e1000_phy_igb ||
+		hw->phy_type == e1000_phy_igc) {
 		ret_val = e1000_copper_link_mgp_setup(hw);
 		if (ret_val)
 			return ret_val;
@@ -4531,6 +4536,8 @@
 	case e1000_igb:
 		while (timeout) {
 			if (hw->mac_type == e1000_igb) {
+				if (hw->phy_type == e1000_phy_igc)
+					break;
 				if (E1000_READ_REG(hw, I210_EEMNGCTL) & cfg_mask)
 					break;
 			} else {
@@ -4769,6 +4776,7 @@
 	case e1000_phy_igp_3:
 	case e1000_phy_ife:
 	case e1000_phy_igb:
+	case e1000_phy_igc:
 		ret_val = e1000_phy_hw_reset(hw);
 		if (ret_val)
 			return ret_val;
@@ -4834,6 +4842,9 @@
 	case I210_I_PHY_ID:
 		hw->phy_type = e1000_phy_igb;
 		break;
+	case I225_I_PHY_ID:
+		hw->phy_type = e1000_phy_igc;
+		break;
 		/* Fall Through */
 	default:
 		/* Should never have loaded on this device */
@@ -4941,6 +4952,8 @@
 	case e1000_igb:
 		if (hw->phy_id == I210_I_PHY_ID)
 			match = true;
+		if (hw->phy_id == I225_I_PHY_ID)
+			match = true;
 		break;
 	default:
 		DEBUGOUT("Invalid MAC type %d\n", hw->mac_type);
diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h
index f788394..e131112 100644
--- a/drivers/net/e1000.h
+++ b/drivers/net/e1000.h
@@ -212,6 +212,7 @@
 	e1000_phy_igp_3,
 	e1000_phy_ife,
 	e1000_phy_igb,
+	e1000_phy_igc,
 	e1000_phy_bm,
 	e1000_phy_undefined = 0xFF
 } e1000_phy_type;
@@ -2420,6 +2421,7 @@
 #define BME1000_E_PHY_ID     0x01410CB0
 
 #define I210_I_PHY_ID		0x01410C00
+#define I225_I_PHY_ID		0x67C9DCC0
 
 /* Miscellaneous PHY bit definitions. */
 #define PHY_PREAMBLE			0xFFFFFFFF
diff --git a/drivers/net/hifemac.c b/drivers/net/hifemac.c
index b61a29e..90cc247 100644
--- a/drivers/net/hifemac.c
+++ b/drivers/net/hifemac.c
@@ -15,6 +15,9 @@
 #include <wait_bit.h>
 #include <asm/io.h>
 #include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <linux/bitfield.h>
+#include <linux/ethtool.h>
 #include <linux/delay.h>
 #include <linux/kernel.h>
 
@@ -124,6 +127,57 @@
 	u32 link_status;
 };
 
+struct hisi_femac_stat_entry {
+	const char *name;
+	u32 offset;
+	u32 mask;
+};
+
+/* please refer to the datasheet for the description of these entries */
+static const struct hisi_femac_stat_entry hisi_femac_stats_table[] = {
+	{ "rxsof_cnt",		0x584,	GENMASK(31, 28) },
+	{ "rxeof_cnt",		0x584,	GENMASK(27, 24) },
+	{ "rxcrcok_cnt",	0x584,	GENMASK(23, 20) },
+	{ "rxcrcbad_cnt",	0x584,	GENMASK(19, 16) },
+	{ "txsof_cnt",		0x584,	GENMASK(15, 12) },
+	{ "txeof_cnt",		0x584,	GENMASK(11, 8) },
+	{ "txcrcok_cnt",	0x584,	GENMASK(7, 4) },
+	{ "txcrcbad_cnt",	0x584,	GENMASK(3, 0) },
+	{ "pkts_cpu",		0x5a0,	GENMASK(15, 0) },
+	{ "addr_cpu",		0x5a4,	GENMASK(15, 0) },
+	{ "pkts_port",		0x5a8,	GENMASK(15, 0) },
+	{ "pkts_cpu2tx",	0x5ac,	GENMASK(15, 0) },
+	{ "rxdvrise",		0x600,	GENMASK(31, 0) },
+	{ "ifinoctets",		0x604,	GENMASK(31, 0) },
+	{ "octets_rx",		0x608,	GENMASK(31, 0) },
+	{ "local_mac_match",	0x60c,	GENMASK(31, 0) },
+	{ "pkts",		0x610,	GENMASK(31, 0) },
+	{ "broadcastpkts",	0x614,	GENMASK(31, 0) },
+	{ "multicastpkts",	0x618,	GENMASK(31, 0) },
+	{ "ifinucastpkts",	0x61c,	GENMASK(31, 0) },
+	{ "ifinerrors",		0x620,	GENMASK(31, 0) },
+	{ "crcerr",		0x624,	GENMASK(31, 0) },
+	{ "abnormalsizepkts",	0x628,	GENMASK(31, 0) },
+	{ "dot3alignmenterr",	0x62c,	GENMASK(31, 0) },
+	{ "dot3pause",		0x630,	GENMASK(31, 0) },
+	{ "dropevents",		0x634,	GENMASK(31, 0) },
+	{ "flux_frame_cnt",	0x638,	GENMASK(31, 0) },
+	{ "flux_drop_cnt",	0x63c,	GENMASK(31, 0) },
+	{ "mac_not2cpu_pkts",	0x64c,	GENMASK(31, 0) },
+	{ "pkts_tx",		0x780,	GENMASK(31, 0) },
+	{ "broadcastpkts_tx",	0x784,	GENMASK(31, 0) },
+	{ "multicastpkts_tx",	0x788,	GENMASK(31, 0) },
+	{ "ifoutucastpkts_tx",	0x78c,	GENMASK(31, 0) },
+	{ "octets_tx",		0x790,	GENMASK(31, 0) },
+	{ "dot3pause",		0x794,	GENMASK(31, 0) },
+	{ "retry_times_tx",	0x798,	GENMASK(31, 0) },
+	{ "collisions",		0x79c,	GENMASK(31, 0) },
+	{ "dot3latecol",	0x7a0,	GENMASK(31, 0) },
+	{ "dot3colok",		0x7a4,	GENMASK(31, 0) },
+	{ "dot3excessivecol",	0x7a8,	GENMASK(31, 0) },
+	{ "dot3colcnt",		0x7ac,	GENMASK(31, 0) },
+};
+
 static void hisi_femac_irq_enable(struct hisi_femac_priv *priv, int irqs)
 {
 	u32 val;
@@ -245,8 +299,10 @@
 	hisi_femac_rx_refill(priv);
 
 	ret = phy_startup(priv->phy);
-	if (ret)
-		return log_msg_ret("Failed to startup phy", ret);
+	if (ret) {
+		dev_err(dev, "Failed to startup phy: %d\n", ret);
+		return log_msg_ret("phy", ret);
+	}
 
 	if (!priv->phy->link) {
 		debug("%s: link down\n", __func__);
@@ -281,8 +337,10 @@
 
 	// wait until FIFO is empty
 	ret = wait_for_bit_le32(priv->glb_base + GLB_IRQ_RAW, IRQ_INT_TX_PER_PACKET, true, 50, false);
-	if (ret == -ETIMEDOUT)
-		return log_msg_ret("FIFO timeout", ret);
+	if (ret == -ETIMEDOUT) {
+		dev_err(dev, "FIFO timeout\n");
+		return log_msg_ret("net", ret);
+	}
 
 	return 0;
 }
@@ -329,10 +387,43 @@
 	writel(SOFT_RESET_ALL, priv->glb_base + GLB_SOFT_RESET);
 }
 
+static int hisi_femac_get_sset_count(struct udevice *dev)
+{
+	return ARRAY_SIZE(hisi_femac_stats_table);
+}
+
+static void hisi_femac_get_strings(struct udevice *dev, u8 *data)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hisi_femac_stats_table); i++)
+		strcpy(data + i * ETH_GSTRING_LEN, hisi_femac_stats_table[i].name);
+}
+
+/* Non-constant mask variant of FIELD_GET/FIELD_PREP */
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+
+static void hisi_femac_get_stats(struct udevice *dev, u64 *data)
+{
+	int i;
+	u32 mask, reg;
+	struct hisi_femac_priv *priv = dev_get_priv(dev);
+	void __iomem *port_base = priv->port_base;
+
+	for (i = 0; i < ARRAY_SIZE(hisi_femac_stats_table); i++) {
+		mask = hisi_femac_stats_table[i].mask;
+		reg = readl(port_base + hisi_femac_stats_table[i].offset);
+
+		data[i] = field_get(mask, reg);
+	}
+}
+
-int hisi_femac_of_to_plat(struct udevice *dev)
+static int hisi_femac_of_to_plat(struct udevice *dev)
 {
 	int ret, i;
 	struct hisi_femac_priv *priv = dev_get_priv(dev);
+	ofnode mdio_node;
+	bool mdio_registered = false;
 	static const char * const clk_strs[] = {
 		[CLK_MAC] = "mac",
 		[CLK_BUS] = "bus",
@@ -340,40 +431,75 @@
 	};
 
 	priv->port_base = dev_remap_addr_name(dev, "port");
-	if (IS_ERR(priv->port_base))
-		return log_msg_ret("Failed to remap port address space", PTR_ERR(priv->port_base));
+	if (!priv->port_base) {
+		dev_err(dev, "Failed to remap port address space\n");
+		return log_msg_ret("net", -EINVAL);
+	}
 
 	priv->glb_base = dev_remap_addr_name(dev, "glb");
-	if (IS_ERR(priv->glb_base))
-		return log_msg_ret("Failed to remap global address space", PTR_ERR(priv->glb_base));
+	if (IS_ERR(priv->glb_base)) {
+		dev_err(dev, "Failed to remap global address space\n");
+		return log_msg_ret("net", -EINVAL);
+	}
 
 	for (i = 0; i < ARRAY_SIZE(clk_strs); i++) {
 		priv->clks[i] = devm_clk_get(dev, clk_strs[i]);
 		if (IS_ERR(priv->clks[i])) {
 			dev_err(dev, "Error getting clock %s\n", clk_strs[i]);
-			return log_msg_ret("Failed to get clocks", PTR_ERR(priv->clks[i]));
+			return log_msg_ret("clk", PTR_ERR(priv->clks[i]));
 		}
 	}
 
 	priv->mac_rst = devm_reset_control_get(dev, "mac");
-	if (IS_ERR(priv->mac_rst))
-		return log_msg_ret("Failed to get MAC reset", PTR_ERR(priv->mac_rst));
+	if (IS_ERR(priv->mac_rst)) {
+		dev_err(dev, "Failed to get MAC reset %ld\n", PTR_ERR(priv->mac_rst));
+		return log_msg_ret("rst", PTR_ERR(priv->mac_rst));
+	}
 
 	priv->phy_rst = devm_reset_control_get(dev, "phy");
-	if (IS_ERR(priv->phy_rst))
-		return log_msg_ret("Failed to get PHY reset", PTR_ERR(priv->phy_rst));
+	if (IS_ERR(priv->phy_rst)) {
+		dev_err(dev, "Failed to get PHY reset %ld\n", PTR_ERR(priv->phy_rst));
+		return log_msg_ret("rst", PTR_ERR(priv->phy_rst));
+	}
 
 	ret = dev_read_u32_array(dev,
 				 PHY_RESET_DELAYS_PROPERTY,
 				 priv->phy_reset_delays,
 				 DELAYS_NUM);
-	if (ret < 0)
-		return log_msg_ret("Failed to get PHY reset delays", ret);
+	if (ret < 0) {
+		dev_err(dev, "Failed to get PHY reset delays %d\n", ret);
+		return log_msg_ret("rst", ret);
+	}
 
 	priv->mac_reset_delay = dev_read_u32_default(dev,
 						     MAC_RESET_DELAY_PROPERTY,
 						     MAC_RESET_ASSERT_PERIOD);
 
+	/* Create MDIO bus */
+	ofnode_for_each_subnode(mdio_node, dev_ofnode(dev)) {
+		const char *subnode_name = ofnode_get_name(mdio_node);
+		struct udevice *mdiodev;
+
+		// Skip subnodes not starting with "mdio"
+		if (strncmp(subnode_name, "mdio", 4))
+			continue;
+
+		ret = device_bind_driver_to_node(dev, "hisi-femac-mdio",
+						 subnode_name, mdio_node, &mdiodev);
+		if (ret) {
+			dev_err(dev, "Failed to register MDIO bus device %d\n", ret);
+			return log_msg_ret("net", ret);
+		}
+
+		mdio_registered = true;
+		break;
+	}
+
+	if (!mdio_registered) {
+		dev_err(dev, "No MDIO subnode is found!\n");
+		return log_msg_ret("mdio", -ENODATA);
+	}
+
 	return 0;
 }
 
@@ -385,37 +511,49 @@
 
 	// Disable MAC clk before phy reset
 	ret = clk_disable(priv->clks[CLK_MAC]);
-	if (ret < 0)
-		return log_msg_ret("Failed to disable MAC clock", ret);
+	if (ret < 0) {
+		pr_err("%s: Failed to disable MAC clock %d\n", __func__, ret);
+		return log_msg_ret("clk", ret);
+	}
 	ret = clk_disable(priv->clks[CLK_BUS]);
-	if (ret < 0)
-		return log_msg_ret("Failed to disable bus clock", ret);
+	if (ret < 0) {
+		pr_err("%s: Failed to disable bus clock %d\n", __func__, ret);
+		return log_msg_ret("clk", ret);
+	}
 
 	udelay(delays[PRE_DELAY]);
 
 	ret = reset_assert(rst);
-	if (ret < 0)
-		return log_msg_ret("Failed to assert reset", ret);
+	if (ret < 0) {
+		pr_err("%s: Failed to assert reset %d\n", __func__, ret);
+		return log_msg_ret("rst", ret);
+	}
 
 	udelay(delays[PULSE]);
 
 	ret = reset_deassert(rst);
-	if (ret < 0)
-		return log_msg_ret("Failed to deassert reset", ret);
+	if (ret < 0) {
+		pr_err("%s: Failed to deassert reset %d\n", __func__, ret);
+		return log_msg_ret("rst", ret);
+	}
 
 	udelay(delays[POST_DELAY]);
 
 	ret = clk_enable(priv->clks[CLK_MAC]);
-	if (ret < 0)
-		return log_msg_ret("Failed to enable MAC clock", ret);
+	if (ret < 0) {
+		pr_err("%s: Failed to enable MAC clock %d\n", __func__, ret);
+		return log_msg_ret("clk", ret);
+	}
 	ret = clk_enable(priv->clks[CLK_BUS]);
-	if (ret < 0)
-		return log_msg_ret("Failed to enable MAC bus clock", ret);
+	if (ret < 0) {
+		pr_err("%s: Failed to enable MAC bus clock %d\n", __func__, ret);
+		return log_msg_ret("clk", ret);
+	}
 
 	return 0;
 }
 
-int hisi_femac_probe(struct udevice *dev)
+static int hisi_femac_probe(struct udevice *dev)
 {
 	struct hisi_femac_priv *priv = dev_get_priv(dev);
 	int ret, i;
@@ -423,30 +561,40 @@
 	// Enable clocks
 	for (i = 0; i < CLK_NUM; i++) {
 		ret = clk_prepare_enable(priv->clks[i]);
-		if (ret < 0)
-			return log_msg_ret("Failed to enable clks", ret);
+		if (ret < 0) {
+			dev_err(dev, "Failed to enable clk %d: %d\n", i, ret);
+			return log_msg_ret("clk", ret);
+		}
 	}
 
 	// Reset MAC
 	ret = reset_assert(priv->mac_rst);
-	if (ret < 0)
-		return log_msg_ret("Failed to assert MAC reset", ret);
+	if (ret < 0) {
+		dev_err(dev, "Failed to assert MAC reset: %d\n", ret);
+		return log_msg_ret("net", ret);
+	}
 
 	udelay(priv->mac_reset_delay);
 
 	ret = reset_deassert(priv->mac_rst);
-	if (ret < 0)
-		return log_msg_ret("Failed to deassert MAC reset", ret);
+	if (ret < 0) {
+		dev_err(dev, "Failed to deassert MAC reset: %d\n", ret);
+		return log_msg_ret("net", ret);
+	}
 
 	// Reset PHY
 	ret = hisi_femac_phy_reset(priv);
-	if (ret < 0)
-		return log_msg_ret("Failed to reset phy", ret);
+	if (ret < 0) {
+		dev_err(dev, "Failed to reset PHY: %d\n", ret);
+		return log_msg_ret("net", ret);
+	}
 
 	// Connect to PHY
 	priv->phy = dm_eth_phy_connect(dev);
-	if (!priv->phy)
-		return log_msg_ret("Failed to connect to phy", -EINVAL);
+	if (!priv->phy) {
+		dev_err(dev, "Failed to connect to phy\n");
+		return log_msg_ret("phy", -EINVAL);
+	}
 
 	hisi_femac_port_init(priv);
 	return 0;
@@ -459,6 +607,9 @@
 	.free_pkt	= hisi_femac_free_pkt,
 	.stop		= hisi_femac_stop,
 	.write_hwaddr	= hisi_femac_set_hw_mac_addr,
+	.get_sset_count	= hisi_femac_get_sset_count,
+	.get_strings	= hisi_femac_get_strings,
+	.get_stats	= hisi_femac_get_stats,
 };
 
 static const struct udevice_id hisi_femac_ids[] = {
diff --git a/drivers/net/hifemac_mdio.c b/drivers/net/hifemac_mdio.c
index 343c5f3..0b59d06 100644
--- a/drivers/net/hifemac_mdio.c
+++ b/drivers/net/hifemac_mdio.c
@@ -8,6 +8,7 @@
 #include <dm.h>
 #include <clk.h>
 #include <miiphy.h>
+#include <dm/device_compat.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 
@@ -74,7 +75,8 @@
 	data->membase = dev_remap_addr(dev);
 	if (IS_ERR(data->membase)) {
 		ret = PTR_ERR(data->membase);
-		return log_msg_ret("Failed to remap base addr", ret);
+		dev_err(dev, "Failed to remap base addr %d\n", ret);
+		return log_msg_ret("mdio", ret);
 	}
 
 	// clk is optional
@@ -89,8 +91,10 @@
 	int ret;
 
 	ret = clk_prepare_enable(data->clk);
-	if (ret)
-		return log_msg_ret("Failed to enable clk", ret);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock: %d\n", ret);
+		return log_msg_ret("clk", ret);
+	}
 
 	return 0;
 }
@@ -112,5 +116,6 @@
 	.of_to_plat = hisi_femac_mdio_of_to_plat,
 	.probe = hisi_femac_mdio_probe,
 	.ops = &hisi_femac_mdio_ops,
+	.plat_auto = sizeof(struct mdio_perdev_priv),
 	.priv_auto = sizeof(struct hisi_femac_mdio_data),
 };
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 82e3bbe..ecccb7c 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -42,6 +42,12 @@
 #define BCM54810_SHD_CLK_CTL				0x3
 #define BCM54810_SHD_CLK_CTL_GTXCLK_EN			BIT(9)
 
+#define BCM54XX_SHD_LEDS1		0x0d
+#define BCM_LED_SRC_LINKSPD2		0x1
+#define BCM_LED_SRC_ACTIVITYLED		0x3
+#define BCM54XX_SHD_LEDS1_LED3(src)	(((src) & 0xf) << 4)
+#define BCM54XX_SHD_LEDS1_LED1(src)	(((src) & 0xf) << 0)
+
 static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
 {
 	/* The register must be written to both the Shadow Register Select and
@@ -148,7 +154,16 @@
 	if (ret < 0)
 		return ret;
 
+	ret = bcm5461_config(phydev);
+	if (ret < 0)
+		return ret;
+
-	return bcm5461_config(phydev);
+	/* Configure LEDs to blink. */
+	bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1,
+			     BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
+			     BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
+
+	return 0;
 }
 
 static int bcm54xx_parse_status(struct phy_device *phydev)
diff --git a/drivers/net/phy/ethernet_id.c b/drivers/net/phy/ethernet_id.c
index 6cb1fd4..4dfdee6 100644
--- a/drivers/net/phy/ethernet_id.c
+++ b/drivers/net/phy/ethernet_id.c
@@ -18,12 +18,11 @@
 {
 	struct phy_device *phydev;
 	struct ofnode_phandle_args phandle_args;
-	struct gpio_desc gpio;
 	const char *node_name;
 	struct udevice *pdev;
-	ofnode node;
-	u32 id, assert, deassert;
 	u16 vendor, device;
+	ofnode node;
+	u32 id;
 	int ret;
 
 	if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
@@ -41,35 +40,9 @@
 		return NULL;
 	}
 
-	if (!IS_ENABLED(CONFIG_DM_ETH_PHY)) {
-		ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio,
-						 GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
-		if (!ret) {
-			assert = ofnode_read_u32_default(node,
-							 "reset-assert-us", 0);
-			deassert = ofnode_read_u32_default(node,
-							   "reset-deassert-us",
-							   0);
-			ret = dm_gpio_set_value(&gpio, 1);
-			if (ret) {
-				dev_err(dev,
-					"Failed assert gpio, err: %d\n", ret);
-				return NULL;
-			}
-
-			udelay(assert);
-
-			ret = dm_gpio_set_value(&gpio, 0);
-			if (ret) {
-				dev_err(dev,
-					"Failed deassert gpio, err: %d\n",
-					ret);
-				return NULL;
-			}
-
-			udelay(deassert);
-		}
-	}
+	ret = phy_gpio_reset(dev);
+	if (ret)
+		return NULL;
 
 	if (phyaddr == -1)
 		phyaddr = ofnode_read_u32_default(phandle_args.node, "reg", -1);
diff --git a/drivers/net/phy/ncsi.c b/drivers/net/phy/ncsi.c
index eb3fd65..2bca116 100644
--- a/drivers/net/phy/ncsi.c
+++ b/drivers/net/phy/ncsi.c
@@ -286,11 +286,11 @@
 	}
 
 	c = &ncsi_priv->packages[np].channels[nc];
-	c->cap_generic = ntohl(gc->cap) & NCSI_CAP_GENERIC_MASK;
-	c->cap_bc = ntohl(gc->bc_cap) & NCSI_CAP_BC_MASK;
-	c->cap_mc = ntohl(gc->mc_cap) & NCSI_CAP_MC_MASK;
-	c->cap_aen = ntohl(gc->aen_cap) & NCSI_CAP_AEN_MASK;
-	c->cap_vlan = ntohl(gc->vlan_mode) & NCSI_CAP_VLAN_MASK;
+	c->cap_generic = get_unaligned_be32(&gc->cap) & NCSI_CAP_GENERIC_MASK;
+	c->cap_bc = get_unaligned_be32(&gc->bc_cap) & NCSI_CAP_BC_MASK;
+	c->cap_mc = get_unaligned_be32(&gc->mc_cap) & NCSI_CAP_MC_MASK;
+	c->cap_aen = get_unaligned_be32(&gc->aen_cap) & NCSI_CAP_AEN_MASK;
+	c->cap_vlan = gc->vlan_mode & NCSI_CAP_VLAN_MASK;
 
 	/* End of probe for this channel */
 }
@@ -551,7 +551,7 @@
 	checksum = ncsi_calculate_checksum((unsigned char *)hdr,
 					   sizeof(*hdr) + len);
 	pchecksum = (__be32 *)((void *)(hdr + 1) + len);
-	put_unaligned_be32(htonl(checksum), pchecksum);
+	put_unaligned_be32(checksum, pchecksum);
 
 	if (wait) {
 		net_set_timeout_handler(1000UL, ncsi_timeout_handler);
@@ -619,9 +619,12 @@
 
 	/* Link or configuration lost - just redo the discovery process */
 	ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
-	for (i = 0; i < ncsi_priv->n_packages; i++)
+	for (i = 0; i < ncsi_priv->n_packages; i++) {
 		free(ncsi_priv->packages[i].channels);
+		ncsi_priv->packages[i].channels = NULL;
+	}
 	free(ncsi_priv->packages);
+	ncsi_priv->packages = NULL;
 	ncsi_priv->n_packages = 0;
 
 	ncsi_priv->current_package = NCSI_PACKAGE_MAX;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 63b3e46..270176c 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -18,6 +18,8 @@
 #include <phy.h>
 #include <errno.h>
 #include <asm/global_data.h>
+#include <asm-generic/gpio.h>
+#include <dm/device_compat.h>
 #include <dm/of_extra.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
@@ -566,7 +568,8 @@
 		return NULL;
 	}
 
-	if (addr >= 0 && addr < PHY_MAX_ADDR && phy_id != PHY_FIXED_ID)
+	if (addr >= 0 && addr < PHY_MAX_ADDR && phy_id != PHY_FIXED_ID &&
+	    phy_id != PHY_NCSI_ID)
 		bus->phymap[addr] = dev;
 
 	return dev;
@@ -642,12 +645,12 @@
 {
 	/* If we have one, return the existing device, with new interface */
 	while (phy_mask) {
-		int addr = ffs(phy_mask) - 1;
+		unsigned int addr = ffs(phy_mask) - 1;
 
 		if (bus->phymap[addr])
 			return bus->phymap[addr];
 
-		phy_mask &= ~(1 << addr);
+		phy_mask &= ~(1U << addr);
 	}
 	return NULL;
 }
@@ -768,6 +771,59 @@
 	return phy_reset(phydev);
 }
 
+#if CONFIG_IS_ENABLED(DM_GPIO) && CONFIG_IS_ENABLED(OF_REAL) && \
+    !IS_ENABLED(CONFIG_DM_ETH_PHY)
+int phy_gpio_reset(struct udevice *dev)
+{
+	struct ofnode_phandle_args phandle_args;
+	struct gpio_desc gpio;
+	u32 assert, deassert;
+	ofnode node;
+	int ret;
+
+	ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
+					 &phandle_args);
+	/* No PHY handle is OK */
+	if (ret)
+		return 0;
+
+	node = phandle_args.node;
+	if (!ofnode_valid(node))
+		return -EINVAL;
+
+	ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio,
+					 GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
+	/* No PHY reset GPIO is OK */
+	if (ret)
+		return 0;
+
+	assert = ofnode_read_u32_default(node, "reset-assert-us", 20000);
+	deassert = ofnode_read_u32_default(node, "reset-deassert-us", 1000);
+	ret = dm_gpio_set_value(&gpio, 1);
+	if (ret) {
+		dev_err(dev, "Failed assert gpio, err: %d\n", ret);
+		return ret;
+	}
+
+	udelay(assert);
+
+	ret = dm_gpio_set_value(&gpio, 0);
+	if (ret) {
+		dev_err(dev, "Failed deassert gpio, err: %d\n", ret);
+		return ret;
+	}
+
+	udelay(deassert);
+
+	return 0;
+}
+#else
+int phy_gpio_reset(struct udevice *dev)
+{
+	return 0;
+}
+#endif
+
 struct phy_device *phy_find_by_mask(struct mii_dev *bus, uint phy_mask)
 {
 	/* Reset the bus */
diff --git a/include/pci_ids.h b/include/pci_ids.h
index b63bf45..f1886c3 100644
--- a/include/pci_ids.h
+++ b/include/pci_ids.h
@@ -2710,6 +2710,8 @@
 #define PCI_DEVICE_ID_INTEL_I211_COPPER			0x1539
 #define PCI_DEVICE_ID_INTEL_I210_COPPER_FLASHLESS	0x157b
 #define PCI_DEVICE_ID_INTEL_I210_SERDES_FLASHLESS	0x157c
+#define PCI_DEVICE_ID_INTEL_I225_UNPROGRAMMED		0x15dF
+#define PCI_DEVICE_ID_INTEL_I225_IT			0x0d9f
 #define PCI_DEVICE_ID_INTEL_80960_RP	0x1960
 #define PCI_DEVICE_ID_INTEL_82840_HB	0x1a21
 #define PCI_DEVICE_ID_INTEL_82845_HB	0x1a30
diff --git a/include/phy.h b/include/phy.h
index ae23814..90b7e36 100644
--- a/include/phy.h
+++ b/include/phy.h
@@ -184,6 +184,15 @@
 int phy_reset(struct phy_device *phydev);
 
 /**
+ * phy_gpio_reset() - Resets the specified PHY using GPIO reset
+ * Toggles the optional PHY reset GPIO
+ *
+ * @dev:	PHY udevice to reset
+ * @return: 0 if OK, -ve on error
+ */
+int phy_gpio_reset(struct udevice *dev);
+
+/**
  * phy_find_by_mask() - Searches for a PHY on the specified MDIO bus
  * The function checks the PHY addresses flagged in phy_mask and returns a
  * phy_device pointer if it detects a PHY.
diff --git a/net/mdio-uclass.c b/net/mdio-uclass.c
index 6fc7034..0ebfb2f 100644
--- a/net/mdio-uclass.c
+++ b/net/mdio-uclass.c
@@ -6,6 +6,8 @@
 
 #include <common.h>
 #include <dm.h>
+#include <dm/lists.h>
+#include <eth_phy.h>
 #include <log.h>
 #include <malloc.h>
 #include <miiphy.h>
@@ -121,6 +123,42 @@
 	return dm_mdio_reset(mii_bus->priv);
 }
 
+static int mdio_bind_phy_nodes(struct udevice *mdio_dev)
+{
+	ofnode mdio_node, phy_node;
+	struct udevice *phy_dev;
+	const char *node_name;
+	int ret;
+
+	mdio_node = dev_ofnode(mdio_dev);
+	if (!ofnode_valid(mdio_node)) {
+		dev_dbg(mdio_dev, "invalid ofnode for mdio_dev\n");
+		return -ENXIO;
+	}
+
+	ofnode_for_each_subnode(phy_node, mdio_node) {
+		node_name = ofnode_get_name(phy_node);
+		dev_dbg(mdio_dev, "* Found child node: '%s'\n", node_name);
+		ret = device_bind_driver_to_node(mdio_dev,
+						 "eth_phy_generic_drv",
+						 node_name, phy_node, &phy_dev);
+		if (ret) {
+			dev_dbg(mdio_dev, "  - Eth phy binding error: %d\n", ret);
+			continue;
+		}
+
+		dev_dbg(mdio_dev, "  - bound phy device: '%s'\n", node_name);
+		ret = device_probe(phy_dev);
+		if (ret) {
+			dev_dbg(mdio_dev, "Device '%s' probe failed\n", phy_dev->name);
+			device_unbind(phy_dev);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
 static int dm_mdio_post_probe(struct udevice *dev)
 {
 	struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
@@ -154,6 +192,9 @@
 		}
 	}
 
+	if (CONFIG_IS_ENABLED(DM_ETH_PHY))
+		mdio_bind_phy_nodes(dev);
+
 	return mdio_register(pdata->mii_bus);
 }