[][kernel][common][eth][Update Airoha EN8811H 2.5G PHY driver to v1.2.2]

[Description]
Change Airoha EN8811H 2.5G PHY driver to v1.2.2.

Change Log:
[2023/09/01] v1.2.2
1.Add DEBUGFS for debugging
2.Modified for Linux coding style.

[2023/07/18] v1.2.1
* MD32 EN8811_0717_05 updated:
1.Fix the system hang issue (need power down/up to link up)
2.Fix Pause frame issue during system initialization
3.Improve linkup time and IoT performance

[2023/06/08] v1.2.0
* MD32 EN8811_0608_01 updated:
1.Improved IoT performance and stability

[2023/06/06] v1.1.9
* MD32 EN8811_0606_01 updated:
1.Improved IoT performance and stability

[2023/06/02] v1.1.8
* MD32 EN8811_0531_01 updated:
1.Improved IoT performance and stability
2.Keep LED settings after power down
3.Fixed MT7986 integration issues

[2023/05/15] v1.1.7
* MD32 EN8811_0512_09 updated:
1.Improved IoT performance and stability

[2023/04/15] v1.1.6
* MD32 FW_8811_SOC_0411_a updated:
1.Improved IoT performance and stability

=====================================================================
If your board that GMAC2 connects with Airoha EN8811H, please change
the eth node as following.

&eth {
	...

	gmac1: mac@1 {
		compatible = "mediatek,eth-mac";
		reg = <1>;
		phy-mode = "2500base-x";
		phy-handle = <&phy15>;
	};

	mdio: mdio-bus {
      		#address-cells = <1>;
      		#size-cells = <0>;

      		phy15: phy@15 {
       			compatible = "ethernet-phy-id03a2.a411";
			reg = <15>;
			phy-mode = "2500base-x";
			full-duplex;
			pause;
		};

       		...
	};
};

=====================================================================
In addition, if EN8811H connects with a RESET GPIO, please check the
GPIO number, and then add reset-gpio related definition to above phy
node.
=====================================================================
reset-gpios = <&gpio 6 1>;
reset-assert-us = <10000>;
reset-deassert-us = <10000>;
=====================================================================

If without this patch, kernel cannot load up-to-date PHY driver
for the Airoha EN8811H.

[Release-log]
N/A


Change-Id: Ib115130d67a4c5fe4b21bddc0cd0e6ee7a48ec2e
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7957288
diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h.h b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h.h
new file mode 100644
index 0000000..6a0afaf
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*************************************************
+ * FILE NAME:  air_en8811h.h
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+#ifndef __EN8811H_H
+#define __EN8811H_H
+
+#define EN8811H_MD32_DM             "EthMD32.dm.bin"
+#define EN8811H_MD32_DSP            "EthMD32.DSP.bin"
+
+#define EN8811H_PHY_ID1             0x03a2
+#define EN8811H_PHY_ID2             0xa411
+#define EN8811H_PHY_ID              ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2)
+#define EN8811H_PHY_READY           0x02
+#define MAX_RETRY                   25
+
+#define EN8811H_TX_POL_NORMAL   0x1
+#define EN8811H_TX_POL_REVERSE  0x0
+
+#define EN8811H_RX_POL_NORMAL   (0x0 << 1)
+#define EN8811H_RX_POL_REVERSE  (0x1 << 1)
+
+
+/***************************************************************
+ * The following led_cfg example is for reference only.
+ * LED0 Link 2500/Blink 2500 TxRx   (GPIO5)    <-> BASE_T_LED0,
+ * LED1 Link 1000/Blink 1000 TxRx   (GPIO4)    <-> BASE_T_LED1,
+ * LED2 Link 100 /Blink 100  TxRx   (GPIO3)    <-> BASE_T_LED2,
+ ***************************************************************/
+/* User-defined.B */
+#define AIR_LED0_ON     (LED_ON_EVT_LINK_2500M)
+#define AIR_LED0_BLK    (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT)
+#define AIR_LED1_ON     (LED_ON_EVT_LINK_1000M)
+#define AIR_LED1_BLK    (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
+#define AIR_LED2_ON     (LED_ON_EVT_LINK_100M)
+#define AIR_LED2_BLK    (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
+/* User-defined.E */
+
+/* CL45 MDIO control */
+#define MII_MMD_ACC_CTL_REG         0x0d
+#define MII_MMD_ADDR_DATA_REG       0x0e
+#define MMD_OP_MODE_DATA            BIT(14)
+
+#define EN8811H_DRIVER_VERSION      "v1.2.2"
+
+#define LED_ON_CTRL(i)              (0x024 + ((i)*2))
+#define LED_ON_EN                   (1 << 15)
+#define LED_ON_POL                  (1 << 14)
+#define LED_ON_EVT_MASK             (0x1ff)
+/* LED ON Event Option.B */
+#define LED_ON_EVT_LINK_2500M       (1 << 8)
+#define LED_ON_EVT_FORCE            (1 << 6)
+#define LED_ON_EVT_LINK_DOWN        (1 << 3)
+#define LED_ON_EVT_LINK_100M        (1 << 1)
+#define LED_ON_EVT_LINK_1000M       (1 << 0)
+/* LED ON Event Option.E */
+
+#define LED_BLK_CTRL(i)             (0x025 + ((i)*2))
+#define LED_BLK_EVT_MASK            (0xfff)
+/* LED Blinking Event Option.B*/
+#define LED_BLK_EVT_2500M_RX_ACT    (1 << 11)
+#define LED_BLK_EVT_2500M_TX_ACT    (1 << 10)
+#define LED_BLK_EVT_FORCE           (1 << 9)
+#define LED_BLK_EVT_100M_RX_ACT     (1 << 3)
+#define LED_BLK_EVT_100M_TX_ACT     (1 << 2)
+#define LED_BLK_EVT_1000M_RX_ACT    (1 << 1)
+#define LED_BLK_EVT_1000M_TX_ACT    (1 << 0)
+/* LED Blinking Event Option.E*/
+#define EN8811H_LED_COUNT           3
+
+#define LED_BCR                     (0x021)
+#define LED_BCR_EXT_CTRL            (1 << 15)
+#define LED_BCR_CLK_EN              (1 << 3)
+#define LED_BCR_TIME_TEST           (1 << 2)
+#define LED_BCR_MODE_MASK           (3)
+#define LED_BCR_MODE_DISABLE        (0)
+
+#define LED_ON_DUR                  (0x022)
+#define LED_ON_DUR_MASK             (0xffff)
+
+#define LED_BLK_DUR                 (0x023)
+#define LED_BLK_DUR_MASK            (0xffff)
+
+#define UNIT_LED_BLINK_DURATION     1024
+
+#define GET_BIT(val, bit) ((val & BIT(bit)) >> bit)
+
+#define INVALID_DATA                0xffff
+#define PBUS_INVALID_DATA           0xffffffff
+
+struct en8811h_priv {
+	struct dentry       *debugfs_root;
+	unsigned int        dm_crc32;
+	unsigned int        dsp_crc32;
+	char                buf[512];
+};
+
+struct air_base_t_led_cfg {
+	u16 en;
+	u16 gpio;
+	u16 pol;
+	u16 on_cfg;
+	u16 blk_cfg;
+};
+enum air_led_gpio {
+	AIR_LED2_GPIO3 = 3,
+	AIR_LED1_GPIO4,
+	AIR_LED0_GPIO5,
+	AIR_LED_LAST
+};
+
+enum air_base_t_led {
+	AIR_BASE_T_LED0,
+	AIR_BASE_T_LED1,
+	AIR_BASE_T_LED2,
+	AIR_BASE_T_LED3
+};
+
+enum air_led_blk_dur {
+	AIR_LED_BLK_DUR_32M,
+	AIR_LED_BLK_DUR_64M,
+	AIR_LED_BLK_DUR_128M,
+	AIR_LED_BLK_DUR_256M,
+	AIR_LED_BLK_DUR_512M,
+	AIR_LED_BLK_DUR_1024M,
+	AIR_LED_BLK_DUR_LAST
+};
+
+enum air_led_polarity {
+	AIR_ACTIVE_LOW,
+	AIR_ACTIVE_HIGH,
+};
+enum air_led_mode {
+	AIR_LED_MODE_DISABLE,
+	AIR_LED_MODE_USER_DEFINE,
+	AIR_LED_MODE_LAST
+};
+
+#endif /* End of __EN8811H_H */
diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.c b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.c
new file mode 100644
index 0000000..731a37d
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.c
@@ -0,0 +1,1227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*************************************************
+ * FILE NAME:  air_en8811h_api.c
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+
+/* INCLUDE FILE DECLARATIONS
+*/
+#include <linux/uaccess.h>
+#include <linux/trace_seq.h>
+#include <linux/seq_file.h>
+#include <linux/u64_stats_sync.h>
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/ctype.h>
+#include <linux/of_mdio.h>
+#include <linux/of_address.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/debugfs.h>
+
+#include "air_en8811h.h"
+#include "air_en8811h_api.h"
+
+/*
+struct air_phy_debug {
+	struct dentry *root;
+};
+struct air_phy_debug air_debug;
+*/
+
+static const char * const tx_rx_string[32] = {
+	"Tx Reverse, Rx Normal",
+	"Tx Normal, Rx Normal",
+	"Tx Reverse, Rx Reverse",
+	"Tx Normal, Rx Reverse",
+};
+
+/* Airoha MII read function */
+static int __air_mii_cl22_read(struct mii_bus *ebus,
+					int addr, unsigned int phy_register)
+{
+	int read_data;
+#if (KERNEL_VERSION(4, 16, 0) < LINUX_VERSION_CODE)
+	read_data = __mdiobus_read(ebus, addr, phy_register);
+#else
+	read_data = ebus->read(ebus, addr, phy_register);
+#endif
+	return read_data;
+}
+/* Airoha MII write function */
+static int __air_mii_cl22_write(struct mii_bus *ebus, int addr,
+			unsigned int phy_register, unsigned int write_data)
+{
+	int ret = 0;
+#if (KERNEL_VERSION(4, 16, 0) < LINUX_VERSION_CODE)
+	ret = __mdiobus_write(ebus, addr, phy_register, write_data);
+#else
+	ret = ebus->write(ebus, addr, phy_register, write_data);
+#endif
+	return ret;
+}
+
+/* Airoha MII read function */
+int air_mii_cl22_read(struct mii_bus *ebus, int addr, unsigned int phy_register)
+{
+	int read_data;
+
+	mutex_lock(&ebus->mdio_lock);
+	read_data = __air_mii_cl22_read(ebus, addr, phy_register);
+	mutex_unlock(&ebus->mdio_lock);
+	return read_data;
+}
+/* Airoha MII write function */
+int air_mii_cl22_write(struct mii_bus *ebus, int addr,
+			unsigned int phy_register, unsigned int write_data)
+{
+	int ret = 0;
+
+	mutex_lock(&ebus->mdio_lock);
+	ret = __air_mii_cl22_write(ebus, addr, phy_register, write_data);
+	mutex_unlock(&ebus->mdio_lock);
+	return ret;
+}
+
+static int __air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
+{
+	int ret = 0;
+	int data;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ACC_CTL_REG, devad);
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ADDR_DATA_REG, reg);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return INVALID_DATA;
+	}
+	data = __air_mii_cl22_read(mbus, addr, MII_MMD_ADDR_DATA_REG);
+	return data;
+}
+
+static int __air_mii_cl45_write(struct phy_device *phydev,
+					int devad, u16 reg, u16 write_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ACC_CTL_REG, devad);
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ADDR_DATA_REG, reg);
+	ret |= __air_mii_cl22_write(mbus, addr,
+		MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			MII_MMD_ADDR_DATA_REG, write_data);
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
+{
+	int data;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	data = __air_mii_cl45_read(phydev, devad, reg);
+	mutex_unlock(&mbus->mdio_lock);
+	if (data == INVALID_DATA) {
+		dev_err(dev, "__airoha_cl45_read fail\n");
+		return INVALID_DATA;
+	}
+	return data;
+}
+
+int air_mii_cl45_write(struct phy_device *phydev,
+				int devad, u16 reg, u16 write_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	ret |= __air_mii_cl45_write(phydev, devad, reg, write_data);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "__airoha_cl45_write, ret: %d\n", ret);
+		return ret;
+	}
+	return ret;
+}
+
+/* EN8811H PBUS read function */
+static unsigned int __air_pbus_reg_read(struct phy_device *phydev,
+		unsigned int pbus_address)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+	struct device *dev = phydev_dev(phydev);
+	unsigned int pbus_data_low, pbus_data_high;
+	unsigned int pbus_data;
+	int ret = 0;
+
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			0x1F, (pbus_address >> 6));
+	pbus_data_low = __air_mii_cl22_read(mbus, (addr + 8),
+				((pbus_address >> 2) & 0xf));
+	pbus_data_high = __air_mii_cl22_read(mbus, (addr + 8), 0x10);
+	pbus_data = (pbus_data_high << 16) + pbus_data_low;
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return pbus_data;
+}
+
+unsigned int air_pbus_reg_read(struct phy_device *phydev,
+		unsigned int pbus_address)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	struct device *dev = phydev_dev(phydev);
+	int ret = 0;
+	unsigned int data;
+
+	mutex_lock(&mbus->mdio_lock);
+	data = __air_pbus_reg_read(phydev, pbus_address);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return data;
+}
+
+/* EN8811H PBUS write function */
+static int __air_pbus_reg_write(struct phy_device *phydev,
+		unsigned int pbus_address, unsigned long pbus_data)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+	struct device *dev = phydev_dev(phydev);
+	int ret = 0;
+
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			0x1F, (pbus_address >> 6));
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			((pbus_address >> 2) & 0xf), (pbus_data & 0xFFFF));
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			0x10, (pbus_data >> 16));
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return 0;
+}
+
+int air_pbus_reg_write(struct phy_device *phydev,
+		unsigned int pbus_address, unsigned int pbus_data)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	struct device *dev = phydev_dev(phydev);
+	int ret = 0;
+
+	mutex_lock(&mbus->mdio_lock);
+	ret |= __air_pbus_reg_write(phydev, pbus_address, pbus_data);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return 0;
+}
+/* EN8811H BUCK write function */
+static int __air_buckpbus_reg_write(struct phy_device *phydev,
+			unsigned int pbus_address, unsigned int pbus_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	/* page 4 */
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 4);
+	ret |= __air_mii_cl22_write(mbus, addr, 0x10, 0);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x11, ((pbus_address >> 16) & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x12, (pbus_address & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x13, ((pbus_data >> 16) & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr, 0x14, (pbus_data & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 0);
+	if (ret < 0) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+/* EN8811H BUCK read function */
+static unsigned int __air_buckpbus_reg_read(struct phy_device *phydev,
+			unsigned int pbus_address)
+{
+	unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	/* page 4 */
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 4);
+	ret |= __air_mii_cl22_write(mbus, addr, 0x10, 0);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x15, ((pbus_address >> 16) & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x16, (pbus_address & 0xffff));
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return PBUS_INVALID_DATA;
+	}
+
+	pbus_data_high = __air_mii_cl22_read(mbus, addr, 0x17);
+	pbus_data_low = __air_mii_cl22_read(mbus, addr, 0x18);
+	pbus_data = (pbus_data_high << 16) + pbus_data_low;
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 0);
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return pbus_data;
+}
+
+unsigned int air_buckpbus_reg_read(struct phy_device *phydev,
+			unsigned int pbus_address)
+{
+	unsigned int data;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	data = __air_buckpbus_reg_read(phydev, pbus_address);
+	mutex_unlock(&mbus->mdio_lock);
+	if (data == INVALID_DATA) {
+		dev_err(dev, "__air_buckpbus_reg_read fail\n");
+		return INVALID_DATA;
+	}
+	return data;
+}
+
+int air_buckpbus_reg_write(struct phy_device *phydev,
+		unsigned int pbus_address, unsigned int pbus_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	ret |= __air_buckpbus_reg_write(phydev, pbus_address, pbus_data);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "__air_buckpbus_reg_write, ret: %d\n", ret);
+		return ret;
+	}
+	return ret;
+}
+
+static int air_resolve_an_speed(struct phy_device *phydev)
+{
+	int lpagb = 0, advgb = 0, common_adv_gb = 0;
+	int lpa = 0, adv = 0, common_adv = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	dev_dbg(dev, "AN mode!\n");
+	dev_dbg(dev, "SPEED 1000/100!\n");
+	lpagb = air_mii_cl22_read(mbus,
+				addr, MII_STAT1000);
+	if (lpagb < 0)
+		return lpagb;
+	advgb = air_mii_cl22_read(mbus,
+				addr, MII_CTRL1000);
+	if (adv < 0)
+		return adv;
+	common_adv_gb = (lpagb & (advgb << 2));
+
+	lpa = air_mii_cl22_read(mbus, addr, MII_LPA);
+	if (lpa < 0)
+		return lpa;
+	adv = air_mii_cl22_read(mbus,
+				addr, MII_ADVERTISE);
+	if (adv < 0)
+		return adv;
+	phydev->pause = GET_BIT(adv, 10);
+	phydev->asym_pause = GET_BIT(adv, 11);
+	common_adv = (lpa & adv);
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_HALF;
+	if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) {
+		phydev->speed = SPEED_1000;
+		if (common_adv_gb & LPA_1000FULL)
+			phydev->duplex = DUPLEX_FULL;
+	} else if (common_adv & (LPA_100FULL | LPA_100HALF)) {
+		phydev->speed = SPEED_100;
+		if (common_adv & LPA_100FULL)
+			phydev->duplex = DUPLEX_FULL;
+	} else {
+		if (common_adv & LPA_10FULL)
+			phydev->duplex = DUPLEX_FULL;
+	}
+	return 0;
+}
+
+int air_get_autonego(struct phy_device *phydev, int *an)
+{
+	int reg;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	reg = air_mii_cl22_read(mbus, addr, MII_BMCR);
+	if (reg < 0)
+		return -EINVAL;
+	if (reg & BMCR_ANENABLE)
+		*an = AUTONEG_ENABLE;
+	else
+		*an = AUTONEG_DISABLE;
+	return 0;
+}
+
+static int air_read_status(struct phy_device *phydev)
+{
+	int ret = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0;
+	u32 pbus_value = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+	phydev->link = 0;
+	phydev->autoneg = AUTONEG_DISABLE;
+	reg = air_mii_cl22_read(mbus, addr, MII_BMSR);
+	if (reg < 0) {
+		dev_err(dev, "MII_BMSR reg %d!\n", reg);
+		return reg;
+	}
+	reg = air_mii_cl22_read(mbus, addr, MII_BMSR);
+	if (reg < 0) {
+		dev_err(dev, "MII_BMSR reg %d!\n", reg);
+		return reg;
+	}
+	if (reg & BMSR_LSTATUS) {
+		phydev->link = 1;
+		ret = air_get_autonego(phydev, &an);
+		if (ret < 0)
+			return ret;
+		phydev->autoneg = an;
+		pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
+		if (0x10 & pbus_value) {
+			phydev->speed = SPEED_2500;
+			phydev->duplex = DUPLEX_FULL;
+		} else {
+			ret = air_get_autonego(phydev, &an);
+			if (phydev->autoneg == AUTONEG_ENABLE) {
+				ret = air_resolve_an_speed(phydev);
+				if (ret < 0)
+					return ret;
+			} else {
+				dev_dbg(dev, "Force mode!\n");
+				bmcr = air_mii_cl22_read(mbus, addr, MII_BMCR);
+
+				if (bmcr < 0)
+					return bmcr;
+
+				if (bmcr & BMCR_FULLDPLX)
+					phydev->duplex = DUPLEX_FULL;
+				else
+					phydev->duplex = DUPLEX_HALF;
+
+				if (bmcr & BMCR_SPEED1000)
+					phydev->speed = SPEED_1000;
+				else if (bmcr & BMCR_SPEED100)
+					phydev->speed = SPEED_100;
+				else
+					phydev->speed = SPEED_UNKNOWN;
+			}
+		}
+	}
+
+	return ret;
+}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+static void air_polarity_help(void)
+{
+	pr_notice("\nUsage:\n"
+			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
+			"echo [tx polarity] [rx polarity] > /[debugfs]/polarity\n"
+			"option: tx_normal, tx_reverse, rx_normal, rx_revers\n");
+}
+
+static int air_set_polarity(struct phy_device *phydev, int tx_rx)
+{
+	int ret = 0;
+	unsigned int pbus_data = 0;
+
+	pr_debug("\nPolarit %s\n", tx_rx_string[tx_rx]);
+	pbus_data = air_buckpbus_reg_read(phydev, 0xca0f8) & ~(BIT(0) | BIT(1));
+	pbus_data |= tx_rx;
+	ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_data);
+	if (ret < 0)
+		pr_notice("\n%s:air_buckpbus_reg_write fail\n", __func__);
+	pbus_data = air_buckpbus_reg_read(phydev, 0xca0f8);
+	pr_notice("\nPolarity %s confirm....(%02lx)\n",
+			tx_rx_string[tx_rx], pbus_data & (BIT(0) | BIT(1)));
+
+	return ret;
+}
+
+static int air_set_mode(struct phy_device *phydev, int dbg_mode)
+{
+	int ret = 0, val = 0;
+	unsigned int pbus_data = 0;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	switch (dbg_mode) {
+	case AIR_PORT_MODE_FORCE_100:
+		pr_notice("\nForce 100M\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) | BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) & ~BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) & ~BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FORCE_1000:
+		pr_notice("\nForce 1000M\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) & ~BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) & ~BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FORCE_2500:
+		pr_notice("\nForce 2500M\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) & ~BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) & ~BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) | BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_AUTONEGO:
+		pr_notice("\nAutonego mode\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) | BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) | BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_POWER_DOWN:
+		pr_notice("\nPower Down\n");
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(11);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_POWER_UP:
+		pr_notice("\nPower Up\n");
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) & ~BIT(11);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FC_DIS:
+		pr_notice("\nFlow Control Disabled\n");
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe0008);
+		pbus_data &= ~(BIT(6) | BIT(7));
+		ret = air_buckpbus_reg_write(phydev, 0xe0008, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe000c);
+		pbus_data &= ~(BIT(0) | BIT(1));
+		ret = air_buckpbus_reg_write(phydev, 0xe000c, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FC_EN:
+		pr_notice("\nFlow Control Enabled\n");
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe0008);
+		pbus_data |= (BIT(6) | BIT(7));
+		ret = air_buckpbus_reg_write(phydev, 0xe0008, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe000c);
+		pbus_data |= (BIT(0) | BIT(1));
+		ret = air_buckpbus_reg_write(phydev, 0xe000c, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	default:
+		pr_notice("\nWrong Port mode\n");
+		break;
+	}
+	return ret;
+}
+
+static int airphy_info_show(struct seq_file *seq, void *v)
+{
+	struct phy_device *phydev = seq->private;
+	struct en8811h_priv *priv = phydev->priv;
+	unsigned int val = 0;
+
+	seq_puts(seq, "<<AIR EN8811H Driver info>>\n");
+	seq_printf(seq, "| Driver Version       : %s\n",
+							EN8811H_DRIVER_VERSION);
+	val = air_buckpbus_reg_read(phydev, 0xcf914);
+	seq_printf(seq, "| Boot mode            : %s\n",
+		((val & BIT(24)) >> 24) ? "Flash" : "Download Code");
+	seq_printf(seq, "| EthMD32.dm.bin  CRC32: %08x\n",
+				priv->dm_crc32);
+	seq_printf(seq, "| EthMD32.DSP.bin CRC32: %08x\n",
+				priv->dsp_crc32);
+	val = air_buckpbus_reg_read(phydev, 0x3b3c);
+	seq_printf(seq, "| MD32 FW Version      : %08x\n", val);
+	val = air_mii_cl45_read(phydev, 0x1e, 0x8009);
+	seq_printf(seq, "| MD32 FW Status       : %08x\n",
+				air_mii_cl45_read(phydev, 0x1e, 0x8009));
+	val = (air_buckpbus_reg_read(phydev, 0xca0f8) & 0x3);
+	seq_printf(seq, "| Tx, Rx Polarity      : %s(%02d)\n",
+						tx_rx_string[val], val);
+
+	seq_puts(seq, "\n");
+
+	return 0;
+}
+
+static int airphy_info_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, airphy_info_show, inode->i_private);
+}
+
+static int airphy_fcm_counter_show(struct phy_device *phydev,
+				struct seq_file *seq)
+{
+	int ret = 0;
+	u32 pkt_cnt = 0;
+
+	seq_puts(seq, "|\t<<FCM Counter>>\n");
+	seq_puts(seq, "| Rx from Line side_S      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0090);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Rx from Line side_T      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0094);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to System side_S      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe009c);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to System side_T      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe00A0);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Rx from System side_S    :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0078);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Rx from System side_T    :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe007C);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to Line side_S        :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0084);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to Line side_T        :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0088);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	ret = air_buckpbus_reg_write(phydev, 0xe0074, 0xf);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int airphy_ss_counter_show(struct phy_device *phydev,
+				struct seq_file *seq)
+{
+	int ret = 0;
+	u32 pkt_cnt = 0;
+
+	seq_puts(seq, "|\t<<SS Counter>>\n");
+	ret = air_buckpbus_reg_write(phydev, 0xC602C, 0x3);
+	if (ret < 0)
+		return ret;
+	seq_puts(seq, "| TX Start                 :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60B0);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| TX Terminal              :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60B4);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| RX Start                 :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60BC);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| RX Terminal              :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60C0);
+	seq_printf(seq, "%010u |\n\n", pkt_cnt);
+	ret = air_buckpbus_reg_write(phydev, 0xC602C, 0x4);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int airphy_counter_show(struct seq_file *seq, void *v)
+{
+	struct phy_device *phydev = seq->private;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int ret = 0, addr = phydev_addr(phydev);
+	u32 pkt_cnt = 0;
+
+	ret = air_read_status(phydev);
+	if (ret < 0)
+		return ret;
+	seq_puts(seq, "==========AIR PHY COUNTER==========\n");
+	ret = airphy_fcm_counter_show(phydev, seq);
+	if (ret < 0)
+		return ret;
+	if (phydev->link && phydev->speed == SPEED_2500) {
+		seq_puts(seq, "|\t<<LS Counter>>\n");
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x10);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x0);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "|\tBefore EF\n");
+		seq_puts(seq, "| TX to Line side_S        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x3071c);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX to Line side_T        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30720);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_S      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x3072c);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_T      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30730);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX_ENC                   :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30724);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX_DEC                   :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30728);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "|\tAfter EF\n");
+		seq_puts(seq, "| TX to Line side_S        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30734);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX to Line side_T        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30738);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_S      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30764);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_T      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30768);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x13);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x3);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x10);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x0);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "|\t<<MAC Counter>>\n");
+		seq_puts(seq, "| TX Error from System side:");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x131000);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX Error to System side  :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x132000);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX from System side      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x131004);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX to System Side        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x132004);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+	}
+	if (phydev->link && ((phydev->speed != SPEED_2500))) {
+		seq_puts(seq, "|\t<<LS Counter>>\n");
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 1);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "| RX from Line side        :");
+		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x12) & 0x7fff;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX Error from Line side  :");
+		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x17) & 0xff;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0);
+		if (ret < 0)
+			return ret;
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0x52B5);
+		if (ret < 0)
+			return ret;
+		ret = air_mii_cl22_write(mbus, addr, 0x10, 0xBF92);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "| TX to Line side          :");
+		pkt_cnt = (air_mii_cl22_read(mbus, addr, 0x11) & 0x7ffe) >> 1;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX Error to Line side    :");
+		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x12);
+		pkt_cnt &= 0x7f;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0);
+		if (ret < 0)
+			return ret;
+	}
+	if (phydev->link) {
+		ret = airphy_ss_counter_show(phydev, seq);
+		if (ret < 0)
+			return ret;
+	}
+	return ret;
+}
+
+static int airphy_counter_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, airphy_counter_show, inode->i_private);
+}
+
+static ssize_t airphy_polarity_write(struct file *file, const char __user *ptr,
+					size_t len, loff_t *off)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[32], param1[32], param2[32];
+	int count = len, ret = 0, tx_rx = 0;
+
+	memset(buf, 0, 32);
+	memset(param1, 0, 32);
+	memset(param2, 0, 32);
+
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, ptr, len))
+		return -EFAULT;
+	if (sscanf(buf, "%s %s", param1, param2) == -1)
+		return -EFAULT;
+
+	if (!strncmp("tx_normal", param1, strlen("tx_normal"))) {
+		if (!strncmp("rx_normal", param2, strlen("rx_normal")))
+			tx_rx = AIR_POL_TX_NOR_RX_NOR;
+		else if (!strncmp("rx_reverse", param2, strlen("rx_reverse")))
+			tx_rx = AIR_POL_TX_NOR_RX_REV;
+		else {
+			pr_notice("\nRx param is not correct.\n");
+			return -EINVAL;
+		}
+	} else if (!strncmp("tx_reverse", param1, strlen("tx_reverse"))) {
+		if (!strncmp("rx_normal", param2, strlen("rx_normal")))
+			tx_rx = AIR_POL_TX_REV_RX_NOR;
+		else if (!strncmp("rx_reverse", param2, strlen("rx_reverse")))
+			tx_rx = AIR_POL_TX_REV_RX_REV;
+		else {
+			pr_notice("\nRx param is not correct.\n");
+			return -EINVAL;
+		}
+	} else {
+		air_polarity_help();
+		return count;
+	}
+	pr_notice("\nSet Polarity %s\n", tx_rx_string[tx_rx]);
+	ret = air_set_polarity(phydev, tx_rx);
+	if (ret < 0)
+		return ret;
+	return count;
+}
+static void airphy_port_mode_help(void)
+{
+	pr_notice("\nUsage:\n"
+			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
+			"echo [mode] [para] > /[debugfs]/port_mode\n"
+			"echo re-an > /[debugfs]/port_mode\n"
+			"echo auto > /[debugfs]/port_mode\n"
+			"echo 2500 > /[debugfs]/port_mode\n"
+			"echo 1000 > /[debugfs]/port_mode\n"
+			"echo 100 > /[debugfs]/port_mode\n"
+			"echo power up/down >  /[debugfs]/port_mode\n");
+}
+
+static ssize_t airphy_port_mode(struct file *file, const char __user *ptr,
+					size_t len, loff_t *off)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[32], cmd[32], param[32];
+	int count = len, ret = 0;
+	int num = 0, val = 0;
+
+	memset(buf, 0, 32);
+	memset(cmd, 0, 32);
+	memset(param, 0, 32);
+
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, ptr, len))
+		return -EFAULT;
+
+	num = sscanf(buf, "%s %s", cmd, param);
+	if (num < 1 || num > 3)
+		return -EFAULT;
+
+	if (!strncmp("auto", cmd, strlen("auto")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_AUTONEGO);
+	else if (!strncmp("2500", cmd, strlen("2500")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_2500);
+	else if (!strncmp("1000", cmd, strlen("1000")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_1000);
+	else if (!strncmp("100", cmd, strlen("100")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_100);
+	else if (!strncmp("re-an", cmd, strlen("re-an"))) {
+		val = phy_read(phydev, MII_BMCR) | BIT(9);
+		ret = phy_write(phydev, MII_BMCR, val);
+	} else if (!strncmp("power", cmd, strlen("power"))) {
+		if (!strncmp("down", param, strlen("down")))
+			ret = air_set_mode(phydev, AIR_PORT_MODE_POWER_DOWN);
+		else if (!strncmp("up", param, strlen("up")))
+			ret = air_set_mode(phydev, AIR_PORT_MODE_POWER_UP);
+	} else if (!strncmp("int_sw_release", cmd, strlen("int_sw_release"))) {
+		ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
+		pr_notice("\ninternal sw reset released\n");
+	} else if (!strncmp("help", cmd, strlen("help"))) {
+		airphy_port_mode_help();
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static void airphy_debugfs_buckpbus_help(void)
+{
+	pr_notice("\nUsage:\n"
+			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
+			"Read:\n"
+			"echo r [buckpbus_register] > /[debugfs]/buckpbus_op\n"
+			"Write:\n"
+			"echo w [buckpbus_register] [value] > /[debugfs]/buckpbus_op\n");
+}
+
+
+static ssize_t airphy_debugfs_buckpbus(struct file *file,
+					const char __user *buffer, size_t count,
+					loff_t *data)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[64];
+	int ret = 0;
+	unsigned int reg, val;
+
+	memset(buf, 0, 64);
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, buffer, count))
+		return -EFAULT;
+
+	if (buf[0] == 'w') {
+		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
+			return -EFAULT;
+
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+			phydev_addr(phydev), reg, val);
+		ret = air_buckpbus_reg_write(phydev, reg, val);
+		if (ret < 0) {
+			pr_notice("\nbuckpbus_reg_write fail\n");
+			return -EIO;
+		}
+		val = air_buckpbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
+			phydev_addr(phydev), reg, val);
+	} else if (buf[0] == 'r') {
+		if (sscanf(buf, "r %x", &reg) == -1)
+			return -EFAULT;
+
+		val = air_buckpbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+		phydev_addr(phydev), reg, val);
+	} else
+		airphy_debugfs_buckpbus_help();
+
+	return count;
+}
+
+static ssize_t airphy_debugfs_pbus(struct file *file,
+					const char __user *buffer, size_t count,
+					loff_t *data)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[64];
+	int ret = 0;
+	unsigned int reg, val;
+
+	memset(buf, 0, 64);
+	if (copy_from_user(buf, buffer, count))
+		return -EFAULT;
+
+	if (buf[0] == 'w') {
+		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
+			return -EFAULT;
+
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+			phydev_addr(phydev), reg, val);
+		ret = air_pbus_reg_write(phydev, reg, val);
+		if (ret < 0) {
+			pr_notice("\npbus_reg_write fail\n");
+			return -EIO;
+		}
+		val = air_pbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
+			phydev_addr(phydev), reg, val);
+	} else if (buf[0] == 'r') {
+		if (sscanf(buf, "r %x", &reg) == -1)
+			return -EFAULT;
+
+		val = air_pbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+		phydev_addr(phydev), reg, val);
+	} else
+		airphy_debugfs_buckpbus_help();
+
+	return count;
+}
+
+static int airphy_link_status(struct seq_file *seq, void *v)
+{
+	int ret = 0;
+	struct phy_device *phydev = seq->private;
+
+	ret = air_read_status(phydev);
+	if (ret < 0)
+		return ret;
+
+	seq_printf(seq, "%s Information:\n", dev_name(phydev_dev(phydev)));
+	seq_printf(seq, "\tPHYAD: %02d\n", phydev_addr(phydev));
+	seq_printf(seq, "\tLink Status: %s\n", phydev->link ? "UP" : "DOWN");
+	if (phydev->link) {
+		seq_printf(seq, "\tAuto-Nego: %s\n",
+				phydev->autoneg ? "on" : "off");
+		seq_puts(seq, "\tSpeed: ");
+		if (phydev->speed == SPEED_UNKNOWN)
+			seq_printf(seq, "Unknown! (%i)\n", phydev->speed);
+		else
+			seq_printf(seq, "%uMb/s\n", phydev->speed);
+
+		seq_printf(seq, "\tDuplex: %s\n",
+				phydev->duplex ? "Full" : "Half");
+		seq_puts(seq, "\n");
+	}
+
+	return ret;
+}
+
+static int airphy_link_status_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, airphy_link_status, inode->i_private);
+}
+
+static int dbg_regs_show(struct seq_file *seq, void *v)
+{
+	struct phy_device *phydev = seq->private;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	seq_puts(seq, "\t<<DEBUG REG DUMP>>\n");
+	seq_printf(seq, "| RG_MII_BMSR           : 0x%08x |\n",
+		   air_mii_cl22_read(mbus, addr, MII_BMSR));
+	seq_printf(seq, "| RG_MII_REF_CLK        : 0x%08x |\n",
+		   air_mii_cl22_read(mbus, addr, 0x1d));
+	seq_printf(seq, "| RG_SYS_LINK_MODE      : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe0004));
+	seq_printf(seq, "| RG_FCM_CTRL           : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe000C));
+	seq_printf(seq, "| RG_SS_PAUSE_TIME      : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe0020));
+	seq_printf(seq, "| RG_MIN_IPG_NUM        : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe002C));
+	seq_printf(seq, "| RG_CTROL_0            : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xc0000));
+	seq_printf(seq, "| RG_LINK_STATUS        : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xc0b04));
+	seq_printf(seq, "| RG_LINK_PARTNER_AN    : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xc0014));
+	seq_printf(seq, "| RG_FN_PWR_CTRL_STATUS : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0x1020c));
+	seq_printf(seq, "| RG_WHILE_LOOP_COUNT   : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0x3A48));
+
+	return 0;
+}
+
+static int airphy_dbg_regs_show_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dbg_regs_show, inode->i_private);
+}
+
+static const struct file_operations airphy_info_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_info_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_counter_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_counter_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_debugfs_buckpbus_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_debugfs_buckpbus,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_debugfs_pbus_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_debugfs_pbus,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_port_mode_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_port_mode,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_polarity_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_polarity_write,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_link_status_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_link_status_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_dbg_reg_show_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_dbg_regs_show_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+int airphy_debugfs_init(struct phy_device *phydev)
+{
+	int ret = 0;
+	struct en8811h_priv *priv = phydev->priv;
+	struct dentry *dir = priv->debugfs_root;
+
+	dev_dbg(phydev_dev(phydev), "%s: start\n", __func__);
+	dir = debugfs_create_dir(dev_name(phydev_dev(phydev)), NULL);
+	if (!dir) {
+		dev_err(phydev_dev(phydev), "%s:err at %d\n",
+					 __func__, __LINE__);
+		ret = -ENOMEM;
+	}
+	debugfs_create_file(DEBUGFS_DRIVER_INFO, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_info_fops);
+	debugfs_create_file(DEBUGFS_COUNTER, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_counter_fops);
+	debugfs_create_file(DEBUGFS_BUCKPBUS_OP, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_debugfs_buckpbus_fops);
+	debugfs_create_file(DEBUGFS_PBUS_OP, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_debugfs_pbus_fops);
+	debugfs_create_file(DEBUGFS_PORT_MODE, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_port_mode_fops);
+	debugfs_create_file(DEBUGFS_POLARITY, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_polarity_fops);
+	debugfs_create_file(DEBUGFS_LINK_STATUS, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_link_status_fops);
+	debugfs_create_file(DEBUGFS_DBG_REG_SHOW, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_dbg_reg_show_fops);
+
+	priv->debugfs_root = dir;
+	return ret;
+}
+
+void air_debugfs_remove(struct phy_device *phydev)
+{
+	struct en8811h_priv *priv = phydev->priv;
+
+	debugfs_remove_recursive(priv->debugfs_root);
+	priv->debugfs_root = NULL;
+}
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.h b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.h
new file mode 100644
index 0000000..a1d97ce
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*************************************************
+ * FILE NAME:  air_en8811h_api.h
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+#ifndef __EN8811H_API_H
+#define __EN8811H_API_H
+#include <linux/version.h>
+
+#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
+#define phydev_mdio_bus(_dev) (_dev->bus)
+#define phydev_addr(_dev) (_dev->addr)
+#define phydev_dev(_dev) (&_dev->dev)
+#else
+#define phydev_mdio_bus(_dev) (_dev->mdio.bus)
+#define phydev_addr(_dev) (_dev->mdio.addr)
+#define phydev_dev(_dev) (&_dev->mdio.dev)
+#endif
+
+#define BUFFER_LENGTH 512
+
+#define DEBUGFS_COUNTER		        "counter"
+#define DEBUGFS_DRIVER_INFO	        "drvinfo"
+#define DEBUGFS_PORT_MODE           "port_mode"
+#define DEBUGFS_BUCKPBUS_OP         "buckpbus_op"
+#define DEBUGFS_PBUS_OP             "pbus_op"
+#define DEBUGFS_POLARITY            "polarity"
+#define DEBUGFS_LINK_STATUS         "link_status"
+#define DEBUGFS_DBG_REG_SHOW        "dbg_regs_show"
+
+enum air_port_mode {
+	AIR_PORT_MODE_FORCE_100,
+	AIR_PORT_MODE_FORCE_1000,
+	AIR_PORT_MODE_FORCE_2500,
+	AIR_PORT_MODE_AUTONEGO,
+	AIR_PORT_MODE_POWER_DOWN,
+	AIR_PORT_MODE_POWER_UP,
+	AIR_PORT_MODE_FC_UNSUPPORT,
+	AIR_PORT_MODE_FC_SUPPORT,
+	AIR_PORT_MODE_FC_DIS,
+	AIR_PORT_MODE_FC_EN,
+	AIR_PORT_MODE_LAST = 0xFF,
+};
+
+enum air_polarity {
+	AIR_POL_TX_REV_RX_NOR,
+	AIR_POL_TX_NOR_RX_NOR,
+	AIR_POL_TX_REV_RX_REV,
+	AIR_POL_TX_NOR_RX_REV,
+	AIR_POL_TX_NOR_RX_LAST = 0xff,
+};
+
+/* Link mode bit indices */
+enum air_link_mode_bit {
+	AIR_LINK_MODE_10baseT_Half_BIT	 = 0,
+	AIR_LINK_MODE_10baseT_Full_BIT	 = 1,
+	AIR_LINK_MODE_100baseT_Half_BIT	 = 2,
+	AIR_LINK_MODE_100baseT_Full_BIT	 = 3,
+	AIR_LINK_MODE_1000baseT_Full_BIT = 4,
+	AIR_LINK_MODE_2500baseT_Full_BIT = 5,
+};
+
+#ifndef unlikely
+#  define unlikely(x)	(x)
+#endif
+int air_pbus_reg_write(struct phy_device *phydev,
+	unsigned int pbus_address, unsigned int pbus_data);
+int air_mii_cl22_write(struct mii_bus *ebus, int addr,
+	unsigned int phy_register, unsigned int write_data);
+int air_mii_cl22_read(struct mii_bus *ebus,
+	int addr, unsigned int phy_register);
+int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg);
+int air_mii_cl45_write(struct phy_device *phydev,
+	int devad, u16 reg, u16 write_data);
+unsigned int air_buckpbus_reg_read(struct phy_device *phydev,
+	unsigned int pbus_address);
+int air_buckpbus_reg_write(struct phy_device *phydev,
+	unsigned int pbus_address, unsigned int pbus_data);
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+int airphy_debugfs_init(struct phy_device *phydev);
+void air_debugfs_remove(struct phy_device *phydev);
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
+#endif /* End of __EN8811H_API_H */
diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_main.c b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_main.c
new file mode 100644
index 0000000..353e226
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/net/phy/air_en8811h_main.c
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0
+/*************************************************
+ * FILE NAME:  air_en8811h_main.c
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+
+/* INCLUDE FILE DECLARATIONS
+*/
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include <linux/debugfs.h>
+#include "air_en8811h_api.h"
+#include "air_en8811h.h"
+
+MODULE_DESCRIPTION("Airoha EN8811H PHY Drivers");
+MODULE_AUTHOR("Airoha");
+MODULE_LICENSE("GPL");
+
+/**************************
+ * GPIO5  <-> BASE_T_LED0,
+ * GPIO4  <-> BASE_T_LED1,
+ * GPIO3  <-> BASE_T_LED2,
+ **************************/
+/* User-defined.B */
+#define AIR_LED_SUPPORT
+#ifdef AIR_LED_SUPPORT
+static const struct air_base_t_led_cfg led_cfg[3] = {
+/********************************************************************
+ *Enable,     GPIO,        LED Polarity,    LED ON,     LED Blink
+*********************************************************************/
+	{1,	 AIR_LED0_GPIO5, AIR_ACTIVE_HIGH, AIR_LED0_ON, AIR_LED0_BLK},
+	{1,	 AIR_LED1_GPIO4, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLK},
+	{1,	 AIR_LED2_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLK},
+};
+static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
+#endif
+/* User-defined.E */
+
+/***********************************************************
+ *                  F U N C T I O N S
+ ***********************************************************/
+
+
+static int MDIOWriteBuf(struct phy_device *phydev,
+		unsigned long address, const struct firmware *fw)
+{
+	unsigned int write_data, offset;
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+	/* page 4 */
+	ret = air_mii_cl22_write(mbus, addr, 0x1F, 4);
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	/* address increment*/
+	ret = air_mii_cl22_write(mbus, addr, 0x10, 0x8000);
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	ret = air_mii_cl22_write(mbus, addr, 0x11,
+					(u32)((address >> 16) & 0xffff));
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	ret = air_mii_cl22_write(mbus, addr, 0x12, (u32)(address & 0xffff));
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+
+	for (offset = 0; offset < fw->size; offset += 4) {
+		write_data = (fw->data[offset + 3] << 8) | fw->data[offset + 2];
+		ret = air_mii_cl22_write(mbus, addr, 0x13, write_data);
+		if (ret < 0) {
+			dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+			return ret;
+		}
+		write_data = (fw->data[offset + 1] << 8) | fw->data[offset];
+		ret = air_mii_cl22_write(mbus, addr, 0x14, write_data);
+		if (ret < 0) {
+			dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+			return ret;
+		}
+	}
+	ret = air_mii_cl22_write(mbus, addr, 0x1F, 0);
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int en8811h_load_firmware(struct phy_device *phydev)
+{
+	struct device *dev = phydev_dev(phydev);
+	const struct firmware *fw;
+	const char *firmware;
+	int ret = 0;
+	u32 pbus_value = 0;
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	struct en8811h_priv *priv = phydev->priv;
+#endif
+	ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
+	if (ret < 0)
+		return ret;
+	pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
+	pbus_value |= BIT(11);
+	ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
+	if (ret < 0)
+		return ret;
+	firmware = EN8811H_MD32_DM;
+	ret = request_firmware_direct(&fw, firmware, dev);
+	if (ret < 0) {
+		dev_err(dev,
+			"failed to load firmware %s, ret: %d\n", firmware, ret);
+		return ret;
+	}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	priv->dm_crc32 = ~crc32(~0, fw->data, fw->size);
+#endif
+	dev_info(dev, "%s: crc32=0x%x\n",
+		firmware, ~crc32(~0, fw->data, fw->size));
+	/* Download DM */
+	ret = MDIOWriteBuf(phydev, 0x00000000, fw);
+	release_firmware(fw);
+	if (ret < 0) {
+		dev_err(dev,
+			"MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
+		return ret;
+	}
+
+	firmware = EN8811H_MD32_DSP;
+	ret = request_firmware_direct(&fw, firmware, dev);
+	if (ret < 0) {
+		dev_info(dev,
+			"failed to load firmware %s, ret: %d\n", firmware, ret);
+		return ret;
+	}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	priv->dsp_crc32 = ~crc32(~0, fw->data, fw->size);
+#endif
+	dev_info(dev, "%s: crc32=0x%x\n",
+		firmware, ~crc32(~0, fw->data, fw->size));
+	/* Download PM */
+	ret = MDIOWriteBuf(phydev, 0x00100000, fw);
+	release_firmware(fw);
+	if (ret < 0) {
+		dev_err(dev,
+			"MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
+		return ret;
+	}
+	pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
+	pbus_value &= ~BIT(11);
+	ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
+	if (ret < 0)
+		return ret;
+	ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+#ifdef AIR_LED_SUPPORT
+static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity,
+				int polar, u16 on_evt, u16 blk_evt)
+{
+	int ret = 0;
+
+	if (polar == AIR_ACTIVE_HIGH)
+		on_evt |= LED_ON_POL;
+	else
+		on_evt &= ~LED_ON_POL;
+
+	ret = air_mii_cl45_write(phydev, 0x1f,
+			LED_ON_CTRL(entity), on_evt | LED_ON_EN);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
+{
+	u16 cl45_data;
+	int err = 0;
+	struct device *dev = phydev_dev(phydev);
+
+	cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
+	switch (mode) {
+	case AIR_LED_MODE_DISABLE:
+		cl45_data &= ~LED_BCR_EXT_CTRL;
+		cl45_data &= ~LED_BCR_MODE_MASK;
+		cl45_data |= LED_BCR_MODE_DISABLE;
+		break;
+	case AIR_LED_MODE_USER_DEFINE:
+		cl45_data |= LED_BCR_EXT_CTRL;
+		cl45_data |= LED_BCR_CLK_EN;
+		break;
+	default:
+		dev_err(dev, "LED mode%d is not supported!\n", mode);
+		return -EINVAL;
+	}
+	err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
+{
+	u16 cl45_data = 0;
+	int err;
+
+	cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
+	if (state == 1)
+		cl45_data |= LED_ON_EN;
+	else
+		cl45_data &= ~LED_ON_EN;
+
+	err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int en8811h_led_init(struct phy_device *phydev)
+{
+
+	unsigned long led_gpio = 0, reg_value = 0;
+	u16 cl45_data = led_dur;
+	int ret = 0, id;
+	struct device *dev = phydev_dev(phydev);
+
+	ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
+	if (ret < 0)
+		return ret;
+	cl45_data >>= 1;
+	ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
+	if (ret < 0)
+		return ret;
+	ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
+	if (ret != 0) {
+		dev_err(dev, "led_set_mode fail(ret:%d)!\n", ret);
+		return ret;
+	}
+	for (id = 0; id < EN8811H_LED_COUNT; id++) {
+		/* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
+		if (led_cfg[id].gpio != (id + (AIR_LED0_GPIO5 - (2 * id)))) {
+			dev_err(dev, "LED%d uses incorrect GPIO%d !\n",
+							id, led_cfg[id].gpio);
+			return -EINVAL;
+		}
+		ret = airoha_led_set_state(phydev, id, led_cfg[id].en);
+		if (ret != 0) {
+			dev_err(dev, "led_set_state fail(ret:%d)!\n", ret);
+			return ret;
+		}
+		if (led_cfg[id].en == 1) {
+			led_gpio |= BIT(led_cfg[id].gpio);
+			ret = airoha_led_set_usr_def(phydev, id,
+				led_cfg[id].pol, led_cfg[id].on_cfg,
+				led_cfg[id].blk_cfg);
+			if (ret != 0) {
+				dev_err(dev, "led_set_usr_def fail!\n");
+				return ret;
+			}
+		}
+	}
+	reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio;
+	ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value);
+	if (ret < 0)
+		return ret;
+	dev_info(dev, "LED initialize OK !\n");
+	return 0;
+}
+#endif /* AIR_LED_SUPPORT */
+#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
+static int en8811h_get_features(struct phy_device *phydev)
+{
+	int ret;
+	struct device *dev = phydev_dev(phydev);
+
+	dev_dbg(dev, "%s()\n", __func__);
+	ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = genphy_read_abilities(phydev);
+	if (ret)
+		return ret;
+	/* EN8811H supports 100M/1G/2.5G speed. */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+			phydev->supported);
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+			phydev->supported);
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+			phydev->supported);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+			phydev->supported);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+			phydev->supported);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+			phydev->supported);
+	return 0;
+}
+#endif
+
+static int en8811h_probe(struct phy_device *phydev)
+{
+	int ret = 0;
+	int reg_value, pid1 = 0, pid2 = 0;
+	u32 retry, pbus_value = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	struct en8811h_priv *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	phydev->priv = priv;
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
+	ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
+	if (ret < 0)
+		return ret;
+	pid1 = air_mii_cl22_read(mbus, addr, MII_PHYSID1);
+	if (pid1 < 0)
+		return pid1;
+	pid2 = air_mii_cl22_read(mbus, addr, MII_PHYSID2);
+	if (pid2 < 0)
+		return pid2;
+	dev_info(dev, "PHY = %x - %x\n", pid1, pid2);
+	if ((pid1 != EN8811H_PHY_ID1) || (pid2 != EN8811H_PHY_ID2)) {
+		dev_err(dev, "EN8811H dose not exist !\n");
+		return -ENODEV;
+	}
+	pbus_value = air_buckpbus_reg_read(phydev, 0xcf914);
+	dev_info(dev, "Bootmode: %s\n",
+			(GET_BIT(pbus_value, 24) ? "Flash" : "Download Code"));
+
+	ret = en8811h_load_firmware(phydev);
+	if (ret < 0) {
+		dev_err(dev, "EN8811H load firmware fail.\n");
+		return ret;
+	}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	ret = airphy_debugfs_init(phydev);
+	if (ret < 0) {
+		dev_err(dev, "air_debug_procfs_init fail. (ret=%d)\n", ret);
+		air_debugfs_remove(phydev);
+		kfree(priv);
+		return ret;
+	}
+#endif /* CONFIG_AIROHA_EN8811H_PHY_DEBUGFS */
+	retry = MAX_RETRY;
+	do {
+		mdelay(300);
+		reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
+		if (reg_value == EN8811H_PHY_READY) {
+			dev_info(dev, "EN8811H PHY ready!\n");
+			break;
+		}
+		retry--;
+	} while (retry);
+	if (retry == 0) {
+		dev_err(dev, "MD32 FW is not ready.(Status 0x%x)\n", reg_value);
+		pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
+		dev_err(dev,
+			"Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
+		dev_err(dev,
+			"EN8811H initialize fail!\n");
+		return 0;
+	}
+	/* Mode selection*/
+	dev_info(dev, "EN8811H Mode 1 !\n");
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
+	if (ret < 0)
+		return ret;
+	/* Serdes polarity */
+	pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
+	pbus_value &= ~0x3;
+	pbus_value |= (EN8811H_RX_POL_NORMAL | EN8811H_TX_POL_NORMAL);
+	ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
+	if (ret < 0)
+		return ret;
+	pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
+	dev_info(dev, "Tx, Rx Polarity : %08x\n", pbus_value);
+	pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
+	dev_info(dev, "MD32 FW Version : %08x\n", pbus_value);
+#if defined(AIR_LED_SUPPORT)
+	ret = en8811h_led_init(phydev);
+	if (ret < 0) {
+		dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret);
+		return ret;
+	}
+#endif
+	dev_info(dev, "EN8811H initialize OK! (%s)\n", EN8811H_DRIVER_VERSION);
+	return 0;
+}
+void en8811h_remove(struct phy_device *phydev)
+{
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	struct en8811h_priv *priv = phydev->priv;
+	struct device *dev = phydev_dev(phydev);
+
+	dev_dbg(dev, "%s: start\n", __func__);
+	if (priv) {
+		dev_info(dev, "%s: air_debugfs_remove\n", __func__);
+		air_debugfs_remove(phydev);
+		kfree(priv);
+	}
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
+}
+
+static struct phy_driver en8811h_driver[] = {
+{
+	.phy_id         = EN8811H_PHY_ID,
+	.name           = "Airoha EN8811H",
+	.phy_id_mask    = 0x0ffffff0,
+	.probe          = en8811h_probe,
+	.remove         = en8811h_remove,
+#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
+	.get_features   = en8811h_get_features,
+	.read_mmd       = air_mii_cl45_read,
+	.write_mmd      = air_mii_cl45_write,
+#endif
+} };
+
+int __init en8811h_phy_driver_register(void)
+{
+	int ret;
+#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
+	ret = phy_driver_register(en8811h_driver);
+#else
+	ret = phy_driver_register(en8811h_driver, THIS_MODULE);
+#endif
+	if (!ret)
+		return 0;
+
+	phy_driver_unregister(en8811h_driver);
+	return ret;
+}
+
+void __exit en8811h_phy_driver_unregister(void)
+{
+	phy_driver_unregister(en8811h_driver);
+}
+
+module_init(en8811h_phy_driver_register);
+module_exit(en8811h_phy_driver_unregister);
diff --git a/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.DSP.bin b/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.DSP.bin
index bf7d9bf..23c3dd1 100644
--- a/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.DSP.bin
+++ b/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.DSP.bin
Binary files differ
diff --git a/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.dm.bin b/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.dm.bin
index c8ce832..efb89b7 100644
--- a/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.dm.bin
+++ b/target/linux/mediatek/mt7981/base-files/lib/firmware/EthMD32.dm.bin
Binary files differ
diff --git a/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.DSP.bin b/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.DSP.bin
index bf7d9bf..23c3dd1 100644
--- a/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.DSP.bin
+++ b/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.DSP.bin
Binary files differ
diff --git a/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.dm.bin b/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.dm.bin
index c8ce832..efb89b7 100644
--- a/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.dm.bin
+++ b/target/linux/mediatek/mt7986/base-files/lib/firmware/EthMD32.dm.bin
Binary files differ
diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.DSP.bin b/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.DSP.bin
index bf7d9bf..23c3dd1 100644
--- a/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.DSP.bin
+++ b/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.DSP.bin
Binary files differ
diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.dm.bin b/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.dm.bin
index c8ce832..efb89b7 100644
--- a/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.dm.bin
+++ b/target/linux/mediatek/mt7988/base-files/lib/firmware/EthMD32.dm.bin
Binary files differ
diff --git a/target/linux/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch b/target/linux/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch
index 5e3c619..366414c 100644
--- a/target/linux/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch
@@ -5,19 +5,15 @@
  [backport-networking-drivers][999-1703-en8811h-2p5gphy-support.patch]
 
 ---
- drivers/net/phy/Kconfig       |   5 +
- drivers/net/phy/Makefile      |   1 +
- drivers/net/phy/air_en8811h.c | 702 ++++++++++++++++++++++++++++++++++
- drivers/net/phy/air_en8811h.h | 151 ++++++++
- 4 files changed, 859 insertions(+)
- create mode 100644 drivers/net/phy/air_en8811h.c
- create mode 100644 drivers/net/phy/air_en8811h.h
+ drivers/net/phy/Kconfig       |  11 +
+ drivers/net/phy/Makefile      |   2 +
+ 2 files changed, 13 insertions(+)
 
 diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
 index 170dd00cd..5eeccfee2 100644
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -345,6 +345,11 @@ config SFP
+@@ -345,6 +345,17 @@ config SFP
  	depends on HWMON || HWMON=n
  	select MDIO_I2C
  
@@ -26,6 +22,12 @@
 +	---help---
 +	  Currently supports the Airoha EN8811H PHY.
 +
++config AIROHA_EN8811H_PHY_DEBUGFS
++	bool "EN8811H debugfs support"
++	depends on AIROHA_EN8811H_PHY
++	---help---
++	  Enable creation of debugfs files for the EN8811H drivers.
++
  config ADIN_PHY
  	tristate "Analog Devices Industrial Ethernet PHYs"
  	help
@@ -33,879 +35,15 @@
 index 2368c1d19..8ea612a85 100644
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -67,6 +67,7 @@ aquantia-objs			+= aquantia_main.o
+@@ -67,6 +67,8 @@ aquantia-objs			+= aquantia_main.o
  ifdef CONFIG_HWMON
  aquantia-objs			+= aquantia_hwmon.o
  endif
++air_en8811h-y := air_en8811h_main.o air_en8811h_api.o
 +obj-$(CONFIG_AIROHA_EN8811H_PHY)	+= air_en8811h.o
  obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia.o
  obj-$(CONFIG_AX88796B_PHY)	+= ax88796b.o
  obj-$(CONFIG_AT803X_PHY)	+= at803x.o
-diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
-new file mode 100644
-index 000000000..cf564fc04
---- /dev/null
-+++ b/drivers/net/phy/air_en8811h.c
-@@ -0,0 +1,702 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+
-+/* FILE NAME:  air_en8811h.c
-+ * PURPOSE:
-+ *      EN8811H phy driver for Linux
-+ * NOTES:
-+ *
-+ */
-+
-+/* INCLUDE FILE DECLARATIONS
-+ */
-+#include <linux/kernel.h>
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/mii.h>
-+#include <linux/phy.h>
-+#include <linux/delay.h>
-+#include <linux/ethtool.h>
-+#include <linux/delay.h>
-+#include <linux/version.h>
-+#include <linux/firmware.h>
-+#include <linux/crc32.h>
-+
-+#include "air_en8811h.h"
-+
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0))
-+#define phydev_mdio_bus(_dev) (_dev->bus)
-+#define phydev_addr(_dev) (_dev->addr)
-+#define phydev_dev(_dev) (&_dev->dev)
-+#else
-+#define phydev_mdio_bus(_dev) (_dev->mdio.bus)
-+#define phydev_addr(_dev) (_dev->mdio.addr)
-+#define phydev_dev(_dev) (&_dev->mdio.dev)
-+#endif
-+
-+MODULE_DESCRIPTION("Airoha EN8811H PHY drivers");
-+MODULE_AUTHOR("Airoha");
-+MODULE_LICENSE("GPL");
-+
-+/*
-+GPIO5  <-> BASE_T_LED0,
-+GPIO4  <-> BASE_T_LED1,
-+GPIO3  <-> BASE_T_LED2,
-+*/
-+/* User-defined.B */
-+#define AIR_LED_SUPPORT
-+#ifdef AIR_LED_SUPPORT
-+static const AIR_BASE_T_LED_CFG_T led_cfg[3] =
-+{
-+    /*
-+     *    LED Enable,            GPIO,            LED Polarity,            LED ON,               LED Blink
-+     */
-+         {LED_ENABLE,      AIR_LED0_GPIO5,       AIR_ACTIVE_HIGH,     BASE_T_LED0_ON_CFG,    BASE_T_LED0_BLK_CFG}, /* BASE-T LED0 */
-+         {LED_ENABLE,      AIR_LED1_GPIO4,       AIR_ACTIVE_HIGH,     BASE_T_LED1_ON_CFG,    BASE_T_LED1_BLK_CFG}, /* BASE-T LED1 */
-+         {LED_ENABLE,      AIR_LED2_GPIO3,       AIR_ACTIVE_HIGH,     BASE_T_LED2_ON_CFG,    BASE_T_LED2_BLK_CFG}, /* BASE-T LED2 */
-+};
-+static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
-+#endif
-+/* User-defined.E */
-+
-+/************************************************************************
-+*                  F U N C T I O N S
-+************************************************************************/
-+#if 0
-+/* Airoha MII read function */
-+static int air_mii_cl22_read(struct mii_bus *ebus, unsigned int phy_addr,unsigned int phy_register)
-+{
-+    int read_data;
-+    read_data = mdiobus_read(ebus, phy_addr, phy_register);
-+    return read_data;
-+}
-+#endif
-+/* Airoha MII write function */
-+static int air_mii_cl22_write(struct mii_bus *ebus, unsigned int phy_addr, unsigned int phy_register,unsigned int write_data)
-+{
-+    int ret = 0;
-+    ret = mdiobus_write(ebus, phy_addr, phy_register, write_data);
-+    return ret;
-+}
-+
-+static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
-+{
-+    int ret = 0;
-+    int data;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, reg);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    data = phy_read(phydev, MII_MMD_ADDR_DATA_REG);
-+    return data;
-+}
-+
-+static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data)
-+{
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, reg);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, write_data);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+/* Use default PBUS_PHY_ID */
-+/* EN8811H PBUS write function */
-+static int air_pbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned long pbus_data)
-+{
-+    struct mii_bus *mbus = phydev_mdio_bus(phydev);
-+    int addr = phydev_addr(phydev);
-+    int ret = 0;
-+    ret = air_mii_cl22_write(mbus, (addr + 8), 0x1F, (unsigned int)(pbus_address >> 6));
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl22_write(mbus, (addr + 8), (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF));
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl22_write(mbus, (addr + 8), 0x10, (unsigned int)(pbus_data >> 16));
-+    AIR_RTN_ERR(ret);
-+    return 0;
-+}
-+
-+/* EN8811H BUCK write function */
-+static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data)
-+{
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, 0x1F, (unsigned int)4);        /* page 4 */
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x10, (unsigned int)0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x11, (unsigned int)((pbus_address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x12, (unsigned int)(pbus_address & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x13, (unsigned int)((pbus_data >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x14, (unsigned int)(pbus_data & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x1F, 0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+
-+/* EN8811H BUCK read function */
-+static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address)
-+{
-+    unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, 0x1F, (unsigned int)4);        /* page 4 */
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, 0x10, (unsigned int)0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, 0x15, (unsigned int)((pbus_address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, 0x16, (unsigned int)(pbus_address & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+
-+    pbus_data_high = phy_read(phydev, 0x17);
-+    pbus_data_low = phy_read(phydev, 0x18);
-+    pbus_data = (pbus_data_high << 16) + pbus_data_low;
-+    ret = phy_write(phydev, 0x1F, 0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return pbus_data;
-+}
-+
-+static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, const struct firmware *fw)
-+{
-+    unsigned int write_data, offset ;
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, 0x1F, (unsigned int)4);            /* page 4 */
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x10, (unsigned int)0x8000);        /* address increment*/
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x11, (unsigned int)((address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x12, (unsigned int)(address & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+
-+    for (offset = 0; offset < fw->size; offset += 4)
-+    {
-+        write_data = (fw->data[offset + 3] << 8) | fw->data[offset + 2];
-+        ret = phy_write(phydev, 0x13, write_data);
-+        if (ret < 0) {
-+            dev_err(dev, "phy_write, ret: %d\n", ret);
-+            return ret;
-+        }
-+        write_data = (fw->data[offset + 1] << 8) | fw->data[offset];
-+        ret = phy_write(phydev, 0x14, write_data);
-+        if (ret < 0) {
-+            dev_err(dev, "phy_write, ret: %d\n", ret);
-+            return ret;
-+        }
-+    }
-+    ret = phy_write(phydev, 0x1F, (unsigned int)0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+
-+static int en8811h_load_firmware(struct phy_device *phydev)
-+{
-+    struct device *dev = phydev_dev(phydev);
-+    const struct firmware *fw;
-+    const char *firmware;
-+    int ret = 0;
-+    unsigned int crc32;
-+    u32 pbus_value = 0;
-+
-+    ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
-+    AIR_RTN_ERR(ret);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
-+    pbus_value |= BIT(11);
-+    ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
-+    AIR_RTN_ERR(ret);
-+    firmware = EN8811H_MD32_DM;
-+    ret = request_firmware_direct(&fw, firmware, dev);
-+    if (ret < 0) {
-+        dev_info(dev, "failed to load firmware %s, ret: %d\n", firmware, ret);
-+        return ret;
-+    }
-+    crc32 = ~crc32(~0, fw->data, fw->size);
-+    dev_info(dev, "%s: crc32=0x%x\n", firmware, crc32);
-+    /* Download DM */
-+    ret = MDIOWriteBuf(phydev, 0x00000000, fw);
-+    if (ret < 0) {
-+        dev_info(dev, "MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
-+        return ret;
-+    }
-+    release_firmware(fw);
-+
-+    firmware = EN8811H_MD32_DSP;
-+    ret = request_firmware_direct(&fw, firmware, dev);
-+    if (ret < 0) {
-+        dev_info(dev, "failed to load firmware %s, ret: %d\n", firmware, ret);
-+        return ret;
-+    }
-+    crc32 = ~crc32(~0, fw->data, fw->size);
-+    dev_info(dev, "%s: crc32=0x%x\n", firmware, crc32);
-+    /* Download PM */
-+    ret = MDIOWriteBuf(phydev, 0x00100000, fw);
-+    if (ret < 0) {
-+            dev_info(dev, "MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
-+            return ret;
-+    }
-+    release_firmware(fw);
-+
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
-+    pbus_value &= ~BIT(11);
-+    ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
-+    AIR_RTN_ERR(ret);
-+    ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
-+    AIR_RTN_ERR(ret);
-+    return 0;
-+}
-+
-+#ifdef  AIR_LED_SUPPORT
-+static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity, int polar,
-+                                   u16 on_evt, u16 blk_evt)
-+{
-+    int ret = 0;
-+    if (AIR_ACTIVE_HIGH == polar) {
-+        on_evt |= LED_ON_POL;
-+    } else {
-+        on_evt &= ~LED_ON_POL ;
-+    }
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
-+    AIR_RTN_ERR(ret);
-+    return 0;
-+}
-+
-+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
-+{
-+    u16 cl45_data;
-+    int err = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
-+    switch (mode) {
-+    case AIR_LED_MODE_DISABLE:
-+        cl45_data &= ~LED_BCR_EXT_CTRL;
-+        cl45_data &= ~LED_BCR_MODE_MASK;
-+        cl45_data |= LED_BCR_MODE_DISABLE;
-+        break;
-+    case AIR_LED_MODE_USER_DEFINE:
-+        cl45_data |= LED_BCR_EXT_CTRL;
-+        cl45_data |= LED_BCR_CLK_EN;
-+        break;
-+    default:
-+        dev_err(dev, "LED mode%d is not supported!\n", mode);
-+        return -EINVAL;
-+    }
-+    err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
-+    AIR_RTN_ERR(err);
-+    return 0;
-+}
-+
-+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
-+{
-+    u16 cl45_data = 0;
-+    int err;
-+
-+    cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
-+    if (LED_ENABLE == state) {
-+        cl45_data |= LED_ON_EN;
-+    } else {
-+        cl45_data &= ~LED_ON_EN;
-+    }
-+
-+    err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
-+    AIR_RTN_ERR(err);
-+    return 0;
-+}
-+
-+static int en8811h_led_init(struct phy_device *phydev)
-+{
-+
-+    unsigned long led_gpio = 0, reg_value = 0;
-+    u16 cl45_data = led_dur;
-+    int ret = 0, led_id;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
-+    AIR_RTN_ERR(ret);
-+    cl45_data >>= 1;
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
-+    AIR_RTN_ERR(ret);
-+    ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
-+    if (ret != 0) {
-+        dev_err(dev, "LED fail to set mode, ret %d !\n", ret);
-+        return ret;
-+    }
-+    for(led_id = 0; led_id < EN8811H_LED_COUNT; led_id++)
-+    {
-+        /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
-+        if (led_cfg[led_id].gpio != (led_id + (AIR_LED0_GPIO5 - (2 * led_id))))
-+        {
-+            dev_err(dev, "LED%d uses incorrect GPIO%d !\n", led_id, led_cfg[led_id].gpio);
-+            return -EINVAL;
-+        }
-+        ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en);
-+        if (ret != 0)
-+        {
-+            dev_err(dev, "LED fail to set state, ret %d !\n", ret);
-+            return ret;
-+        }
-+        if (LED_ENABLE == led_cfg[led_id].en)
-+        {
-+            led_gpio |= BIT(led_cfg[led_id].gpio);
-+            ret = airoha_led_set_usr_def(phydev, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg);
-+            if (ret != 0)
-+            {
-+                dev_err(dev, "LED fail to set default, ret %d !\n", ret);
-+                return ret;
-+            }
-+        }
-+    }
-+    reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio;
-+    ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value);
-+    AIR_RTN_ERR(ret);
-+
-+    dev_info(dev, "LED initialize OK !\n");
-+    return 0;
-+}
-+#endif /* AIR_LED_SUPPORT */
-+#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 5, 0))
-+static int en8811h_get_features(struct phy_device *phydev)
-+{
-+    int ret;
-+    struct device *dev = phydev_dev(phydev);
-+    dev_info(dev, "%s()\n", __func__);
-+    ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
-+    AIR_RTN_ERR(ret);
-+    ret = genphy_read_abilities(phydev);
-+    if (ret)
-+        return ret;
-+
-+    /* EN8811H supports 100M/1G/2.5G speed. */
-+    linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-+               phydev->supported);
-+    linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
-+               phydev->supported);
-+    return 0;
-+}
-+#endif
-+static int en8811h_phy_probe(struct phy_device *phydev)
-+{
-+    int ret = 0;
-+    int reg_value, pid1 = 0, pid2 = 0;
-+    u32 pbus_value = 0, retry;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
-+    AIR_RTN_ERR(ret);
-+    pid1 = phy_read(phydev, MII_PHYSID1);
-+    if (pid1 < 0)
-+        return pid1;
-+    pid2 = phy_read(phydev, MII_PHYSID2);
-+    if (pid2 < 0)
-+        return pid2;
-+    dev_info(dev, "PHY = %x - %x\n", pid1, pid2);
-+    if ((EN8811H_PHY_ID1 != pid1) || (EN8811H_PHY_ID2 != pid2))
-+    {
-+        dev_err(dev, "EN8811H dose not exist !\n");
-+        return -ENODEV;
-+    }
-+    ret = en8811h_load_firmware(phydev);
-+    if (ret)
-+    {
-+        dev_err(dev,"EN8811H load firmware fail.\n");
-+        return ret;
-+    }
-+    retry = MAX_RETRY;
-+    do {
-+        mdelay(300);
-+        reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
-+        if (EN8811H_PHY_READY == reg_value)
-+        {
-+            dev_info(dev, "EN8811H PHY ready!\n");
-+            break;
-+        }
-+        retry--;
-+    } while (retry);
-+    if (0 == retry)
-+    {
-+        dev_err(dev, "EN8811H initialize fail ! reg: 0x%x\n", reg_value);
-+        return -EIO;
-+    }
-+    /* Mode selection*/
-+    dev_info(dev, "EN8811H Mode 1 !\n");
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
-+    AIR_RTN_ERR(ret);
-+
-+    /* Serdes polarity */
-+    pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
-+    pbus_value = (pbus_value & 0xfffffffc) | EN8811H_RX_POLARITY_NORMAL | EN8811H_TX_POLARITY_NORMAL;
-+    ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
-+    AIR_RTN_ERR(ret);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
-+    dev_info(dev, "0xca0f8= 0x%x\n", pbus_value);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
-+    dev_info(dev, "Version(0x3b3c)= %x\n", pbus_value);
-+#if defined(AIR_LED_SUPPORT)
-+    ret = en8811h_led_init(phydev);
-+    if (ret < 0)
-+    {
-+        dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret);
-+        return ret;
-+    }
-+#endif
-+    dev_info(dev, "EN8811H initialize OK ! (%s)\n", EN8811H_FW_VERSION);
-+    return 0;
-+}
-+
-+static int en8811h_get_autonego(struct phy_device *phydev, int *an)
-+{
-+    int reg;
-+    reg = phy_read(phydev, MII_BMCR);
-+    if (reg < 0)
-+        return -EINVAL;
-+    if (reg & BMCR_ANENABLE)
-+        *an = AUTONEG_ENABLE;
-+    else
-+        *an = AUTONEG_DISABLE;
-+    return 0;
-+}
-+
-+static int en8811h_read_status(struct phy_device *phydev)
-+{
-+    int ret = 0, lpagb = 0, lpa = 0, common_adv_gb = 0, common_adv = 0, advgb = 0, adv = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0;
-+    int old_link = phydev->link;
-+    u32 pbus_value = 0;
-+    struct device *dev = phydev_dev(phydev);
-+	ret = genphy_update_link(phydev);
-+	if (ret)
-+    {
-+        dev_err(dev, "ret %d!\n", ret);
-+		return ret;
-+    }
-+
-+	if (old_link && phydev->link)
-+		return 0;
-+
-+	phydev->speed = SPEED_UNKNOWN;
-+	phydev->duplex = DUPLEX_UNKNOWN;
-+	phydev->pause = 0;
-+	phydev->asym_pause = 0;
-+
-+    reg = phy_read(phydev, MII_BMSR);
-+    if (reg < 0)
-+    {
-+        dev_err(dev, "MII_BMSR reg %d!\n", reg);
-+		return reg;
-+    }
-+    reg = phy_read(phydev, MII_BMSR);
-+    if (reg < 0)
-+    {
-+        dev_err(dev, "MII_BMSR reg %d!\n", reg);
-+		return reg;
-+    }
-+    if(reg & BMSR_LSTATUS)
-+    {
-+        pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
-+        if (0x10 & pbus_value) {
-+            phydev->speed = SPEED_2500;
-+            phydev->duplex = DUPLEX_FULL;
-+        }
-+        else
-+        {
-+            ret = en8811h_get_autonego(phydev, &an);
-+            if ((AUTONEG_ENABLE == an) && (0 == ret))
-+            {
-+                dev_dbg(dev, "AN mode!\n");
-+                dev_dbg(dev, "SPEED 1000/100!\n");
-+                lpagb = phy_read(phydev, MII_STAT1000);
-+                if (lpagb < 0 )
-+                    return lpagb;
-+                advgb = phy_read(phydev, MII_CTRL1000);
-+                if (adv < 0 )
-+                    return adv;
-+                common_adv_gb = (lpagb & (advgb << 2));
-+
-+                lpa = phy_read(phydev, MII_LPA);
-+                if (lpa < 0 )
-+                    return lpa;
-+                adv = phy_read(phydev, MII_ADVERTISE);
-+                if (adv < 0 )
-+                    return adv;
-+                common_adv = (lpa & adv);
-+
-+                phydev->speed = SPEED_10;
-+                phydev->duplex = DUPLEX_HALF;
-+                if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF))
-+                {
-+                    phydev->speed = SPEED_1000;
-+                    if (common_adv_gb & LPA_1000FULL)
-+
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+                else if (common_adv & (LPA_100FULL | LPA_100HALF))
-+                {
-+                    phydev->speed = SPEED_100;
-+                    if (common_adv & LPA_100FULL)
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+                else
-+                {
-+                    if (common_adv & LPA_10FULL)
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+            }
-+            else
-+            {
-+                dev_dbg(dev, "Force mode!\n");
-+                bmcr = phy_read(phydev, MII_BMCR);
-+
-+                if (bmcr < 0)
-+                    return bmcr;
-+
-+                if (bmcr & BMCR_FULLDPLX)
-+                    phydev->duplex = DUPLEX_FULL;
-+                else
-+                    phydev->duplex = DUPLEX_HALF;
-+
-+                if (bmcr & BMCR_SPEED1000)
-+                    phydev->speed = SPEED_1000;
-+                else if (bmcr & BMCR_SPEED100)
-+                    phydev->speed = SPEED_100;
-+                else
-+                    phydev->speed = SPEED_UNKNOWN;
-+            }
-+        }
-+    }
-+
-+	return ret;
-+}
-+static struct phy_driver en8811h_driver[] = {
-+{
-+    .phy_id         = EN8811H_PHY_ID,
-+    .name           = "Airoha EN8811H",
-+    .phy_id_mask    = 0x0ffffff0,
-+    .probe          = en8811h_phy_probe,
-+    .read_status    = en8811h_read_status,
-+#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 5, 0))
-+    .get_features   = en8811h_get_features,
-+    .read_mmd       = air_mii_cl45_read,
-+    .write_mmd      = air_mii_cl45_write,
-+#endif
-+} };
-+
-+int __init en8811h_phy_driver_register(void)
-+{
-+    int ret;
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0))
-+    ret = phy_driver_register(en8811h_driver);
-+#else
-+    ret = phy_driver_register(en8811h_driver, THIS_MODULE);
-+#endif
-+    if (!ret)
-+        return 0;
-+
-+    phy_driver_unregister(en8811h_driver);
-+    return ret;
-+}
-+
-+void __exit en8811h_phy_driver_unregister(void)
-+{
-+    phy_driver_unregister(en8811h_driver);
-+}
-+
-+module_init(en8811h_phy_driver_register);
-+module_exit(en8811h_phy_driver_unregister);
-diff --git a/drivers/net/phy/air_en8811h.h b/drivers/net/phy/air_en8811h.h
-new file mode 100644
-index 000000000..1c9162795
---- /dev/null
-+++ b/drivers/net/phy/air_en8811h.h
-@@ -0,0 +1,151 @@
-+#ifndef __EN8811H_H
-+#define __EN8811H_H
-+
-+#define EN8811H_MD32_DM             "EthMD32.dm.bin"
-+#define EN8811H_MD32_DSP            "EthMD32.DSP.bin"
-+
-+#define EN8811H_PHY_ID1             0x03a2
-+#define EN8811H_PHY_ID2             0xa411
-+#define EN8811H_PHY_ID              ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2)
-+#define EN8811H_PHY_READY           0x02
-+#define MAX_RETRY                   5
-+
-+#define EN8811H_TX_POLARITY_NORMAL   0x1
-+#define EN8811H_TX_POLARITY_REVERSE  0x0
-+
-+#define EN8811H_RX_POLARITY_REVERSE  (0x1 << 1)
-+#define EN8811H_RX_POLARITY_NORMAL   (0x0 << 1)
-+
-+
-+/*
-+The following led_cfg example is for reference only.
-+LED0 Link 2500/Blink 2500 TxRx   (GPIO5)    <-> BASE_T_LED0,
-+LED1 Link 1000/Blink 1000 TxRx   (GPIO4)    <-> BASE_T_LED1,
-+LED2 Link 100 /Blink 100  TxRx   (GPIO3)    <-> BASE_T_LED2,
-+*/
-+/* User-defined.B */
-+#define BASE_T_LED0_ON_CFG      (LED_ON_EVT_LINK_2500M)
-+#define BASE_T_LED0_BLK_CFG     (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT)
-+#define BASE_T_LED1_ON_CFG      (LED_ON_EVT_LINK_1000M)
-+#define BASE_T_LED1_BLK_CFG     (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
-+#define BASE_T_LED2_ON_CFG      (LED_ON_EVT_LINK_100M)
-+#define BASE_T_LED2_BLK_CFG     (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
-+/* User-defined.E */
-+
-+/* CL45 MDIO control */
-+#define MII_MMD_ACC_CTL_REG         0x0d
-+#define MII_MMD_ADDR_DATA_REG       0x0e
-+#define MMD_OP_MODE_DATA            BIT(14)
-+
-+#define EN8811H_FW_VERSION          "1.1.3"
-+
-+#define LED_ON_CTRL(i)              (0x024 + ((i)*2))
-+#define LED_ON_EN                   (1 << 15)
-+#define LED_ON_POL                  (1 << 14)
-+#define LED_ON_EVT_MASK             (0x1ff)
-+/* LED ON Event Option.B */
-+#define LED_ON_EVT_LINK_2500M       (1 << 8)
-+#define LED_ON_EVT_FORCE            (1 << 6)
-+#define LED_ON_EVT_LINK_DOWN        (1 << 3)
-+#define LED_ON_EVT_LINK_100M        (1 << 1)
-+#define LED_ON_EVT_LINK_1000M       (1 << 0)
-+/* LED ON Event Option.E */
-+
-+#define LED_BLK_CTRL(i)             (0x025 + ((i)*2))
-+#define LED_BLK_EVT_MASK            (0xfff)
-+/* LED Blinking Event Option.B*/
-+#define LED_BLK_EVT_2500M_RX_ACT    (1 << 11)
-+#define LED_BLK_EVT_2500M_TX_ACT    (1 << 10)
-+#define LED_BLK_EVT_FORCE           (1 << 9)
-+#define LED_BLK_EVT_100M_RX_ACT     (1 << 3)
-+#define LED_BLK_EVT_100M_TX_ACT     (1 << 2)
-+#define LED_BLK_EVT_1000M_RX_ACT    (1 << 1)
-+#define LED_BLK_EVT_1000M_TX_ACT    (1 << 0)
-+/* LED Blinking Event Option.E*/
-+#define LED_ENABLE                  1
-+#define LED_DISABLE                 0
-+
-+#define EN8811H_LED_COUNT           3
-+
-+#define LED_BCR                     (0x021)
-+#define LED_BCR_EXT_CTRL            (1 << 15)
-+#define LED_BCR_CLK_EN              (1 << 3)
-+#define LED_BCR_TIME_TEST           (1 << 2)
-+#define LED_BCR_MODE_MASK           (3)
-+#define LED_BCR_MODE_DISABLE        (0)
-+
-+#define LED_ON_DUR                  (0x022)
-+#define LED_ON_DUR_MASK             (0xffff)
-+
-+#define LED_BLK_DUR                 (0x023)
-+#define LED_BLK_DUR_MASK            (0xffff)
-+
-+#define UNIT_LED_BLINK_DURATION     1024
-+
-+#define AIR_RTN_ON_ERR(cond, err)  \
-+    do { if ((cond)) return (err); } while(0)
-+
-+#define AIR_RTN_ERR(err)            AIR_RTN_ON_ERR(err < 0, err)
-+
-+#define LED_SET_EVT(reg, cod, result, bit) do         \
-+    {                                                 \
-+        if(reg & cod) {                               \
-+            result |= bit;                            \
-+        }                                             \
-+    } while(0)
-+
-+#define LED_SET_GPIO_SEL(gpio, led, val) do           \
-+    {                                                 \
-+        val |= (led << (8 * (gpio % 4)));         \
-+    } while(0)
-+
-+#define INVALID_DATA                0xffff
-+#define PBUS_INVALID_DATA           0xffffffff
-+
-+typedef struct AIR_BASE_T_LED_CFG_S
-+{
-+    u16 en;
-+    u16 gpio;
-+    u16 pol;
-+    u16 on_cfg;
-+    u16 blk_cfg;
-+}AIR_BASE_T_LED_CFG_T;
-+typedef enum
-+{
-+    AIR_LED2_GPIO3 = 3,
-+    AIR_LED1_GPIO4,
-+    AIR_LED0_GPIO5,
-+    AIR_LED_LAST
-+} AIR_LED_GPIO;
-+
-+typedef enum {
-+    AIR_BASE_T_LED0,
-+    AIR_BASE_T_LED1,
-+    AIR_BASE_T_LED2,
-+    AIR_BASE_T_LED3
-+}AIR_BASE_T_LED;
-+
-+typedef enum
-+{
-+    AIR_LED_BLK_DUR_32M,
-+    AIR_LED_BLK_DUR_64M,
-+    AIR_LED_BLK_DUR_128M,
-+    AIR_LED_BLK_DUR_256M,
-+    AIR_LED_BLK_DUR_512M,
-+    AIR_LED_BLK_DUR_1024M,
-+    AIR_LED_BLK_DUR_LAST
-+} AIR_LED_BLK_DUT_T;
-+
-+typedef enum
-+{
-+    AIR_ACTIVE_LOW,
-+    AIR_ACTIVE_HIGH,
-+} AIR_LED_POLARITY;
-+typedef enum
-+{
-+    AIR_LED_MODE_DISABLE,
-+    AIR_LED_MODE_USER_DEFINE,
-+    AIR_LED_MODE_LAST
-+} AIR_LED_MODE_T;
-+
-+#endif /* End of __EN8811H_MD32_H */
 -- 
 2.34.1
 
diff --git a/target/linux/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch b/target/linux/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch
index 47168b9..27ba8be 100644
--- a/target/linux/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch
@@ -33,6 +33,7 @@
  aquantia-objs			+= aquantia_hwmon.o
  endif
 +obj-$(CONFIG_AIROHA_EN8801SC_PHY)	+= en8801sc.o
+ air_en8811h-y := air_en8811h_main.o air_en8811h_api.o
  obj-$(CONFIG_AIROHA_EN8811H_PHY)	+= air_en8811h.o
  obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia.o
  obj-$(CONFIG_AX88796B_PHY)	+= ax88796b.o