From: Tianling Shen Date: Tue, 9 Sep 2025 14:20:49 +0000 (+0800) Subject: uboot-mediatek: update Airoha EN8811H driver X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F20000%2Fhead;p=thirdparty%2Fopenwrt.git uboot-mediatek: update Airoha EN8811H driver Use the version sent to upstream U-Boot[1], which has much better code quality and also works better (without packet loss). The `en8811h_read_fw` func is adapted to use current read logic. 1. https://lore.kernel.org/u-boot/20250720122852.22563-1-lucienzx159@gmail.com/ Signed-off-by: Tianling Shen Link: https://github.com/openwrt/openwrt/pull/20000 Signed-off-by: Robert Marko --- diff --git a/package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch b/package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch index b7fc8f45a5e..191185f35ec 100644 --- a/package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch +++ b/package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch @@ -1015,26 +1015,31 @@ Signed-off-by: Weijie Gao +#endif /* __EN8801S_H */ --- /dev/null +++ b/drivers/net/phy/air_en8811h.c -@@ -0,0 +1,725 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/************************************************* -+ * FILE NAME: air_en8811h.c -+ * PURPOSE: -+ * EN8811H PHY Driver for Uboot -+ * NOTES: +@@ -0,0 +1,886 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * -+ * Copyright (C) 2023 Airoha Technology Corp. -+ *************************************************/ -+ -+/* INCLUDE FILE DECLARATIONS -+*/ -+#include -+#include ++ * Limitations of the EN8811H: ++ * - Only full duplex supported ++ * - Forced speed (AN off) is not supported by hardware (100Mbps) ++ * ++ * Source originated from linux air_en8811h.c ++ * ++ * Copyright (C) 2025 Airoha Technology Corp. ++ */ +#include +#include ++#include ++#include +#include -+#include -+#include "air_en8811h.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include + +#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI +#include @@ -1048,882 +1053,852 @@ Signed-off-by: Weijie Gao +#include +#endif + -+#if AIR_UBOOT_REVISION > 0x202004 -+#include -+#endif ++#define EN8811H_PHY_ID 0x03a2a411 ++ ++#define AIR_FW_ADDR_DM 0x00000000 ++#define AIR_FW_ADDR_DSP 0x00100000 ++ ++#define EN8811H_MD32_DM_SIZE 0x4000 ++#define EN8811H_MD32_DSP_SIZE 0x20000 ++ ++ #define EN8811H_FW_CTRL_1 0x0f0018 ++ #define EN8811H_FW_CTRL_1_START 0x0 ++ #define EN8811H_FW_CTRL_1_FINISH 0x1 ++ #define EN8811H_FW_CTRL_2 0x800000 ++ #define EN8811H_FW_CTRL_2_LOADING BIT(11) ++ ++ /* MII Registers */ ++ #define AIR_AUX_CTRL_STATUS 0x1d ++ #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) ++ #define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 ++ #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 ++ #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc ++ ++#define AIR_EXT_PAGE_ACCESS 0x1f ++#define AIR_PHY_PAGE_STANDARD 0x0000 ++#define AIR_PHY_PAGE_EXTENDED_4 0x0004 ++ ++/* MII Registers Page 4*/ ++#define AIR_BPBUS_MODE 0x10 ++#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 ++#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) ++#define AIR_BPBUS_WR_ADDR_HIGH 0x11 ++#define AIR_BPBUS_WR_ADDR_LOW 0x12 ++#define AIR_BPBUS_WR_DATA_HIGH 0x13 ++#define AIR_BPBUS_WR_DATA_LOW 0x14 ++#define AIR_BPBUS_RD_ADDR_HIGH 0x15 ++#define AIR_BPBUS_RD_ADDR_LOW 0x16 ++#define AIR_BPBUS_RD_DATA_HIGH 0x17 ++#define AIR_BPBUS_RD_DATA_LOW 0x18 ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define EN8811H_PHY_FW_STATUS 0x8009 ++#define EN8811H_PHY_READY 0x02 ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define AIR_PHY_LED_BCR 0x021 ++#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0) ++#define AIR_PHY_LED_BCR_TIME_TEST BIT(2) ++#define AIR_PHY_LED_BCR_CLK_EN BIT(3) ++#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15) ++ ++#define AIR_PHY_LED_DUR_ON 0x022 ++ ++#define AIR_PHY_LED_DUR_BLINK 0x023 ++ ++#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) ++#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8)) ++#define AIR_PHY_LED_ON_LINK1000 BIT(0) ++#define AIR_PHY_LED_ON_LINK100 BIT(1) ++#define AIR_PHY_LED_ON_LINK10 BIT(2) ++#define AIR_PHY_LED_ON_LINKDOWN BIT(3) ++#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define AIR_PHY_LED_ON_FORCE_ON BIT(6) ++#define AIR_PHY_LED_ON_LINK2500 BIT(8) ++#define AIR_PHY_LED_ON_POLARITY BIT(14) ++#define AIR_PHY_LED_ON_ENABLE BIT(15) ++ ++#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) ++#define AIR_PHY_LED_BLINK_1000TX BIT(0) ++#define AIR_PHY_LED_BLINK_1000RX BIT(1) ++#define AIR_PHY_LED_BLINK_100TX BIT(2) ++#define AIR_PHY_LED_BLINK_100RX BIT(3) ++#define AIR_PHY_LED_BLINK_10TX BIT(4) ++#define AIR_PHY_LED_BLINK_10RX BIT(5) ++#define AIR_PHY_LED_BLINK_COLLISION BIT(6) ++#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++#define AIR_PHY_LED_BLINK_2500TX BIT(10) ++#define AIR_PHY_LED_BLINK_2500RX BIT(11) ++ ++#define EN8811H_FW_VERSION 0x3b3c ++ ++#define EN8811H_POLARITY 0xca0f8 ++#define EN8811H_POLARITY_TX_NORMAL BIT(0) ++#define EN8811H_POLARITY_RX_REVERSE BIT(1) ++ ++#define EN8811H_CLK_CGM 0xcf958 ++#define EN8811H_CLK_CGM_CKO BIT(26) ++#define EN8811H_HWTRAP1 0xcf914 ++#define EN8811H_HWTRAP1_CKO BIT(12) ++ ++#define air_upper_16_bits(n) ((u16)((n) >> 16)) ++#define air_lower_16_bits(n) ((u16)((n) & 0xffff)) ++#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap) ++ ++/* Led definitions */ ++#define EN8811H_LED_COUNT 3 ++ ++struct led { ++ unsigned long rules; ++ unsigned long state; ++}; + -+/************************** -+ * 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_s 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}, ++enum { ++ AIR_PHY_LED_STATE_FORCE_ON, ++ AIR_PHY_LED_STATE_FORCE_BLINK, +}; -+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 -+ **************************************************************/ -+/* Airoha MII read function */ -+static int air_mii_cl22_read(struct mii_dev *bus, int phy_addr, int phy_register) -+{ -+ int read_data = bus->read(bus, phy_addr, MDIO_DEVAD_NONE, phy_register); + -+ if (read_data < 0) -+ return -EIO; -+ return read_data; -+} ++enum { ++ AIR_PHY_LED_DUR_BLINK_32MS, ++ AIR_PHY_LED_DUR_BLINK_64MS, ++ AIR_PHY_LED_DUR_BLINK_128MS, ++ AIR_PHY_LED_DUR_BLINK_256MS, ++ AIR_PHY_LED_DUR_BLINK_512MS, ++ AIR_PHY_LED_DUR_BLINK_1024MS, ++}; + -+/* Airoha MII write function */ -+static int air_mii_cl22_write(struct mii_dev *bus, int phy_addr, int phy_register, int write_data) -+{ -+ int ret = 0; ++enum { ++ AIR_LED_DISABLE, ++ AIR_LED_ENABLE, ++}; + -+ ret = bus->write(bus, phy_addr, MDIO_DEVAD_NONE, phy_register, write_data); -+ if (ret < 0) { -+ printf("bus->write, ret: %d\n", ret); -+ return ret; -+ } -+ return ret; -+} ++enum { ++ AIR_ACTIVE_LOW, ++ AIR_ACTIVE_HIGH, ++}; + -+static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg) -+{ -+ int ret = 0; -+ int data; ++enum { ++ AIR_LED_MODE_DISABLE, ++ AIR_LED_MODE_USER_DEFINE, ++}; + -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return INVALID_DATA; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return INVALID_DATA; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return INVALID_DATA; -+ } -+ data = phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG); -+ return data; -+} ++/* Trigger specific enum */ ++enum air_led_trigger_netdev_modes { ++ AIR_TRIGGER_NETDEV_LINK = 0, ++ AIR_TRIGGER_NETDEV_LINK_10, ++ AIR_TRIGGER_NETDEV_LINK_100, ++ AIR_TRIGGER_NETDEV_LINK_1000, ++ AIR_TRIGGER_NETDEV_LINK_2500, ++ AIR_TRIGGER_NETDEV_LINK_5000, ++ AIR_TRIGGER_NETDEV_LINK_10000, ++ AIR_TRIGGER_NETDEV_HALF_DUPLEX, ++ AIR_TRIGGER_NETDEV_FULL_DUPLEX, ++ AIR_TRIGGER_NETDEV_TX, ++ AIR_TRIGGER_NETDEV_RX, ++ AIR_TRIGGER_NETDEV_TX_ERR, ++ AIR_TRIGGER_NETDEV_RX_ERR, ++ ++ /* Keep last */ ++ __AIR_TRIGGER_NETDEV_MAX, ++}; + -+static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data) -+{ -+ int ret = 0; ++/* Default LED setup: ++ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx ++ * GPIO4 <-> LED1 On: Link detected at 2500 and 1000 Mbps ++ * GPIO3 <-> LED2 On: Link detected at 2500 and 100 Mbps ++ */ ++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(AIR_TRIGGER_NETDEV_LINK) | \ ++ BIT(AIR_TRIGGER_NETDEV_RX) | \ ++ BIT(AIR_TRIGGER_NETDEV_TX)) ++#define AIR_DEFAULT_TRIGGER_LED1 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(AIR_TRIGGER_NETDEV_LINK_1000)) ++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(AIR_TRIGGER_NETDEV_LINK_100)) ++ ++#define AIR_PHY_LED_DUR_UNIT 781 ++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) ++ ++struct en8811h_priv { ++ int firmware_version; ++ bool mcu_needs_restart; ++ struct led led[EN8811H_LED_COUNT]; ++}; + -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, write_data); -+ if (ret < 0) { -+ printf("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) ++static int air_phy_read_page(struct phy_device *phydev) +{ -+ int ret = 0; -+ struct mii_dev *mbus = phydev->bus; -+ -+ ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), 0x1F, (unsigned int)(pbus_address >> 6)); -+ if (ret < 0) -+ return ret; -+ ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF)); -+ if (ret < 0) -+ return ret; -+ ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), 0x10, (unsigned int)(pbus_data >> 16)); -+ if (ret < 0) -+ return ret; -+ return 0; ++ return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS); +} + -+/* EN8811H BUCK write function */ -+static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data) ++static int air_phy_write_page(struct phy_device *phydev, int page) +{ -+ int ret = 0; -+ -+ /* page 4 */ -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (unsigned int)((pbus_address >> 16) & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (unsigned int)(pbus_address & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x13, (unsigned int)((pbus_data >> 16) & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, (unsigned int)(pbus_data & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ return 0; ++ return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page); +} + -+/* EN8811H BUCK read function */ -+static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address) ++int air_phy_select_page(struct phy_device *phydev, int page) +{ -+ unsigned int pbus_data = 0, pbus_data_low, pbus_data_high; -+ int ret = 0; ++ int ret, oldpage; + -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4); /* page 4 */ -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return PBUS_INVALID_DATA; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return PBUS_INVALID_DATA; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x15, (unsigned int)((pbus_address >> 16) & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return PBUS_INVALID_DATA; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x16, (unsigned int)(pbus_address & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return PBUS_INVALID_DATA; -+ } ++ oldpage = air_phy_read_page(phydev); ++ if (oldpage < 0) ++ return oldpage; + -+ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, 0x17); -+ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, 0x18); -+ pbus_data = (pbus_data_high << 16) + pbus_data_low; -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)0); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ return pbus_data; ++ if (oldpage != page) { ++ ret = air_phy_write_page(phydev, page); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return oldpage; +} + -+static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, unsigned long array_size, const unsigned char *buffer) ++int air_phy_restore_page(struct phy_device *phydev, int oldpage, int ret) +{ -+ unsigned int write_data, offset ; -+ int ret = 0; ++ int r; + -+ /* page 4 */ -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ /* address increment*/ -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0x8000); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (unsigned int)((address >> 16) & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (unsigned int)(address & 0xffff)); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } ++ if (oldpage < 0) ++ return oldpage; + -+ for (offset = 0; offset < array_size; offset += 4) { -+ write_data = (buffer[offset + 3] << 8) | buffer[offset + 2]; -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x13, write_data); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ write_data = (buffer[offset + 1] << 8) | buffer[offset]; -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, write_data); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ } -+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)0); -+ if (ret < 0) { -+ printf("phy_write, ret: %d\n", ret); -+ return ret; -+ } -+ return 0; ++ r = air_phy_write_page(phydev, oldpage); ++ if (ret >= 0 && r < 0) ++ ret = r; ++ ++ 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) ++static int air_buckpbus_reg_write(struct phy_device *phydev, ++ u32 pbus_address, u32 pbus_data) +{ -+ 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); -+ 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; ++ int ret, saved_page; ++ ++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ if (saved_page < 0) ++ return saved_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, ++ AIR_BPBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH, ++ air_upper_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW, ++ air_lower_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, ++ air_upper_16_bits(pbus_data)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, ++ air_lower_16_bits(pbus_data)); ++ if (ret < 0) ++ goto restore_page; ++ ++restore_page: ++ if (ret < 0) ++ printf("%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ ++ return air_phy_restore_page(phydev, saved_page, ret); +} + -+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode) ++static int air_buckpbus_reg_read(struct phy_device *phydev, ++ u32 pbus_address, u32 *pbus_data) +{ -+ u16 cl45_data; -+ int err = 0; -+ -+ 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: -+ printf("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; ++ int pbus_data_low, pbus_data_high; ++ int ret = 0, saved_page; ++ ++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ if (saved_page < 0) ++ return saved_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, ++ AIR_BPBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH, ++ air_upper_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW, ++ air_lower_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) { ++ ret = pbus_data_high; ++ goto restore_page; ++ } ++ ++ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) { ++ ret = pbus_data_low; ++ goto restore_page; ++ } ++ ++ *pbus_data = pbus_data_low | (pbus_data_high << 16); ++ ++restore_page: ++ if (ret < 0) ++ printf("%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ ++ return air_phy_restore_page(phydev, saved_page, ret); +} + -+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state) ++static int air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) +{ -+ u16 cl45_data; -+ 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); -+ if (err < 0) -+ return err; -+ return 0; ++ int pbus_data_low, pbus_data_high; ++ u32 pbus_data_old, pbus_data_new; ++ int ret = 0, saved_page; ++ ++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ if (saved_page < 0) ++ return saved_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, ++ AIR_BPBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH, ++ air_upper_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW, ++ air_lower_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) ++ return pbus_data_high; ++ ++ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) ++ return pbus_data_low; ++ ++ pbus_data_old = pbus_data_low | (pbus_data_high << 16); ++ pbus_data_new = (pbus_data_old & ~mask) | set; ++ if (pbus_data_new == pbus_data_old) ++ return 0; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH, ++ air_upper_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW, ++ air_lower_16_bits(pbus_address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, ++ air_upper_16_bits(pbus_data_new)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, ++ air_lower_16_bits(pbus_data_new)); ++ if (ret < 0) ++ goto restore_page; ++ ++restore_page: ++ if (ret < 0) ++ printf("%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ ++ return air_phy_restore_page(phydev, saved_page, ret); +} + -+static int en8811h_led_init(struct phy_device *phydev) ++static int air_write_buf(struct phy_device *phydev, unsigned long address, ++ unsigned long array_size, const unsigned char *buffer) +{ -+ unsigned int led_gpio = 0, reg_value = 0; -+ u16 cl45_data = led_dur; -+ int ret, led_id; -+ -+ cl45_data = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M; -+ 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; ++ unsigned int offset; ++ int ret, saved_page; ++ u16 val; ++ ++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ if (saved_page < 0) ++ return saved_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, ++ AIR_BPBUS_MODE_ADDR_INCR); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH, ++ air_upper_16_bits(address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW, ++ air_lower_16_bits(address)); ++ if (ret < 0) ++ goto restore_page; ++ ++ for (offset = 0; offset < array_size; offset += 4) { ++ val = get_unaligned_le16(&buffer[offset + 2]); ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, val); ++ if (ret < 0) ++ goto restore_page; ++ ++ val = get_unaligned_le16(&buffer[offset]); ++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, val); ++ if (ret < 0) ++ goto restore_page; ++ } ++ ++restore_page: ++ if (ret < 0) ++ printf("%s 0x%08lx failed: %d\n", __func__, ++ address, ret); ++ ++ return air_phy_restore_page(phydev, saved_page, ret); ++} + -+ ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE); -+ if (ret != 0) { -+ printf("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)))) { -+ printf("LED%d uses incorrect GPIO%d !\n", led_id, led_cfg[led_id].gpio); -+ return -EINVAL; -+ } -+ reg_value = 0; -+ if (led_cfg[led_id].en == LED_ENABLE) -+ { -+ led_gpio |= BIT(led_cfg[led_id].gpio); -+ ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en); -+ if (ret != 0) { -+ printf("LED fail to set state, ret %d !\n", ret); -+ 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) { -+ printf("LED fail to set default, ret %d !\n", ret); -+ return ret; -+ } -+ } -+ } -+ ret = air_buckpbus_reg_write(phydev, 0xcf8b8, led_gpio); -+ if (ret < 0) -+ return ret; -+ printf("LED initialize OK !\n"); -+ return 0; ++static int en8811h_wait_mcu_ready(struct phy_device *phydev) ++{ ++ int ret, reg_value; ++ ++ /* Because of mdio-lock, may have to wait for multiple loads */ ++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ EN8811H_PHY_FW_STATUS, reg_value, ++ reg_value == EN8811H_PHY_READY, ++ 20000, 7500000, true); ++ if (ret) { ++ printf("MCU not ready: 0x%x\n", reg_value); ++ return -ENODEV; ++ } ++ ++ return 0; +} -+#endif /* AIR_LED_SUPPORT */ + -+static char *firmware_buf; -+static int en8811h_load_firmware(struct phy_device *phydev) ++__weak int en8811h_read_fw(void **addr) +{ -+ u32 pbus_value; -+ int ret = 0; ++ u32 ca_crc32; ++ void *buffer; ++ int ret; + -+ if (!firmware_buf) { -+ firmware_buf = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE); -+ if (!firmware_buf) { -+ printf("[Airoha] cannot allocated buffer for firmware.\n"); -+ return -ENOMEM; -+ } ++ /* Allocate memory to store both DM and DSP firmware */ ++ buffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE); ++ if (!buffer) { ++ printf("Failed to allocate memory for firmware\n"); ++ return -ENOMEM; ++ } + +#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI -+ ret = ubi_volume_read("en8811h-fw", firmware_buf, 0, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE); -+ if (ret) { -+ printf("[Airoha] read firmware from UBI failed.\n"); -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return ret; -+ } ++ ret = ubi_volume_read("en8811h-fw", buffer, 0, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE); ++ if (ret) { ++ printf("[Airoha] read firmware from UBI failed.\n"); ++ free(buffer); ++ buffer = NULL; ++ return ret; ++ } +#elif defined(CONFIG_PHY_AIROHA_FW_IN_MMC) -+ struct mmc *mmc = find_mmc_device(0); -+ if (!mmc) { -+ printf("[Airoha] opening MMC device failed.\n"); -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return -ENODEV; -+ } -+ if (mmc_init(mmc)) { -+ printf("[Airoha] initializing MMC device failed.\n"); -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return -ENODEV; -+ } -+ if (IS_SD(mmc)) { -+ printf("[Airoha] SD card is not supported.\n"); -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return -EINVAL; -+ } -+ ret = mmc_set_part_conf(mmc, 1, 2, 2); -+ if (ret) { -+ printf("[Airoha] cannot access eMMC boot1 hw partition.\n"); -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return ret; -+ } -+ ret = blk_dread(mmc_get_blk_desc(mmc), 0, 0x120, firmware_buf); -+ mmc_set_part_conf(mmc, 1, 1, 0); -+ if (ret != 0x120) { -+ printf("[Airoha] cannot read firmware from eMMC.\n"); -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return -EIO; -+ } ++ struct mmc *mmc = find_mmc_device(0); ++ if (!mmc) { ++ printf("[Airoha] opening MMC device failed.\n"); ++ free(buffer); ++ buffer = NULL; ++ return -ENODEV; ++ } ++ if (mmc_init(mmc)) { ++ printf("[Airoha] initializing MMC device failed.\n"); ++ free(buffer); ++ buffer = NULL; ++ return -ENODEV; ++ } ++ if (IS_SD(mmc)) { ++ printf("[Airoha] SD card is not supported.\n"); ++ free(buffer); ++ buffer = NULL; ++ return -EINVAL; ++ } ++ ret = mmc_set_part_conf(mmc, 1, 2, 2); ++ if (ret) { ++ printf("[Airoha] cannot access eMMC boot1 hw partition.\n"); ++ free(buffer); ++ buffer = NULL; ++ return ret; ++ } ++ ret = blk_dread(mmc_get_blk_desc(mmc), 0, 0x120, buffer); ++ mmc_set_part_conf(mmc, 1, 1, 0); ++ if (ret != 0x120) { ++ printf("[Airoha] cannot read firmware from eMMC.\n"); ++ free(buffer); ++ buffer = NULL; ++ return -EIO; ++ } +#else +#warning EN8811H firmware loading not implemented -+ free(firmware_buf); -+ firmware_buf = NULL; -+ return -EOPNOTSUPP; ++ free(buffer); ++ buffer = NULL; ++ return -EOPNOTSUPP; +#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; -+ /* Download DM */ -+ ret = MDIOWriteBuf(phydev, 0x00000000, EN8811H_MD32_DM_SIZE, firmware_buf); -+ if (ret < 0) { -+ printf("[Airoha] MDIOWriteBuf 0x00000000 fail.\n"); -+ return ret; -+ } -+ /* Download PM */ -+ ret = MDIOWriteBuf(phydev, 0x00100000, EN8811H_MD32_DSP_SIZE, firmware_buf + EN8811H_MD32_DM_SIZE); -+ if (ret < 0) { -+ printf("[Airoha] MDIOWriteBuf 0x00100000 fail.\n"); -+ 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; -+} -+ -+static int en8811h_config(struct phy_device *phydev) -+{ -+ int ret = 0; -+ int pid1 = 0, pid2 = 0; ++ /* Calculate CRC32 of DM firmware for verification */ ++ ca_crc32 = crc32(0, (unsigned char *)buffer, EN8811H_MD32_DM_SIZE); ++ debug("DM crc32: 0x%x\n", ca_crc32); + -+ ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0); -+ if (ret < 0) -+ return ret; ++ /* Calculate CRC32 of DSP firmware for verification */ ++ ca_crc32 = crc32(0, (unsigned char *)buffer + EN8811H_MD32_DM_SIZE, ++ EN8811H_MD32_DSP_SIZE); ++ debug("DSP crc32: 0x%x\n", ca_crc32); + -+ pid1 = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHYSID1); -+ pid2 = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHYSID2); -+ if ((EN8811H_PHY_ID1 != pid1) || (EN8811H_PHY_ID2 != pid2)) { -+ printf("EN8811H does not exist !\n"); -+ return -ENODEV; -+ } ++ *addr = buffer; ++ debug("Found Airoha Firmware.\n"); + -+ return 0; ++ return 0; +} + -+static int en8811h_get_autonego(struct phy_device *phydev, int *an) ++static int en8811h_load_firmware(struct phy_device *phydev) +{ -+ int reg; -+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); -+ if (reg < 0) -+ return -EINVAL; -+ if (reg & BMCR_ANENABLE) -+ *an = AUTONEG_ENABLE; -+ else -+ *an = AUTONEG_DISABLE; -+ return 0; ++ struct en8811h_priv *priv = phydev->priv; ++ void *buffer; ++ int ret; ++ ++ ret = en8811h_read_fw(&buffer); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_START); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, ++ EN8811H_FW_CTRL_2_LOADING); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_write_buf(phydev, AIR_FW_ADDR_DM, EN8811H_MD32_DM_SIZE, ++ (unsigned char *)buffer); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, EN8811H_MD32_DSP_SIZE, ++ (unsigned char *)buffer + EN8811H_MD32_DM_SIZE); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, 0); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_FINISH); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = en8811h_wait_mcu_ready(phydev); ++ ++ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, ++ &priv->firmware_version); ++ printf("MD32 firmware version: %08x\n", ++ priv->firmware_version); ++ ++en8811h_load_firmware_out: ++ free(buffer); ++ if (ret < 0) ++ printf("Firmware loading failed: %d\n", ret); ++ ++ return ret; +} + -+static int en8811h_startup(struct phy_device *phydev) ++static int en8811h_restart_mcu(struct phy_device *phydev) +{ -+ ofnode node = phy_get_ofnode(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, reg_value; -+ int old_link = phydev->link; -+ u32 pbus_value = 0, retry; -+ -+ eth_phy_reset(phydev->dev, 1); -+ mdelay(10); -+ eth_phy_reset(phydev->dev, 0); -+ mdelay(1); -+ -+ ret = en8811h_load_firmware(phydev); -+ if (ret) { -+ printf("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) { -+ printf("EN8811H PHY ready!\n"); -+ break; -+ } -+ retry--; -+ } while (retry); -+ if (0 == retry) { -+ printf("EN8811H PHY is not ready. (MD32 FW Status reg: 0x%x)\n", reg_value); -+ pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c); -+ printf("Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value); -+ printf("EN8811H initialize fail!\n"); -+ return 0; -+ } -+ /* Mode selection*/ -+ printf("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 &= 0xfffffffc; -+ pbus_value |= ofnode_read_bool(node, "airoha,rx-pol-reverse") ? -+ EN8811H_RX_POLARITY_REVERSE : EN8811H_RX_POLARITY_NORMAL; -+ pbus_value |= ofnode_read_bool(node, "airoha,tx-pol-reverse") ? -+ EN8811H_TX_POLARITY_REVERSE : EN8811H_TX_POLARITY_NORMAL; -+ ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value); -+ if (ret < 0) -+ return ret; -+ pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8); -+ printf("Tx, Rx Polarity(0xca0f8): %08x\n", pbus_value); -+ pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c); -+ printf("MD32 FW Version(0x3b3c) : %08x\n", pbus_value); -+#if defined(AIR_LED_SUPPORT) -+ ret = en8811h_led_init(phydev); -+ if (ret < 0) { -+ printf("en8811h_led_init fail\n"); -+ } -+#endif -+ printf("EN8811H initialize OK ! (%s)\n", EN8811H_DRIVER_VERSION); -+ -+ ret = genphy_update_link(phydev); -+ if (ret) -+ { -+ printf("ret %d!\n", ret); -+ return ret; -+ } -+ -+ ret = genphy_parse_link(phydev); -+ if (ret) -+ { -+ printf("ret %d!\n", ret); -+ return ret; -+ } ++ int ret; + -+ if (old_link && phydev->link) -+ return 0; ++ ret = phy_write_mmd(phydev, 0x1e, 0x8009, 0x0); ++ if (ret < 0) ++ return ret; + -+ phydev->speed = SPEED_100; -+ phydev->duplex = DUPLEX_FULL; -+ phydev->pause = 0; -+ phydev->asym_pause = 0; ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_START); ++ if (ret < 0) ++ return ret; + -+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); -+ if (reg < 0) -+ { -+ printf("MII_BMSR reg %d!\n", reg); -+ return reg; -+ } -+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); -+ if (reg < 0) -+ { -+ printf("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)) -+ { -+ printf("AN mode!\n"); -+ printf("SPEED 1000/100!\n"); -+ lpagb = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); -+ if (lpagb < 0 ) -+ return lpagb; -+ advgb = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); -+ if (adv < 0 ) -+ return adv; -+ common_adv_gb = (lpagb & (advgb << 2)); -+ -+ lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); -+ if (lpa < 0 ) -+ return lpa; -+ adv = phy_read(phydev, MDIO_DEVAD_NONE, 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 -+ { -+ printf("Force mode!\n"); -+ bmcr = phy_read(phydev, MDIO_DEVAD_NONE, 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_100; -+ } -+ } -+ } -+ -+ return ret; ++ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_FINISH); +} + -+#if AIR_UBOOT_REVISION > 0x202303 -+U_BOOT_PHY_DRIVER(en8811h) = { -+ .name = "Airoha EN8811H", -+ .uid = EN8811H_PHY_ID, -+ .mask = 0x0ffffff0, -+ .config = &en8811h_config, -+ .startup = &en8811h_startup, -+ .shutdown = &genphy_shutdown, -+}; -+#else -+static struct phy_driver AIR_EN8811H_driver = { -+ .name = "Airoha EN8811H", -+ .uid = EN8811H_PHY_ID, -+ .mask = 0x0ffffff0, -+ .config = &en8811h_config, -+ .startup = &en8811h_startup, -+ .shutdown = &genphy_shutdown, ++static int air_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ priv->led[index].rules = rules; ++ ++ if (rules & BIT(AIR_TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= AIR_PHY_LED_ON_FDX; ++ ++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_10) | BIT(AIR_TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_100) | BIT(AIR_TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_1000) | BIT(AIR_TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK1000; ++ ++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | BIT(AIR_TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK2500; ++ ++ if (rules & BIT(AIR_TRIGGER_NETDEV_RX)) { ++ blink |= AIR_PHY_LED_BLINK_10RX | ++ AIR_PHY_LED_BLINK_100RX | ++ AIR_PHY_LED_BLINK_1000RX | ++ AIR_PHY_LED_BLINK_2500RX; ++ } ++ ++ if (rules & BIT(AIR_TRIGGER_NETDEV_TX)) { ++ blink |= AIR_PHY_LED_BLINK_10TX | ++ AIR_PHY_LED_BLINK_100TX | ++ AIR_PHY_LED_BLINK_1000TX | ++ AIR_PHY_LED_BLINK_2500TX; ++ } ++ ++ if (blink || on) { ++ /* switch hw-control on, so led-on and led-blink are off */ ++ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); ++ } else { ++ priv->led[index].rules = 0; ++ } ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_MASK, on); ++ ++ if (ret < 0) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index), ++ blink); +}; + -+int phy_air_en8811h_init(void) ++static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) +{ -+ phy_register(&AIR_EN8811H_driver); -+ return 0; -+} -+#endif ---- /dev/null -+++ b/drivers/net/phy/air_en8811h.h -@@ -0,0 +1,163 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/************************************************* -+ * FILE NAME: air_en8811h.h -+ * PURPOSE: -+ * EN8811H PHY Driver for Uboot -+ * NOTES: -+ * -+ * Copyright (C) 2023 Airoha Technology Corp. -+ *************************************************/ -+ -+#ifndef __EN8811H_H -+#define __EN8811H_H -+ -+#define AIR_UBOOT_REVISION ((((U_BOOT_VERSION_NUM / 1000) % 10) << 20) | \ -+ (((U_BOOT_VERSION_NUM / 100) % 10) << 16) | \ -+ (((U_BOOT_VERSION_NUM / 10) % 10) << 12) | \ -+ ((U_BOOT_VERSION_NUM % 10) << 8) | \ -+ (((U_BOOT_VERSION_NUM_PATCH / 10) % 10) << 4) | \ -+ ((U_BOOT_VERSION_NUM_PATCH % 10) << 0)) -+ -+#define EN8811H_PHY_ID1 0x03a2 -+#define EN8811H_PHY_ID2 0xa411 -+#define EN8811H_PHY_ID ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2) -+#define EN8811H_SPEED_2500 0x03 -+#define EN8811H_PHY_READY 0x02 -+#define MAX_RETRY 5 -+ -+#define EN8811H_MD32_DM_SIZE 0x4000 -+#define EN8811H_MD32_DSP_SIZE 0x20000 ++ int val = 0; ++ int err; + -+#define EN8811H_TX_POLARITY_NORMAL 0x1 -+#define EN8811H_TX_POLARITY_REVERSE 0x0 ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; + -+#define EN8811H_RX_POLARITY_NORMAL (0x0 << 1) -+#define EN8811H_RX_POLARITY_REVERSE (0x1 << 1) ++ if (state == AIR_LED_ENABLE) ++ val |= AIR_PHY_LED_ON_ENABLE; ++ else ++ val &= ~AIR_PHY_LED_ON_ENABLE; + -+#ifndef BIT -+#define BIT(nr) (1UL << (nr)) -+#endif ++ if (pol == AIR_ACTIVE_HIGH) ++ val |= AIR_PHY_LED_ON_POLARITY; ++ else ++ val &= ~AIR_PHY_LED_ON_POLARITY; + -+/* CL45 MDIO control */ -+#define MII_MMD_ACC_CTL_REG 0x0d -+#define MII_MMD_ADDR_DATA_REG 0x0e -+#define MMD_OP_MODE_DATA BIT(14) -+/* MultiGBASE-T AN register */ -+#define MULTIG_ANAR_2500M (0x0080) -+#define MULTIG_LPAR_2500M (0x0020) -+ -+#define EN8811H_DRIVER_VERSION "v1.0.4" -+ -+/************************************************************ -+ * 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 */ ++ err = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_ON(index), val); ++ if (err < 0) ++ return err; + -+#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_HDX (1 << 5) -+#define LED_ON_EVT_FDX (1 << 4) -+#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 ++ return 0; ++} + -+#define EN8811H_LED_COUNT 3 ++static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ int ret, i; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, ++ dur); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, ++ dur >> 1); ++ if (ret < 0) ++ return ret; ++ ++ switch (mode) { ++ case AIR_LED_MODE_DISABLE: ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_MODE_MASK, 0); ++ break; ++ case AIR_LED_MODE_USER_DEFINE: ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN); ++ if (ret < 0) ++ return ret; ++ break; ++ default: ++ printf("LED mode %d is not supported\n", mode); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < num; ++i) { ++ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); ++ if (ret < 0) { ++ printf("LED%d init failed: %d\n", i, ret); ++ return ret; ++ } ++ air_led_hw_control_set(phydev, i, priv->led[i].rules); ++ } ++ ++ return 0; ++} + -+#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_BCR_MODE_2LED (1) -+#define LED_BCR_MODE_3LED_1 (2) -+#define LED_BCR_MODE_3LED_2 (3) ++static int en8811h_config(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ ofnode node = phy_get_ofnode(phydev); ++ u32 pbus_value = 0; ++ int ret = 0; ++ ++ /* If restart happened in .probe(), no need to restart now */ ++ if (priv->mcu_needs_restart) { ++ ret = en8811h_restart_mcu(phydev); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = en8811h_load_firmware(phydev); ++ if (ret) { ++ printf("Load firmware fail.\n"); ++ return ret; ++ } ++ /* Next calls to .config() mcu needs to restart */ ++ priv->mcu_needs_restart = true; ++ } ++ ++ ret = phy_write_mmd(phydev, 0x1e, 0x800c, 0x0); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, 0x1e, 0x800d, 0x0); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, 0x1e, 0x800e, 0x1101); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, 0x1e, 0x800f, 0x0002); ++ if (ret < 0) ++ return ret; ++ ++ /* Serdes polarity */ ++ pbus_value = 0; ++ if (ofnode_read_bool(node, "airoha,pnswap-rx")) ++ pbus_value |= EN8811H_POLARITY_RX_REVERSE; ++ else ++ pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; ++ if (ofnode_read_bool(node, "airoha,pnswap-tx")) ++ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; ++ else ++ pbus_value |= EN8811H_POLARITY_TX_NORMAL; ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, ++ EN8811H_POLARITY_RX_REVERSE | ++ EN8811H_POLARITY_TX_NORMAL, pbus_value); ++ if (ret < 0) ++ return ret; ++ ++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, ++ AIR_LED_MODE_USER_DEFINE); ++ if (ret < 0) { ++ printf("Failed to disable leds: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} + -+#define LED_ON_DUR (0x022) -+#define LED_ON_DUR_MASK (0xffff) ++static int en8811h_parse_status(struct phy_device *phydev) ++{ ++ int ret = 0, reg_value; ++ ++ phydev->duplex = DUPLEX_FULL; ++ ++ reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS); ++ if (reg_value < 0) ++ return reg_value; ++ ++ switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) { ++ case AIR_AUX_CTRL_STATUS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ case AIR_AUX_CTRL_STATUS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case AIR_AUX_CTRL_STATUS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ default: ++ printf("Auto-neg error, defaulting to 100M/FD\n"); ++ phydev->speed = SPEED_100; ++ break; ++ } ++ ++ return ret; ++} + -+#define LED_BLK_DUR (0x023) -+#define LED_BLK_DUR_MASK (0xffff) ++static int en8811h_startup(struct phy_device *phydev) ++{ ++ int ret = 0; + -+#define LED_GPIO_SEL_MASK 0x7FFFFFF ++ ret = genphy_update_link(phydev); ++ if (ret) ++ return ret; + -+#define UNIT_LED_BLINK_DURATION 1024 ++ return en8811h_parse_status(phydev); ++} + -+#define INVALID_DATA 0xffff -+#define PBUS_INVALID_DATA 0xffffffff ++static int en8811h_probe(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv; + -+struct air_base_t_led_cfg_s { -+ u16 en; -+ u16 gpio; -+ u16 pol; -+ u16 on_cfg; -+ u16 blk_cfg; -+}; ++ priv = malloc(sizeof(*priv)); ++ if (!priv) ++ return -ENOMEM; ++ memset(priv, 0, sizeof(*priv)); + -+enum { -+ AIR_LED2_GPIO3 = 3, -+ AIR_LED1_GPIO4, -+ AIR_LED0_GPIO5, -+ AIR_LED_LAST -+}; ++ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; ++ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; ++ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; + -+enum { -+ AIR_BASE_T_LED0, -+ AIR_BASE_T_LED1, -+ AIR_BASE_T_LED2, -+ AIR_BASE_T_LED3 -+}; ++ /* mcu has just restarted after firmware load */ ++ priv->mcu_needs_restart = false; + -+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 -+}; ++ phydev->priv = priv; + -+enum { -+ AIR_ACTIVE_LOW, -+ AIR_ACTIVE_HIGH, -+}; ++ return 0; ++} + -+enum { -+ AIR_LED_MODE_DISABLE, -+ AIR_LED_MODE_USER_DEFINE, -+ AIR_LED_MODE_LAST ++U_BOOT_PHY_DRIVER(en8811h) = { ++ .name = "Airoha EN8811H", ++ .uid = EN8811H_PHY_ID, ++ .mask = 0x0ffffff0, ++ .config = &en8811h_config, ++ .probe = &en8811h_probe, ++ .startup = &en8811h_startup, ++ .shutdown = &genphy_shutdown, +}; -+ -+#endif /* End of __EN8811H_MD32_H */ -+ ---- a/drivers/net/eth-phy-uclass.c -+++ b/drivers/net/eth-phy-uclass.c -@@ -154,7 +154,7 @@ static int eth_phy_of_to_plat(struct ude - return 0; - } - --static void eth_phy_reset(struct udevice *dev, int value) -+void eth_phy_reset(struct udevice *dev, int value) - { - struct eth_phy_device_priv *uc_priv = dev_get_uclass_priv(dev); - u32 delay; ---- a/include/eth_phy.h -+++ b/include/eth_phy.h -@@ -14,5 +14,6 @@ int eth_phy_binds_nodes(struct udevice * - int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus); - struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev); - int eth_phy_get_addr(struct udevice *dev); -+void eth_phy_reset(struct udevice *dev, int value); - - #endif diff --git a/package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch b/package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch index 35cd54b5ff3..8bf9eb0a0d5 100644 --- a/package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch +++ b/package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch @@ -498,7 +498,7 @@ + compatible = "ethernet-phy-id03a2.a411"; + reg = <14>; + -+ airoha,rx-pol-reverse; ++ airoha,pnswap-rx; + + reset-gpios = <&pio 49 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>;