net: mediatek: add support for Airoha AN8855 ethernet switch
authorWeijie Gao <weijie.gao@mediatek.com>
Fri, 10 Jan 2025 08:41:24 +0000 (16:41 +0800)
committerTom Rini <trini@konsulko.com>
Fri, 24 Jan 2025 00:46:42 +0000 (18:46 -0600)
Airoha AN8855 is a 5-port gigabit switch with a 2.5G HSGMII CPU port

Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
drivers/net/mtk_eth/Kconfig
drivers/net/mtk_eth/Makefile
drivers/net/mtk_eth/an8855.c [new file with mode: 0644]

index 4676847..e8cdf40 100644 (file)
@@ -32,4 +32,8 @@ config MTK_ETH_SWITCH_MT7988
        depends on TARGET_MT7988
        default y
 
+config MTK_ETH_SWITCH_AN8855
+       bool "Support for Airoha AN8855 ethernet switch"
+       default y if TARGET_MT7981 || TARGET_MT7987
+
 endif # MEDIATEK_ETH
index 885375c..a342325 100644 (file)
@@ -7,3 +7,4 @@ obj-y += mtk_eth.o
 obj-$(CONFIG_MTK_ETH_SWITCH_MT7530) += mt753x.o mt7530.o
 obj-$(CONFIG_MTK_ETH_SWITCH_MT7531) += mt753x.o mt7531.o
 obj-$(CONFIG_MTK_ETH_SWITCH_MT7988) += mt753x.o mt7988.o
+obj-$(CONFIG_MTK_ETH_SWITCH_AN8855) += an8855.o
diff --git a/drivers/net/mtk_eth/an8855.c b/drivers/net/mtk_eth/an8855.c
new file mode 100644 (file)
index 0000000..4bd7506
--- /dev/null
@@ -0,0 +1,1096 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 MediaTek Inc.
+ *
+ * Author: Neal Yen <neal.yen@mediatek.com>
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <phy.h>
+#include <miiphy.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include "mtk_eth.h"
+
+/* AN8855 Register Definitions */
+#define AN8855_SYS_CTRL_REG                    0x100050c0
+#define AN8855_SW_SYS_RST                      BIT(31)
+
+#define AN8855_PMCR_REG(p)                     (0x10210000 + (p) * 0x200)
+#define AN8855_FORCE_MODE_LNK                  BIT(31)
+#define AN8855_FORCE_MODE                      0xb31593f0
+
+#define AN8855_PORT_CTRL_BASE                  (0x10208000)
+#define AN8855_PORT_CTRL_REG(p, r)             (AN8855_PORT_CTRL_BASE + (p) * 0x200 + (r))
+
+#define AN8855_PORTMATRIX_REG(p)               AN8855_PORT_CTRL_REG(p, 0x44)
+
+#define AN8855_PVC(p)                          AN8855_PORT_CTRL_REG(p, 0x10)
+#define AN8855_STAG_VPID_S                     16
+#define AN8855_STAG_VPID_M                     0xffff0000
+#define AN8855_VLAN_ATTR_S                     6
+#define AN8855_VLAN_ATTR_M                     0xc0
+
+#define VLAN_ATTR_USER                         0
+
+#define AN8855_INT_MASK                                0x100050F0
+#define AN8855_INT_SYS_BIT                     BIT(15)
+
+#define AN8855_RG_CLK_CPU_ICG                  0x10005034
+#define AN8855_MCU_ENABLE                      BIT(3)
+
+#define AN8855_RG_TIMER_CTL                    0x1000a100
+#define AN8855_WDOG_ENABLE                     BIT(25)
+
+#define AN8855_CKGCR                           0x10213e1c
+
+#define AN8855_SCU_BASE                        0x10000000
+#define AN8855_RG_RGMII_TXCK_C                 (AN8855_SCU_BASE + 0x1d0)
+#define AN8855_RG_GPIO_LED_MODE                        (AN8855_SCU_BASE + 0x0054)
+#define AN8855_RG_GPIO_LED_SEL(i)              (AN8855_SCU_BASE + (0x0058 + ((i) * 4)))
+#define AN8855_RG_INTB_MODE                    (AN8855_SCU_BASE + 0x0080)
+#define AN8855_RG_GDMP_RAM                     (AN8855_SCU_BASE + 0x10000)
+#define AN8855_RG_GPIO_L_INV                   (AN8855_SCU_BASE + 0x0010)
+#define AN8855_RG_GPIO_CTRL                    (AN8855_SCU_BASE + 0xa300)
+#define AN8855_RG_GPIO_DATA                    (AN8855_SCU_BASE + 0xa304)
+#define AN8855_RG_GPIO_OE                      (AN8855_SCU_BASE + 0xa314)
+
+#define AN8855_HSGMII_AN_CSR_BASE              0x10220000
+#define AN8855_SGMII_REG_AN0                   (AN8855_HSGMII_AN_CSR_BASE + 0x000)
+#define AN8855_SGMII_REG_AN_13                 (AN8855_HSGMII_AN_CSR_BASE + 0x034)
+#define AN8855_SGMII_REG_AN_FORCE_CL37         (AN8855_HSGMII_AN_CSR_BASE + 0x060)
+
+#define AN8855_HSGMII_CSR_PCS_BASE             0x10220000
+#define AN8855_RG_HSGMII_PCS_CTROL_1           (AN8855_HSGMII_CSR_PCS_BASE + 0xa00)
+#define AN8855_RG_AN_SGMII_MODE_FORCE          (AN8855_HSGMII_CSR_PCS_BASE + 0xa24)
+
+#define AN8855_MULTI_SGMII_CSR_BASE            0x10224000
+#define AN8855_SGMII_STS_CTRL_0                (AN8855_MULTI_SGMII_CSR_BASE + 0x018)
+#define AN8855_MSG_RX_CTRL_0                   (AN8855_MULTI_SGMII_CSR_BASE + 0x100)
+#define AN8855_MSG_RX_LIK_STS_0                (AN8855_MULTI_SGMII_CSR_BASE + 0x514)
+#define AN8855_MSG_RX_LIK_STS_2                (AN8855_MULTI_SGMII_CSR_BASE + 0x51c)
+#define AN8855_PHY_RX_FORCE_CTRL_0             (AN8855_MULTI_SGMII_CSR_BASE + 0x520)
+
+#define AN8855_XFI_CSR_PCS_BASE                0x10225000
+#define AN8855_RG_USXGMII_AN_CONTROL_0         (AN8855_XFI_CSR_PCS_BASE + 0xbf8)
+
+#define AN8855_MULTI_PHY_RA_CSR_BASE           0x10226000
+#define AN8855_RG_RATE_ADAPT_CTRL_0            (AN8855_MULTI_PHY_RA_CSR_BASE + 0x000)
+#define AN8855_RATE_ADP_P0_CTRL_0              (AN8855_MULTI_PHY_RA_CSR_BASE + 0x100)
+#define AN8855_MII_RA_AN_ENABLE                (AN8855_MULTI_PHY_RA_CSR_BASE + 0x300)
+
+#define AN8855_QP_DIG_CSR_BASE                 0x1022a000
+#define AN8855_QP_CK_RST_CTRL_4                (AN8855_QP_DIG_CSR_BASE + 0x310)
+#define AN8855_QP_DIG_MODE_CTRL_0              (AN8855_QP_DIG_CSR_BASE + 0x324)
+#define AN8855_QP_DIG_MODE_CTRL_1              (AN8855_QP_DIG_CSR_BASE + 0x330)
+
+#define AN8855_QP_PMA_TOP_BASE                 0x1022e000
+#define AN8855_PON_RXFEDIG_CTRL_0              (AN8855_QP_PMA_TOP_BASE + 0x100)
+#define AN8855_PON_RXFEDIG_CTRL_9              (AN8855_QP_PMA_TOP_BASE + 0x124)
+
+#define AN8855_SS_LCPLL_PWCTL_SETTING_2        (AN8855_QP_PMA_TOP_BASE + 0x208)
+#define AN8855_SS_LCPLL_TDC_FLT_2              (AN8855_QP_PMA_TOP_BASE + 0x230)
+#define AN8855_SS_LCPLL_TDC_FLT_5              (AN8855_QP_PMA_TOP_BASE + 0x23c)
+#define AN8855_SS_LCPLL_TDC_PCW_1              (AN8855_QP_PMA_TOP_BASE + 0x248)
+#define AN8855_INTF_CTRL_8                     (AN8855_QP_PMA_TOP_BASE + 0x320)
+#define AN8855_INTF_CTRL_9                     (AN8855_QP_PMA_TOP_BASE + 0x324)
+#define AN8855_PLL_CTRL_0                      (AN8855_QP_PMA_TOP_BASE + 0x400)
+#define AN8855_PLL_CTRL_2                      (AN8855_QP_PMA_TOP_BASE + 0x408)
+#define AN8855_PLL_CTRL_3                      (AN8855_QP_PMA_TOP_BASE + 0x40c)
+#define AN8855_PLL_CTRL_4                      (AN8855_QP_PMA_TOP_BASE + 0x410)
+#define AN8855_PLL_CK_CTRL_0                   (AN8855_QP_PMA_TOP_BASE + 0x414)
+#define AN8855_RX_DLY_0                        (AN8855_QP_PMA_TOP_BASE + 0x614)
+#define AN8855_RX_CTRL_2                       (AN8855_QP_PMA_TOP_BASE + 0x630)
+#define AN8855_RX_CTRL_5                       (AN8855_QP_PMA_TOP_BASE + 0x63c)
+#define AN8855_RX_CTRL_6                       (AN8855_QP_PMA_TOP_BASE + 0x640)
+#define AN8855_RX_CTRL_7                       (AN8855_QP_PMA_TOP_BASE + 0x644)
+#define AN8855_RX_CTRL_8                       (AN8855_QP_PMA_TOP_BASE + 0x648)
+#define AN8855_RX_CTRL_26                      (AN8855_QP_PMA_TOP_BASE + 0x690)
+#define AN8855_RX_CTRL_42                      (AN8855_QP_PMA_TOP_BASE + 0x6d0)
+
+#define AN8855_QP_ANA_CSR_BASE                 0x1022f000
+#define AN8855_RG_QP_RX_DAC_EN                 (AN8855_QP_ANA_CSR_BASE + 0x00)
+#define AN8855_RG_QP_RXAFE_RESERVE             (AN8855_QP_ANA_CSR_BASE + 0x04)
+#define AN8855_RG_QP_CDR_LPF_MJV_LIM           (AN8855_QP_ANA_CSR_BASE + 0x0c)
+#define AN8855_RG_QP_CDR_LPF_SETVALUE          (AN8855_QP_ANA_CSR_BASE + 0x14)
+#define AN8855_RG_QP_CDR_PR_CKREF_DIV1         (AN8855_QP_ANA_CSR_BASE + 0x18)
+#define AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE     (AN8855_QP_ANA_CSR_BASE + 0x1c)
+#define AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF  (AN8855_QP_ANA_CSR_BASE + 0x20)
+#define AN8855_RG_QP_TX_MODE_16B_EN            (AN8855_QP_ANA_CSR_BASE + 0x28)
+#define AN8855_RG_QP_PLL_IPLL_DIG_PWR_SEL      (AN8855_QP_ANA_CSR_BASE + 0x3c)
+#define AN8855_RG_QP_PLL_SDM_ORD               (AN8855_QP_ANA_CSR_BASE + 0x40)
+
+#define AN8855_ETHER_SYS_BASE                  0x1028c800
+#define RG_GPHY_AFE_PWD                                (AN8855_ETHER_SYS_BASE + 0x40)
+
+#define AN8855_PKG_SEL                         0x10000094
+#define PAG_SEL_AN8855H                                0x2
+
+/* PHY LED Register bitmap of define */
+#define PHY_LED_CTRL_SELECT                    0x3e8
+#define PHY_SINGLE_LED_ON_CTRL(i)              (0x3e0 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_CTRL(i)             (0x3e1 + ((i) * 2))
+#define PHY_SINGLE_LED_ON_DUR(i)               (0x3e9 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_DUR(i)              (0x3ea + ((i) * 2))
+
+#define PHY_PMA_CTRL                           (0x340)
+
+#define PHY_DEV1F                              0x1f
+
+#define PHY_LED_ON_CTRL(i)                     (0x24 + ((i) * 2))
+#define LED_ON_EN                              (1 << 15)
+#define LED_ON_POL                             (1 << 14)
+#define LED_ON_EVT_MASK                                (0x7f)
+
+/* LED ON Event */
+#define LED_ON_EVT_FORCE                       (1 << 6)
+#define LED_ON_EVT_LINK_HD                     (1 << 5)
+#define LED_ON_EVT_LINK_FD                     (1 << 4)
+#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)
+
+#define PHY_LED_BLK_CTRL(i)                    (0x25 + ((i) * 2))
+#define LED_BLK_EVT_MASK                       (0x3ff)
+/* LED Blinking Event */
+#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)
+
+#define PHY_LED_BCR                            (0x21)
+#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 PHY_LED_ON_DUR                         (0x22)
+#define LED_ON_DUR_MASK                                (0xffff)
+
+#define PHY_LED_BLK_DUR                                (0x23)
+#define LED_BLK_DUR_MASK                       (0xffff)
+
+#define PHY_LED_BLINK_DUR_CTRL                 (0x720)
+
+/* Definition of LED */
+#define LED_ON_EVENT   (LED_ON_EVT_LINK_1000M | \
+                       LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M |\
+                       LED_ON_EVT_LINK_HD | LED_ON_EVT_LINK_FD)
+
+#define LED_BLK_EVENT  (LED_BLK_EVT_1000M_TX_ACT | \
+                       LED_BLK_EVT_1000M_RX_ACT | \
+                       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 LED_FREQ                               AIR_LED_BLK_DUR_64M
+
+#define AN8855_NUM_PHYS                                5
+#define AN8855_NUM_PORTS                       6
+#define AN8855_PHY_ADDR(base, addr)            (((base) + (addr)) & 0x1f)
+
+/* PHY LED Register bitmap of define */
+#define PHY_LED_CTRL_SELECT                    0x3e8
+#define PHY_SINGLE_LED_ON_CTRL(i)              (0x3e0 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_CTRL(i)             (0x3e1 + ((i) * 2))
+#define PHY_SINGLE_LED_ON_DUR(i)               (0x3e9 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_DUR(i)              (0x3ea + ((i) * 2))
+
+/* AN8855 LED */
+enum an8855_led_blk_dur {
+       AIR_LED_BLK_DUR_32M,
+       AIR_LED_BLK_DUR_64M,
+       AIR_LED_BLK_DUR_128M,
+       AIR_LED_BLK_DUR_256M,
+       AIR_LED_BLK_DUR_512M,
+       AIR_LED_BLK_DUR_1024M,
+       AIR_LED_BLK_DUR_LAST
+};
+
+enum an8855_led_polarity {
+       LED_LOW,
+       LED_HIGH,
+};
+
+enum an8855_led_mode {
+       AN8855_LED_MODE_DISABLE,
+       AN8855_LED_MODE_USER_DEFINE,
+       AN8855_LED_MODE_LAST
+};
+
+enum phy_led_idx {
+       P0_LED0,
+       P0_LED1,
+       P0_LED2,
+       P0_LED3,
+       P1_LED0,
+       P1_LED1,
+       P1_LED2,
+       P1_LED3,
+       P2_LED0,
+       P2_LED1,
+       P2_LED2,
+       P2_LED3,
+       P3_LED0,
+       P3_LED1,
+       P3_LED2,
+       P3_LED3,
+       P4_LED0,
+       P4_LED1,
+       P4_LED2,
+       P4_LED3,
+       PHY_LED_MAX
+};
+
+struct an8855_led_cfg {
+       u16 en;
+       u8  phy_led_idx;
+       u16 pol;
+       u16 on_cfg;
+       u16 blk_cfg;
+       u8 led_freq;
+};
+
+struct an8855_switch_priv {
+       struct mtk_eth_switch_priv epriv;
+       struct mii_dev *mdio_bus;
+       u32 phy_base;
+};
+
+/* AN8855 Reference Board */
+static const struct an8855_led_cfg led_cfg[] = {
+/*************************************************************************
+ * Enable, LED idx, LED Polarity, LED ON event,  LED Blink event  LED Freq
+ *************************************************************************
+ */
+       /* GPIO0 */
+       {1, P4_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO1 */
+       {1, P4_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO2 */
+       {1, P0_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO3 */
+       {1, P0_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO4 */
+       {1, P1_LED0, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO5 */
+       {1, P1_LED1, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO6 */
+       {0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO7 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO8 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO9 */
+       {1, P2_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO10 */
+       {1, P2_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO11 */
+       {1, P3_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO12 */
+       {1, P3_LED1, LED_HIGH,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO13 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO14 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO15 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO16 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO17 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO18 */
+       {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO19 */
+       {0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+       /* GPIO20 */
+       {0, PHY_LED_MAX, LED_LOW,  LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+};
+
+static int __an8855_reg_read(struct mtk_eth_priv *priv, u8 phy_base, u32 reg, u32 *data)
+{
+       int ret, low_word, high_word;
+
+       ret = mtk_mii_write(priv, phy_base, 0x1f, 0x4);
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv, phy_base, 0x10, 0);
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv, phy_base, 0x15, ((reg >> 16) & 0xFFFF));
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv, phy_base, 0x16, (reg & 0xFFFF));
+       if (ret)
+               return ret;
+
+       low_word = mtk_mii_read(priv, phy_base, 0x18);
+       if (low_word < 0)
+               return low_word;
+
+       high_word = mtk_mii_read(priv, phy_base, 0x17);
+       if (high_word < 0)
+               return high_word;
+
+       ret = mtk_mii_write(priv, phy_base, 0x1f, 0);
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv, phy_base, 0x10, 0);
+       if (ret)
+               return ret;
+
+       if (data)
+               *data = ((u32)high_word << 16) | (low_word & 0xffff);
+
+       return 0;
+}
+
+static int an8855_reg_read(struct an8855_switch_priv *priv, u32 reg, u32 *data)
+{
+       return __an8855_reg_read(priv->epriv.eth, priv->phy_base, reg, data);
+}
+
+static int an8855_reg_write(struct an8855_switch_priv *priv, u32 reg, u32 data)
+{
+       int ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0x4);
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0);
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x11,
+                           ((reg >> 16) & 0xFFFF));
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x12,
+                           (reg & 0xFFFF));
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x13,
+                           ((data >> 16) & 0xFFFF));
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x14,
+                           (data & 0xFFFF));
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0);
+       if (ret)
+               return ret;
+
+       ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int an8855_phy_cl45_read(struct an8855_switch_priv *priv, int port,
+                               int devad, int regnum, u16 *data)
+{
+       u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port);
+
+       *data = mtk_mmd_ind_read(priv->epriv.eth, phy_addr, devad, regnum);
+
+       return 0;
+}
+
+static int an8855_phy_cl45_write(struct an8855_switch_priv *priv, int port,
+                                int devad, int regnum, u16 data)
+{
+       u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port);
+
+       mtk_mmd_ind_write(priv->epriv.eth, phy_addr, devad, regnum, data);
+
+       return 0;
+}
+
+static int an8855_port_sgmii_init(struct an8855_switch_priv *priv, u32 port)
+{
+       u32 val = 0;
+
+       if (port != 5) {
+               printf("an8855: port %d is not a SGMII port\n", port);
+               return -EINVAL;
+       }
+
+       /* PLL */
+       an8855_reg_read(priv, AN8855_QP_DIG_MODE_CTRL_1, &val);
+       val &= ~(0x3 << 2);
+       val |= (0x1 << 2);
+       an8855_reg_write(priv, AN8855_QP_DIG_MODE_CTRL_1, val);
+
+       /* PLL - LPF */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+       val &= ~(0x3 << 0);
+       val |= (0x1 << 0);
+       val &= ~(0x7 << 2);
+       val |= (0x5 << 2);
+       val &= ~GENMASK(7, 6);
+       val &= ~(0x7 << 8);
+       val |= (0x3 << 8);
+       val |= BIT(29);
+       val &= ~GENMASK(13, 12);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+       /* PLL - ICO */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val);
+       val |= BIT(2);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_4, val);
+
+       an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+       val &= ~BIT(14);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+       /* PLL - CHP */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+       val &= ~(0xf << 16);
+       val |= (0x6 << 16);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+       /* PLL - PFD */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+       val &= ~(0x3 << 20);
+       val |= (0x1 << 20);
+       val &= ~(0x3 << 24);
+       val |= (0x1 << 24);
+       val &= ~BIT(26);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+       /* PLL - POSTDIV */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+       val |= BIT(22);
+       val &= ~BIT(27);
+       val &= ~BIT(28);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+       /* PLL - SDM */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val);
+       val &= ~GENMASK(4, 3);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_4, val);
+
+       an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+       val &= ~BIT(30);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+       an8855_reg_read(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, &val);
+       val &= ~(0x3 << 16);
+       val |= (0x1 << 16);
+       an8855_reg_write(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, val);
+
+       an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_2, 0x7a000000);
+       an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_PCW_1, 0x7a000000);
+
+       an8855_reg_read(priv, AN8855_SS_LCPLL_TDC_FLT_5, &val);
+       val &= ~BIT(24);
+       an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_5, val);
+
+       an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val);
+       val &= ~BIT(8);
+       an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val);
+
+       /* PLL - SS */
+       an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val);
+       val &= ~GENMASK(15, 0);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_3, val);
+
+       an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val);
+       val &= ~GENMASK(1, 0);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_4, val);
+
+       an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val);
+       val &= ~GENMASK(31, 16);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_3, val);
+
+       /* PLL - TDC */
+       an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val);
+       val &= ~BIT(9);
+       an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_PLL_SDM_ORD, &val);
+       val |= BIT(3);
+       val |= BIT(4);
+       an8855_reg_write(priv, AN8855_RG_QP_PLL_SDM_ORD, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_RX_DAC_EN, &val);
+       val &= ~(0x3 << 16);
+       val |= (0x2 << 16);
+       an8855_reg_write(priv, AN8855_RG_QP_RX_DAC_EN, val);
+
+       /* TCL Disable (only for Co-SIM) */
+       an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_0, &val);
+       val &= ~BIT(12);
+       an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_0, val);
+
+       /* TX Init */
+       an8855_reg_read(priv, AN8855_RG_QP_TX_MODE_16B_EN, &val);
+       val &= ~BIT(0);
+       val &= ~(0xffff << 16);
+       val |= (0x4 << 16);
+       an8855_reg_write(priv, AN8855_RG_QP_TX_MODE_16B_EN, val);
+
+       /* RX Control */
+       an8855_reg_read(priv, AN8855_RG_QP_RXAFE_RESERVE, &val);
+       val |= BIT(11);
+       an8855_reg_write(priv, AN8855_RG_QP_RXAFE_RESERVE, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, &val);
+       val &= ~(0x3 << 4);
+       val |= (0x1 << 4);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, &val);
+       val &= ~(0xf << 25);
+       val |= (0x1 << 25);
+       val &= ~(0x7 << 29);
+       val |= (0x3 << 29);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val);
+       val &= ~(0x1f << 8);
+       val |= (0xf << 8);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val);
+       val &= ~(0x3f << 0);
+       val |= (0x19 << 0);
+       val &= ~BIT(6);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, &val);
+       val &= ~(0x7f << 6);
+       val |= (0x21 << 6);
+       val &= ~(0x3 << 16);
+       val |= (0x2 << 16);
+       val &= ~BIT(13);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val);
+       val &= ~BIT(30);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
+
+       an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val);
+       val &= ~(0x7 << 24);
+       val |= (0x4 << 24);
+       an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val);
+
+       an8855_reg_read(priv, AN8855_PLL_CTRL_0, &val);
+       val |= BIT(0);
+       an8855_reg_write(priv, AN8855_PLL_CTRL_0, val);
+
+       an8855_reg_read(priv, AN8855_RX_CTRL_26, &val);
+       val &= ~BIT(23);
+       val |= BIT(26);
+       an8855_reg_write(priv, AN8855_RX_CTRL_26, val);
+
+       an8855_reg_read(priv, AN8855_RX_DLY_0, &val);
+       val &= ~(0xff << 0);
+       val |= (0x6f << 0);
+       val |= GENMASK(13, 8);
+       an8855_reg_write(priv, AN8855_RX_DLY_0, val);
+
+       an8855_reg_read(priv, AN8855_RX_CTRL_42, &val);
+       val &= ~(0x1fff << 0);
+       val |= (0x150 << 0);
+       an8855_reg_write(priv, AN8855_RX_CTRL_42, val);
+
+       an8855_reg_read(priv, AN8855_RX_CTRL_2, &val);
+       val &= ~(0x1fff << 16);
+       val |= (0x150 << 16);
+       an8855_reg_write(priv, AN8855_RX_CTRL_2, val);
+
+       an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_9, &val);
+       val &= ~(0x7 << 0);
+       val |= (0x1 << 0);
+       an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_9, val);
+
+       an8855_reg_read(priv, AN8855_RX_CTRL_8, &val);
+       val &= ~(0xfff << 16);
+       val |= (0x200 << 16);
+       val &= ~(0x7fff << 14);
+       val |= (0xfff << 14);
+       an8855_reg_write(priv, AN8855_RX_CTRL_8, val);
+
+       /* Frequency memter */
+       an8855_reg_read(priv, AN8855_RX_CTRL_5, &val);
+       val &= ~(0xfffff << 10);
+       val |= (0x10 << 10);
+       an8855_reg_write(priv, AN8855_RX_CTRL_5, val);
+
+       an8855_reg_read(priv, AN8855_RX_CTRL_6, &val);
+       val &= ~(0xfffff << 0);
+       val |= (0x64 << 0);
+       an8855_reg_write(priv, AN8855_RX_CTRL_6, val);
+
+       an8855_reg_read(priv, AN8855_RX_CTRL_7, &val);
+       val &= ~(0xfffff << 0);
+       val |= (0x2710 << 0);
+       an8855_reg_write(priv, AN8855_RX_CTRL_7, val);
+
+       /* PCS Init */
+       an8855_reg_read(priv, AN8855_RG_HSGMII_PCS_CTROL_1, &val);
+       val &= ~BIT(30);
+       an8855_reg_write(priv, AN8855_RG_HSGMII_PCS_CTROL_1, val);
+
+       /* Rate Adaption */
+       an8855_reg_read(priv, AN8855_RATE_ADP_P0_CTRL_0, &val);
+       val &= ~BIT(31);
+       an8855_reg_write(priv, AN8855_RATE_ADP_P0_CTRL_0, val);
+
+       an8855_reg_read(priv, AN8855_RG_RATE_ADAPT_CTRL_0, &val);
+       val |= BIT(0);
+       val |= BIT(4);
+       val |= GENMASK(27, 26);
+       an8855_reg_write(priv, AN8855_RG_RATE_ADAPT_CTRL_0, val);
+
+       /* Disable AN */
+       an8855_reg_read(priv, AN8855_SGMII_REG_AN0, &val);
+       val &= ~BIT(12);
+       an8855_reg_write(priv, AN8855_SGMII_REG_AN0, val);
+
+       /* Force Speed */
+       an8855_reg_read(priv, AN8855_SGMII_STS_CTRL_0, &val);
+       val |= BIT(2);
+       val |= GENMASK(5, 4);
+       an8855_reg_write(priv, AN8855_SGMII_STS_CTRL_0, val);
+
+       /* bypass flow control to MAC */
+       an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_0, 0x01010107);
+       an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_2, 0x00000EEF);
+
+       return 0;
+}
+
+static void an8855_led_set_usr_def(struct an8855_switch_priv *priv, u8 entity,
+                                  enum an8855_led_polarity pol, u16 on_evt,
+                                  u16 blk_evt, u8 led_freq)
+{
+       u32 cl45_data;
+
+       if (pol == LED_HIGH)
+               on_evt |= LED_ON_POL;
+       else
+               on_evt &= ~LED_ON_POL;
+
+       /* LED on event */
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+                             PHY_SINGLE_LED_ON_CTRL(entity % 4),
+                             on_evt | LED_ON_EN);
+
+       /* LED blink event */
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+                             PHY_SINGLE_LED_BLK_CTRL(entity % 4),
+                             blk_evt);
+
+       /* LED freq */
+       switch (led_freq) {
+       case AIR_LED_BLK_DUR_32M:
+               cl45_data = 0x30e;
+               break;
+
+       case AIR_LED_BLK_DUR_64M:
+               cl45_data = 0x61a;
+               break;
+
+       case AIR_LED_BLK_DUR_128M:
+               cl45_data = 0xc35;
+               break;
+
+       case AIR_LED_BLK_DUR_256M:
+               cl45_data = 0x186a;
+               break;
+
+       case AIR_LED_BLK_DUR_512M:
+               cl45_data = 0x30d4;
+               break;
+
+       case AIR_LED_BLK_DUR_1024M:
+               cl45_data = 0x61a8;
+               break;
+
+       default:
+               cl45_data = 0;
+               break;
+       }
+
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+                             PHY_SINGLE_LED_BLK_DUR(entity % 4),
+                             cl45_data);
+
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+                             PHY_SINGLE_LED_ON_DUR(entity % 4),
+                             (cl45_data >> 1));
+
+       /* Disable DATA & BAD_SSD for port LED blink behavior */
+       cl45_data = mtk_mmd_ind_read(priv->epriv.eth, (entity / 4), 0x1e, PHY_PMA_CTRL);
+       cl45_data &= ~BIT(0);
+       cl45_data &= ~BIT(15);
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_PMA_CTRL, cl45_data);
+}
+
+static int an8855_led_set_mode(struct an8855_switch_priv *priv, u8 mode)
+{
+       u16 cl45_data;
+
+       an8855_phy_cl45_read(priv, 0, 0x1f, PHY_LED_BCR, &cl45_data);
+
+       switch (mode) {
+       case AN8855_LED_MODE_DISABLE:
+               cl45_data &= ~LED_BCR_EXT_CTRL;
+               cl45_data &= ~LED_BCR_MODE_MASK;
+               cl45_data |= LED_BCR_MODE_DISABLE;
+               break;
+
+       case AN8855_LED_MODE_USER_DEFINE:
+               cl45_data |= LED_BCR_EXT_CTRL;
+               cl45_data |= LED_BCR_CLK_EN;
+               break;
+
+       default:
+               printf("an8855: LED mode%d is not supported!\n", mode);
+               return -EINVAL;
+       }
+
+       an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BCR, cl45_data);
+
+       return 0;
+}
+
+static int an8855_led_set_state(struct an8855_switch_priv *priv, u8 entity,
+                               u8 state)
+{
+       u16 cl45_data = 0;
+
+       /* Change to per port contorl */
+       an8855_phy_cl45_read(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT,
+                            &cl45_data);
+
+       if (state == 1)
+               cl45_data |= (1 << (entity % 4));
+       else
+               cl45_data &= ~(1 << (entity % 4));
+
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT,
+                             cl45_data);
+
+       /* LED enable setting */
+       an8855_phy_cl45_read(priv, (entity / 4), 0x1e,
+                            PHY_SINGLE_LED_ON_CTRL(entity % 4), &cl45_data);
+
+       if (state == 1)
+               cl45_data |= LED_ON_EN;
+       else
+               cl45_data &= ~LED_ON_EN;
+
+       an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+                             PHY_SINGLE_LED_ON_CTRL(entity % 4), cl45_data);
+
+       return 0;
+}
+
+static int an8855_led_init(struct an8855_switch_priv *priv)
+{
+       u32 val, id, tmp_id = 0;
+       int ret;
+
+       ret = an8855_led_set_mode(priv, AN8855_LED_MODE_USER_DEFINE);
+       if (ret) {
+               printf("an8855: led_set_mode failed with %d!\n", ret);
+               return ret;
+       }
+
+       for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+               ret = an8855_led_set_state(priv, led_cfg[id].phy_led_idx,
+                                          led_cfg[id].en);
+               if (ret != 0) {
+                       printf("an8855: led_set_state failed with %d!\n", ret);
+                       return ret;
+               }
+
+               if (led_cfg[id].en == 1) {
+                       an8855_led_set_usr_def(priv,
+                                              led_cfg[id].phy_led_idx,
+                                              led_cfg[id].pol,
+                                              led_cfg[id].on_cfg,
+                                              led_cfg[id].blk_cfg,
+                                              led_cfg[id].led_freq);
+               }
+       }
+
+       /* Setting for System LED & Loop LED */
+       an8855_reg_write(priv, AN8855_RG_GPIO_OE, 0x0);
+       an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x0);
+       an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, 0);
+
+       an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x1001);
+       an8855_reg_read(priv, AN8855_RG_GPIO_DATA, &val);
+       val |= GENMASK(3, 1);
+       val &= ~(BIT(0));
+       val &= ~(BIT(6));
+       an8855_reg_write(priv, AN8855_RG_GPIO_DATA, val);
+
+       an8855_reg_read(priv, AN8855_RG_GPIO_OE, &val);
+       val |= 0x41;
+       an8855_reg_write(priv, AN8855_RG_GPIO_OE, val);
+
+       /* Mapping between GPIO & LED */
+       val = 0;
+       for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+               /* Skip GPIO6, due to GPIO6 does not support PORT LED */
+               if (id == 6)
+                       continue;
+
+               if (led_cfg[id].en == 1) {
+                       if (id < 7)
+                               val |= led_cfg[id].phy_led_idx << ((id % 4) * 8);
+                       else
+                               val |= led_cfg[id].phy_led_idx << (((id - 1) % 4) * 8);
+               }
+
+               if (id < 7)
+                       tmp_id = id;
+               else
+                       tmp_id = id - 1;
+
+               if ((tmp_id % 4) == 0x3) {
+                       an8855_reg_write(priv,
+                                        AN8855_RG_GPIO_LED_SEL(tmp_id / 4),
+                                        val);
+                       val = 0;
+               }
+       }
+
+       /* Turn on LAN LED mode */
+       val = 0;
+       for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+               if (led_cfg[id].en == 1)
+                       val |= 0x1 << id;
+       }
+       an8855_reg_write(priv, AN8855_RG_GPIO_LED_MODE, val);
+
+       /* Force clear blink pulse for per port LED */
+       an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0x1f);
+       udelay(1000);
+       an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0);
+
+       return 0;
+}
+
+static void an8855_port_isolation(struct an8855_switch_priv *priv)
+{
+       u32 i;
+
+       for (i = 0; i < AN8855_NUM_PORTS; i++) {
+               /* Set port matrix mode */
+               if (i != 5)
+                       an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x20);
+               else
+                       an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x1f);
+
+               /* Set port mode to user port */
+               an8855_reg_write(priv, AN8855_PVC(i),
+                                (0x8100 << AN8855_STAG_VPID_S) |
+                                (VLAN_ATTR_USER << AN8855_VLAN_ATTR_S));
+       }
+}
+
+static void an8855_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable)
+{
+       struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv;
+       u32 pmcr = AN8855_FORCE_MODE_LNK;
+
+       if (enable)
+               pmcr = AN8855_FORCE_MODE;
+
+       an8855_reg_write(priv, AN8855_PMCR_REG(5), pmcr);
+}
+
+static int an8855_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+       struct an8855_switch_priv *priv = bus->priv;
+
+       if (devad < 0)
+               return mtk_mii_read(priv->epriv.eth, addr, reg);
+
+       return mtk_mmd_ind_read(priv->epriv.eth, addr, devad, reg);
+}
+
+static int an8855_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+                            u16 val)
+{
+       struct an8855_switch_priv *priv = bus->priv;
+
+       if (devad < 0)
+               return mtk_mii_write(priv->epriv.eth, addr, reg, val);
+
+       return mtk_mmd_ind_write(priv->epriv.eth, addr, devad, reg, val);
+}
+
+static int an8855_mdio_register(struct an8855_switch_priv *priv)
+{
+       struct mii_dev *mdio_bus = mdio_alloc();
+       int ret;
+
+       if (!mdio_bus)
+               return -ENOMEM;
+
+       mdio_bus->read = an8855_mdio_read;
+       mdio_bus->write = an8855_mdio_write;
+       snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name);
+
+       mdio_bus->priv = priv;
+
+       ret = mdio_register(mdio_bus);
+       if (ret) {
+               mdio_free(mdio_bus);
+               return ret;
+       }
+
+       priv->mdio_bus = mdio_bus;
+
+       return 0;
+}
+
+static int an8855_setup(struct mtk_eth_switch_priv *swpriv)
+{
+       struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv;
+       u16 phy_addr, phy_val;
+       u32 i, id, val = 0;
+       int ret;
+
+       priv->phy_base = 1;
+
+       /* Turn off PHYs */
+       for (i = 0; i < AN8855_NUM_PHYS; i++) {
+               phy_addr = AN8855_PHY_ADDR(priv->phy_base, i);
+               phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR);
+               phy_val |= BMCR_PDOWN;
+               mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val);
+       }
+
+       /* Force MAC link down before reset */
+       an8855_reg_write(priv, AN8855_PMCR_REG(5), AN8855_FORCE_MODE_LNK);
+
+       /* Switch soft reset */
+       an8855_reg_write(priv, AN8855_SYS_CTRL_REG, AN8855_SW_SYS_RST);
+       udelay(100000);
+
+       an8855_reg_read(priv, AN8855_PKG_SEL, &val);
+       if ((val & 0x7) == PAG_SEL_AN8855H) {
+               /* Release power down */
+               an8855_reg_write(priv, RG_GPHY_AFE_PWD, 0x0);
+
+               /* Invert for LED activity change */
+               an8855_reg_read(priv, AN8855_RG_GPIO_L_INV, &val);
+               for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+                       if ((led_cfg[id].pol == LED_HIGH) &&
+                           (led_cfg[id].en == 1))
+                               val |= 0x1 << id;
+               }
+               an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, (val | 0x1));
+
+               /* MCU NOP CMD */
+               an8855_reg_write(priv, AN8855_RG_GDMP_RAM, 0x846);
+               an8855_reg_write(priv, AN8855_RG_GDMP_RAM + 4, 0x4a);
+
+               /* Enable MCU */
+               an8855_reg_read(priv, AN8855_RG_CLK_CPU_ICG, &val);
+               an8855_reg_write(priv, AN8855_RG_CLK_CPU_ICG,
+                                val | AN8855_MCU_ENABLE);
+               udelay(1000);
+
+               /* Disable MCU watchdog */
+               an8855_reg_read(priv, AN8855_RG_TIMER_CTL, &val);
+               an8855_reg_write(priv, AN8855_RG_TIMER_CTL,
+                                (val & (~AN8855_WDOG_ENABLE)));
+
+               /* LED settings for T830 reference board */
+               ret = an8855_led_init(priv);
+               if (ret < 0) {
+                       printf("an8855: an8855_led_init failed with %d\n", ret);
+                       return ret;
+               }
+       }
+
+       switch (priv->epriv.phy_interface) {
+       case PHY_INTERFACE_MODE_2500BASEX:
+               an8855_port_sgmii_init(priv, 5);
+               break;
+
+       default:
+               break;
+       }
+
+       an8855_reg_read(priv, AN8855_CKGCR, &val);
+       val &= ~(0x3);
+       an8855_reg_write(priv, AN8855_CKGCR, val);
+
+       /* Enable port isolation to block inter-port communication */
+       an8855_port_isolation(priv);
+
+       /* Turn on PHYs */
+       for (i = 0; i < AN8855_NUM_PHYS; i++) {
+               phy_addr = AN8855_PHY_ADDR(priv->phy_base, i);
+               phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR);
+               phy_val &= ~BMCR_PDOWN;
+               mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val);
+       }
+
+       return an8855_mdio_register(priv);
+}
+
+static int an8855_cleanup(struct mtk_eth_switch_priv *swpriv)
+{
+       struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv;
+
+       mdio_unregister(priv->mdio_bus);
+
+       return 0;
+}
+
+static int an8855_detect(struct mtk_eth_priv *priv)
+{
+       int ret;
+       u32 val;
+
+       ret = __an8855_reg_read(priv, 1, 0x10005000, &val);
+       if (ret)
+               return ret;
+
+       if (val == 0x8855)
+               return 0;
+
+       return -ENODEV;
+}
+
+MTK_ETH_SWITCH(an8855) = {
+       .name = "an8855",
+       .desc = "Airoha AN8855",
+       .priv_size = sizeof(struct an8855_switch_priv),
+       .reset_wait_time = 100,
+
+       .detect = an8855_detect,
+       .setup = an8855_setup,
+       .cleanup = an8855_cleanup,
+       .mac_control = an8855_mac_control,
+};