Merge a patch series to improve dc2114x support

This patch series by Hanyuan Zhao <hanyuan-z@qq.com> provides a number of
improvements to the dc2114x driver.

Link: https://lore.kernel.org/r/tencent_BD4B002FC63A5F77969D9BD1FFF125371C08@qq.com
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 6fd037b..576cd2d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -525,11 +525,11 @@
 	  The Microchip KS8851 parallel bus external ethernet interface chip.
 
 config KSZ9477
-	bool "Microchip KSZ9477 I2C controller driver"
-	depends on DM_DSA && DM_I2C
+	bool "Microchip KSZ9477 controller driver"
+	depends on DM_DSA && (DM_I2C || DM_SPI)
 	help
 	  This driver implements a DSA switch driver for the KSZ9477 family
-	  of GbE switches using the I2C interface.
+	  of GbE switches using the I2C or SPI interface.
 
 config LITEETH
 	bool "LiteX LiteEth Ethernet MAC"
diff --git a/drivers/net/dc2114x.c b/drivers/net/dc2114x.c
index e1edda8..7c0665f 100644
--- a/drivers/net/dc2114x.c
+++ b/drivers/net/dc2114x.c
@@ -522,7 +522,7 @@
 	struct dc2114x_priv *priv = dev_get_priv(dev);
 	int rval;
 
-	if(!priv->enetaddr) {
+	if (!priv->enetaddr) {
 		rval = eth_env_get_enetaddr("ethaddr", priv->enetaddr);
 
 		if (!rval) {
@@ -653,7 +653,7 @@
 	struct dc2114x_priv *priv = dev_get_priv(dev);
 
 	plat->iobase = (phys_addr_t)map_physmem((phys_addr_t)devfdt_get_addr(dev), 0, MAP_NOCACHE);
-	priv->iobase = (void*)plat->iobase;
+	priv->iobase = (void *)plat->iobase;
 
 	return 0;
 }
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index d18a8d5..f64dbb7 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -678,7 +678,7 @@
 	status = le16_to_cpu(desc->status);
 
 	if (!(status & RFD_STATUS_C))
-		return 0;
+		return -EAGAIN;
 
 	/* Valid frame status. */
 	if (status & RFD_STATUS_OK) {
diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c
index 43baa69..7ebbe19 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -11,7 +11,12 @@
 #include <eth_phy.h>
 #include <linux/delay.h>
 #include <miiphy.h>
-#include <i2c.h>
+#if CONFIG_IS_ENABLED(DM_I2C)
+# include <i2c.h>
+#endif
+#if CONFIG_IS_ENABLED(DM_SPI)
+# include <spi.h>
+#endif
 #include <net/dsa.h>
 
 #include <asm-generic/gpio.h>
@@ -71,15 +76,157 @@
 #define MMD_SETUP(mode, dev)		(((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev))
 #define REG_PORT_PHY_MMD_INDEX_DATA	0x011C
 
+/* SPI specific define (opcodes) */
+#define KSZ_SPI_OP_RD			3
+#define KSZ_SPI_OP_WR			2
+
+#define KSZ9477_SPI_ADDR_SHIFT		24
+#define KSZ9477_SPI_ADDR_ALIGN		3
+#define KSZ9477_SPI_TURNAROUND_SHIFT	5
+
+/**
+ * struct ksz_phy_ops - low-level KSZ bus operations
+ */
+struct ksz_phy_ops {
+	/* read() - Read bytes from the device
+	 *
+	 * @udev: bus device
+	 * @reg:  register offset
+	 * @val:  data read
+	 * @len:  Number of bytes to read
+	 *
+	 * @return: 0 on success, negative on failure
+	 */
+	int (*read)(struct udevice *udev, u32 reg, u8 *val, int len);
+
+	/* write() - Write bytes to the device
+	 *
+	 * @udev: bus device
+	 * @reg:  register offset
+	 * @val:  data to write
+	 * @len:  Number of bytes to write
+	 *
+	 * @return: 0 on success, negative on failure
+	 */
+	int (*write)(struct udevice *udev, u32 reg, u8 *val, int len);
+};
+
 struct ksz_dsa_priv {
 	struct udevice *dev;
+	struct ksz_phy_ops *phy_ops;
 
 	u32 features;			/* chip specific features */
 };
+
+#if CONFIG_IS_ENABLED(DM_I2C)
+static inline int ksz_i2c_read(struct udevice *dev, u32 reg, u8 *val, int len)
+{
+	return dm_i2c_read(dev, reg, val, len);
+}
+
+static inline int ksz_i2c_write(struct udevice *dev, u32 reg, u8 *val, int len)
+{
+	return dm_i2c_write(dev, reg, val, len);
+}
+
+static struct ksz_phy_ops phy_i2c_ops = {
+	.read = ksz_i2c_read,
+	.write = ksz_i2c_write,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(DM_SPI)
+/**
+ * ksz_spi_xfer() - only used for 8/16/32 bits bus access
+ *
+ * @dev:	The SPI slave device which will be sending/receiving the data.
+ * @reg:	register address.
+ * @out:	Pointer to a string of bits to send out.  The bits are
+ *		held in a byte array and are sent MSB first.
+ * @in:		Pointer to a string of bits that will be filled in.
+ * @len:	number of bytes to read.
+ *
+ * Return: 0 on success, not 0 on failure
+ */
+static int ksz_spi_xfer(struct udevice *dev, u32 reg, const u8 *out,
+			u8 *in, u16 len)
+{
+	int ret;
+	u32 addr = 0;
+	u8 opcode;
+
+	if (in && out) {
+		printf("%s: can't do full duplex\n", __func__);
+		return -EINVAL;
+	}
+
+	if (len > 4 || len == 0) {
+		printf("%s: only 8/16/32 bits bus access supported\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	ret = dm_spi_claim_bus(dev);
+	if (ret < 0) {
+		printf("%s: could not claim bus\n", __func__);
+		return ret;
+	}
+
+	opcode = (in ? KSZ_SPI_OP_RD : KSZ_SPI_OP_WR);
+
+	/* The actual device address space is 16 bits (A15 - A0),
+	 * so the values of address bits A23 - A16 in the SPI
+	 * command/address phase are “don't care”.
+	 */
+	addr |= opcode << (KSZ9477_SPI_ADDR_SHIFT + KSZ9477_SPI_TURNAROUND_SHIFT);
+	addr |= reg << KSZ9477_SPI_TURNAROUND_SHIFT;
+
+	addr = __swab32(addr);
+
+	ret = dm_spi_xfer(dev, 32, &addr, NULL, SPI_XFER_BEGIN);
+	if (ret) {
+		printf("%s ERROR: dm_spi_xfer addr (%u)\n", __func__, ret);
+		goto release_bus;
+	}
+
+	ret = dm_spi_xfer(dev, len * 8, out, in, SPI_XFER_END);
+	if (ret) {
+		printf("%s ERROR: dm_spi_xfer data (%u)\n", __func__, ret);
+		goto release_bus;
+	}
+
+release_bus:
+	/* If an error occurred, release the chip by deasserting the CS */
+	if (ret < 0)
+		dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
+
+	dm_spi_release_bus(dev);
+
+	return ret;
+}
+
+static inline int ksz_spi_read(struct udevice *dev, u32 reg, u8 *val, int len)
+{
+	return ksz_spi_xfer(dev, reg, NULL, val, len);
+}
+
+static inline int ksz_spi_write(struct udevice *dev, u32 reg, u8 *val, int len)
+{
+	return ksz_spi_xfer(dev, reg, val, NULL, len);
+}
+
+static struct ksz_phy_ops phy_spi_ops = {
+	.read = ksz_spi_read,
+	.write = ksz_spi_write,
+};
+#endif
 
 static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val)
 {
-	int ret = dm_i2c_read(dev, reg, val, 1);
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	struct ksz_phy_ops *phy_ops = priv->phy_ops;
+
+	int ret = phy_ops->read(dev, reg, val, 1);
 
 	dev_dbg(dev, "%s 0x%04x<<0x%02x\n", __func__, reg, *val);
 
@@ -93,8 +240,11 @@
 
 static inline int ksz_write8(struct udevice *dev, u32 reg, u8 val)
 {
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	struct ksz_phy_ops *phy_ops = priv->phy_ops;
+
 	dev_dbg(dev, "%s 0x%04x>>0x%02x\n", __func__, reg, val);
-	return dm_i2c_write(dev, reg, &val, 1);
+	return phy_ops->write(dev, reg, &val, 1);
 }
 
 static inline int ksz_pwrite8(struct udevice *dev, int port, int reg, u8 val)
@@ -104,13 +254,15 @@
 
 static inline int ksz_write16(struct udevice *dev, u32 reg, u16 val)
 {
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	struct ksz_phy_ops *phy_ops = priv->phy_ops;
 	u8 buf[2];
 
 	buf[1] = val & 0xff;
 	buf[0] = val >> 8;
 	dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val);
 
-	return dm_i2c_write(dev, reg, buf, 2);
+	return phy_ops->write(dev, reg, buf, 2);
 }
 
 static inline int ksz_pwrite16(struct udevice *dev, int port, int reg, u16 val)
@@ -120,10 +272,12 @@
 
 static inline int ksz_read16(struct udevice *dev, u32 reg, u16 *val)
 {
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	struct ksz_phy_ops *phy_ops = priv->phy_ops;
 	u8 buf[2];
 	int ret;
 
-	ret = dm_i2c_read(dev, reg, buf, 2);
+	ret = phy_ops->read(dev, reg, buf, 2);
 	*val = (buf[0] << 8) | buf[1];
 	dev_dbg(dev, "%s 0x%04x<<0x%04x\n", __func__, reg, *val);
 
@@ -137,7 +291,10 @@
 
 static inline int ksz_read32(struct udevice *dev, u32 reg, u32 *val)
 {
-	return dm_i2c_read(dev, reg, (u8 *)val, 4);
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	struct ksz_phy_ops *phy_ops = priv->phy_ops;
+
+	return phy_ops->read(dev, reg, (u8 *)val, 4);
 }
 
 static inline int ksz_pread32(struct udevice *dev, int port, int reg, u32 *val)
@@ -147,6 +304,8 @@
 
 static inline int ksz_write32(struct udevice *dev, u32 reg, u32 val)
 {
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	struct ksz_phy_ops *phy_ops = priv->phy_ops;
 	u8 buf[4];
 
 	buf[3] = val & 0xff;
@@ -155,7 +314,7 @@
 	buf[0] = (val >> 8) & 0xff;
 	dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val);
 
-	return dm_i2c_write(dev, reg, buf, 4);
+	return phy_ops->write(dev, reg, buf, 4);
 }
 
 static inline int ksz_pwrite32(struct udevice *dev, int port, int reg, u32 val)
@@ -276,7 +435,7 @@
 	struct ksz_mdio_priv *priv = dev_get_priv(dev);
 
 	dev_dbg(dev, "%s\n", __func__);
-	priv->ksz = dev_get_parent_priv(dev->parent);
+	priv->ksz = dev_get_priv(dev->parent);
 
 	return 0;
 }
@@ -355,12 +514,12 @@
 			  phy_interface_t interface)
 {
 	struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
 	u8 data8;
 
 	dev_dbg(dev, "%s P%d %s\n", __func__, port + 1,
 		(port == pdata->cpu_port) ? "cpu" : "");
 
-	struct ksz_dsa_priv *priv = dev_get_priv(dev);
 	if (port != pdata->cpu_port) {
 		if (priv->features & NEW_XMII)
 			/* phy port: config errata and leds */
@@ -503,25 +662,61 @@
 	return 0;
 }
 
-/*
- * I2C driver
- */
-static int ksz_i2c_probe(struct udevice *dev)
+static void ksz_ops_register(struct udevice *dev, struct ksz_phy_ops *ops)
+{
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+
+	priv->phy_ops = ops;
+}
+
+static bool dsa_ksz_check_ops(struct ksz_phy_ops *phy_ops)
+{
+	if (!phy_ops || !phy_ops->read || !phy_ops->write)
+		return false;
+
+	return true;
+}
+
+static int ksz_probe(struct udevice *dev)
 {
 	struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
 	struct ksz_dsa_priv *priv = dev_get_priv(dev);
+	enum uclass_id parent_id = UCLASS_INVALID;
 	int i, ret;
 	u8 data8;
 	u32 id;
 
-	dev_set_parent_priv(dev, priv);
+	parent_id = device_get_uclass_id(dev_get_parent(dev));
+	switch (parent_id) {
+#if CONFIG_IS_ENABLED(DM_I2C)
+	case UCLASS_I2C: {
+		ksz_ops_register(dev, &phy_i2c_ops);
 
-	ret = i2c_set_chip_offset_len(dev, 2);
-	if (ret) {
-		printf("i2c_set_chip_offset_len failed: %d\n", ret);
-		return ret;
+		ret = i2c_set_chip_offset_len(dev, 2);
+		if (ret) {
+			printf("i2c_set_chip_offset_len failed: %d\n", ret);
+			return ret;
+		}
+		break;
+	}
+#endif
+#if CONFIG_IS_ENABLED(DM_SPI)
+	case UCLASS_SPI: {
+		ksz_ops_register(dev, &phy_spi_ops);
+		break;
+	}
+#endif
+	default:
+		dev_err(dev, "invalid parent bus (%s)\n",
+			uclass_get_name(parent_id));
+		return -EINVAL;
 	}
 
+	if (!dsa_ksz_check_ops(priv->phy_ops)) {
+		printf("Driver bug. No bus ops defined\n");
+		return -EINVAL;
+	}
+
 	/* default config */
 	priv->dev = dev;
 
@@ -543,6 +738,9 @@
 	case 0x00956700:
 		puts("KSZ9567R: ");
 		break;
+	case 0x00989600:
+		puts("KSZ9896C: ");
+		break;
 	case 0x00989700:
 		puts("KSZ9897S: ");
 		break;
@@ -573,19 +771,20 @@
 	return 0;
 };
 
-static const struct udevice_id ksz_i2c_ids[] = {
+static const struct udevice_id ksz_ids[] = {
 	{ .compatible = "microchip,ksz9897" },
 	{ .compatible = "microchip,ksz9477" },
 	{ .compatible = "microchip,ksz9567" },
 	{ .compatible = "microchip,ksz9893" },
+	{ .compatible = "microchip,ksz9896" },
 	{ }
 };
 
 U_BOOT_DRIVER(ksz) = {
 	.name		= "ksz-switch",
 	.id		= UCLASS_DSA,
-	.of_match	= ksz_i2c_ids,
-	.probe		= ksz_i2c_probe,
+	.of_match	= ksz_ids,
+	.probe		= ksz_probe,
 	.ops		= &ksz_dsa_ops,
 	.priv_auto	= sizeof(struct ksz_dsa_priv),
 };
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index a96430c..4d67203 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -12,6 +12,7 @@
 
 #define PHY_ID_YT8511				0x0000010a
 #define PHY_ID_YT8531				0x4f51e91b
+#define PHY_ID_YT8821				0x4f51ea19
 #define PHY_ID_MASK				GENMASK(31, 0)
 
 /* Extended Register's Address Offset Register */
@@ -102,8 +103,12 @@
 #define YTPHY_SPECIFIC_STATUS_REG		0x11
 #define YTPHY_DUPLEX_MASK			BIT(13)
 #define YTPHY_DUPLEX_SHIFT			13
-#define YTPHY_SPEED_MODE_MASK			GENMASK(15, 14)
-#define YTPHY_SPEED_MODE_SHIFT			14
+#define YTPHY_SPEED_MASK			((0x3 << 14) | BIT(9))
+#define YTPHY_SPEED_10M				((0x0 << 14))
+#define YTPHY_SPEED_100M			((0x1 << 14))
+#define YTPHY_SPEED_1000M			((0x2 << 14))
+#define YTPHY_SPEED_10G				((0x3 << 14))
+#define YTPHY_SPEED_2500M			((0x0 << 14) | BIT(9))
 
 #define YT8531_EXTREG_SLEEP_CONTROL1_REG	0x27
 #define YT8531_ESC1R_SLEEP_SW			BIT(15)
@@ -131,6 +136,91 @@
 #define TX_CLK_100_INVERTED			BIT(4)
 #define TX_CLK_1000_INVERTED			BIT(5)
 
+#define YT8821_SDS_EXT_CSR_CTRL_REG		0x23
+#define YT8821_SDS_EXT_CSR_VCO_LDO_EN		BIT(15)
+#define YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN	BIT(8)
+
+#define YT8821_UTP_EXT_PI_CTRL_REG		0x56
+#define YT8821_UTP_EXT_PI_RST_N_FIFO		BIT(5)
+#define YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE	BIT(4)
+#define YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE	BIT(3)
+#define YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE	BIT(2)
+#define YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE	BIT(1)
+#define YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE	BIT(0)
+
+#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG	0x97
+#define YT8821_UTP_EXT_FECHO_AMP_TH_HUGE	GENMASK(15, 8)
+
+#define YT8821_UTP_EXT_ECHO_CTRL_REG		0x336
+#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000	GENMASK(14, 8)
+
+#define YT8821_UTP_EXT_GAIN_CTRL_REG		0x340
+#define YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_RPDN_CTRL_REG		0x34E
+#define YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500	BIT(15)
+#define YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500	BIT(7)
+#define YT8821_UTP_EXT_RPDN_IPR_SHT_2500	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG	0x36A
+#define YT8821_UTP_EXT_TH_20DB_2500		GENMASK(15, 0)
+
+#define YT8821_UTP_EXT_TRACE_CTRL_REG		0x372
+#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500	GENMASK(14, 8)
+#define YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG	0x374
+#define YT8821_UTP_EXT_ALPHA_SHT_2500		GENMASK(14, 8)
+#define YT8821_UTP_EXT_IPR_LNG_2500		GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_PLL_CTRL_REG		0x450
+#define YT8821_UTP_EXT_PLL_SPARE_CFG		GENMASK(7, 0)
+
+#define YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG	0x466
+#define YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG	GENMASK(14, 8)
+#define YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG	0x467
+#define YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG	GENMASK(14, 8)
+#define YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG	0x468
+#define YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG	GENMASK(14, 8)
+#define YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG	0x469
+#define YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG	GENMASK(14, 8)
+#define YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG	GENMASK(6, 0)
+
+#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG	0x4B3
+#define YT8821_UTP_EXT_MU_COARSE_FR_F_FFE	GENMASK(14, 12)
+#define YT8821_UTP_EXT_MU_COARSE_FR_F_FBE	GENMASK(10, 8)
+
+#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG	0x4B5
+#define YT8821_UTP_EXT_MU_FINE_FR_F_FFE		GENMASK(14, 12)
+#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE		GENMASK(10, 8)
+
+#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG	0x4D2
+#define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER	GENMASK(7, 4)
+#define YT8821_UTP_EXT_VGA_LPF1_CAP_2500	GENMASK(3, 0)
+
+#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG	0x4D3
+#define YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER	GENMASK(7, 4)
+#define YT8821_UTP_EXT_VGA_LPF2_CAP_2500	GENMASK(3, 0)
+
+#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG	0x660
+#define YT8821_UTP_EXT_NFR_TX_ABILITY		BIT(3)
+
+#define YT8821_CHIP_MODE_FORCE_BX2500		1
+
+/* chip config register */
+#define YTPHY_CCR_MODE_SEL_MASK			GENMASK(2, 0)
+
+#define YTPHY_REG_SPACE_SELECT_REG		0xA000
+#define YTPHY_RSSR_SPACE_MASK			BIT(1)
+#define YTPHY_RSSR_FIBER_SPACE			(0x1 << 1)
+#define YTPHY_RSSR_UTP_SPACE			(0x0 << 1)
+
 struct ytphy_plat_priv {
 	u32 rx_delay_ps;
 	u32 tx_delay_ps;
@@ -295,15 +385,15 @@
 	if (val < 0)
 		return val;
 
-	speed_mode = (val & YTPHY_SPEED_MODE_MASK) >> YTPHY_SPEED_MODE_SHIFT;
+	speed_mode = (val & YTPHY_SPEED_MASK);
 	switch (speed_mode) {
-	case 2:
+	case YTPHY_SPEED_1000M:
 		speed = SPEED_1000;
 		break;
-	case 1:
+	case YTPHY_SPEED_100M:
 		speed = SPEED_100;
 		break;
-	default:
+	case YTPHY_SPEED_10M:
 		speed = SPEED_10;
 		break;
 	}
@@ -632,6 +722,398 @@
 	return 0;
 }
 
+static int ytphy_save_page(struct phy_device *phydev)
+{
+	int old_page;
+
+	old_page = ytphy_read_ext(phydev, YTPHY_REG_SPACE_SELECT_REG);
+	if (old_page < 0)
+		return old_page;
+
+	if ((old_page & YTPHY_RSSR_SPACE_MASK) == YTPHY_RSSR_FIBER_SPACE)
+		return YTPHY_RSSR_FIBER_SPACE;
+
+	return YTPHY_RSSR_UTP_SPACE;
+};
+
+static int ytphy_restore_page(struct phy_device *phydev, int page,
+			      int ret)
+{
+	int mask = YTPHY_RSSR_SPACE_MASK;
+	int set;
+	int r;
+
+	if ((page & YTPHY_RSSR_SPACE_MASK) == YTPHY_RSSR_FIBER_SPACE)
+		set = YTPHY_RSSR_FIBER_SPACE;
+	else
+		set = YTPHY_RSSR_UTP_SPACE;
+
+	r = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG, mask,
+			     set);
+	if (ret >= 0 && r < 0)
+		ret = r;
+
+	return ret;
+};
+
+static int ytphy_write_ext(struct phy_device *phydev, u16 regnum,
+			   u16 val)
+{
+	int ret;
+
+	ret = phy_write(phydev, MDIO_DEVAD_NONE,
+			YTPHY_PAGE_SELECT, regnum);
+	if (ret < 0)
+		return ret;
+
+	return phy_write(phydev, MDIO_DEVAD_NONE, YTPHY_PAGE_DATA, val);
+}
+
+static int yt8821_probe(struct phy_device *phydev)
+{
+	phydev->advertising = PHY_GBIT_FEATURES |
+				SUPPORTED_2500baseX_Full |
+				SUPPORTED_Pause |
+				SUPPORTED_Asym_Pause;
+	phydev->supported = phydev->advertising;
+
+	return 0;
+}
+
+static int yt8821_serdes_init(struct phy_device *phydev)
+{
+	int old_page;
+	u16 mask;
+	u16 set;
+	int ret;
+
+	old_page = ytphy_save_page(phydev);
+	if (old_page < 0)
+		return old_page;
+
+	ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG,
+			       YTPHY_RSSR_SPACE_MASK,
+			       YTPHY_RSSR_FIBER_SPACE);
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret = phy_modify(phydev, MDIO_DEVAD_NONE, MII_BMCR,
+			 BMCR_ANENABLE, 0);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_SDS_EXT_CSR_VCO_LDO_EN |
+		YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN;
+	set = YT8821_SDS_EXT_CSR_VCO_LDO_EN;
+	ret = ytphy_modify_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, mask,
+			       set);
+
+err_restore_page:
+	return ytphy_restore_page(phydev, old_page, ret);
+}
+
+static int yt8821_utp_init(struct phy_device *phydev)
+{
+	int old_page;
+	u16 mask;
+	u16 save;
+	u16 set;
+	int ret;
+
+	old_page = ytphy_save_page(phydev);
+	if (old_page < 0)
+		return old_page;
+
+	ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG,
+			       YTPHY_RSSR_SPACE_MASK,
+			       YTPHY_RSSR_UTP_SPACE);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 |
+		YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 |
+		YT8821_UTP_EXT_RPDN_IPR_SHT_2500;
+	set = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 |
+		YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500;
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_RPDN_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER |
+		YT8821_UTP_EXT_VGA_LPF1_CAP_2500;
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG,
+			       mask, 0);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER |
+		YT8821_UTP_EXT_VGA_LPF2_CAP_2500;
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG,
+			       mask, 0);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 |
+		YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500;
+	set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500, 0x5a) |
+		FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500, 0x3c);
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_IPR_LNG_2500;
+	set = FIELD_PREP(YT8821_UTP_EXT_IPR_LNG_2500, 0x6c);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000;
+	set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000, 0x2a);
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000;
+	set = FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000, 0x22);
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_TH_20DB_2500;
+	set = FIELD_PREP(YT8821_UTP_EXT_TH_20DB_2500, 0x8000);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_MU_COARSE_FR_F_FFE |
+		YT8821_UTP_EXT_MU_COARSE_FR_F_FBE;
+	set = FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FFE, 0x7) |
+		FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FBE, 0x7);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_MU_FINE_FR_F_FFE |
+		YT8821_UTP_EXT_MU_FINE_FR_F_FBE;
+	set = FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FFE, 0x2) |
+		FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FBE, 0x2);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	/* save YT8821_UTP_EXT_PI_CTRL_REG's val for use later */
+	ret = ytphy_read_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG);
+	if (ret < 0)
+		goto err_restore_page;
+
+	save = ret;
+
+	mask = YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE |
+		YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE |
+		YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE |
+		YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE |
+		YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE;
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG,
+			       mask, 0);
+	if (ret < 0)
+		goto err_restore_page;
+
+	/* restore YT8821_UTP_EXT_PI_CTRL_REG's val */
+	ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, save);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_FECHO_AMP_TH_HUGE;
+	set = FIELD_PREP(YT8821_UTP_EXT_FECHO_AMP_TH_HUGE, 0x38);
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_VCT_CFG6_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_NFR_TX_ABILITY;
+	set = YT8821_UTP_EXT_NFR_TX_ABILITY;
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_PLL_SPARE_CFG;
+	set = FIELD_PREP(YT8821_UTP_EXT_PLL_SPARE_CFG, 0xe9);
+	ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG |
+		YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG;
+	set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG, 0x64) |
+		FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG, 0x64);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG |
+		YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG;
+	set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG, 0x64) |
+		FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG, 0x64);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG |
+		YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG;
+	set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG, 0x64) |
+		FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG, 0x64);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG,
+			       mask, set);
+	if (ret < 0)
+		goto err_restore_page;
+
+	mask = YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG |
+		YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG;
+	set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG, 0x64) |
+		FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG, 0x64);
+	ret = ytphy_modify_ext(phydev,
+			       YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG,
+			       mask, set);
+
+err_restore_page:
+	return ytphy_restore_page(phydev, old_page, ret);
+}
+
+static int yt8821_auto_sleep_config(struct phy_device *phydev,
+				    bool enable)
+{
+	int old_page;
+	int ret;
+
+	old_page = ytphy_save_page(phydev);
+	if (old_page < 0)
+		return old_page;
+
+	ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG,
+			       YTPHY_RSSR_SPACE_MASK,
+			       YTPHY_RSSR_UTP_SPACE);
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret = ytphy_modify_ext(phydev,
+			       YT8531_EXTREG_SLEEP_CONTROL1_REG,
+			       YT8531_ESC1R_SLEEP_SW,
+			       enable ? 1 : 0);
+
+err_restore_page:
+	return ytphy_restore_page(phydev, old_page, ret);
+}
+
+static int yt8821_soft_reset(struct phy_device *phydev)
+{
+	return ytphy_modify_ext(phydev, YT8531_CHIP_CONFIG_REG,
+				YT8531_CCR_SW_RST, 0);
+}
+
+static int yt8821_config(struct phy_device *phydev)
+{
+	u8 mode = YT8821_CHIP_MODE_FORCE_BX2500;
+	int ret;
+	u16 set;
+
+	set = FIELD_PREP(YTPHY_CCR_MODE_SEL_MASK, mode);
+	ret = ytphy_modify_ext(phydev,
+			       YT8531_CHIP_CONFIG_REG,
+			       YTPHY_CCR_MODE_SEL_MASK,
+			       set);
+	if (ret < 0)
+		return ret;
+
+	ret = yt8821_serdes_init(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = yt8821_utp_init(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = yt8821_auto_sleep_config(phydev, false);
+	if (ret < 0)
+		return ret;
+
+	return yt8821_soft_reset(phydev);
+}
+
+static void yt8821_parse_status(struct phy_device *phydev, int val)
+{
+	int speed_mode;
+	int speed;
+
+	speed_mode = val & YTPHY_SPEED_MASK;
+	switch (speed_mode) {
+	case YTPHY_SPEED_2500M:
+		speed = SPEED_2500;
+		break;
+	case YTPHY_SPEED_1000M:
+		speed = SPEED_1000;
+		break;
+	case YTPHY_SPEED_100M:
+		speed = SPEED_100;
+		break;
+	case YTPHY_SPEED_10M:
+		speed = SPEED_10;
+		break;
+	}
+
+	phydev->speed = speed;
+	phydev->duplex = FIELD_GET(YTPHY_DUPLEX_MASK, val);
+}
+
+static int yt8821_startup(struct phy_device *phydev)
+{
+	u16 val;
+	int ret;
+
+	ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG,
+			       YTPHY_RSSR_SPACE_MASK,
+			       YTPHY_RSSR_UTP_SPACE);
+	if (ret)
+		return ret;
+
+	ret = genphy_update_link(phydev);
+	if (ret)
+		return ret;
+
+	ret = phy_read(phydev, MDIO_DEVAD_NONE,
+		       YTPHY_SPECIFIC_STATUS_REG);
+	if (ret < 0)
+		return ret;
+
+	val = ret;
+
+	if (phydev->link)
+		yt8821_parse_status(phydev, val);
+
+	return 0;
+}
+
 U_BOOT_PHY_DRIVER(motorcomm8511) = {
 	.name          = "YT8511 Gigabit Ethernet",
 	.uid           = PHY_ID_YT8511,
@@ -652,3 +1134,14 @@
 	.startup       = &yt8531_startup,
 	.shutdown      = &genphy_shutdown,
 };
+
+U_BOOT_PHY_DRIVER(motorcomm8821) = {
+	.name		= "YT8821 2.5G Ethernet",
+	.uid		= PHY_ID_YT8821,
+	.mask		= PHY_ID_MASK,
+	.mmds		= (MDIO_MMD_PMAPMD | MDIO_MMD_PCS | MDIO_MMD_AN),
+	.probe		= &yt8821_probe,
+	.config		= &yt8821_config,
+	.startup	= &yt8821_startup,
+	.shutdown	= &genphy_shutdown,
+};
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
index 2e0afad..5f4b1e2 100644
--- a/drivers/net/rtl8139.c
+++ b/drivers/net/rtl8139.c
@@ -433,7 +433,7 @@
 	int length = 0;
 
 	if (inb(priv->ioaddr + RTL_REG_CHIPCMD) & RTL_REG_CHIPCMD_RXBUFEMPTY)
-		return 0;
+		return -EAGAIN;
 
 	priv->rxstatus = inw(priv->ioaddr + RTL_REG_INTRSTATUS);
 	/* See below for the rest of the interrupt acknowledges.  */