From cedafee9ff39d13aaf8b80361b673445a85f117e Mon Sep 17 00:00:00 2001
From: Weijie Gao <weijie.gao@mediatek.com>
Date: Fri, 10 Jan 2025 16:41:24 +0800
Subject: [PATCH 3/3] net: mediatek: add support for Airoha AN8855 ethernet
 switch

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  |    4 +
 drivers/net/mtk_eth/Makefile |    1 +
 drivers/net/mtk_eth/an8855.c | 1096 ++++++++++++++++++++++++++++++++++
 3 files changed, 1101 insertions(+)
 create mode 100644 drivers/net/mtk_eth/an8855.c

--- a/drivers/net/mtk_eth/Kconfig
+++ b/drivers/net/mtk_eth/Kconfig
@@ -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
--- a/drivers/net/mtk_eth/Makefile
+++ b/drivers/net/mtk_eth/Makefile
@@ -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
--- /dev/null
+++ b/drivers/net/mtk_eth/an8855.c
@@ -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,
+};
