[][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.
ð {
...
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/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