[openwrt/openwrt] mediatek: add Airoha EN8801SC PHY driver

LEDE Commits lede-commits at lists.infradead.org
Thu Apr 11 06:34:02 PDT 2024


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/2dba4308d10d638d3da6ca354d82e3e469865ed5

commit 2dba4308d10d638d3da6ca354d82e3e469865ed5
Author: Robert Marko <robert.marko at sartura.hr>
AuthorDate: Thu Apr 11 13:09:56 2024 +0200

    mediatek: add Airoha EN8801SC PHY driver
    
    Airoha EN8801SC PHY is a gigabit PHY used on Edgecore EAP111 so, include
    the MTK driver with some cleanups.
    
    Unfortunatelly, there is no specification sheet nor datasheet available
    in order to demistify the magic PBUS writes and work on upstreaming
    this driver.
    
    Signed-off-by: Robert Marko <robert.marko at sartura.hr>
---
 .../mediatek/files/drivers/net/phy/en8801sc.c      | 1117 ++++++++++++++++++++
 .../mediatek/files/drivers/net/phy/en8801sc.h      |  250 +++++
 target/linux/mediatek/filogic/config-6.1           |    1 +
 target/linux/mediatek/filogic/config-6.6           |    1 +
 target/linux/mediatek/mt7622/config-6.1            |    1 +
 target/linux/mediatek/mt7622/config-6.6            |    1 +
 target/linux/mediatek/mt7623/config-6.1            |    1 +
 target/linux/mediatek/mt7623/config-6.6            |    1 +
 target/linux/mediatek/mt7629/config-6.1            |    1 +
 target/linux/mediatek/mt7629/config-6.6            |    1 +
 .../735-net-phy-add-Airoha-EN8801SC-PHY.patch      |   38 +
 .../734-net-phy-add-Airoha-EN8801SC-PHY.patch      |   38 +
 12 files changed, 1451 insertions(+)

diff --git a/target/linux/mediatek/files/drivers/net/phy/en8801sc.c b/target/linux/mediatek/files/drivers/net/phy/en8801sc.c
new file mode 100644
index 0000000000..08774e3b88
--- /dev/null
+++ b/target/linux/mediatek/files/drivers/net/phy/en8801sc.c
@@ -0,0 +1,1117 @@
+// SPDX-License-Identifier: GPL-2.0
+/* FILE NAME:  en8801sc.c
+ * PURPOSE:
+ *      EN8801SC phy driver for Linux
+ * NOTES:
+ *
+ */
+
+/* INCLUDE FILE DECLARATIONS
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#include "en8801sc.h"
+
+MODULE_DESCRIPTION("Airoha EN8801S PHY drivers for MediaTek SoC");
+MODULE_AUTHOR("Airoha");
+MODULE_LICENSE("GPL");
+
+#define airoha_mdio_lock(bus)   mutex_lock(&((bus)->mdio_lock))
+#define airoha_mdio_unlock(bus) mutex_unlock(&((bus)->mdio_lock))
+
+#define phydev_mdio_bus(_dev) (_dev->mdio.bus)
+#define phydev_phy_addr(_dev) (_dev->mdio.addr)
+#define phydev_dev(_dev) (&_dev->mdio.dev)
+#define phydev_pbus_addr(dev) ((dev)->mdio.addr + 1)
+
+enum {
+	PHY_STATE_DONE = 0,
+	PHY_STATE_INIT = 1,
+	PHY_STATE_PROCESS = 2,
+	PHY_STATE_FAIL = 3,
+};
+
+struct en8801s_priv {
+	bool first_init;
+	u16 count;
+	u16 pro_version;
+};
+
+/*
+The following led_cfg example is for reference only.
+LED5 1000M/LINK/ACT   (GPIO5)  <-> BASE_T_LED0,
+LED6 10/100M/LINK/ACT (GPIO9)  <-> BASE_T_LED1,
+LED4 100M/LINK/ACT    (GPIO8)  <-> BASE_T_LED2,
+*/
+/* User-defined.B */
+#define AIR_LED_SUPPORT
+#ifdef AIR_LED_SUPPORT
+static const struct AIR_BASE_T_LED_CFG_S led_cfg[4] = {
+/*
+*   {LED Enable,    GPIO,   LED Polarity,   LED ON, LED Blink}
+*/
+	/* BASE-T LED0 */
+	{LED_ENABLE, 5, AIR_ACTIVE_LOW,
+	 BASE_T_LED0_ON_CFG, BASE_T_LED0_BLK_CFG},
+	/* BASE-T LED1 */
+	{LED_ENABLE, 9, AIR_ACTIVE_LOW,
+	 BASE_T_LED1_ON_CFG, BASE_T_LED1_BLK_CFG},
+	/* BASE-T LED2 */
+	{LED_ENABLE, 8, AIR_ACTIVE_LOW,
+	 BASE_T_LED2_ON_CFG, BASE_T_LED2_BLK_CFG},
+	/* BASE-T LED3 */
+	{LED_DISABLE, 1, AIR_ACTIVE_LOW,
+	 BASE_T_LED3_ON_CFG, BASE_T_LED3_BLK_CFG},
+};
+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 en8801s_phase2_init(struct phy_device *phydev);
+
+static int __airoha_cl45_write(struct mii_bus *bus, int port,
+			u32 devad, u32 reg, u16 val)
+{
+	int ret = 0;
+	struct device *dev = &bus->dev;
+
+	ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+	ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+	ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG,
+				MMD_OP_MODE_DATA | devad);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+	ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, val);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int __airoha_cl45_read(struct mii_bus *bus, int port,
+			u32 devad, u32 reg, u16 *read_data)
+{
+	int ret = 0;
+	struct device *dev = &bus->dev;
+
+	ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+	ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+	ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG,
+				MMD_OP_MODE_DATA | devad);
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return ret;
+	}
+	*read_data = __mdiobus_read(bus, port, MII_MMD_ADDR_DATA_REG);
+
+	return ret;
+}
+
+static int airoha_cl45_write(struct mii_bus *bus, int port,
+			u32 devad, u32 reg, u16 val)
+{
+	int ret = 0;
+
+	airoha_mdio_lock(bus);
+	ret = __airoha_cl45_write(bus, port, devad, reg, val);
+	airoha_mdio_unlock(bus);
+
+	return ret;
+}
+
+static int airoha_cl45_read(struct mii_bus *bus, int port,
+			u32 devad, u32 reg, u16 *read_data)
+{
+	int ret = 0;
+
+	airoha_mdio_lock(bus);
+	ret = __airoha_cl45_read(bus, port, devad, reg, read_data);
+	airoha_mdio_unlock(bus);
+
+	return ret;
+}
+
+static int __airoha_pbus_write(struct mii_bus *ebus, int pbus_id,
+			unsigned long pbus_address, unsigned long pbus_data)
+{
+	int ret = 0;
+
+	ret = __mdiobus_write(ebus, pbus_id, 0x1F,
+				(unsigned int)(pbus_address >> 6));
+	if (ret < 0)
+		return ret;
+	ret = __mdiobus_write(ebus, pbus_id,
+				(unsigned int)((pbus_address >> 2) & 0xf),
+				(unsigned int)(pbus_data & 0xFFFF));
+	if (ret < 0)
+		return ret;
+	ret = __mdiobus_write(ebus, pbus_id, 0x10,
+				(unsigned int)(pbus_data >> 16));
+	if (ret < 0)
+		return ret;
+	return ret;
+}
+
+static unsigned long __airoha_pbus_read(struct mii_bus *ebus, int pbus_id,
+			unsigned long pbus_address)
+{
+	unsigned long pbus_data;
+	unsigned int pbus_data_low, pbus_data_high;
+	int ret = 0;
+	struct device *dev = &ebus->dev;
+
+	ret = __mdiobus_write(ebus, pbus_id, 0x1F,
+				(unsigned int)(pbus_address >> 6));
+	if (ret < 0) {
+		dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret);
+		return INVALID_DATA;
+	}
+	pbus_data_low = __mdiobus_read(ebus, pbus_id,
+				(unsigned int)((pbus_address >> 2) & 0xf));
+	pbus_data_high = __mdiobus_read(ebus, pbus_id, 0x10);
+	pbus_data = (pbus_data_high << 16) + pbus_data_low;
+	return pbus_data;
+}
+
+static int airoha_pbus_write(struct mii_bus *ebus, int pbus_id,
+			unsigned long pbus_address, unsigned long pbus_data)
+{
+	int ret = 0;
+
+	airoha_mdio_lock(ebus);
+	ret = __airoha_pbus_write(ebus, pbus_id, pbus_address, pbus_data);
+	airoha_mdio_unlock(ebus);
+
+	return ret;
+}
+
+static unsigned long airoha_pbus_read(struct mii_bus *ebus, int pbus_id,
+			unsigned long pbus_address)
+{
+	unsigned long pbus_data;
+
+	airoha_mdio_lock(ebus);
+	pbus_data = __airoha_pbus_read(ebus, pbus_id, pbus_address);
+	airoha_mdio_unlock(ebus);
+
+	return pbus_data;
+}
+
+/* Airoha Token Ring Write function */
+static int airoha_tr_reg_write(struct phy_device *phydev,
+			unsigned long tr_address, unsigned long tr_data)
+{
+	int ret = 0;
+	int phy_addr = phydev_phy_addr(phydev);
+	struct mii_bus *ebus = phydev_mdio_bus(phydev);
+
+	airoha_mdio_lock(ebus);
+	ret = __mdiobus_write(ebus, phy_addr, 0x1F, 0x52b5); /* page select */
+	ret = __mdiobus_write(ebus, phy_addr, 0x11,
+				(unsigned int)(tr_data & 0xffff));
+	ret = __mdiobus_write(ebus, phy_addr, 0x12,
+				(unsigned int)(tr_data >> 16));
+	ret = __mdiobus_write(ebus, phy_addr, 0x10,
+				(unsigned int)(tr_address | TrReg_WR));
+	ret = __mdiobus_write(ebus, phy_addr, 0x1F, 0x0);    /* page resetore */
+	airoha_mdio_unlock(ebus);
+
+	return ret;
+}
+
+#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;
+	int phy_addr = phydev_phy_addr(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	if (polar == AIR_ACTIVE_HIGH)
+		on_evt |= LED_ON_POL;
+	else
+		on_evt &= ~LED_ON_POL;
+
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1f,
+				LED_ON_CTRL(entity), on_evt | LED_ON_EN);
+	if (ret < 0)
+		return ret;
+
+	ret = airoha_cl45_write(mbus, phy_addr, 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;
+	int phy_addr = phydev_phy_addr(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	err = airoha_cl45_read(mbus, phy_addr, 0x1f, LED_BCR, &cl45_data);
+	if (err < 0)
+		return err;
+
+	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:
+		return -EINVAL;
+	}
+
+	err = airoha_cl45_write(mbus, phy_addr, 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;
+	int err;
+	int phy_addr = phydev_phy_addr(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	err = airoha_cl45_read(mbus, phy_addr, 0x1f,
+				LED_ON_CTRL(entity), &cl45_data);
+	if (err < 0)
+		return err;
+	if (state == LED_ENABLE)
+		cl45_data |= LED_ON_EN;
+	else
+		cl45_data &= ~LED_ON_EN;
+
+	err = airoha_cl45_write(mbus, phy_addr, 0x1f,
+				LED_ON_CTRL(entity), cl45_data);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int en8801s_led_init(struct phy_device *phydev)
+{
+
+	unsigned long led_gpio = 0, reg_value = 0;
+	int ret = 0, led_id;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int gpio_led_rg[3] = {0x1870, 0x1874, 0x1878};
+	u16 cl45_data = led_dur;
+	struct device *dev = phydev_dev(phydev);
+	int phy_addr = phydev_phy_addr(phydev);
+	int pbus_addr = phydev_pbus_addr(phydev);
+
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_BLK_DUR, cl45_data);
+	if (ret < 0)
+		return ret;
+	cl45_data >>= 1;
+	ret = airoha_cl45_write(mbus, phy_addr, 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 fail to set mode, ret %d !\n", ret);
+		return ret;
+	}
+	for (led_id = 0; led_id < EN8801S_LED_COUNT; led_id++) {
+		reg_value = 0;
+		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_cfg[led_id].en == LED_ENABLE) {
+			if ((led_cfg[led_id].gpio < 0)
+				|| led_cfg[led_id].gpio > 9) {
+				dev_err(dev, "GPIO%d is out of range!! GPIO number is 0~9.\n",
+					led_cfg[led_id].gpio);
+				return -EIO;
+			}
+			led_gpio |= BIT(led_cfg[led_id].gpio);
+			reg_value = airoha_pbus_read(mbus, pbus_addr,
+					gpio_led_rg[led_cfg[led_id].gpio / 4]);
+			LED_SET_GPIO_SEL(led_cfg[led_id].gpio,
+					led_id, reg_value);
+			dev_dbg(dev, "[Airoha] gpio%d, reg_value 0x%lx\n",
+					led_cfg[led_id].gpio, reg_value);
+			ret = airoha_pbus_write(mbus, pbus_addr,
+					gpio_led_rg[led_cfg[led_id].gpio / 4],
+					reg_value);
+			if (ret < 0)
+				return ret;
+			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 usr def, ret %d !\n",
+				ret);
+				return ret;
+			}
+		}
+	}
+	reg_value = (airoha_pbus_read(mbus, pbus_addr, 0x1880) & ~led_gpio);
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x1880, reg_value);
+	if (ret < 0)
+		return ret;
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x186c, led_gpio);
+	if (ret < 0)
+		return ret;
+	dev_info(dev, "LED initialize OK !\n");
+	return 0;
+}
+#endif
+static int en8801s_phy_process(struct phy_device *phydev)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	unsigned long reg_value = 0;
+	int ret = 0;
+	int pbus_addr = phydev_pbus_addr(phydev);
+
+	reg_value = airoha_pbus_read(mbus, pbus_addr, 0x19e0);
+	reg_value |= BIT(0);
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, reg_value);
+	if (ret < 0)
+		return ret;
+	reg_value = airoha_pbus_read(mbus, pbus_addr, 0x19e0);
+	reg_value &= ~BIT(0);
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, reg_value);
+	if (ret < 0)
+		return ret;
+	return ret;
+}
+
+static int en8801s_phase1_init(struct phy_device *phydev)
+{
+	unsigned long pbus_data;
+	int pbus_addr = EN8801S_PBUS_DEFAULT_ADDR;
+	u16 reg_value;
+	int retry, ret = 0;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	struct device *dev = phydev_dev(phydev);
+	struct en8801s_priv *priv = phydev->priv;
+
+	priv->count = 1;
+	msleep(1000);
+
+	retry = MAX_OUI_CHECK;
+	while (1) {
+		pbus_data = airoha_pbus_read(mbus, pbus_addr,
+				EN8801S_RG_ETHER_PHY_OUI);      /* PHY OUI */
+		if (pbus_data == EN8801S_PBUS_OUI) {
+			dev_info(dev, "PBUS addr 0x%x: Start initialized.\n",
+					pbus_addr);
+			break;
+		}
+		pbus_addr = phydev_pbus_addr(phydev);
+		if (0 == --retry) {
+			dev_err(dev, "Probe fail !\n");
+			return 0;
+		}
+	}
+
+	ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_BUCK_CTL, 0x03);
+	if (ret < 0)
+		return ret;
+	pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_PROD_VER);
+	priv->pro_version = pbus_data & 0xf;
+	dev_info(dev, "EN8801S Procduct Version :E%d\n", priv->pro_version);
+	mdelay(10);
+	pbus_data = (airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_LTR_CTL)
+				 & 0xfffffffc) | BIT(2);
+	ret = airoha_pbus_write(mbus, pbus_addr,
+				EN8801S_RG_LTR_CTL, pbus_data);
+	if (ret < 0)
+		return ret;
+	mdelay(500);
+	pbus_data = (pbus_data & ~BIT(2)) |
+				EN8801S_RX_POLARITY_NORMAL |
+				EN8801S_TX_POLARITY_NORMAL;
+	ret = airoha_pbus_write(mbus, pbus_addr,
+				EN8801S_RG_LTR_CTL, pbus_data);
+	if (ret < 0)
+		return ret;
+	mdelay(500);
+	if (priv->pro_version == 4) {
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1900);
+		dev_dbg(dev, "Before 0x1900 0x%lx\n", pbus_data);
+		ret = airoha_pbus_write(mbus, pbus_addr, 0x1900, 0x101009f);
+		if (ret < 0)
+			return ret;
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1900);
+		dev_dbg(dev, "After 0x1900 0x%lx\n", pbus_data);
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19a8);
+		dev_dbg(dev, "Before 19a8 0x%lx\n", pbus_data);
+		ret = airoha_pbus_write(mbus, pbus_addr,
+				0x19a8, pbus_data & ~BIT(16));
+		if (ret < 0)
+			return ret;
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19a8);
+		dev_dbg(dev, "After 19a8 0x%lx\n", pbus_data);
+	}
+	pbus_data = airoha_pbus_read(mbus, pbus_addr,
+				EN8801S_RG_SMI_ADDR); /* SMI ADDR */
+	pbus_data = (pbus_data & 0xffff0000) |
+				(unsigned long)(phydev_pbus_addr(phydev) << 8) |
+				(unsigned long)(phydev_phy_addr(phydev));
+	dev_info(phydev_dev(phydev), "SMI_ADDR=%lx (renew)\n", pbus_data);
+	ret = airoha_pbus_write(mbus, pbus_addr,
+				EN8801S_RG_SMI_ADDR, pbus_data);
+	mdelay(10);
+
+	retry = MAX_RETRY;
+	while (1) {
+		mdelay(10);
+		reg_value = phy_read(phydev, MII_PHYSID2);
+		if (reg_value == EN8801S_PHY_ID2)
+			break;    /* wait GPHY ready */
+
+		retry--;
+		if (retry == 0) {
+			dev_err(dev, "Initialize fail !\n");
+			return 0;
+		}
+	}
+	/* Software Reset PHY */
+	reg_value = phy_read(phydev, MII_BMCR);
+	reg_value |= BMCR_RESET;
+	ret = phy_write(phydev, MII_BMCR, reg_value);
+	if (ret < 0)
+		return ret;
+	retry = MAX_RETRY;
+	do {
+		mdelay(10);
+		reg_value = phy_read(phydev, MII_BMCR);
+		retry--;
+		if (retry == 0) {
+			dev_err(dev, "Reset fail !\n");
+			return 0;
+		}
+	} while (reg_value & BMCR_RESET);
+
+	phydev->dev_flags = PHY_STATE_INIT;
+
+	dev_info(dev, "Phase1 initialize OK ! (%s)\n", EN8801S_DRIVER_VERSION);
+	if (priv->pro_version == 4) {
+		ret = en8801s_phase2_init(phydev);
+		if (ret != 0) {
+			dev_info(dev, "en8801_phase2_init failed\n");
+			phydev->dev_flags = PHY_STATE_FAIL;
+			return 0;
+		}
+		phydev->dev_flags = PHY_STATE_PROCESS;
+	}
+
+	return 0;
+}
+
+static int en8801s_phase2_init(struct phy_device *phydev)
+{
+	union gephy_all_REG_LpiReg1Ch      GPHY_RG_LPI_1C;
+	union gephy_all_REG_dev1Eh_reg324h GPHY_RG_1E_324;
+	union gephy_all_REG_dev1Eh_reg012h GPHY_RG_1E_012;
+	union gephy_all_REG_dev1Eh_reg017h GPHY_RG_1E_017;
+	unsigned long pbus_data;
+	int phy_addr = phydev_phy_addr(phydev);
+	int pbus_addr = phydev_pbus_addr(phydev);
+	u16 cl45_value;
+	int retry, ret = 0;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	struct device *dev = phydev_dev(phydev);
+	struct en8801s_priv *priv = phydev->priv;
+
+	pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1690);
+	pbus_data |= BIT(31);
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x1690, pbus_data);
+	if (ret < 0)
+		return ret;
+
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
+	if (ret < 0)
+		return ret;
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD801);
+	if (ret < 0)
+		return ret;
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x0,  0x9140);
+	if (ret < 0)
+		return ret;
+
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0003);
+	if (ret < 0)
+		return ret;
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
+	if (ret < 0)
+		return ret;
+	/* Set FCM control */
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x004b);
+	if (ret < 0)
+		return ret;
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007);
+	if (ret < 0)
+		return ret;
+
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x142c, 0x05050505);
+	if (ret < 0)
+		return ret;
+	pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1440);
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x1440, pbus_data & ~BIT(11));
+	if (ret < 0)
+		return ret;
+
+	pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1408);
+	ret = airoha_pbus_write(mbus, pbus_addr, 0x1408, pbus_data | BIT(5));
+	if (ret < 0)
+		return ret;
+
+	/* Set GPHY Perfomance*/
+	/* Token Ring */
+	ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_15h, 0x0055A0);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_17h, 0x07FF3F);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_PMA_00h,      0x00001E);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_PMA_01h,      0x6FB90A);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_PMA_17h,      0x060671);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_PMA_18h,      0x0E2F00);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_TR_26h,       0x444444);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_03h,     0x000000);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_06h,     0x2EBAEF);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_08h,     0x00000B);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Ch,     0x00504D);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Dh,     0x02314F);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Fh,     0x003028);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_10h,     0x005010);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_11h,     0x040001);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_13h,     0x018670);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_14h,     0x00024A);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Bh,     0x000072);
+	if (ret < 0)
+		return ret;
+	ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Ch,     0x003210);
+	if (ret < 0)
+		return ret;
+
+	/* CL22 & CL45 */
+	ret = phy_write(phydev, 0x1f, 0x03);
+	if (ret < 0)
+		return ret;
+	GPHY_RG_LPI_1C.DATA = phy_read(phydev, RgAddr_LPI_1Ch);
+	GPHY_RG_LPI_1C.DataBitField.smi_deton_th = 0x0C;
+	ret = phy_write(phydev, RgAddr_LPI_1Ch, GPHY_RG_LPI_1C.DATA);
+	if (ret < 0)
+		return ret;
+	ret = phy_write(phydev, RgAddr_LPI_1Ch, 0xC92);
+	if (ret < 0)
+		return ret;
+	ret = phy_write(phydev, RgAddr_AUXILIARY_1Dh, 0x1);
+	if (ret < 0)
+		return ret;
+	ret = phy_write(phydev, 0x1f, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x120, 0x8014);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x122, 0xffff);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x123, 0xffff);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x144, 0x0200);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x14A, 0xEE20);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x189, 0x0110);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x19B, 0x0111);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x234, 0x0181);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x238, 0x0120);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x239, 0x0117);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x268, 0x07F4);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x2D1, 0x0733);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x323, 0x0011);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324, 0x013F);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x326, 0x0037);
+	if (ret < 0)
+		return ret;
+
+	ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x324, &cl45_value);
+	if (ret < 0)
+		return ret;
+	GPHY_RG_1E_324.DATA = cl45_value;
+	GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = 0;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324,
+				GPHY_RG_1E_324.DATA);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x19E, 0xC2);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x013, 0x0);
+	if (ret < 0)
+		return ret;
+
+	/* EFUSE */
+	airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40000040);
+	retry = MAX_RETRY;
+	while (retry != 0) {
+		mdelay(1);
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08);
+		if ((pbus_data & BIT(30)) == 0)
+			break;
+
+		retry--;
+	}
+	pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C38); /* RAW#2 */
+	ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x12, &cl45_value);
+	if (ret < 0)
+		return ret;
+	GPHY_RG_1E_012.DATA = cl45_value;
+	GPHY_RG_1E_012.DataBitField.da_tx_i2mpb_a_tbt =
+				(u16)(pbus_data & 0x03f);
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x12,
+				GPHY_RG_1E_012.DATA);
+	if (ret < 0)
+		return ret;
+	ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x17, &cl45_value);
+	if (ret < 0)
+		return ret;
+	GPHY_RG_1E_017.DATA = cl45_value;
+	GPHY_RG_1E_017.DataBitField.da_tx_i2mpb_b_tbt =
+				(u16)((pbus_data >> 8) & 0x03f);
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x17,
+				GPHY_RG_1E_017.DATA);
+	if (ret < 0)
+		return ret;
+
+	airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40400040);
+	retry = MAX_RETRY;
+	while (retry != 0) {
+		mdelay(1);
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08);
+		if ((pbus_data & BIT(30)) == 0)
+			break;
+
+		retry--;
+	}
+	pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C30); /* RAW#16 */
+	GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off =
+				(u16)((pbus_data >> 12) & 0x01);
+	ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324,
+				GPHY_RG_1E_324.DATA);
+	if (ret < 0)
+		return ret;
+#ifdef AIR_LED_SUPPORT
+	ret = en8801s_led_init(phydev);
+	if (ret != 0)
+		dev_err(dev, "en8801s_led_init fail (ret:%d) !\n", ret);
+#endif
+
+	ret = airoha_cl45_read(mbus, phy_addr, MDIO_MMD_AN,
+				MDIO_AN_EEE_ADV, &cl45_value);
+	if (ret < 0)
+		return ret;
+	if (cl45_value == 0) {
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1960);
+		if (0xA == ((pbus_data & 0x07c00000) >> 22)) {
+			pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22);
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
+						pbus_data);
+			if (ret < 0)
+				return ret;
+			mdelay(10);
+			pbus_data = (pbus_data & 0xf83fffff) | (0xE << 22);
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
+						pbus_data);
+			if (ret < 0)
+				return ret;
+			mdelay(10);
+		}
+	} else {
+		pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1960);
+		if (0xE == ((pbus_data & 0x07c00000) >> 22)) {
+			pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22);
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
+						pbus_data);
+			if (ret < 0)
+				return ret;
+			mdelay(10);
+			pbus_data = (pbus_data & 0xf83fffff) | (0xA << 22);
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1960,
+						pbus_data);
+			if (ret < 0)
+				return ret;
+			mdelay(10);
+		}
+	}
+
+	priv->first_init = false;
+	dev_info(phydev_dev(phydev), "Phase2 initialize OK !\n");
+	return 0;
+}
+
+static int en8801s_read_status(struct phy_device *phydev)
+{
+	int ret = 0, preSpeed = phydev->speed;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	u32 reg_value;
+	struct device *dev = phydev_dev(phydev);
+	int pbus_addr = phydev_pbus_addr(phydev);
+	struct en8801s_priv *priv = phydev->priv;
+
+	ret = genphy_read_status(phydev);
+	if (phydev->link == LINK_DOWN)
+		preSpeed = phydev->speed = 0;
+
+	if (phydev->dev_flags == PHY_STATE_PROCESS) {
+		en8801s_phy_process(phydev);
+		phydev->dev_flags = PHY_STATE_DONE;
+	}
+
+	if (phydev->dev_flags == PHY_STATE_INIT) {
+		dev_dbg(dev, "phydev->link %d, count %d\n",
+					phydev->link, priv->count);
+		if ((phydev->link) || (priv->count == 5)) {
+			if (priv->pro_version != 4) {
+				ret = en8801s_phase2_init(phydev);
+				if (ret != 0) {
+					dev_info(dev, "en8801_phase2_init failed\n");
+					phydev->dev_flags = PHY_STATE_FAIL;
+					return 0;
+				}
+				phydev->dev_flags = PHY_STATE_PROCESS;
+			}
+		}
+		priv->count++;
+	}
+
+	if ((preSpeed != phydev->speed) && (phydev->link == LINK_UP)) {
+		preSpeed = phydev->speed;
+
+		if (preSpeed == SPEED_10) {
+			reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1694);
+			reg_value |= BIT(31);
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1694,
+					reg_value);
+			if (ret < 0)
+				return ret;
+			phydev->dev_flags = PHY_STATE_PROCESS;
+		} else {
+			reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1694);
+			reg_value &= ~BIT(31);
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1694,
+					reg_value);
+			if (ret < 0)
+				return ret;
+			phydev->dev_flags = PHY_STATE_PROCESS;
+		}
+
+		airoha_pbus_write(mbus, pbus_addr, 0x0600,
+				0x0c000c00);
+		if (preSpeed == SPEED_1000) {
+			dev_dbg(dev, "SPEED_1000\n");
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x10,
+					0xD801);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0,
+					0x9140);
+			if (ret < 0)
+				return ret;
+
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14,
+					0x0003);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0600,
+					0x0c000c00);
+			if (ret < 0)
+				return ret;
+			mdelay(2);      /* delay 2 ms */
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1404,
+					0x004b);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x140c,
+					0x0007);
+			if (ret < 0)
+				return ret;
+		} else if (preSpeed == SPEED_100) {
+			dev_dbg(dev, "SPEED_100\n");
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x10,
+					0xD401);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0,
+					0x9140);
+			if (ret < 0)
+				return ret;
+
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14,
+					0x0007);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0600,
+					0x0c11);
+			if (ret < 0)
+				return ret;
+			mdelay(2);      /* delay 2 ms */
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1404,
+					0x0027);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x140c,
+					0x0007);
+			if (ret < 0)
+				return ret;
+		} else if (preSpeed == SPEED_10) {
+			dev_dbg(dev, "SPEED_10\n");
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x10,
+					0xD001);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0,
+					0x9140);
+			if (ret < 0)
+				return ret;
+
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14,
+					0x000b);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x0600,
+					0x0c11);
+			if (ret < 0)
+				return ret;
+			mdelay(2);      /* delay 2 ms */
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x1404,
+					0x0027);
+			if (ret < 0)
+				return ret;
+			ret = airoha_pbus_write(mbus, pbus_addr, 0x140c,
+					0x0007);
+			if (ret < 0)
+				return ret;
+		}
+	}
+	return ret;
+}
+
+static int en8801s_probe(struct phy_device *phydev)
+{
+	struct en8801s_priv *priv;
+	unsigned long phy_addr = phydev_phy_addr(phydev);
+	struct mdio_device *mdiodev = &phydev->mdio;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->count = 0;
+	priv->first_init = true;
+
+	if (mdiodev->reset_gpio) {
+		dev_dbg(phydev_dev(phydev),
+				"Assert PHY %lx HWRST until phy_init_hw\n",
+				phy_addr);
+		phy_device_reset(phydev, 1);
+	}
+
+	phydev->priv = priv;
+
+	return 0;
+}
+
+static int airoha_mmd_read(struct phy_device *phydev,
+			int devad, u16 reg)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int phy_addr = phydev_phy_addr(phydev);
+	int ret = 0;
+	u16 cl45_value;
+
+	ret = __airoha_cl45_read(mbus, phy_addr, devad, reg, &cl45_value);
+	if (ret < 0)
+		return ret;
+
+	return cl45_value;
+}
+
+static int airoha_mmd_write(struct phy_device *phydev,
+			int devad, u16 reg, u16 val)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int phy_addr = phydev_phy_addr(phydev);
+	int pbus_addr = phydev_pbus_addr(phydev);
+	unsigned long pbus_data;
+	int ret = 0;
+
+	if (MDIO_MMD_AN == devad && MDIO_AN_EEE_ADV == reg) {
+		if (val == 0) {
+			pbus_data = __airoha_pbus_read(mbus, pbus_addr, 0x1960);
+			if (0xA == ((pbus_data & 0x07c00000) >> 22)) {
+				pbus_data = (pbus_data & 0xf83fffff) |
+							(0xC << 22);
+				__airoha_pbus_write(mbus, pbus_addr, 0x1960,
+							pbus_data);
+				mdelay(10);
+				pbus_data = (pbus_data & 0xf83fffff) |
+							(0xE << 22);
+				__airoha_pbus_write(mbus, pbus_addr, 0x1960,
+							pbus_data);
+				mdelay(10);
+			}
+		} else {
+			pbus_data = __airoha_pbus_read(mbus, pbus_addr, 0x1960);
+			if (0xE == ((pbus_data & 0x07c00000) >> 22)) {
+				pbus_data = (pbus_data & 0xf83fffff) |
+							(0xC << 22);
+				__airoha_pbus_write(mbus, pbus_addr, 0x1960,
+							pbus_data);
+				mdelay(10);
+				pbus_data = (pbus_data & 0xf83fffff) |
+							(0xA << 22);
+				__airoha_pbus_write(mbus, pbus_addr, 0x1960,
+							pbus_data);
+				mdelay(10);
+			}
+		}
+	}
+	ret = __airoha_cl45_write(mbus, phy_addr, devad, reg, val);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct phy_driver Airoha_driver[] = {
+	{
+		.phy_id         = EN8801SC_PHY_ID,
+		.name           = "Airoha EN8801SC",
+		.phy_id_mask    = 0x0ffffff0,
+		.features       = PHY_GBIT_FEATURES,
+		.probe          = en8801s_probe,
+		.config_init    = en8801s_phase1_init,
+		.config_aneg    = genphy_config_aneg,
+		.read_status    = en8801s_read_status,
+		.suspend        = genphy_suspend,
+		.resume         = genphy_resume,
+		.read_mmd       = airoha_mmd_read,
+		.write_mmd      = airoha_mmd_write,
+	}
+};
+
+module_phy_driver(Airoha_driver);
+
+static struct mdio_device_id __maybe_unused Airoha_tbl[] = {
+	{ EN8801SC_PHY_ID, 0x0ffffff0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, Airoha_tbl);
diff --git a/target/linux/mediatek/files/drivers/net/phy/en8801sc.h b/target/linux/mediatek/files/drivers/net/phy/en8801sc.h
new file mode 100644
index 0000000000..d1e268c9aa
--- /dev/null
+++ b/target/linux/mediatek/files/drivers/net/phy/en8801sc.h
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/* FILE NAME:  en8801sc.h
+ * PURPOSE:
+ *      Define EN8801SC driver function
+ *
+ * NOTES:
+ *
+ */
+
+#ifndef __EN8801SC_H
+#define __EN8801SC_H
+
+/* NAMING DECLARATIONS
+ */
+#define EN8801S_DRIVER_VERSION  "1.1.8_Generic"
+#define EN8801S_PBUS_DEFAULT_ADDR 0x1e
+#define EN8801S_PHY_DEFAULT_ADDR 0x1d
+#define EN8801S_RG_ETHER_PHY_OUI 0x19a4
+#define EN8801S_RG_SMI_ADDR      0x19a8
+#define EN8801S_RG_BUCK_CTL      0x1a20
+#define EN8801S_RG_LTR_CTL       0x0cf8
+#define EN8801S_RG_PROD_VER      0x18e0
+
+#define EN8801S_PBUS_OUI        0x17a5
+#define EN8801S_PHY_ID1         0x03a2
+#define EN8801S_PHY_ID2         0x9461
+#define EN8801SC_PHY_ID         0x03a29471
+
+#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             (0x7f)
+/* LED ON Event Option.B */
+#define LED_ON_EVT_FORCE            (1 << 6)
+#define LED_ON_EVT_LINK_DOWN        (1 << 3)
+#define LED_ON_EVT_LINK_10M         (1 << 2)
+#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            (0x3ff)
+/* LED Blinking Event Option.B*/
+#define LED_BLK_EVT_FORCE           (1 << 9)
+#define LED_BLK_EVT_10M_RX_ACT      (1 << 5)
+#define LED_BLK_EVT_10M_TX_ACT      (1 << 4)
+#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 LINK_UP                 1
+#define LINK_DOWN               0
+
+/*
+SFP Sample for verification
+Tx Reverse, Rx Reverse
+*/
+#define EN8801S_TX_POLARITY_NORMAL   0x0
+#define EN8801S_TX_POLARITY_REVERSE  0x1
+
+#define EN8801S_RX_POLARITY_NORMAL   (0x1 << 1)
+#define EN8801S_RX_POLARITY_REVERSE  (0x0 << 1)
+
+/*
+The following led_cfg example is for reference only.
+LED5 1000M/LINK/ACT  (GPIO5)  <-> BASE_T_LED0,
+LED6 10/100M/LINK/ACT(GPIO9)  <-> BASE_T_LED1,
+LED4 100M/LINK/ACT   (GPIO8)  <-> BASE_T_LED2,
+*/
+/* User-defined.B */
+#define BASE_T_LED0_ON_CFG      (LED_ON_EVT_LINK_1000M)
+#define BASE_T_LED0_BLK_CFG \
+			(LED_BLK_EVT_1000M_TX_ACT | \
+			LED_BLK_EVT_1000M_RX_ACT)
+#define BASE_T_LED1_ON_CFG \
+			(LED_ON_EVT_LINK_100M | \
+			 LED_ON_EVT_LINK_10M)
+#define BASE_T_LED1_BLK_CFG \
+			(LED_BLK_EVT_100M_TX_ACT | \
+			 LED_BLK_EVT_100M_RX_ACT | \
+			 LED_BLK_EVT_10M_TX_ACT | \
+			 LED_BLK_EVT_10M_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)
+#define BASE_T_LED3_ON_CFG      (0x0)
+#define BASE_T_LED3_BLK_CFG     (0x0)
+/* User-defined.E */
+
+#define EN8801S_LED_COUNT       4
+
+#define MAX_RETRY               5
+#define MAX_OUI_CHECK           2
+/* 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 MAX_TRG_COUNTER         5
+
+/* CL22 Reg Support Page Select */
+#define RgAddr_Reg1Fh        0x1f
+#define CL22_Page_Reg        0x0000
+#define CL22_Page_ExtReg     0x0001
+#define CL22_Page_MiscReg    0x0002
+#define CL22_Page_LpiReg     0x0003
+#define CL22_Page_tReg       0x02A3
+#define CL22_Page_TrReg      0x52B5
+
+/* CL45 Reg Support DEVID */
+#define DEVID_03             0x03
+#define DEVID_07             0x07
+#define DEVID_1E             0x1E
+#define DEVID_1F             0x1F
+
+/* TokenRing Reg Access */
+#define TrReg_PKT_XMT_STA    0x8000
+#define TrReg_WR             0x8000
+#define TrReg_RD             0xA000
+
+#define RgAddr_LPI_1Ch       0x1c
+#define RgAddr_AUXILIARY_1Dh 0x1d
+#define RgAddr_PMA_00h       0x0f80
+#define RgAddr_PMA_01h       0x0f82
+#define RgAddr_PMA_17h       0x0fae
+#define RgAddr_PMA_18h       0x0fb0
+#define RgAddr_DSPF_03h      0x1686
+#define RgAddr_DSPF_06h      0x168c
+#define RgAddr_DSPF_08h      0x1690
+#define RgAddr_DSPF_0Ch      0x1698
+#define RgAddr_DSPF_0Dh      0x169a
+#define RgAddr_DSPF_0Fh      0x169e
+#define RgAddr_DSPF_10h      0x16a0
+#define RgAddr_DSPF_11h      0x16a2
+#define RgAddr_DSPF_13h      0x16a6
+#define RgAddr_DSPF_14h      0x16a8
+#define RgAddr_DSPF_1Bh      0x16b6
+#define RgAddr_DSPF_1Ch      0x16b8
+#define RgAddr_TR_26h        0x0ecc
+#define RgAddr_R1000DEC_15h  0x03aa
+#define RgAddr_R1000DEC_17h  0x03ae
+
+#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 LED_GPIO_SEL_MASK 0x7FFFFFF
+
+#define UNIT_LED_BLINK_DURATION     1024
+
+/* Invalid data */
+#define INVALID_DATA            0xffffffff
+
+#define LED_SET_GPIO_SEL(gpio, led, val)             \
+			(val |= (led << (8 * (gpio % 4))))       \
+
+#define GET_BIT(val, bit) ((val & BIT(bit)) >> bit)
+/* DATA TYPE DECLARATIONS
+ */
+struct AIR_BASE_T_LED_CFG_S {
+	u16 en;
+	u16 gpio;
+	u16 pol;
+	u16 on_cfg;
+	u16 blk_cfg;
+};
+
+union gephy_all_REG_LpiReg1Ch {
+	struct {
+		/* b[15:00] */
+		u16 smi_deton_wt                             : 3;
+		u16 smi_det_mdi_inv                          : 1;
+		u16 smi_detoff_wt                            : 3;
+		u16 smi_sigdet_debouncing_en                 : 1;
+		u16 smi_deton_th                             : 6;
+		u16 rsv_14                                   : 2;
+	} DataBitField;
+	u16 DATA;
+};
+
+union gephy_all_REG_dev1Eh_reg324h {
+	struct {
+		/* b[15:00] */
+		u16 rg_smi_detcnt_max                        : 6;
+		u16 rsv_6                                    : 2;
+		u16 rg_smi_det_max_en                        : 1;
+		u16 smi_det_deglitch_off                     : 1;
+		u16 rsv_10                                   : 6;
+	} DataBitField;
+	u16 DATA;
+};
+
+union gephy_all_REG_dev1Eh_reg012h {
+	struct {
+		/* b[15:00] */
+		u16 da_tx_i2mpb_a_tbt                        : 6;
+		u16 rsv_6                                    : 4;
+		u16 da_tx_i2mpb_a_gbe                        : 6;
+	} DataBitField;
+	u16 DATA;
+};
+
+union gephy_all_REG_dev1Eh_reg017h {
+	struct {
+		/* b[15:00] */
+		u16 da_tx_i2mpb_b_tbt                        : 6;
+		u16 rsv_6                                    : 2;
+		u16 da_tx_i2mpb_b_gbe                        : 6;
+		u16 rsv_14                                   : 2;
+	} DataBitField;
+	u16 DATA;
+};
+
+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
+};
+
+enum {
+	AIR_ACTIVE_LOW,
+	AIR_ACTIVE_HIGH,
+};
+
+enum {
+	AIR_LED_MODE_DISABLE,
+	AIR_LED_MODE_USER_DEFINE,
+	AIR_LED_MODE_LAST
+};
+
+#endif /* End of __EN8801SC_H */
diff --git a/target/linux/mediatek/filogic/config-6.1 b/target/linux/mediatek/filogic/config-6.1
index 276c6fe6c4..ce6a2f8ddf 100644
--- a/target/linux/mediatek/filogic/config-6.1
+++ b/target/linux/mediatek/filogic/config-6.1
@@ -1,5 +1,6 @@
 CONFIG_64BIT=y
 # CONFIG_AHCI_MTK is not set
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
 CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
 CONFIG_ARCH_DMA_ADDR_T_64BIT=y
diff --git a/target/linux/mediatek/filogic/config-6.6 b/target/linux/mediatek/filogic/config-6.6
index e6c2d18c73..a87fdc9bc8 100644
--- a/target/linux/mediatek/filogic/config-6.6
+++ b/target/linux/mediatek/filogic/config-6.6
@@ -1,5 +1,6 @@
 CONFIG_64BIT=y
 # CONFIG_AHCI_MTK is not set
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
 CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
 CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y
diff --git a/target/linux/mediatek/mt7622/config-6.1 b/target/linux/mediatek/mt7622/config-6.1
index f10f1f7204..68bdd87074 100644
--- a/target/linux/mediatek/mt7622/config-6.1
+++ b/target/linux/mediatek/mt7622/config-6.1
@@ -1,5 +1,6 @@
 CONFIG_64BIT=y
 # CONFIG_AHCI_MTK is not set
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_AQUANTIA_PHY=y
 CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
 CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
diff --git a/target/linux/mediatek/mt7622/config-6.6 b/target/linux/mediatek/mt7622/config-6.6
index 0dc30969b0..067dd02d31 100644
--- a/target/linux/mediatek/mt7622/config-6.6
+++ b/target/linux/mediatek/mt7622/config-6.6
@@ -1,5 +1,6 @@
 CONFIG_64BIT=y
 # CONFIG_AHCI_MTK is not set
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_AQUANTIA_PHY=y
 CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
 CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
diff --git a/target/linux/mediatek/mt7623/config-6.1 b/target/linux/mediatek/mt7623/config-6.1
index 3f7c1bee16..1407188e2e 100644
--- a/target/linux/mediatek/mt7623/config-6.1
+++ b/target/linux/mediatek/mt7623/config-6.1
@@ -1,4 +1,5 @@
 # CONFIG_AIO is not set
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_ALIGNMENT_TRAP=y
 CONFIG_ARCH_32BIT_OFF_T=y
 CONFIG_ARCH_FORCE_MAX_ORDER=11
diff --git a/target/linux/mediatek/mt7623/config-6.6 b/target/linux/mediatek/mt7623/config-6.6
index 6e9948c516..de32b83e94 100644
--- a/target/linux/mediatek/mt7623/config-6.6
+++ b/target/linux/mediatek/mt7623/config-6.6
@@ -1,4 +1,5 @@
 # CONFIG_AIO is not set
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_ALIGNMENT_TRAP=y
 CONFIG_ARCH_32BIT_OFF_T=y
 CONFIG_ARCH_HIBERNATION_POSSIBLE=y
diff --git a/target/linux/mediatek/mt7629/config-6.1 b/target/linux/mediatek/mt7629/config-6.1
index 91d54d595e..5a15286c46 100644
--- a/target/linux/mediatek/mt7629/config-6.1
+++ b/target/linux/mediatek/mt7629/config-6.1
@@ -1,3 +1,4 @@
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_ALIGNMENT_TRAP=y
 CONFIG_ARCH_32BIT_OFF_T=y
 CONFIG_ARCH_FORCE_MAX_ORDER=11
diff --git a/target/linux/mediatek/mt7629/config-6.6 b/target/linux/mediatek/mt7629/config-6.6
index 3274864ac2..ec029de2fe 100644
--- a/target/linux/mediatek/mt7629/config-6.6
+++ b/target/linux/mediatek/mt7629/config-6.6
@@ -1,3 +1,4 @@
+# CONFIG_AIROHA_EN8801SC_PHY is not set
 CONFIG_ALIGNMENT_TRAP=y
 CONFIG_ARCH_32BIT_OFF_T=y
 CONFIG_ARCH_HIBERNATION_POSSIBLE=y
diff --git a/target/linux/mediatek/patches-6.1/735-net-phy-add-Airoha-EN8801SC-PHY.patch b/target/linux/mediatek/patches-6.1/735-net-phy-add-Airoha-EN8801SC-PHY.patch
new file mode 100644
index 0000000000..51f2a197e9
--- /dev/null
+++ b/target/linux/mediatek/patches-6.1/735-net-phy-add-Airoha-EN8801SC-PHY.patch
@@ -0,0 +1,38 @@
+From 5314e73cb941b47e6866b49b3b78c25e32d62df8 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko at sartura.hr>
+Date: Sat, 23 Mar 2024 20:21:14 +0100
+Subject: [PATCH] net: phy: add Airoha EN8801SC PHY
+
+Airoha EN8801SC Gigabit PHY is used on Edgecore EAP111, so include a
+modified version of MTK SDK driver.
+
+Signed-off-by: Robert Marko <robert.marko at sartura.hr>
+---
+ drivers/net/phy/Kconfig  | 5 +++++
+ drivers/net/phy/Makefile | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -143,6 +143,11 @@ endif # RTL8366_SMI
+ 
+ comment "MII PHY device drivers"
+ 
++config AIROHA_EN8801SC_PHY
++	tristate "Airoha EN8801SC Gigabit PHY"
++	help
++	  Currently supports the Airoha EN8801SC PHY.
++
+ config AIR_EN8811H_PHY
+ 	tristate "Airoha EN8811H 2.5 Gigabit PHY"
+ 	help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -47,6 +47,7 @@ obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
+ 
+ obj-$(CONFIG_ADIN_PHY)		+= adin.o
+ obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
++obj-$(CONFIG_AIROHA_EN8801SC_PHY)   += en8801sc.o
+ obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
+ obj-$(CONFIG_AMD_PHY)		+= amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia/
diff --git a/target/linux/mediatek/patches-6.6/734-net-phy-add-Airoha-EN8801SC-PHY.patch b/target/linux/mediatek/patches-6.6/734-net-phy-add-Airoha-EN8801SC-PHY.patch
new file mode 100644
index 0000000000..90c030fb89
--- /dev/null
+++ b/target/linux/mediatek/patches-6.6/734-net-phy-add-Airoha-EN8801SC-PHY.patch
@@ -0,0 +1,38 @@
+From 5314e73cb941b47e6866b49b3b78c25e32d62df8 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko at sartura.hr>
+Date: Sat, 23 Mar 2024 20:21:14 +0100
+Subject: [PATCH] net: phy: add Airoha EN8801SC PHY
+
+Airoha EN8801SC Gigabit PHY is used on Edgecore EAP111, so include a
+modified version of MTK SDK driver.
+
+Signed-off-by: Robert Marko <robert.marko at sartura.hr>
+---
+ drivers/net/phy/Kconfig  | 5 +++++
+ drivers/net/phy/Makefile | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -142,6 +142,11 @@ endif # RTL8366_SMI
+ 
+ comment "MII PHY device drivers"
+ 
++config AIROHA_EN8801SC_PHY
++	tristate "Airoha EN8801SC Gigabit PHY"
++	help
++	  Currently supports the Airoha EN8801SC PHY.
++
+ config AIR_EN8811H_PHY
+ 	tristate "Airoha EN8811H 2.5 Gigabit PHY"
+ 	help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -49,6 +49,7 @@ obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
+ 
+ obj-$(CONFIG_ADIN_PHY)		+= adin.o
+ obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
++obj-$(CONFIG_AIROHA_EN8801SC_PHY)   += en8801sc.o
+ obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
+ obj-$(CONFIG_AMD_PHY)		+= amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia/




More information about the lede-commits mailing list