diff options
-rw-r--r-- | drivers/net/Kconfig | 21 | ||||
-rw-r--r-- | drivers/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/mtk_eth/Kconfig | 39 | ||||
-rw-r--r-- | drivers/net/mtk_eth/Makefile | 10 | ||||
-rw-r--r-- | drivers/net/mtk_eth/an8855.c | 1096 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mt7530.c | 281 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mt7531.c | 293 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mt753x.c | 262 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mt753x.h | 286 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mt7988.c | 160 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mtk_eth.c (renamed from drivers/net/mtk_eth.c) | 981 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mtk_eth.h (renamed from drivers/net/mtk_eth.h) | 301 |
12 files changed, 2631 insertions, 1101 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index dcf6b4c81fc..1563404ca17 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -966,26 +966,7 @@ config TSEC_ENET This driver implements support for the (Enhanced) Three-Speed Ethernet Controller found on Freescale SoCs. -config MEDIATEK_ETH - bool "MediaTek Ethernet GMAC Driver" - select PHYLIB - select DM_GPIO - select DM_RESET - help - This Driver support MediaTek Ethernet GMAC - Say Y to enable support for the MediaTek Ethernet GMAC. - -if MEDIATEK_ETH - -config MTK_ETH_SGMII - bool - default y if ARCH_MEDIATEK && !TARGET_MT7623 - -config MTK_ETH_XGMII - bool - default y if TARGET_MT7987 || TARGET_MT7988 - -endif # MEDIATEK_ETH +source "drivers/net/mtk_eth/Kconfig" config HIFEMAC_ETH bool "HiSilicon Fast Ethernet Controller" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index c6217f08f14..80d70212971 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -69,7 +69,7 @@ obj-$(CONFIG_MDIO_MUX_MESON_GXL) += mdio_mux_meson_gxl.o obj-$(CONFIG_MDIO_MUX_MMIOREG) += mdio_mux_mmioreg.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o -obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o +obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth/ obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o diff --git a/drivers/net/mtk_eth/Kconfig b/drivers/net/mtk_eth/Kconfig new file mode 100644 index 00000000000..e8cdf408237 --- /dev/null +++ b/drivers/net/mtk_eth/Kconfig @@ -0,0 +1,39 @@ + +config MEDIATEK_ETH + bool "MediaTek Ethernet GMAC Driver" + select PHYLIB + select DM_GPIO + select DM_RESET + help + This Driver support MediaTek Ethernet GMAC + Say Y to enable support for the MediaTek Ethernet GMAC. + +if MEDIATEK_ETH + +config MTK_ETH_SGMII + bool + default y if ARCH_MEDIATEK && !TARGET_MT7623 + +config MTK_ETH_XGMII + bool + default y if TARGET_MT7987 || TARGET_MT7988 + +config MTK_ETH_SWITCH_MT7530 + bool "Support for MediaTek MT7530 ethernet switch" + default y if TARGET_MT7623 || SOC_MT7621 + +config MTK_ETH_SWITCH_MT7531 + bool "Support for MediaTek MT7531 ethernet switch" + default y if TARGET_MT7622 || TARGET_MT7629 || TARGET_MT7981 || \ + TARGET_MT7986 || TARGET_MT7987 + +config MTK_ETH_SWITCH_MT7988 + bool "Support for MediaTek MT7988 built-in ethernet switch" + 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 diff --git a/drivers/net/mtk_eth/Makefile b/drivers/net/mtk_eth/Makefile new file mode 100644 index 00000000000..a342325ed5d --- /dev/null +++ b/drivers/net/mtk_eth/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 MediaTek Inc. +# Author: Weijie Gao <weijie.gao@mediatek.com> + +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 index 00000000000..4bd7506a58b --- /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, +}; diff --git a/drivers/net/mtk_eth/mt7530.c b/drivers/net/mtk_eth/mt7530.c new file mode 100644 index 00000000000..3065d35e126 --- /dev/null +++ b/drivers/net/mtk_eth/mt7530.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <miiphy.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include "mtk_eth.h" +#include "mt753x.h" + +#define CHIP_REV 0x7ffc +#define CHIP_NAME_S 16 +#define CHIP_NAME_M 0xffff0000 +#define CHIP_REV_S 0 +#define CHIP_REV_M 0x0f + +static void mt7530_core_reg_write(struct mt753x_switch_priv *priv, u32 reg, + u32 val) +{ + u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0); + + mtk_mmd_ind_write(priv->epriv.eth, phy_addr, 0x1f, reg, val); +} + +static int mt7530_pad_clk_setup(struct mt753x_switch_priv *priv, int mode) +{ + u32 ncpo1, ssc_delta; + + switch (mode) { + case PHY_INTERFACE_MODE_RGMII: + ncpo1 = 0x0c80; + ssc_delta = 0x87; + break; + + default: + printf("error: xMII mode %d is not supported\n", mode); + return -EINVAL; + } + + /* Disable MT7530 core clock */ + mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0); + + /* Disable MT7530 PLL */ + mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1, + (2 << RG_GSWPLL_POSDIV_200M_S) | + (32 << RG_GSWPLL_FBKDIV_200M_S)); + + /* For MT7530 core clock = 500Mhz */ + mt7530_core_reg_write(priv, CORE_GSWPLL_GRP2, + (1 << RG_GSWPLL_POSDIV_500M_S) | + (25 << RG_GSWPLL_FBKDIV_500M_S)); + + /* Enable MT7530 PLL */ + mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1, + (2 << RG_GSWPLL_POSDIV_200M_S) | + (32 << RG_GSWPLL_FBKDIV_200M_S) | + RG_GSWPLL_EN_PRE); + + udelay(20); + + mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); + + /* Setup the MT7530 TRGMII Tx Clock */ + mt7530_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1); + mt7530_core_reg_write(priv, CORE_PLL_GROUP6, 0); + mt7530_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta); + mt7530_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta); + mt7530_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN | + RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); + + mt7530_core_reg_write(priv, CORE_PLL_GROUP2, + RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | + (1 << RG_SYSPLL_POSDIV_S)); + + mt7530_core_reg_write(priv, CORE_PLL_GROUP7, + RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) | + RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); + + /* Enable MT7530 core clock */ + mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, + REG_GSWCK_EN | REG_TRGMIICK_EN); + + return 0; +} + +static void mt7530_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 pmcr = FORCE_MODE; + + if (enable) + pmcr = priv->pmcr; + + mt753x_reg_write(priv, PMCR_REG(6), pmcr); +} + +static int mt7530_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mt753x_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 mt7530_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mt753x_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 mt7530_mdio_register(struct mt753x_switch_priv *priv) +{ + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = mt7530_mdio_read; + mdio_bus->write = mt7530_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 mt7530_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u16 phy_addr, phy_val; + u32 i, val, txdrv; + + priv->smi_addr = MT753X_DFL_SMI_ADDR; + priv->reg_read = mt753x_mdio_reg_read; + priv->reg_write = mt753x_mdio_reg_write; + + if (!MTK_HAS_CAPS(priv->epriv.soc->caps, MTK_TRGMII_MT7621_CLK)) { + /* Select 250MHz clk for RGMII mode */ + mtk_ethsys_rmw(priv->epriv.eth, ETHSYS_CLKCFG0_REG, + ETHSYS_TRGMII_CLK_SEL362_5, 0); + + txdrv = 8; + } else { + txdrv = 4; + } + + /* Modify HWTRAP first to allow direct access to internal PHYs */ + mt753x_reg_read(priv, HWTRAP_REG, &val); + val |= CHG_TRAP; + val &= ~C_MDIO_BPS; + mt753x_reg_write(priv, MHWTRAP_REG, val); + + /* Calculate the phy base address */ + val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3; + priv->phy_base = (val | 0x7) + 1; + + /* Turn off PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_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 */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); + + /* MT7530 reset */ + mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); + udelay(100); + + val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | FORCE_MODE | + MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + (SPEED_1000M << FORCE_SPD_S) | + FORCE_DPX | FORCE_LINK; + + /* MT7530 Port6: Forced 1000M/FD, FC disabled */ + priv->pmcr = val; + + /* MT7530 Port5: Forced link down */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); + + /* Keep MAC link down before starting eth */ + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); + + /* MT7530 Port6: Set to RGMII */ + mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII); + + /* Hardware Trap: Enable Port6, Disable Port5 */ + mt753x_reg_read(priv, HWTRAP_REG, &val); + val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS | + (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) | + (P5_INTF_MODE_RGMII << P5_INTF_MODE_S); + val &= ~(C_MDIO_BPS | P6_INTF_DIS); + mt753x_reg_write(priv, MHWTRAP_REG, val); + + /* Setup switch core pll */ + mt7530_pad_clk_setup(priv, priv->epriv.phy_interface); + + /* Lower Tx Driving for TRGMII path */ + for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) + mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i), + (txdrv << TD_DM_DRVP_S) | + (txdrv << TD_DM_DRVN_S)); + + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16); + + /* Enable port isolation to block inter-port communication */ + mt753x_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_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 mt7530_mdio_register(priv); +} + +static int mt7530_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +static int mt7530_detect(struct mtk_eth_priv *priv) +{ + int ret; + u32 rev; + + ret = __mt753x_mdio_reg_read(priv, MT753X_DFL_SMI_ADDR, CHIP_REV, &rev); + if (ret) + return ret; + + if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == 0x7530) + return 0; + + return -ENODEV; +} + +MTK_ETH_SWITCH(mt7530) = { + .name = "mt7530", + .desc = "MediaTek MT7530", + .priv_size = sizeof(struct mt753x_switch_priv), + .reset_wait_time = 1000, + + .detect = mt7530_detect, + .setup = mt7530_setup, + .cleanup = mt7530_cleanup, + .mac_control = mt7530_mac_control, +}; diff --git a/drivers/net/mtk_eth/mt7531.c b/drivers/net/mtk_eth/mt7531.c new file mode 100644 index 00000000000..32d6bebbbdb --- /dev/null +++ b/drivers/net/mtk_eth/mt7531.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <miiphy.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include "mtk_eth.h" +#include "mt753x.h" + +#define CHIP_REV 0x781C +#define CHIP_NAME_S 16 +#define CHIP_NAME_M 0xffff0000 +#define CHIP_REV_S 0 +#define CHIP_REV_M 0x0f +#define CHIP_REV_E1 0x0 + +static int mt7531_core_reg_read(struct mt753x_switch_priv *priv, u32 reg) +{ + u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0); + + return mt7531_mmd_read(priv, phy_addr, 0x1f, reg); +} + +static void mt7531_core_reg_write(struct mt753x_switch_priv *priv, u32 reg, + u32 val) +{ + u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0); + + mt7531_mmd_write(priv, phy_addr, 0x1f, reg, val); +} + +static void mt7531_core_pll_setup(struct mt753x_switch_priv *priv) +{ + /* Step 1 : Disable MT7531 COREPLL */ + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0); + + /* Step 2: switch to XTAL output */ + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW); + + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0); + + /* Step 3: disable PLLGP and enable program PLLGP */ + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP); + + /* Step 4: program COREPLL output frequency to 500MHz */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M, + 2 << RG_COREPLL_POSDIV_S); + udelay(25); + + /* Currently, support XTAL 25Mhz only */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M, + 0x140000 << RG_COREPLL_SDM_PCW_S); + + /* Set feedback divide ratio update signal to high */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, + RG_COREPLL_SDM_PCW_CHG); + + /* Wait for at least 16 XTAL clocks */ + udelay(10); + + /* Step 5: set feedback divide ratio update signal to low */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0); + + /* add enable 325M clock for SGMII */ + mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000); + + /* add enable 250SSC clock for RGMII */ + mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000); + + /*Step 6: Enable MT7531 PLL */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN); + + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL); + + udelay(25); +} + +static int mt7531_port_sgmii_init(struct mt753x_switch_priv *priv, u32 port) +{ + if (port != 5 && port != 6) { + printf("mt7531: port %d is not a SGMII port\n", port); + return -EINVAL; + } + + /* Set SGMII GEN2 speed(2.5G) */ + mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), SGMSYS_SPEED_MASK, + FIELD_PREP(SGMSYS_SPEED_MASK, SGMSYS_SPEED_2500)); + + /* Disable SGMII AN */ + mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port), + SGMII_AN_ENABLE, 0); + + /* SGMII force mode setting */ + mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE); + + /* Release PHYA power down state */ + mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port), + SGMII_PHYA_PWD, 0); + + return 0; +} + +static int mt7531_port_rgmii_init(struct mt753x_switch_priv *priv, u32 port) +{ + u32 val; + + if (port != 5) { + printf("error: RGMII mode is not available for port %d\n", + port); + return -EINVAL; + } + + mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val); + val |= GP_CLK_EN; + val &= ~GP_MODE_M; + val |= GP_MODE_RGMII << GP_MODE_S; + val |= TXCLK_NO_REVERSE; + val |= RXCLK_NO_DELAY; + val &= ~CLK_SKEW_IN_M; + val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S; + val &= ~CLK_SKEW_OUT_M; + val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S; + mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val); + + return 0; +} + +static void mt7531_phy_setting(struct mt753x_switch_priv *priv) +{ + int i; + u32 val; + + for (i = 0; i < MT753X_NUM_PHYS; i++) { + /* Enable HW auto downshift */ + mt7531_mii_write(priv, i, 0x1f, 0x1); + val = mt7531_mii_read(priv, i, PHY_EXT_REG_14); + val |= PHY_EN_DOWN_SHFIT; + mt7531_mii_write(priv, i, PHY_EXT_REG_14, val); + + /* PHY link down power saving enable */ + val = mt7531_mii_read(priv, i, PHY_EXT_REG_17); + val |= PHY_LINKDOWN_POWER_SAVING_EN; + mt7531_mii_write(priv, i, PHY_EXT_REG_17, val); + + val = mt7531_mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6); + val &= ~PHY_POWER_SAVING_M; + val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; + mt7531_mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val); + } +} + +static void mt7531_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 pmcr = FORCE_MODE_LNK; + + if (enable) + pmcr = priv->pmcr; + + mt753x_reg_write(priv, PMCR_REG(5), pmcr); + mt753x_reg_write(priv, PMCR_REG(6), pmcr); +} + +static int mt7531_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 i, val, pmcr, port5_sgmii; + u16 phy_addr, phy_val; + + priv->smi_addr = MT753X_DFL_SMI_ADDR; + priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK; + priv->reg_read = mt753x_mdio_reg_read; + priv->reg_write = mt753x_mdio_reg_write; + + /* Turn off PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val |= BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + /* Force MAC link down before reset */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); + + /* Switch soft reset */ + mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); + udelay(100); + + /* Enable MDC input Schmitt Trigger */ + mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN, + SMT_IOLB_5_SMI_MDC_EN); + + mt7531_core_pll_setup(priv); + + mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val); + port5_sgmii = !!(val & PAD_DUAL_SGMII_EN); + + /* port5 support either RGMII or SGMII, port6 only support SGMII. */ + switch (priv->epriv.phy_interface) { + case PHY_INTERFACE_MODE_RGMII: + if (!port5_sgmii) + mt7531_port_rgmii_init(priv, 5); + break; + + case PHY_INTERFACE_MODE_2500BASEX: + mt7531_port_sgmii_init(priv, 6); + if (port5_sgmii) + mt7531_port_sgmii_init(priv, 5); + break; + + default: + break; + } + + pmcr = MT7531_FORCE_MODE | + (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + FORCE_RX_FC | FORCE_TX_FC | + (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | + FORCE_LINK; + + priv->pmcr = pmcr; + + /* Keep MAC link down before starting eth */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); + + /* Enable port isolation to block inter-port communication */ + mt753x_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + mt7531_phy_setting(priv); + + /* Enable Internal PHYs */ + val = mt7531_core_reg_read(priv, CORE_PLL_GROUP4); + val |= MT7531_BYPASS_MODE; + val &= ~MT7531_POWER_ON_OFF; + mt7531_core_reg_write(priv, CORE_PLL_GROUP4, val); + + return mt7531_mdio_register(priv); +} + +static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +static int mt7531_detect(struct mtk_eth_priv *priv) +{ + int ret; + u32 rev; + + ret = __mt753x_mdio_reg_read(priv, MT753X_DFL_SMI_ADDR, CHIP_REV, &rev); + if (ret) + return ret; + + if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == 0x7531) + return 0; + + return -ENODEV; +} + +MTK_ETH_SWITCH(mt7531) = { + .name = "mt7531", + .desc = "MediaTek MT7531", + .priv_size = sizeof(struct mt753x_switch_priv), + .reset_wait_time = 200, + + .detect = mt7531_detect, + .setup = mt7531_setup, + .cleanup = mt7531_cleanup, + .mac_control = mt7531_mac_control, +}; diff --git a/drivers/net/mtk_eth/mt753x.c b/drivers/net/mtk_eth/mt753x.c new file mode 100644 index 00000000000..cdd52f3ff1b --- /dev/null +++ b/drivers/net/mtk_eth/mt753x.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <errno.h> +#include <time.h> +#include "mtk_eth.h" +#include "mt753x.h" + +/* + * MT753x Internal Register Address Bits + * ------------------------------------------------------------------- + * | 15 14 13 12 11 10 9 8 7 6 | 5 4 3 2 | 1 0 | + * |----------------------------------------|---------------|--------| + * | Page Address | Reg Address | Unused | + * ------------------------------------------------------------------- + */ + +int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg, + u32 *data) +{ + int ret, low_word, high_word; + + /* Write page address */ + ret = mtk_mii_write(priv, smi_addr, 0x1f, reg >> 6); + if (ret) + return ret; + + /* Read low word */ + low_word = mtk_mii_read(priv, smi_addr, (reg >> 2) & 0xf); + if (low_word < 0) + return low_word; + + /* Read high word */ + high_word = mtk_mii_read(priv, smi_addr, 0x10); + if (high_word < 0) + return high_word; + + if (data) + *data = ((u32)high_word << 16) | (low_word & 0xffff); + + return 0; +} + +int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) +{ + return __mt753x_mdio_reg_read(priv->epriv.eth, priv->smi_addr, reg, + data); +} + +int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) +{ + int ret; + + /* Write page address */ + ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x1f, reg >> 6); + if (ret) + return ret; + + /* Write low word */ + ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, (reg >> 2) & 0xf, + data & 0xffff); + if (ret) + return ret; + + /* Write high word */ + return mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x10, data >> 16); +} + +int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) +{ + return priv->reg_read(priv, reg, data); +} + +int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) +{ + return priv->reg_write(priv, reg, data); +} + +void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set) +{ + u32 val; + + priv->reg_read(priv, reg, &val); + val &= ~clr; + val |= set; + priv->reg_write(priv, reg, val); +} + +/* Indirect MDIO clause 22/45 access */ +static int mt7531_mii_rw(struct mt753x_switch_priv *priv, int phy, int reg, + u16 data, u32 cmd, u32 st) +{ + u32 val, timeout_ms; + ulong timeout; + int ret = 0; + + val = (st << MDIO_ST_S) | + ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | + ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | + ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); + + if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR) + val |= data & MDIO_RW_DATA_M; + + mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST); + + timeout_ms = 100; + timeout = get_timer(0); + while (1) { + mt753x_reg_read(priv, MT7531_PHY_IAC, &val); + + if ((val & PHY_ACS_ST) == 0) + break; + + if (get_timer(timeout) > timeout_ms) + return -ETIMEDOUT; + } + + if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) { + mt753x_reg_read(priv, MT7531_PHY_IAC, &val); + ret = val & MDIO_RW_DATA_M; + } + + return ret; +} + +int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg) +{ + u8 phy_addr; + + if (phy >= MT753X_NUM_PHYS) + return -EINVAL; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy); + + return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ, + MDIO_ST_C22); +} + +int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val) +{ + u8 phy_addr; + + if (phy >= MT753X_NUM_PHYS) + return -EINVAL; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy); + + return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE, + MDIO_ST_C22); +} + +int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg) +{ + u8 phy_addr; + int ret; + + if (addr >= MT753X_NUM_PHYS) + return -EINVAL; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr); + + ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, + MDIO_ST_C45); + if (ret) + return ret; + + return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45, + MDIO_ST_C45); +} + +int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg, u16 val) +{ + u8 phy_addr; + int ret; + + if (addr >= MT753X_NUM_PHYS) + return 0; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr); + + ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, + MDIO_ST_C45); + if (ret) + return ret; + + return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE, + MDIO_ST_C45); +} + +static int mt7531_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mt753x_switch_priv *priv = bus->priv; + + if (devad < 0) + return mt7531_mii_read(priv, addr, reg); + + return mt7531_mmd_read(priv, addr, devad, reg); +} + +static int mt7531_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mt753x_switch_priv *priv = bus->priv; + + if (devad < 0) + return mt7531_mii_write(priv, addr, reg, val); + + return mt7531_mmd_write(priv, addr, devad, reg, val); +} + +int mt7531_mdio_register(struct mt753x_switch_priv *priv) +{ + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = mt7531_mdio_read; + mdio_bus->write = mt7531_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; +} + +void mt753x_port_isolation(struct mt753x_switch_priv *priv) +{ + u32 i; + + for (i = 0; i < MT753X_NUM_PORTS; i++) { + /* Set port matrix mode */ + if (i != 6) + mt753x_reg_write(priv, PCR_REG(i), + (0x40 << PORT_MATRIX_S)); + else + mt753x_reg_write(priv, PCR_REG(i), + (0x3f << PORT_MATRIX_S)); + + /* Set port mode to user port */ + mt753x_reg_write(priv, PVC_REG(i), + (0x8100 << STAG_VPID_S) | + (VLAN_ATTR_USER << VLAN_ATTR_S)); + } +} diff --git a/drivers/net/mtk_eth/mt753x.h b/drivers/net/mtk_eth/mt753x.h new file mode 100644 index 00000000000..65046a1421c --- /dev/null +++ b/drivers/net/mtk_eth/mt753x.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#ifndef _MTK_ETH_MT753X_H_ +#define _MTK_ETH_MT753X_H_ + +#include <phy.h> +#include <miiphy.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> + +struct mtk_eth_priv; + +#define MT753X_NUM_PHYS 5 +#define MT753X_NUM_PORTS 7 +#define MT753X_DFL_SMI_ADDR 31 +#define MT753X_SMI_ADDR_MASK 0x1f + +#define MT753X_PHY_ADDR(base, addr) \ + (((base) + (addr)) & 0x1f) + +/* MT7530 Registers */ +#define PCR_REG(p) (0x2004 + (p) * 0x100) +#define PORT_MATRIX_S 16 +#define PORT_MATRIX_M 0xff0000 + +#define PVC_REG(p) (0x2010 + (p) * 0x100) +#define STAG_VPID_S 16 +#define STAG_VPID_M 0xffff0000 +#define VLAN_ATTR_S 6 +#define VLAN_ATTR_M 0xc0 + +/* VLAN_ATTR: VLAN attributes */ +#define VLAN_ATTR_USER 0 +#define VLAN_ATTR_STACK 1 +#define VLAN_ATTR_TRANSLATION 2 +#define VLAN_ATTR_TRANSPARENT 3 + +#define PMCR_REG(p) (0x3000 + (p) * 0x100) +/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR + * MT7531 specific fields are defined below + */ +#define FORCE_MODE_EEE1G BIT(25) +#define FORCE_MODE_EEE100 BIT(26) +#define FORCE_MODE_TX_FC BIT(27) +#define FORCE_MODE_RX_FC BIT(28) +#define FORCE_MODE_DPX BIT(29) +#define FORCE_MODE_SPD BIT(30) +#define FORCE_MODE_LNK BIT(31) +#define MT7531_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ + FORCE_MODE_DPX | FORCE_MODE_SPD | \ + FORCE_MODE_LNK +#define MT7988_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ + FORCE_MODE_DPX | FORCE_MODE_SPD | \ + FORCE_MODE_LNK + +/* MT7531 SGMII Registers */ +#define MT7531_SGMII_REG_BASE 0x5000 +#define MT7531_SGMII_REG_PORT_BASE 0x1000 +#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \ + (p) * MT7531_SGMII_REG_PORT_BASE + (r)) +#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(((p) - 5), 0x00) +#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(((p) - 5), 0x20) +#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8) +#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(((p) - 5), 0x128) +#define MT7531_PHYA_ANA_SYSPLL(p) MT7531_SGMII_REG(((p) - 5), 0x158) +/* XXX: all fields of MT7531 SGMII are defined under SGMSYS */ + +/* MT753x System Control Register */ +#define SYS_CTRL_REG 0x7000 +#define SW_PHY_RST BIT(2) +#define SW_SYS_RST BIT(1) +#define SW_REG_RST BIT(0) + +/* MT7531 */ +#define MT7531_PHY_IAC 0x701c +/* XXX: all fields are defined under GMAC_PIAC_REG */ + +#define MT7531_CLKGEN_CTRL 0x7500 +#define CLK_SKEW_OUT_S 8 +#define CLK_SKEW_OUT_M 0x300 +#define CLK_SKEW_IN_S 6 +#define CLK_SKEW_IN_M 0xc0 +#define RXCLK_NO_DELAY BIT(5) +#define TXCLK_NO_REVERSE BIT(4) +#define GP_MODE_S 1 +#define GP_MODE_M 0x06 +#define GP_CLK_EN BIT(0) + +/* Values of GP_MODE */ +#define GP_MODE_RGMII 0 +#define GP_MODE_MII 1 +#define GP_MODE_REV_MII 2 + +/* Values of CLK_SKEW_IN */ +#define CLK_SKEW_IN_NO_CHANGE 0 +#define CLK_SKEW_IN_DELAY_100PPS 1 +#define CLK_SKEW_IN_DELAY_200PPS 2 +#define CLK_SKEW_IN_REVERSE 3 + +/* Values of CLK_SKEW_OUT */ +#define CLK_SKEW_OUT_NO_CHANGE 0 +#define CLK_SKEW_OUT_DELAY_100PPS 1 +#define CLK_SKEW_OUT_DELAY_200PPS 2 +#define CLK_SKEW_OUT_REVERSE 3 + +#define HWTRAP_REG 0x7800 +/* MT7530 Modified Hardware Trap Status Registers */ +#define MHWTRAP_REG 0x7804 +#define CHG_TRAP BIT(16) +#define LOOPDET_DIS BIT(14) +#define P5_INTF_SEL_S 13 +#define P5_INTF_SEL_M 0x2000 +#define SMI_ADDR_S 11 +#define SMI_ADDR_M 0x1800 +#define XTAL_FSEL_S 9 +#define XTAL_FSEL_M 0x600 +#define P6_INTF_DIS BIT(8) +#define P5_INTF_MODE_S 7 +#define P5_INTF_MODE_M 0x80 +#define P5_INTF_DIS BIT(6) +#define C_MDIO_BPS BIT(5) +#define CHIP_MODE_S 0 +#define CHIP_MODE_M 0x0f + +/* P5_INTF_SEL: Interface type of Port5 */ +#define P5_INTF_SEL_GPHY 0 +#define P5_INTF_SEL_GMAC5 1 + +/* P5_INTF_MODE: Interface mode of Port5 */ +#define P5_INTF_MODE_GMII_MII 0 +#define P5_INTF_MODE_RGMII 1 + +#define MT7530_P6ECR 0x7830 +#define P6_INTF_MODE_M 0x3 +#define P6_INTF_MODE_S 0 + +/* P6_INTF_MODE: Interface mode of Port6 */ +#define P6_INTF_MODE_RGMII 0 +#define P6_INTF_MODE_TRGMII 1 + +#define MT7530_TRGMII_RD(n) (0x7a10 + (n) * 8) +#define RD_TAP_S 0 +#define RD_TAP_M 0x7f + +#define MT7530_TRGMII_TD_ODT(n) (0x7a54 + (n) * 8) +/* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */ + +/* TOP Signals Status Register */ +#define MT7531_TOP_SIG_SR 0x780c +#define PAD_MCM_SMI_EN BIT(0) +#define PAD_DUAL_SGMII_EN BIT(1) + +/* MT7531 PLLGP Registers */ +#define MT7531_PLLGP_EN 0x7820 +#define EN_COREPLL BIT(2) +#define SW_CLKSW BIT(1) +#define SW_PLLGP BIT(0) + +#define MT7531_PLLGP_CR0 0x78a8 +#define RG_COREPLL_EN BIT(22) +#define RG_COREPLL_POSDIV_S 23 +#define RG_COREPLL_POSDIV_M 0x3800000 +#define RG_COREPLL_SDM_PCW_S 1 +#define RG_COREPLL_SDM_PCW_M 0x3ffffe +#define RG_COREPLL_SDM_PCW_CHG BIT(0) + +/* MT7531 RGMII and SGMII PLL clock */ +#define MT7531_ANA_PLLGP_CR2 0x78b0 +#define MT7531_ANA_PLLGP_CR5 0x78bc + +/* MT7531 GPIO GROUP IOLB SMT0 Control */ +#define MT7531_SMT0_IOLB 0x7f04 +#define SMT_IOLB_5_SMI_MDC_EN BIT(5) + +/* MT7530 GPHY MDIO MMD Registers */ +#define CORE_PLL_GROUP2 0x401 +#define RG_SYSPLL_EN_NORMAL BIT(15) +#define RG_SYSPLL_VODEN BIT(14) +#define RG_SYSPLL_POSDIV_S 5 +#define RG_SYSPLL_POSDIV_M 0x60 + +#define CORE_PLL_GROUP4 0x403 +#define MT7531_BYPASS_MODE BIT(4) +#define MT7531_POWER_ON_OFF BIT(5) +#define RG_SYSPLL_DDSFBK_EN BIT(12) +#define RG_SYSPLL_BIAS_EN BIT(11) +#define RG_SYSPLL_BIAS_LPF_EN BIT(10) + +#define CORE_PLL_GROUP5 0x404 +#define RG_LCDDS_PCW_NCPO1_S 0 +#define RG_LCDDS_PCW_NCPO1_M 0xffff + +#define CORE_PLL_GROUP6 0x405 +#define RG_LCDDS_PCW_NCPO0_S 0 +#define RG_LCDDS_PCW_NCPO0_M 0xffff + +#define CORE_PLL_GROUP7 0x406 +#define RG_LCDDS_PWDB BIT(15) +#define RG_LCDDS_ISO_EN BIT(13) +#define RG_LCCDS_C_S 4 +#define RG_LCCDS_C_M 0x70 +#define RG_LCDDS_PCW_NCPO_CHG BIT(3) + +#define CORE_PLL_GROUP10 0x409 +#define RG_LCDDS_SSC_DELTA_S 0 +#define RG_LCDDS_SSC_DELTA_M 0xfff + +#define CORE_PLL_GROUP11 0x40a +#define RG_LCDDS_SSC_DELTA1_S 0 +#define RG_LCDDS_SSC_DELTA1_M 0xfff + +#define CORE_GSWPLL_GRP1 0x40d +#define RG_GSWPLL_POSDIV_200M_S 12 +#define RG_GSWPLL_POSDIV_200M_M 0x3000 +#define RG_GSWPLL_EN_PRE BIT(11) +#define RG_GSWPLL_FBKDIV_200M_S 0 +#define RG_GSWPLL_FBKDIV_200M_M 0xff + +#define CORE_GSWPLL_GRP2 0x40e +#define RG_GSWPLL_POSDIV_500M_S 8 +#define RG_GSWPLL_POSDIV_500M_M 0x300 +#define RG_GSWPLL_FBKDIV_500M_S 0 +#define RG_GSWPLL_FBKDIV_500M_M 0xff + +#define CORE_TRGMII_GSW_CLK_CG 0x410 +#define REG_GSWCK_EN BIT(0) +#define REG_TRGMIICK_EN BIT(1) + +/* Extend PHY Control Register 3 */ +#define PHY_EXT_REG_14 0x14 + +/* Fields of PHY_EXT_REG_14 */ +#define PHY_EN_DOWN_SHFIT BIT(4) + +/* Extend PHY Control Register 4 */ +#define PHY_EXT_REG_17 0x17 + +/* Fields of PHY_EXT_REG_17 */ +#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4) + +/* PHY RXADC Control Register 7 */ +#define PHY_DEV1E_REG_0C6 0x0c6 + +/* Fields of PHY_DEV1E_REG_0C6 */ +#define PHY_POWER_SAVING_S 8 +#define PHY_POWER_SAVING_M 0x300 +#define PHY_POWER_SAVING_TX 0x0 + +struct mt753x_switch_priv { + struct mtk_eth_switch_priv epriv; + struct mii_dev *mdio_bus; + u32 smi_addr; + u32 phy_base; + u32 pmcr; + + int (*reg_read)(struct mt753x_switch_priv *priv, u32 reg, u32 *data); + int (*reg_write)(struct mt753x_switch_priv *priv, u32 reg, u32 data); +}; + +int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg, + u32 *data); +int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data); +int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data); + +int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data); +int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data); +void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set); + +int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg); +int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val); +int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg); +int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg, u16 val); + +int mt7531_mdio_register(struct mt753x_switch_priv *priv); + +void mt753x_port_isolation(struct mt753x_switch_priv *priv); + +#endif /* _MTK_ETH_MT753X_H_ */ diff --git a/drivers/net/mtk_eth/mt7988.c b/drivers/net/mtk_eth/mt7988.c new file mode 100644 index 00000000000..a416d87840c --- /dev/null +++ b/drivers/net/mtk_eth/mt7988.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <miiphy.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include <linux/io.h> +#include "mtk_eth.h" +#include "mt753x.h" + +static int mt7988_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) +{ + *data = readl(priv->epriv.ethsys_base + GSW_BASE + reg); + + return 0; +} + +static int mt7988_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) +{ + writel(data, priv->epriv.ethsys_base + GSW_BASE + reg); + + return 0; +} + +static void mt7988_phy_setting(struct mt753x_switch_priv *priv) +{ + u16 val; + u32 i; + + for (i = 0; i < MT753X_NUM_PHYS; i++) { + /* Enable HW auto downshift */ + mt7531_mii_write(priv, i, 0x1f, 0x1); + val = mt7531_mii_read(priv, i, PHY_EXT_REG_14); + val |= PHY_EN_DOWN_SHFIT; + mt7531_mii_write(priv, i, PHY_EXT_REG_14, val); + + /* PHY link down power saving enable */ + val = mt7531_mii_read(priv, i, PHY_EXT_REG_17); + val |= PHY_LINKDOWN_POWER_SAVING_EN; + mt7531_mii_write(priv, i, PHY_EXT_REG_17, val); + } +} + +static void mt7988_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 pmcr = FORCE_MODE_LNK; + + if (enable) + pmcr = priv->pmcr; + + mt7988_reg_write(priv, PMCR_REG(6), pmcr); +} + +static int mt7988_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u16 phy_addr, phy_val; + u32 pmcr; + int i; + + priv->smi_addr = MT753X_DFL_SMI_ADDR; + priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK; + priv->reg_read = mt7988_reg_read; + priv->reg_write = mt7988_reg_write; + + /* Turn off PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val |= BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + switch (priv->epriv.phy_interface) { + case PHY_INTERFACE_MODE_USXGMII: + /* Use CPU bridge instead of actual USXGMII path */ + + /* Disable GDM1 RX CRC stripping */ + /* mtk_fe_rmw(priv, 0x500, BIT(16), 0); */ + + /* Set GDM1 no drop */ + mtk_fe_rmw(priv->epriv.eth, PSE_NO_DROP_CFG_REG, 0, + PSE_NO_DROP_GDM1); + + /* Enable GSW CPU bridge as USXGMII */ + /* mtk_fe_rmw(priv, 0x504, BIT(31), BIT(31)); */ + + /* Enable GDM1 to GSW CPU bridge */ + mtk_gmac_rmw(priv->epriv.eth, GMAC_MAC_MISC_REG, 0, BIT(0)); + + /* XGMAC force link up */ + mtk_gmac_rmw(priv->epriv.eth, GMAC_XGMAC_STS_REG, 0, + P1_XGMAC_FORCE_LINK); + + /* Setup GSW CPU bridge IPG */ + mtk_gmac_rmw(priv->epriv.eth, GMAC_GSW_CFG_REG, + GSWTX_IPG_M | GSWRX_IPG_M, + (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S)); + break; + default: + printf("Error: MT7988 GSW does not support %s interface\n", + phy_string_for_interface(priv->epriv.phy_interface)); + break; + } + + pmcr = MT7988_FORCE_MODE | + (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + FORCE_RX_FC | FORCE_TX_FC | + (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | + FORCE_LINK; + + priv->pmcr = pmcr; + + /* Keep MAC link down before starting eth */ + mt7988_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); + + /* Enable port isolation to block inter-port communication */ + mt753x_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + mt7988_phy_setting(priv); + + return mt7531_mdio_register(priv); +} + +static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +MTK_ETH_SWITCH(mt7988) = { + .name = "mt7988", + .desc = "MediaTek MT7988 built-in switch", + .priv_size = sizeof(struct mt753x_switch_priv), + .reset_wait_time = 50, + + .setup = mt7988_setup, + .cleanup = mt7531_cleanup, + .mac_control = mt7988_mac_control, +}; diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth/mtk_eth.c index 454caa3cd3a..5d6a42bceb4 100644 --- a/drivers/net/mtk_eth.c +++ b/drivers/net/mtk_eth/mtk_eth.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 MediaTek Inc. + * Copyright (C) 2025 MediaTek Inc. * * Author: Weijie Gao <weijie.gao@mediatek.com> * Author: Mark Lee <mark-mc.lee@mediatek.com> @@ -35,14 +35,6 @@ #define RX_TOTAL_BUF_SIZE (NUM_RX_DESC * PKTSIZE_ALIGN) #define TOTAL_PKT_BUF_SIZE (TX_TOTAL_BUF_SIZE + RX_TOTAL_BUF_SIZE) -#define MT753X_NUM_PHYS 5 -#define MT753X_NUM_PORTS 7 -#define MT753X_DFL_SMI_ADDR 31 -#define MT753X_SMI_ADDR_MASK 0x1f - -#define MT753X_PHY_ADDR(base, addr) \ - (((base) + (addr)) & 0x1f) - #define GDMA_FWD_TO_CPU \ (0x20000000 | \ GDM_ICS_EN | \ @@ -75,32 +67,6 @@ (DP_DISCARD << MC_DP_S) | \ (DP_DISCARD << UN_DP_S)) -enum mtk_switch { - SW_NONE, - SW_MT7530, - SW_MT7531, - SW_MT7988, -}; - -/* struct mtk_soc_data - This is the structure holding all differences - * among various plaforms - * @caps Flags shown the extra capability for the SoC - * @ana_rgc3: The offset for register ANA_RGC3 related to - * sgmiisys syscon - * @gdma_count: Number of GDMAs - * @pdma_base: Register base of PDMA block - * @txd_size: Tx DMA descriptor size. - * @rxd_size: Rx DMA descriptor size. - */ -struct mtk_soc_data { - u32 caps; - u32 ana_rgc3; - u32 gdma_count; - u32 pdma_base; - u32 txd_size; - u32 rxd_size; -}; - struct mtk_eth_priv { char pkt_pool[TOTAL_PKT_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); @@ -113,7 +79,6 @@ struct mtk_eth_priv { void __iomem *fe_base; void __iomem *gmac_base; void __iomem *sgmii_base; - void __iomem *gsw_base; struct regmap *ethsys_regmap; @@ -125,11 +90,6 @@ struct mtk_eth_priv { struct regmap *toprgu_regmap; struct mii_dev *mdio_bus; - int (*mii_read)(struct mtk_eth_priv *priv, u8 phy, u8 reg); - int (*mii_write)(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 val); - int (*mmd_read)(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg); - int (*mmd_write)(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, - u16 val); const struct mtk_soc_data *soc; int gmac_id; @@ -143,13 +103,8 @@ struct mtk_eth_priv { int phy_interface; int phy_addr; - enum mtk_switch sw; - int (*switch_init)(struct mtk_eth_priv *priv); - void (*switch_mac_control)(struct mtk_eth_priv *priv, bool enable); - u32 mt753x_smi_addr; - u32 mt753x_phy_base; - u32 mt753x_pmcr; - u32 mt753x_reset_wait_time; + struct mtk_eth_switch_priv *swpriv; + const char *swname; struct gpio_desc rst_gpio; int mcm; @@ -184,7 +139,7 @@ static void mtk_gdma_write(struct mtk_eth_priv *priv, int no, u32 reg, writel(val, priv->fe_base + gdma_base + reg); } -static void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) +void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) { clrsetbits_le32(priv->fe_base + reg, clr, set); } @@ -199,13 +154,12 @@ static void mtk_gmac_write(struct mtk_eth_priv *priv, u32 reg, u32 val) writel(val, priv->gmac_base + reg); } -static void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) +void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) { clrsetbits_le32(priv->gmac_base + reg, clr, set); } -static void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, - u32 set) +void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) { uint val; @@ -226,16 +180,6 @@ static void mtk_infra_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, regmap_write(priv->infra_regmap, reg, val); } -static u32 mtk_gsw_read(struct mtk_eth_priv *priv, u32 reg) -{ - return readl(priv->gsw_base + reg); -} - -static void mtk_gsw_write(struct mtk_eth_priv *priv, u32 reg, u32 val) -{ - writel(val, priv->gsw_base + reg); -} - /* Direct MDIO clause 22/45 access via SoC */ static int mtk_mii_rw(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data, u32 cmd, u32 st) @@ -269,19 +213,19 @@ static int mtk_mii_rw(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data, } /* Direct MDIO clause 22 read via SoC */ -static int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg) +int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg) { return mtk_mii_rw(priv, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22); } /* Direct MDIO clause 22 write via SoC */ -static int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data) +int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data) { return mtk_mii_rw(priv, phy, reg, data, MDIO_CMD_WRITE, MDIO_ST_C22); } /* Direct MDIO clause 45 read via SoC */ -static int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) +int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) { int ret; @@ -294,8 +238,8 @@ static int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) } /* Direct MDIO clause 45 write via SoC */ -static int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg, u16 val) +int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val) { int ret; @@ -308,232 +252,52 @@ static int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, } /* Indirect MDIO clause 45 read via MII registers */ -static int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg) +int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) { int ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_ADDR << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_ADDR << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); + ret = mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_DATA << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_DATA << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - return priv->mii_read(priv, addr, MII_MMD_ADDR_DATA_REG); + return mtk_mii_read(priv, addr, MII_MMD_ADDR_DATA_REG); } /* Indirect MDIO clause 45 write via MII registers */ -static int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg, u16 val) +int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val) { int ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_ADDR << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_ADDR << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); + ret = mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_DATA << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_DATA << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - return priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val); -} - -/* - * MT7530 Internal Register Address Bits - * ------------------------------------------------------------------- - * | 15 14 13 12 11 10 9 8 7 6 | 5 4 3 2 | 1 0 | - * |----------------------------------------|---------------|--------| - * | Page Address | Reg Address | Unused | - * ------------------------------------------------------------------- - */ - -static int mt753x_reg_read(struct mtk_eth_priv *priv, u32 reg, u32 *data) -{ - int ret, low_word, high_word; - - if (priv->sw == SW_MT7988) { - *data = mtk_gsw_read(priv, reg); - return 0; - } - - /* Write page address */ - ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6); - if (ret) - return ret; - - /* Read low word */ - low_word = mtk_mii_read(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf); - if (low_word < 0) - return low_word; - - /* Read high word */ - high_word = mtk_mii_read(priv, priv->mt753x_smi_addr, 0x10); - if (high_word < 0) - return high_word; - - if (data) - *data = ((u32)high_word << 16) | (low_word & 0xffff); - - return 0; -} - -static int mt753x_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 data) -{ - int ret; - - if (priv->sw == SW_MT7988) { - mtk_gsw_write(priv, reg, data); - return 0; - } - - /* Write page address */ - ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6); - if (ret) - return ret; - - /* Write low word */ - ret = mtk_mii_write(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf, - data & 0xffff); - if (ret) - return ret; - - /* Write high word */ - return mtk_mii_write(priv, priv->mt753x_smi_addr, 0x10, data >> 16); -} - -static void mt753x_reg_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, - u32 set) -{ - u32 val; - - mt753x_reg_read(priv, reg, &val); - val &= ~clr; - val |= set; - mt753x_reg_write(priv, reg, val); -} - -/* Indirect MDIO clause 22/45 access */ -static int mt7531_mii_rw(struct mtk_eth_priv *priv, int phy, int reg, u16 data, - u32 cmd, u32 st) -{ - ulong timeout; - u32 val, timeout_ms; - int ret = 0; - - val = (st << MDIO_ST_S) | - ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | - ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | - ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); - - if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR) - val |= data & MDIO_RW_DATA_M; - - mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST); - - timeout_ms = 100; - timeout = get_timer(0); - while (1) { - mt753x_reg_read(priv, MT7531_PHY_IAC, &val); - - if ((val & PHY_ACS_ST) == 0) - break; - - if (get_timer(timeout) > timeout_ms) - return -ETIMEDOUT; - } - - if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) { - mt753x_reg_read(priv, MT7531_PHY_IAC, &val); - ret = val & MDIO_RW_DATA_M; - } - - return ret; -} - -static int mt7531_mii_ind_read(struct mtk_eth_priv *priv, u8 phy, u8 reg) -{ - u8 phy_addr; - - if (phy >= MT753X_NUM_PHYS) - return -EINVAL; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy); - - return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ, - MDIO_ST_C22); -} - -static int mt7531_mii_ind_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, - u16 val) -{ - u8 phy_addr; - - if (phy >= MT753X_NUM_PHYS) - return -EINVAL; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy); - - return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE, - MDIO_ST_C22); -} - -static int mt7531_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg) -{ - u8 phy_addr; - int ret; - - if (addr >= MT753X_NUM_PHYS) - return -EINVAL; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, addr); - - ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, - MDIO_ST_C45); - if (ret) - return ret; - - return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45, - MDIO_ST_C45); -} - -static int mt7531_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg, u16 val) -{ - u8 phy_addr; - int ret; - - if (addr >= MT753X_NUM_PHYS) - return 0; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, addr); - - ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, - MDIO_ST_C45); - if (ret) - return ret; - - return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE, - MDIO_ST_C45); + return mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val); } static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) @@ -541,9 +305,9 @@ static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) struct mtk_eth_priv *priv = bus->priv; if (devad < 0) - return priv->mii_read(priv, addr, reg); - else - return priv->mmd_read(priv, addr, devad, reg); + return mtk_mii_read(priv, addr, reg); + + return mtk_mmd_read(priv, addr, devad, reg); } static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, @@ -552,9 +316,9 @@ static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, struct mtk_eth_priv *priv = bus->priv; if (devad < 0) - return priv->mii_write(priv, addr, reg, val); - else - return priv->mmd_write(priv, addr, devad, reg, val); + return mtk_mii_write(priv, addr, reg, val); + + return mtk_mmd_write(priv, addr, devad, reg, val); } static int mtk_mdio_register(struct udevice *dev) @@ -566,28 +330,6 @@ static int mtk_mdio_register(struct udevice *dev) if (!mdio_bus) return -ENOMEM; - /* Assign MDIO access APIs according to the switch/phy */ - switch (priv->sw) { - case SW_MT7530: - priv->mii_read = mtk_mii_read; - priv->mii_write = mtk_mii_write; - priv->mmd_read = mtk_mmd_ind_read; - priv->mmd_write = mtk_mmd_ind_write; - break; - case SW_MT7531: - case SW_MT7988: - priv->mii_read = mt7531_mii_ind_read; - priv->mii_write = mt7531_mii_ind_write; - priv->mmd_read = mt7531_mmd_ind_read; - priv->mmd_write = mt7531_mmd_ind_write; - break; - default: - priv->mii_read = mtk_mii_read; - priv->mii_write = mtk_mii_write; - priv->mmd_read = mtk_mmd_read; - priv->mmd_write = mtk_mmd_write; - } - mdio_bus->read = mtk_mdio_read; mdio_bus->write = mtk_mdio_write; snprintf(mdio_bus->name, sizeof(mdio_bus->name), dev->name); @@ -604,531 +346,91 @@ static int mtk_mdio_register(struct udevice *dev) return 0; } -static int mt753x_core_reg_read(struct mtk_eth_priv *priv, u32 reg) -{ - u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0); - - return priv->mmd_read(priv, phy_addr, 0x1f, reg); -} - -static void mt753x_core_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 val) -{ - u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0); - - priv->mmd_write(priv, phy_addr, 0x1f, reg, val); -} - -static int mt7530_pad_clk_setup(struct mtk_eth_priv *priv, int mode) -{ - u32 ncpo1, ssc_delta; - - switch (mode) { - case PHY_INTERFACE_MODE_RGMII: - ncpo1 = 0x0c80; - ssc_delta = 0x87; - break; - default: - printf("error: xMII mode %d not supported\n", mode); - return -EINVAL; - } - - /* Disable MT7530 core clock */ - mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0); - - /* Disable MT7530 PLL */ - mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1, - (2 << RG_GSWPLL_POSDIV_200M_S) | - (32 << RG_GSWPLL_FBKDIV_200M_S)); - - /* For MT7530 core clock = 500Mhz */ - mt753x_core_reg_write(priv, CORE_GSWPLL_GRP2, - (1 << RG_GSWPLL_POSDIV_500M_S) | - (25 << RG_GSWPLL_FBKDIV_500M_S)); - - /* Enable MT7530 PLL */ - mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1, - (2 << RG_GSWPLL_POSDIV_200M_S) | - (32 << RG_GSWPLL_FBKDIV_200M_S) | - RG_GSWPLL_EN_PRE); - - udelay(20); - - mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); - - /* Setup the MT7530 TRGMII Tx Clock */ - mt753x_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1); - mt753x_core_reg_write(priv, CORE_PLL_GROUP6, 0); - mt753x_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta); - mt753x_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta); - mt753x_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN | - RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); - - mt753x_core_reg_write(priv, CORE_PLL_GROUP2, - RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | - (1 << RG_SYSPLL_POSDIV_S)); - - mt753x_core_reg_write(priv, CORE_PLL_GROUP7, - RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) | - RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); - - /* Enable MT7530 core clock */ - mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, - REG_GSWCK_EN | REG_TRGMIICK_EN); - - return 0; -} - -static void mt7530_mac_control(struct mtk_eth_priv *priv, bool enable) -{ - u32 pmcr = FORCE_MODE; - - if (enable) - pmcr = priv->mt753x_pmcr; - - mt753x_reg_write(priv, PMCR_REG(6), pmcr); -} - -static int mt7530_setup(struct mtk_eth_priv *priv) -{ - u16 phy_addr, phy_val; - u32 val, txdrv; - int i; - - if (!MTK_HAS_CAPS(priv->soc->caps, MTK_TRGMII_MT7621_CLK)) { - /* Select 250MHz clk for RGMII mode */ - mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG, - ETHSYS_TRGMII_CLK_SEL362_5, 0); - - txdrv = 8; - } else { - txdrv = 4; - } - - /* Modify HWTRAP first to allow direct access to internal PHYs */ - mt753x_reg_read(priv, HWTRAP_REG, &val); - val |= CHG_TRAP; - val &= ~C_MDIO_BPS; - mt753x_reg_write(priv, MHWTRAP_REG, val); - - /* Calculate the phy base address */ - val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3; - priv->mt753x_phy_base = (val | 0x7) + 1; - - /* Turn off PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val |= BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - /* Force MAC link down before reset */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); - - /* MT7530 reset */ - mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); - udelay(100); - - val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | - MAC_MODE | FORCE_MODE | - MAC_TX_EN | MAC_RX_EN | - BKOFF_EN | BACKPR_EN | - (SPEED_1000M << FORCE_SPD_S) | - FORCE_DPX | FORCE_LINK; - - /* MT7530 Port6: Forced 1000M/FD, FC disabled */ - priv->mt753x_pmcr = val; - - /* MT7530 Port5: Forced link down */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); - - /* Keep MAC link down before starting eth */ - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); - - /* MT7530 Port6: Set to RGMII */ - mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII); - - /* Hardware Trap: Enable Port6, Disable Port5 */ - mt753x_reg_read(priv, HWTRAP_REG, &val); - val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS | - (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) | - (P5_INTF_MODE_RGMII << P5_INTF_MODE_S); - val &= ~(C_MDIO_BPS | P6_INTF_DIS); - mt753x_reg_write(priv, MHWTRAP_REG, val); - - /* Setup switch core pll */ - mt7530_pad_clk_setup(priv, priv->phy_interface); - - /* Lower Tx Driving for TRGMII path */ - for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) - mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i), - (txdrv << TD_DM_DRVP_S) | - (txdrv << TD_DM_DRVN_S)); - - for (i = 0 ; i < NUM_TRGMII_CTRL; i++) - mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16); - - /* Turn on PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val &= ~BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - return 0; -} - -static void mt7531_core_pll_setup(struct mtk_eth_priv *priv, int mcm) +static int mtk_switch_init(struct mtk_eth_priv *priv) { - /* Step 1 : Disable MT7531 COREPLL */ - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0); - - /* Step 2: switch to XTAL output */ - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW); - - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0); - - /* Step 3: disable PLLGP and enable program PLLGP */ - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP); - - /* Step 4: program COREPLL output frequency to 500MHz */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M, - 2 << RG_COREPLL_POSDIV_S); - udelay(25); - - /* Currently, support XTAL 25Mhz only */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M, - 0x140000 << RG_COREPLL_SDM_PCW_S); - - /* Set feedback divide ratio update signal to high */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, - RG_COREPLL_SDM_PCW_CHG); - - /* Wait for at least 16 XTAL clocks */ - udelay(10); - - /* Step 5: set feedback divide ratio update signal to low */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0); - - /* add enable 325M clock for SGMII */ - mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000); - - /* add enable 250SSC clock for RGMII */ - mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000); - - /*Step 6: Enable MT7531 PLL */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN); - - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL); - - udelay(25); -} - -static int mt7531_port_sgmii_init(struct mtk_eth_priv *priv, - u32 port) -{ - if (port != 5 && port != 6) { - printf("mt7531: port %d is not a SGMII port\n", port); - return -EINVAL; - } - - /* Set SGMII GEN2 speed(2.5G) */ - mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), SGMSYS_SPEED_MASK, - FIELD_PREP(SGMSYS_SPEED_MASK, SGMSYS_SPEED_2500)); - - /* Disable SGMII AN */ - mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port), - SGMII_AN_ENABLE, 0); - - /* SGMII force mode setting */ - mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE); - - /* Release PHYA power down state */ - mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port), - SGMII_PHYA_PWD, 0); - - return 0; -} - -static int mt7531_port_rgmii_init(struct mtk_eth_priv *priv, u32 port) -{ - u32 val; - - if (port != 5) { - printf("error: RGMII mode is not available for port %d\n", - port); - return -EINVAL; - } - - mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val); - val |= GP_CLK_EN; - val &= ~GP_MODE_M; - val |= GP_MODE_RGMII << GP_MODE_S; - val |= TXCLK_NO_REVERSE; - val |= RXCLK_NO_DELAY; - val &= ~CLK_SKEW_IN_M; - val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S; - val &= ~CLK_SKEW_OUT_M; - val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S; - mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val); - - return 0; -} - -static void mt7531_phy_setting(struct mtk_eth_priv *priv) -{ - int i; - u32 val; - - for (i = 0; i < MT753X_NUM_PHYS; i++) { - /* Enable HW auto downshift */ - priv->mii_write(priv, i, 0x1f, 0x1); - val = priv->mii_read(priv, i, PHY_EXT_REG_14); - val |= PHY_EN_DOWN_SHFIT; - priv->mii_write(priv, i, PHY_EXT_REG_14, val); - - /* PHY link down power saving enable */ - val = priv->mii_read(priv, i, PHY_EXT_REG_17); - val |= PHY_LINKDOWN_POWER_SAVING_EN; - priv->mii_write(priv, i, PHY_EXT_REG_17, val); - - val = priv->mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6); - val &= ~PHY_POWER_SAVING_M; - val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; - priv->mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val); - } -} - -static void mt7531_mac_control(struct mtk_eth_priv *priv, bool enable) -{ - u32 pmcr = FORCE_MODE_LNK; - - if (enable) - pmcr = priv->mt753x_pmcr; - - mt753x_reg_write(priv, PMCR_REG(5), pmcr); - mt753x_reg_write(priv, PMCR_REG(6), pmcr); -} - -static int mt7531_setup(struct mtk_eth_priv *priv) -{ - u16 phy_addr, phy_val; - u32 val; - u32 pmcr; - u32 port5_sgmii; - int i; - - priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) & - MT753X_SMI_ADDR_MASK; - - /* Turn off PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val |= BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - /* Force MAC link down before reset */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); - - /* Switch soft reset */ - mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); - udelay(100); - - /* Enable MDC input Schmitt Trigger */ - mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN, - SMT_IOLB_5_SMI_MDC_EN); - - mt7531_core_pll_setup(priv, priv->mcm); - - mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val); - port5_sgmii = !!(val & PAD_DUAL_SGMII_EN); - - /* port5 support either RGMII or SGMII, port6 only support SGMII. */ - switch (priv->phy_interface) { - case PHY_INTERFACE_MODE_RGMII: - if (!port5_sgmii) - mt7531_port_rgmii_init(priv, 5); - break; - case PHY_INTERFACE_MODE_2500BASEX: - mt7531_port_sgmii_init(priv, 6); - if (port5_sgmii) - mt7531_port_sgmii_init(priv, 5); - break; - default: - break; - } - - pmcr = MT7531_FORCE_MODE | - (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | - MAC_MODE | MAC_TX_EN | MAC_RX_EN | - BKOFF_EN | BACKPR_EN | - FORCE_RX_FC | FORCE_TX_FC | - (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | - FORCE_LINK; - - priv->mt753x_pmcr = pmcr; - - /* Keep MAC link down before starting eth */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); - - /* Turn on PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val &= ~BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - mt7531_phy_setting(priv); - - /* Enable Internal PHYs */ - val = mt753x_core_reg_read(priv, CORE_PLL_GROUP4); - val |= MT7531_BYPASS_MODE; - val &= ~MT7531_POWER_ON_OFF; - mt753x_core_reg_write(priv, CORE_PLL_GROUP4, val); - - return 0; -} - -static void mt7988_phy_setting(struct mtk_eth_priv *priv) -{ - u16 val; - u32 i; - - for (i = 0; i < MT753X_NUM_PHYS; i++) { - /* Enable HW auto downshift */ - priv->mii_write(priv, i, 0x1f, 0x1); - val = priv->mii_read(priv, i, PHY_EXT_REG_14); - val |= PHY_EN_DOWN_SHFIT; - priv->mii_write(priv, i, PHY_EXT_REG_14, val); - - /* PHY link down power saving enable */ - val = priv->mii_read(priv, i, PHY_EXT_REG_17); - val |= PHY_LINKDOWN_POWER_SAVING_EN; - priv->mii_write(priv, i, PHY_EXT_REG_17, val); - } -} - -static void mt7988_mac_control(struct mtk_eth_priv *priv, bool enable) -{ - u32 pmcr = FORCE_MODE_LNK; - - if (enable) - pmcr = priv->mt753x_pmcr; - - mt753x_reg_write(priv, PMCR_REG(6), pmcr); -} - -static int mt7988_setup(struct mtk_eth_priv *priv) -{ - u16 phy_addr, phy_val; - u32 pmcr; - int i; - - priv->gsw_base = regmap_get_range(priv->ethsys_regmap, 0) + GSW_BASE; - - priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) & - MT753X_SMI_ADDR_MASK; - - /* Turn off PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val |= BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - switch (priv->phy_interface) { - case PHY_INTERFACE_MODE_USXGMII: - /* Use CPU bridge instead of actual USXGMII path */ - - /* Set GDM1 no drop */ - mtk_fe_rmw(priv, PSE_NO_DROP_CFG_REG, 0, PSE_NO_DROP_GDM1); - - /* Enable GDM1 to GSW CPU bridge */ - mtk_gmac_rmw(priv, GMAC_MAC_MISC_REG, 0, BIT(0)); - - /* XGMAC force link up */ - mtk_gmac_rmw(priv, GMAC_XGMAC_STS_REG, 0, P1_XGMAC_FORCE_LINK); - - /* Setup GSW CPU bridge IPG */ - mtk_gmac_rmw(priv, GMAC_GSW_CFG_REG, GSWTX_IPG_M | GSWRX_IPG_M, - (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S)); - break; - default: - printf("Error: MT7988 GSW does not support %s interface\n", - phy_string_for_interface(priv->phy_interface)); - break; - } + struct mtk_eth_switch *swdrvs = ll_entry_start(struct mtk_eth_switch, + mtk_eth_switch); + const u32 n_swdrvs = ll_entry_count(struct mtk_eth_switch, + mtk_eth_switch); + struct mtk_eth_switch *tmp, *swdrv = NULL; + u32 reset_wait_time = 500; + size_t priv_size; + int ret; - pmcr = MT7988_FORCE_MODE | - (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | - MAC_MODE | MAC_TX_EN | MAC_RX_EN | - BKOFF_EN | BACKPR_EN | - FORCE_RX_FC | FORCE_TX_FC | - (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | - FORCE_LINK; - - priv->mt753x_pmcr = pmcr; - - /* Keep MAC link down before starting eth */ - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); - - /* Turn on PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val &= ~BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); + if (strcmp(priv->swname, "auto")) { + for (tmp = swdrvs; tmp < swdrvs + n_swdrvs; tmp++) { + if (!strcmp(tmp->name, priv->swname)) { + swdrv = tmp; + break; + } + } } - mt7988_phy_setting(priv); - - return 0; -} - -static int mt753x_switch_init(struct mtk_eth_priv *priv) -{ - int ret; - int i; + if (swdrv) + reset_wait_time = swdrv->reset_wait_time; /* Global reset switch */ if (priv->mcm) { reset_assert(&priv->rst_mcm); udelay(1000); reset_deassert(&priv->rst_mcm); - mdelay(priv->mt753x_reset_wait_time); + mdelay(reset_wait_time); } else if (dm_gpio_is_valid(&priv->rst_gpio)) { dm_gpio_set_value(&priv->rst_gpio, 0); udelay(1000); dm_gpio_set_value(&priv->rst_gpio, 1); - mdelay(priv->mt753x_reset_wait_time); + mdelay(reset_wait_time); } - ret = priv->switch_init(priv); - if (ret) - return ret; + if (!swdrv) { + for (tmp = swdrvs; tmp < swdrvs + n_swdrvs; tmp++) { + if (!tmp->detect) + continue; - /* Set port isolation */ - for (i = 0; i < MT753X_NUM_PORTS; i++) { - /* Set port matrix mode */ - if (i != 6) - mt753x_reg_write(priv, PCR_REG(i), - (0x40 << PORT_MATRIX_S)); - else - mt753x_reg_write(priv, PCR_REG(i), - (0x3f << PORT_MATRIX_S)); + ret = tmp->detect(priv); + if (!ret) { + swdrv = tmp; + break; + } + } + + if (!swdrv) { + printf("Error: unable to detect switch\n"); + return -ENODEV; + } + } else { + if (swdrv->detect) { + ret = swdrv->detect(priv); + if (ret) { + printf("Error: switch probing failed\n"); + return -ENODEV; + } + } + } + + printf("%s\n", swdrv->desc); + + priv_size = swdrv->priv_size; + if (priv_size < sizeof(struct mtk_eth_switch_priv)) + priv_size = sizeof(struct mtk_eth_switch_priv); + + priv->swpriv = calloc(1, priv_size); + if (!priv->swpriv) { + printf("Error: no memory for switch data\n"); + return -ENOMEM; + } + + priv->swpriv->eth = priv; + priv->swpriv->soc = priv->soc; + priv->swpriv->phy_interface = priv->phy_interface; + priv->swpriv->sw = swdrv; + priv->swpriv->ethsys_base = regmap_get_range(priv->ethsys_regmap, 0); - /* Set port mode to user port */ - mt753x_reg_write(priv, PVC_REG(i), - (0x8100 << STAG_VPID_S) | - (VLAN_ATTR_USER << VLAN_ATTR_S)); + ret = swdrv->setup(priv->swpriv); + if (ret) { + free(priv->swpriv); + priv->swpriv = NULL; + return ret; } return 0; @@ -1759,7 +1061,8 @@ static int mtk_eth_start(struct udevice *dev) } if (MTK_HAS_CAPS(priv->soc->caps, MTK_NETSYS_V3)) { - if (priv->sw == SW_MT7988 && priv->gmac_id == 0) { + if (priv->swpriv && !strcmp(priv->swpriv->sw->name, "mt7988") && + priv->gmac_id == 0) { mtk_gdma_write(priv, priv->gmac_id, GDMA_IG_CTRL_REG, GDMA_BRIDGE_TO_CPU); @@ -1778,11 +1081,12 @@ static int mtk_eth_start(struct udevice *dev) mtk_eth_fifo_init(priv); - if (priv->switch_mac_control) - priv->switch_mac_control(priv, true); - - /* Start PHY */ - if (priv->sw == SW_NONE) { + if (priv->swpriv) { + /* Enable communication with switch */ + if (priv->swpriv->sw->mac_control) + priv->swpriv->sw->mac_control(priv->swpriv, true); + } else { + /* Start PHY */ ret = mtk_phy_start(priv); if (ret) return ret; @@ -1799,8 +1103,10 @@ static void mtk_eth_stop(struct udevice *dev) { struct mtk_eth_priv *priv = dev_get_priv(dev); - if (priv->switch_mac_control) - priv->switch_mac_control(priv, false); + if (priv->swpriv) { + if (priv->swpriv->sw->mac_control) + priv->swpriv->sw->mac_control(priv->swpriv, false); + } mtk_pdma_rmw(priv, PDMA_GLO_CFG_REG, TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN, 0); @@ -1953,11 +1259,11 @@ static int mtk_eth_probe(struct udevice *dev) return ret; /* Probe phy if switch is not specified */ - if (priv->sw == SW_NONE) + if (!priv->swname) return mtk_phy_probe(dev); /* Initialize switch */ - return mt753x_switch_init(priv); + return mtk_switch_init(priv); } static int mtk_eth_remove(struct udevice *dev) @@ -1971,6 +1277,12 @@ static int mtk_eth_remove(struct udevice *dev) /* Stop possibly started DMA */ mtk_eth_stop(dev); + if (priv->swpriv) { + if (priv->swpriv->sw->cleanup) + priv->swpriv->sw->cleanup(priv->swpriv); + free(priv->swpriv); + } + return 0; } @@ -1980,7 +1292,6 @@ static int mtk_eth_of_to_plat(struct udevice *dev) struct mtk_eth_priv *priv = dev_get_priv(dev); struct ofnode_phandle_args args; struct regmap *regmap; - const char *str; ofnode subnode; int ret; @@ -2126,36 +1437,8 @@ static int mtk_eth_of_to_plat(struct udevice *dev) return PTR_ERR(priv->toprgu_regmap); } - /* check for switch first, otherwise phy will be used */ - priv->sw = SW_NONE; - priv->switch_init = NULL; - priv->switch_mac_control = NULL; - str = dev_read_string(dev, "mediatek,switch"); - - if (str) { - if (!strcmp(str, "mt7530")) { - priv->sw = SW_MT7530; - priv->switch_init = mt7530_setup; - priv->switch_mac_control = mt7530_mac_control; - priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR; - priv->mt753x_reset_wait_time = 1000; - } else if (!strcmp(str, "mt7531")) { - priv->sw = SW_MT7531; - priv->switch_init = mt7531_setup; - priv->switch_mac_control = mt7531_mac_control; - priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR; - priv->mt753x_reset_wait_time = 200; - } else if (!strcmp(str, "mt7988")) { - priv->sw = SW_MT7988; - priv->switch_init = mt7988_setup; - priv->switch_mac_control = mt7988_mac_control; - priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR; - priv->mt753x_reset_wait_time = 50; - } else { - printf("error: unsupported switch\n"); - return -EINVAL; - } - + priv->swname = dev_read_string(dev, "mediatek,switch"); + if (priv->swname) { priv->mcm = dev_read_bool(dev, "mediatek,mcm"); if (priv->mcm) { ret = reset_get_by_name(dev, "mcm", &priv->rst_mcm); @@ -2194,6 +1477,15 @@ static const struct mtk_soc_data mt7988_data = { .rxd_size = sizeof(struct mtk_rx_dma_v2), }; +static const struct mtk_soc_data mt7987_data = { + .caps = MT7987_CAPS, + .ana_rgc3 = 0x128, + .gdma_count = 3, + .pdma_base = PDMA_V3_BASE, + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), +}; + static const struct mtk_soc_data mt7986_data = { .caps = MT7986_CAPS, .ana_rgc3 = 0x128, @@ -2248,6 +1540,7 @@ static const struct mtk_soc_data mt7621_data = { static const struct udevice_id mtk_eth_ids[] = { { .compatible = "mediatek,mt7988-eth", .data = (ulong)&mt7988_data }, + { .compatible = "mediatek,mt7987-eth", .data = (ulong)&mt7987_data }, { .compatible = "mediatek,mt7986-eth", .data = (ulong)&mt7986_data }, { .compatible = "mediatek,mt7981-eth", .data = (ulong)&mt7981_data }, { .compatible = "mediatek,mt7629-eth", .data = (ulong)&mt7629_data }, @@ -2271,10 +1564,10 @@ U_BOOT_DRIVER(mtk_eth) = { .id = UCLASS_ETH, .of_match = mtk_eth_ids, .of_to_plat = mtk_eth_of_to_plat, - .plat_auto = sizeof(struct eth_pdata), + .plat_auto = sizeof(struct eth_pdata), .probe = mtk_eth_probe, .remove = mtk_eth_remove, .ops = &mtk_eth_ops, - .priv_auto = sizeof(struct mtk_eth_priv), + .priv_auto = sizeof(struct mtk_eth_priv), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; diff --git a/drivers/net/mtk_eth.h b/drivers/net/mtk_eth/mtk_eth.h index 1aa037907c5..0ad32799128 100644 --- a/drivers/net/mtk_eth.h +++ b/drivers/net/mtk_eth/mtk_eth.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 MediaTek Inc. + * Copyright (C) 2025 MediaTek Inc. * * Author: Weijie Gao <weijie.gao@mediatek.com> * Author: Mark Lee <mark-mc.lee@mediatek.com> @@ -9,9 +9,55 @@ #ifndef _MTK_ETH_H_ #define _MTK_ETH_H_ +#include <linker_lists.h> #include <linux/bitops.h> #include <linux/bitfield.h> +struct mtk_eth_priv; +struct mtk_eth_switch_priv; + +/* struct mtk_soc_data - This is the structure holding all differences + * among various plaforms + * @caps Flags shown the extra capability for the SoC + * @ana_rgc3: The offset for register ANA_RGC3 related to + * sgmiisys syscon + * @gdma_count: Number of GDMAs + * @pdma_base: Register base of PDMA block + * @txd_size: Tx DMA descriptor size. + * @rxd_size: Rx DMA descriptor size. + */ +struct mtk_soc_data { + u32 caps; + u32 ana_rgc3; + u32 gdma_count; + u32 pdma_base; + u32 txd_size; + u32 rxd_size; +}; + +struct mtk_eth_switch { + const char *name; + const char *desc; + size_t priv_size; + u32 reset_wait_time; + + int (*detect)(struct mtk_eth_priv *priv); + int (*setup)(struct mtk_eth_switch_priv *priv); + int (*cleanup)(struct mtk_eth_switch_priv *priv); + void (*mac_control)(struct mtk_eth_switch_priv *priv, bool enable); +}; + +#define MTK_ETH_SWITCH(__name) \ + ll_entry_declare(struct mtk_eth_switch, __name, mtk_eth_switch) + +struct mtk_eth_switch_priv { + struct mtk_eth_priv *eth; + const struct mtk_eth_switch *sw; + const struct mtk_soc_data *soc; + void *ethsys_base; + int phy_interface; +}; + enum mkt_eth_capabilities { MTK_TRGMII_BIT, MTK_TRGMII_MT7621_CLK_BIT, @@ -36,7 +82,6 @@ enum mkt_eth_capabilities { /* Supported path present on SoCs */ #define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT) - #define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT) #define MTK_ETH_PATH_MT7622_SGMII BIT(MTK_ETH_PATH_MT7622_SGMII_BIT) #define MTK_ETH_PATH_MT7629_GMAC2 BIT(MTK_ETH_PATH_MT7629_GMAC2_BIT) @@ -59,6 +104,8 @@ enum mkt_eth_capabilities { #define MT7986_CAPS (MTK_NETSYS_V2) +#define MT7987_CAPS (MTK_NETSYS_V3 | MTK_GMAC2_U3_QPHY | MTK_INFRA) + #define MT7988_CAPS (MTK_NETSYS_V3 | MTK_INFRA) /* Frame Engine Register Bases */ @@ -72,7 +119,6 @@ enum mkt_eth_capabilities { #define GSW_BASE 0x20000 /* Ethernet subsystem registers */ - #define ETHSYS_SYSCFG1_REG 0x14 #define SYSCFG1_GE_MODE_S(n) (12 + ((n) * 2)) #define SYSCFG1_GE_MODE_M 0x3 @@ -191,7 +237,6 @@ enum mkt_eth_capabilities { #define DP_DISCARD 7 /* GMAC Registers */ - #define GMAC_PPSC_REG 0x0000 #define PHY_MDC_CFG GENMASK(29, 24) #define MDC_TURBO BIT(20) @@ -272,6 +317,8 @@ enum mkt_eth_capabilities { #define RX_RST BIT(31) #define RXC_DQSISEL BIT(30) +#define NUM_TRGMII_CTRL 5 + #define GMAC_TRGMII_TD_ODT(n) (0x354 + (n) * 8) #define TD_DM_DRVN_S 4 #define TD_DM_DRVN_M 0xf0 @@ -288,164 +335,7 @@ enum mkt_eth_capabilities { #define XGMAC_FORCE_TX_FC BIT(5) #define XGMAC_FORCE_RX_FC BIT(4) -/* MT7530 Registers */ - -#define PCR_REG(p) (0x2004 + (p) * 0x100) -#define PORT_MATRIX_S 16 -#define PORT_MATRIX_M 0xff0000 - -#define PVC_REG(p) (0x2010 + (p) * 0x100) -#define STAG_VPID_S 16 -#define STAG_VPID_M 0xffff0000 -#define VLAN_ATTR_S 6 -#define VLAN_ATTR_M 0xc0 - -/* VLAN_ATTR: VLAN attributes */ -#define VLAN_ATTR_USER 0 -#define VLAN_ATTR_STACK 1 -#define VLAN_ATTR_TRANSLATION 2 -#define VLAN_ATTR_TRANSPARENT 3 - -#define PMCR_REG(p) (0x3000 + (p) * 0x100) -/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR - * MT7531 specific fields are defined below - */ -#define FORCE_MODE_EEE1G BIT(25) -#define FORCE_MODE_EEE100 BIT(26) -#define FORCE_MODE_TX_FC BIT(27) -#define FORCE_MODE_RX_FC BIT(28) -#define FORCE_MODE_DPX BIT(29) -#define FORCE_MODE_SPD BIT(30) -#define FORCE_MODE_LNK BIT(31) -#define MT7531_FORCE_MODE FORCE_MODE_EEE1G | FORCE_MODE_EEE100 |\ - FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ - FORCE_MODE_DPX | FORCE_MODE_SPD | \ - FORCE_MODE_LNK -#define MT7988_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ - FORCE_MODE_DPX | FORCE_MODE_SPD | \ - FORCE_MODE_LNK - -/* MT7531 SGMII Registers */ -#define MT7531_SGMII_REG_BASE 0x5000 -#define MT7531_SGMII_REG_PORT_BASE 0x1000 -#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \ - (p) * MT7531_SGMII_REG_PORT_BASE + (r)) -#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(((p) - 5), 0x00) -#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(((p) - 5), 0x20) -#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8) -#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(((p) - 5), 0x128) -/* XXX: all fields of MT7531 SGMII are defined under SGMSYS */ - -/* MT753x System Control Register */ -#define SYS_CTRL_REG 0x7000 -#define SW_PHY_RST BIT(2) -#define SW_SYS_RST BIT(1) -#define SW_REG_RST BIT(0) - -/* MT7531 */ -#define MT7531_PHY_IAC 0x701c -/* XXX: all fields are defined under GMAC_PIAC_REG */ - -#define MT7531_CLKGEN_CTRL 0x7500 -#define CLK_SKEW_OUT_S 8 -#define CLK_SKEW_OUT_M 0x300 -#define CLK_SKEW_IN_S 6 -#define CLK_SKEW_IN_M 0xc0 -#define RXCLK_NO_DELAY BIT(5) -#define TXCLK_NO_REVERSE BIT(4) -#define GP_MODE_S 1 -#define GP_MODE_M 0x06 -#define GP_CLK_EN BIT(0) - -/* Values of GP_MODE */ -#define GP_MODE_RGMII 0 -#define GP_MODE_MII 1 -#define GP_MODE_REV_MII 2 - -/* Values of CLK_SKEW_IN */ -#define CLK_SKEW_IN_NO_CHANGE 0 -#define CLK_SKEW_IN_DELAY_100PPS 1 -#define CLK_SKEW_IN_DELAY_200PPS 2 -#define CLK_SKEW_IN_REVERSE 3 - -/* Values of CLK_SKEW_OUT */ -#define CLK_SKEW_OUT_NO_CHANGE 0 -#define CLK_SKEW_OUT_DELAY_100PPS 1 -#define CLK_SKEW_OUT_DELAY_200PPS 2 -#define CLK_SKEW_OUT_REVERSE 3 - -#define HWTRAP_REG 0x7800 -/* MT7530 Modified Hardware Trap Status Registers */ -#define MHWTRAP_REG 0x7804 -#define CHG_TRAP BIT(16) -#define LOOPDET_DIS BIT(14) -#define P5_INTF_SEL_S 13 -#define P5_INTF_SEL_M 0x2000 -#define SMI_ADDR_S 11 -#define SMI_ADDR_M 0x1800 -#define XTAL_FSEL_S 9 -#define XTAL_FSEL_M 0x600 -#define P6_INTF_DIS BIT(8) -#define P5_INTF_MODE_S 7 -#define P5_INTF_MODE_M 0x80 -#define P5_INTF_DIS BIT(6) -#define C_MDIO_BPS BIT(5) -#define CHIP_MODE_S 0 -#define CHIP_MODE_M 0x0f - -/* P5_INTF_SEL: Interface type of Port5 */ -#define P5_INTF_SEL_GPHY 0 -#define P5_INTF_SEL_GMAC5 1 - -/* P5_INTF_MODE: Interface mode of Port5 */ -#define P5_INTF_MODE_GMII_MII 0 -#define P5_INTF_MODE_RGMII 1 - -#define MT7530_P6ECR 0x7830 -#define P6_INTF_MODE_M 0x3 -#define P6_INTF_MODE_S 0 - -/* P6_INTF_MODE: Interface mode of Port6 */ -#define P6_INTF_MODE_RGMII 0 -#define P6_INTF_MODE_TRGMII 1 - -#define NUM_TRGMII_CTRL 5 - -#define MT7530_TRGMII_RD(n) (0x7a10 + (n) * 8) -#define RD_TAP_S 0 -#define RD_TAP_M 0x7f - -#define MT7530_TRGMII_TD_ODT(n) (0x7a54 + (n) * 8) -/* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */ - -/* TOP Signals Status Register */ -#define MT7531_TOP_SIG_SR 0x780c -#define PAD_MCM_SMI_EN BIT(0) -#define PAD_DUAL_SGMII_EN BIT(1) - -/* MT7531 PLLGP Registers */ -#define MT7531_PLLGP_EN 0x7820 -#define EN_COREPLL BIT(2) -#define SW_CLKSW BIT(1) -#define SW_PLLGP BIT(0) - -#define MT7531_PLLGP_CR0 0x78a8 -#define RG_COREPLL_EN BIT(22) -#define RG_COREPLL_POSDIV_S 23 -#define RG_COREPLL_POSDIV_M 0x3800000 -#define RG_COREPLL_SDM_PCW_S 1 -#define RG_COREPLL_SDM_PCW_M 0x3ffffe -#define RG_COREPLL_SDM_PCW_CHG BIT(0) - -/* MT7531 RGMII and SGMII PLL clock */ -#define MT7531_ANA_PLLGP_CR2 0x78b0 -#define MT7531_ANA_PLLGP_CR5 0x78bc - -/* MT7531 GPIO GROUP IOLB SMT0 Control */ -#define MT7531_SMT0_IOLB 0x7f04 -#define SMT_IOLB_5_SMI_MDC_EN BIT(5) - -/* MT7530 GPHY MDIO Indirect Access Registers */ +/* MDIO Indirect Access Registers */ #define MII_MMD_ACC_CTL_REG 0x0d #define MMD_CMD_S 14 #define MMD_CMD_M 0xc000 @@ -460,80 +350,6 @@ enum mkt_eth_capabilities { #define MII_MMD_ADDR_DATA_REG 0x0e -/* MT7530 GPHY MDIO MMD Registers */ -#define CORE_PLL_GROUP2 0x401 -#define RG_SYSPLL_EN_NORMAL BIT(15) -#define RG_SYSPLL_VODEN BIT(14) -#define RG_SYSPLL_POSDIV_S 5 -#define RG_SYSPLL_POSDIV_M 0x60 - -#define CORE_PLL_GROUP4 0x403 -#define MT7531_BYPASS_MODE BIT(4) -#define MT7531_POWER_ON_OFF BIT(5) -#define RG_SYSPLL_DDSFBK_EN BIT(12) -#define RG_SYSPLL_BIAS_EN BIT(11) -#define RG_SYSPLL_BIAS_LPF_EN BIT(10) - -#define CORE_PLL_GROUP5 0x404 -#define RG_LCDDS_PCW_NCPO1_S 0 -#define RG_LCDDS_PCW_NCPO1_M 0xffff - -#define CORE_PLL_GROUP6 0x405 -#define RG_LCDDS_PCW_NCPO0_S 0 -#define RG_LCDDS_PCW_NCPO0_M 0xffff - -#define CORE_PLL_GROUP7 0x406 -#define RG_LCDDS_PWDB BIT(15) -#define RG_LCDDS_ISO_EN BIT(13) -#define RG_LCCDS_C_S 4 -#define RG_LCCDS_C_M 0x70 -#define RG_LCDDS_PCW_NCPO_CHG BIT(3) - -#define CORE_PLL_GROUP10 0x409 -#define RG_LCDDS_SSC_DELTA_S 0 -#define RG_LCDDS_SSC_DELTA_M 0xfff - -#define CORE_PLL_GROUP11 0x40a -#define RG_LCDDS_SSC_DELTA1_S 0 -#define RG_LCDDS_SSC_DELTA1_M 0xfff - -#define CORE_GSWPLL_GRP1 0x40d -#define RG_GSWPLL_POSDIV_200M_S 12 -#define RG_GSWPLL_POSDIV_200M_M 0x3000 -#define RG_GSWPLL_EN_PRE BIT(11) -#define RG_GSWPLL_FBKDIV_200M_S 0 -#define RG_GSWPLL_FBKDIV_200M_M 0xff - -#define CORE_GSWPLL_GRP2 0x40e -#define RG_GSWPLL_POSDIV_500M_S 8 -#define RG_GSWPLL_POSDIV_500M_M 0x300 -#define RG_GSWPLL_FBKDIV_500M_S 0 -#define RG_GSWPLL_FBKDIV_500M_M 0xff - -#define CORE_TRGMII_GSW_CLK_CG 0x410 -#define REG_GSWCK_EN BIT(0) -#define REG_TRGMIICK_EN BIT(1) - -/* Extend PHY Control Register 3 */ -#define PHY_EXT_REG_14 0x14 - -/* Fields of PHY_EXT_REG_14 */ -#define PHY_EN_DOWN_SHFIT BIT(4) - -/* Extend PHY Control Register 4 */ -#define PHY_EXT_REG_17 0x17 - -/* Fields of PHY_EXT_REG_17 */ -#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4) - -/* PHY RXADC Control Register 7 */ -#define PHY_DEV1E_REG_0C6 0x0c6 - -/* Fields of PHY_DEV1E_REG_0C6 */ -#define PHY_POWER_SAVING_S 8 -#define PHY_POWER_SAVING_M 0x300 -#define PHY_POWER_SAVING_TX 0x0 - /* PDMA descriptors */ struct mtk_rx_dma { unsigned int rxd1; @@ -597,4 +413,17 @@ struct mtk_tx_dma_v2 { #define PDMA_V2_RXD2_PLEN0_GET(_v) FIELD_GET(PDMA_V2_RXD2_PLEN0_M, (_v)) #define PDMA_V2_RXD2_PLEN0_SET(_v) FIELD_PREP(PDMA_V2_RXD2_PLEN0_M, (_v)) +void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set); +void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set); +void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set); + +int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg); +int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data); +int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg); +int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val); +int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg); +int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val); + #endif /* _MTK_ETH_H_ */ |