diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/dwc_eth_qos_adi.c | 1 | ||||
-rw-r--r-- | drivers/net/e1000_spi.c | 25 | ||||
-rw-r--r-- | drivers/net/fm/eth.c | 9 | ||||
-rw-r--r-- | drivers/net/fm/memac_phy.c | 4 | ||||
-rw-r--r-- | drivers/net/fsl-mc/mc.c | 28 | ||||
-rw-r--r-- | drivers/net/fsl_enetc_netc_blk_ctrl.c | 2 | ||||
-rw-r--r-- | drivers/net/mdio_mux_meson_gxl.c | 3 | ||||
-rw-r--r-- | drivers/net/mtk_eth/mtk_eth.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/Kconfig | 2 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/cortina.c | 9 | ||||
-rw-r--r-- | drivers/net/phy/mediatek/Kconfig | 16 | ||||
-rw-r--r-- | drivers/net/phy/mediatek/Makefile | 4 | ||||
-rw-r--r-- | drivers/net/phy/mediatek/mtk-2p5ge.c | 627 | ||||
-rw-r--r-- | drivers/net/phy/mediatek/mtk-phy-lib.c | 106 | ||||
-rw-r--r-- | drivers/net/phy/mediatek/mtk.h | 103 | ||||
-rw-r--r-- | drivers/net/ti/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/ti/am65-cpsw-nuss.c | 77 | ||||
-rw-r--r-- | drivers/net/ti/icssg_prueth.c | 2 |
19 files changed, 975 insertions, 49 deletions
diff --git a/drivers/net/dwc_eth_qos_adi.c b/drivers/net/dwc_eth_qos_adi.c index 0e6a901e303..fee50a88156 100644 --- a/drivers/net/dwc_eth_qos_adi.c +++ b/drivers/net/dwc_eth_qos_adi.c @@ -10,6 +10,7 @@ #include <clk.h> #include <dm.h> +#include <env.h> #include <net.h> #include <phy.h> #include <reset.h> diff --git a/drivers/net/e1000_spi.c b/drivers/net/e1000_spi.c index 1e830b99f1d..33d38b57824 100644 --- a/drivers/net/e1000_spi.c +++ b/drivers/net/e1000_spi.c @@ -472,6 +472,7 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, uint16_t i, length, checksum = 0, checksum_reg; uint16_t *buffer; bool upd; + int ret = 0; if (argc == 0) upd = 0; @@ -493,14 +494,15 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, /* Acquire the EEPROM */ if (e1000_acquire_eeprom(hw)) { E1000_ERR(hw, "EEPROM SPI cannot be acquired!\n"); - return 1; + ret = 1; + goto free_exit; } /* Read the EEPROM */ if (e1000_spi_eeprom_dump(hw, buffer, 0, length, true) < 0) { E1000_ERR(hw, "Interrupted!\n"); - e1000_release_eeprom(hw); - return 1; + ret = 1; + goto release_exit; } /* Compute the checksum and read the expected value */ @@ -513,8 +515,8 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, if (checksum_reg == checksum) { printf("%s: INFO: EEPROM checksum is correct! (0x%04hx)\n", hw->name, checksum); - e1000_release_eeprom(hw); - return 0; + ret = 0; + goto release_exit; } /* Hrm, verification failed, print an error */ @@ -524,8 +526,8 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, /* If they didn't ask us to update it, just return an error */ if (!upd) { - e1000_release_eeprom(hw); - return 1; + ret = 1; + goto release_exit; } /* Ok, correct it! */ @@ -534,12 +536,15 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, if (e1000_spi_eeprom_program(hw, &buffer[i], i * sizeof(uint16_t), sizeof(uint16_t), true)) { E1000_ERR(hw, "Interrupted!\n"); - e1000_release_eeprom(hw); - return 1; + ret = 1; + /* goto release_exit; */ } +release_exit: e1000_release_eeprom(hw); - return 0; +free_exit: + free(buffer); + return ret; } int do_e1000_spi(struct cmd_tbl *cmdtp, struct e1000_hw *hw, diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c index 63fe4b2d33c..f0e7c0eca42 100644 --- a/drivers/net/fm/eth.c +++ b/drivers/net/fm/eth.c @@ -169,7 +169,7 @@ static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port) /* wait until the rx port is not busy */ while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--) ; - if (!timeout) + if (timeout == -1) printf("%s - timeout\n", __func__); } @@ -199,7 +199,7 @@ static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port) /* wait until the tx port is not busy */ while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--) ; - if (!timeout) + if (timeout == -1) printf("%s - timeout\n", __func__); } @@ -727,12 +727,15 @@ static int fm_eth_bind(struct udevice *dev) char mac_name[11]; u32 fm, num; + if (!dev) + return -EINVAL; + if (ofnode_read_u32(ofnode_get_parent(dev_ofnode(dev)), "cell-index", &fm)) { printf("FMan node property cell-index missing\n"); return -EINVAL; } - if (dev && dev_read_u32(dev, "cell-index", &num)) { + if (dev_read_u32(dev, "cell-index", &num)) { printf("FMan MAC node property cell-index missing\n"); return -EINVAL; } diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c index 26425d94ae5..1ad3c053593 100644 --- a/drivers/net/fm/memac_phy.c +++ b/drivers/net/fm/memac_phy.c @@ -46,7 +46,7 @@ static int memac_wait_until_free(struct memac_mdio_controller *regs) while ((memac_in_32(®s->mdio_stat) & MDIO_STAT_BSY) && timeout--) ; - if (!timeout) { + if (timeout == -1) { printf("timeout waiting for MDIO bus to be free\n"); return -ETIMEDOUT; } @@ -64,7 +64,7 @@ static int memac_wait_until_done(struct memac_mdio_controller *regs) while ((memac_in_32(®s->mdio_stat) & MDIO_STAT_BSY) && timeout--) ; - if (!timeout) { + if (timeout == -1) { printf("timeout waiting for MDIO operation to complete\n"); return -ETIMEDOUT; } diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 86daf0fb2bb..c8ed702f50a 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -366,8 +366,8 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id, noff = fdt_add_subnode(blob, nodeoffset, mac_name); if (noff < 0) { printf("fdt_add_subnode: err=%s\n", - fdt_strerror(err)); - return err; + fdt_strerror(noff)); + return noff; } /* add default property of fixed link */ @@ -1178,6 +1178,7 @@ err_get_api_ver: dflt_dpio->dpio_id); err_create: free(dflt_dpio); + dflt_dpio = NULL; err_calloc: return err; } @@ -1186,6 +1187,9 @@ static int dpio_exit(void) { int err; + if (!dflt_dpio) + return -ENODEV; + err = dpio_disable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); if (err < 0) { printf("dpio_disable() failed: %d\n", err); @@ -1211,8 +1215,8 @@ static int dpio_exit(void) printf("Exit: DPIO.%d\n", dflt_dpio->dpio_id); #endif - if (dflt_dpio) - free(dflt_dpio); + free(dflt_dpio); + dflt_dpio = NULL; return 0; err: @@ -1434,6 +1438,7 @@ err_close: err_open: err_create: free(dflt_dpbp); + dflt_dpbp = NULL; err_calloc: return err; } @@ -1442,6 +1447,9 @@ static int dpbp_exit(void) { int err; + if (!dflt_dpbp) + return -ENODEV; + err = dpbp_destroy(dflt_mc_io, dflt_dprc_handle, MC_CMD_NO_FLAGS, dflt_dpbp->dpbp_id); if (err < 0) { @@ -1453,8 +1461,8 @@ static int dpbp_exit(void) printf("Exit: DPBP.%d\n", dflt_dpbp->dpbp_attr.id); #endif - if (dflt_dpbp) - free(dflt_dpbp); + free(dflt_dpbp); + dflt_dpbp = NULL; return 0; err: @@ -1531,6 +1539,7 @@ err_get_version: dflt_dpni->dpni_id); err_create: free(dflt_dpni); + dflt_dpni = NULL; err_calloc: return err; } @@ -1539,6 +1548,9 @@ static int dpni_exit(void) { int err; + if (!dflt_dpni) + return -ENODEV; + err = dpni_destroy(dflt_mc_io, dflt_dprc_handle, MC_CMD_NO_FLAGS, dflt_dpni->dpni_id); if (err < 0) { @@ -1550,8 +1562,8 @@ static int dpni_exit(void) printf("Exit: DPNI.%d\n", dflt_dpni->dpni_id); #endif - if (dflt_dpni) - free(dflt_dpni); + free(dflt_dpni); + dflt_dpni = NULL; return 0; err: diff --git a/drivers/net/fsl_enetc_netc_blk_ctrl.c b/drivers/net/fsl_enetc_netc_blk_ctrl.c index 46b68d3d8a4..fecd66eb15a 100644 --- a/drivers/net/fsl_enetc_netc_blk_ctrl.c +++ b/drivers/net/fsl_enetc_netc_blk_ctrl.c @@ -293,7 +293,7 @@ static int netc_blk_ctrl_probe(struct udevice *dev) err = clk_prepare_enable(ipg_clk); if (err) { dev_err(dev, "Enable ipg clock failed\n"); - return PTR_ERR(ipg_clk); + return err; } regs = dev_read_addr_name(dev, "ierb"); diff --git a/drivers/net/mdio_mux_meson_gxl.c b/drivers/net/mdio_mux_meson_gxl.c index 8ef3ae598b7..31898ed437e 100644 --- a/drivers/net/mdio_mux_meson_gxl.c +++ b/drivers/net/mdio_mux_meson_gxl.c @@ -19,6 +19,7 @@ #define REG2_LEDACT GENMASK(23, 22) #define REG2_LEDLINK GENMASK(25, 24) #define REG2_DIV4SEL BIT(27) +#define REG2_REVERSED BIT(28) #define REG2_ADCBYPASS BIT(30) #define REG2_CLKINSEL BIT(31) #define ETH_REG3 0x4 @@ -66,7 +67,7 @@ static int meson_gxl_enable_internal_mdio(struct mdio_mux_meson_gxl_priv *priv) * The only constraint is that it must match the one in * drivers/net/phy/meson-gxl.c to properly match the PHY. */ - writel(FIELD_PREP(REG2_PHYID, EPHY_GXL_ID), + writel(REG2_REVERSED | FIELD_PREP(REG2_PHYID, EPHY_GXL_ID), priv->regs + ETH_REG2); /* Enable the internal phy */ diff --git a/drivers/net/mtk_eth/mtk_eth.c b/drivers/net/mtk_eth/mtk_eth.c index b172838ba3a..a35754ccd1f 100644 --- a/drivers/net/mtk_eth/mtk_eth.c +++ b/drivers/net/mtk_eth/mtk_eth.c @@ -100,6 +100,7 @@ struct mtk_eth_priv { bool pn_swap; struct phy_device *phydev; + ofnode phy_node; int phy_interface; int phy_addr; @@ -572,6 +573,7 @@ static int mtk_phy_probe(struct udevice *dev) if (!phydev) return -ENODEV; + phydev->node = priv->phy_node; phydev->supported &= PHY_GBIT_FEATURES; phydev->advertising = phydev->supported; @@ -1458,6 +1460,8 @@ static int mtk_eth_of_to_plat(struct udevice *dev) return ret; } + priv->phy_node = args.node; + priv->phy_addr = ofnode_read_s32_default(args.node, "reg", -1); if (priv->phy_addr < 0) { printf("error: phy address is not specified\n"); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 21bf983056a..185c6a3156e 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -184,6 +184,8 @@ config PHY_MARVELL_10G help Support for the Marvell Alaska MV88X3310 and compatible PHYs. +source "drivers/net/phy/mediatek/Kconfig" + config PHY_MESON_GXL bool "Amlogic Meson GXL Internal PHY support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a119eb5e177..a339b8ac29d 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_PHY_ET1011C) += et1011c.o obj-$(CONFIG_PHY_LXT) += lxt.o obj-$(CONFIG_PHY_MARVELL) += marvell.o obj-$(CONFIG_PHY_MARVELL_10G) += marvell10g.o +obj-y += mediatek/ obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index be480ecef6c..dec024844b5 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -135,6 +135,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) int i, line_cnt = 0, column_cnt = 0; struct cortina_reg_config fw_temp; char *addr = NULL; + char *to_be_freed = NULL; ulong cortina_fw_addr = (ulong)cs4340_get_fw_addr(); #ifdef CONFIG_TFABOOT @@ -147,6 +148,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) size_t fw_length = CONFIG_CORTINA_FW_LENGTH; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ret = nand_read(get_nand_dev_by_index(0), (loff_t)cortina_fw_addr, &fw_length, (u_char *)addr); if (ret == -EUCLEAN) { @@ -158,6 +160,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) struct spi_flash *ucode_flash; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ucode_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!ucode_flash) { @@ -179,6 +182,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) puts("Failed to find MMC device for Cortina ucode\n"); } else { addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; printf("MMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); @@ -199,6 +203,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) size_t fw_length = CONFIG_CORTINA_FW_LENGTH; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ret = nand_read(get_nand_dev_by_index(0), (loff_t)cortina_fw_addr, &fw_length, (u_char *)addr); @@ -211,6 +216,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) struct spi_flash *ucode_flash; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ucode_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!ucode_flash) { @@ -232,6 +238,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) puts("Failed to find MMC device for Cortina ucode\n"); } else { addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; printf("MMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); @@ -280,6 +287,8 @@ void cs4340_upload_firmware(struct phy_device *phydev) 0xffff; phy_write(phydev, 0x00, fw_temp.reg_addr, fw_temp.reg_value); } + if (to_be_freed) + free(to_be_freed); } #endif diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig new file mode 100644 index 00000000000..933271f01fa --- /dev/null +++ b/drivers/net/phy/mediatek/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config MTK_NET_PHYLIB + bool + +config PHY_MEDIATEK_2P5GE + bool "MediaTek built-in 2.5Gb ethernet PHYs" + depends on OF_CONTROL && (TARGET_MT7987 || TARGET_MT7988) + select FW_LOADER + select MTK_NET_PHYLIB + help + Supports MediaTek SoC built-in 2.5Gb ethernet PHYs. + + This driver requires firmware download for PHY to enable its + functionality. The board can override certian firmware downloading + function to provide the firmware data. diff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile new file mode 100644 index 00000000000..bc8dd4e878c --- /dev/null +++ b/drivers/net/phy/mediatek/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_MTK_NET_PHYLIB) += mtk-phy-lib.o +obj-$(CONFIG_PHY_MEDIATEK_2P5GE) += mtk-2p5ge.o diff --git a/drivers/net/phy/mediatek/mtk-2p5ge.c b/drivers/net/phy/mediatek/mtk-2p5ge.c new file mode 100644 index 00000000000..4090db0b474 --- /dev/null +++ b/drivers/net/phy/mediatek/mtk-2p5ge.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. All Rights Reserved. + * + * Author: Sky Huang <SkyLake.Huang@mediatek.com> + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ +#include <asm/io.h> +#include <dm/device_compat.h> +#include <dm/of_access.h> +#include <dm/pinctrl.h> +#include <dm/ofnode.h> +#include <fw_loader.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <phy.h> +#include "mtk.h" + +#define MTK_2P5GPHY_ID_MT7987 0x00339c91 +#define MTK_2P5GPHY_ID_MT7988 0x00339c11 + +#define PBUS_BASE 0x0f000000 +#define PBUS_SIZE 0x1f0024 + +#define MTK_2P5GPHY_PMD_REG 0x010000 +#define DO_NOT_RESET (MTK_2P5GPHY_PMD_REG + 0x28) +#define DO_NOT_RESET_XBZ BIT(0) +#define DO_NOT_RESET_PMA BIT(3) +#define DO_NOT_RESET_RX BIT(5) +#define FNPLL_PWR_CTRL1 (MTK_2P5GPHY_PMD_REG + 0x208) +#define RG_SPEED_MASK GENMASK(3, 0) +#define RG_SPEED_2500 BIT(3) +#define RG_SPEED_100 BIT(0) +#define FNPLL_PWR_CTRL_STATUS (MTK_2P5GPHY_PMD_REG + 0x20c) +#define RG_STABLE_MASK GENMASK(3, 0) +#define RG_SPEED_2500_STABLE BIT(3) +#define RG_SPEED_100_STABLE BIT(0) + +#define MTK_2P5GPHY_XBZ_PCS 0x030000 +#define PHY_CTRL_CONFIG (MTK_2P5GPHY_XBZ_PCS + 0x200) +#define PMU_WP (MTK_2P5GPHY_XBZ_PCS + 0x800) +#define WRITE_PROTECT_KEY 0xCAFEF00D +#define PMU_PMA_AUTO_CFG (MTK_2P5GPHY_XBZ_PCS + 0x820) +#define POWER_ON_AUTO_MODE BIT(16) +#define PMU_AUTO_MODE_EN BIT(0) +#define PMU_PMA_STATUS (MTK_2P5GPHY_XBZ_PCS + 0x840) +#define CLK_IS_DISABLED BIT(3) + +#define MTK_2P5GPHY_XBZ_PMA_RX 0x080000 +#define SMEM_WDAT0 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5000) +#define SMEM_WDAT1 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5004) +#define SMEM_WDAT2 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5008) +#define SMEM_WDAT3 (MTK_2P5GPHY_XBZ_PMA_RX + 0x500c) +#define SMEM_CTRL (MTK_2P5GPHY_XBZ_PMA_RX + 0x5024) +#define SMEM_HW_RDATA_ZERO BIT(24) +#define SMEM_ADDR_REF_ADDR (MTK_2P5GPHY_XBZ_PMA_RX + 0x502c) +#define CM_CTRL_P01 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5100) +#define CM_CTRL_P23 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5124) +#define DM_CTRL_P01 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5200) +#define DM_CTRL_P23 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5224) + +#define MTK_2P5GPHY_CHIP_SCU 0x0cf800 +#define SYS_SW_RESET (MTK_2P5GPHY_CHIP_SCU + 0x128) +#define RESET_RST_CNT BIT(0) + +#define MTK_2P5GPHY_MCU_CSR 0x0f0000 +#define MD32_EN_CFG (MTK_2P5GPHY_MCU_CSR + 0x18) +#define MD32_EN BIT(0) + +#define MTK_2P5GPHY_PMB_FW 0x100000 + +#define MTK_2P5GPHY_FCM_BASE 0x0e0000 +#define FC_LWM (MTK_2P5GPHY_FCM_BASE + 0x14) +#define TX_FC_LWM_MASK GENMASK(31, 16) +#define MIN_IPG_NUM (MTK_2P5GPHY_FCM_BASE + 0x2c) +#define LS_MIN_IPG_NUM_MASK GENMASK(7, 0) +#define FIFO_CTRL (MTK_2P5GPHY_FCM_BASE + 0x40) +#define TX_SFIFO_IDLE_CNT_MASK GENMASK(31, 28) +#define TX_SFIFO_DEL_IPG_WM_MASK GENMASK(23, 16) + +#define MTK_2P5GPHY_APB_BASE 0x11c30000 +#define MTK_2P5GPHY_APB_SIZE 0x9c +#define SW_RESET 0x94 +#define MD32_RESTART_EN_CLEAR BIT(9) + +/* Registers on CL22 page 0 */ +#define PHY_AUX_CTRL_STATUS 0x1d +#define PHY_AUX_DPX_MASK GENMASK(5, 5) +#define PHY_AUX_SPEED_MASK GENMASK(4, 2) + +enum { + PHY_AUX_SPD_10 = 0, + PHY_AUX_SPD_100, + PHY_AUX_SPD_1000, + PHY_AUX_SPD_2500, +}; + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_LINK_STATUS_RELATED 0x147 +#define MTK_PHY_BYPASS_LINK_STATUS_OK BIT(4) +#define MTK_PHY_FORCE_LINK_STATUS_HCD BIT(3) + +#define MTK_PHY_AN_FORCE_SPEED_REG 0x313 +#define MTK_PHY_MASTER_FORCE_SPEED_SEL_EN BIT(7) +#define MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK GENMASK(6, 0) + +#define MTK_PHY_LPI_PCS_DSP_CTRL 0x121 +#define MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK GENMASK(12, 8) + +#define MTK_PHY_PMA_PMD_SPEED_ABILITY 0x300 +#define CAP_100X_HDX BIT(14) +#define CAP_10T_HDX BIT(12) + +/* Registers on MDIO_MMD_VEND2 */ +#define MT7987_OPTIONS 0x110 +#define NORMAL_RETRAIN_DISABLE BIT(0) + +/* Registers on Token Ring debug nodes */ +/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */ +#define AUTO_NP_10XEN BIT(6) + +/* Firmware file size */ +#define MT7987_2P5GE_PMB_FW_SIZE 0x18000 +#define MT7988_2P5GE_PMB_FW_SIZE 0x20000 + +#define MT7987_2P5GE_DSPBITTB_SIZE 0x7000 + +struct mtk_i2p5ge_fw_info { + u8 datecode[4]; + u8 plat[2]; + u8 ver[2]; +}; + +struct mtk_i2p5ge_priv { + void __iomem *reg_base; +}; + +static inline void pbus_write(struct mtk_i2p5ge_priv *priv, u32 offset, + u32 val) +{ + writel(val, priv->reg_base + offset); +} + +static inline void pbus_rmw(struct mtk_i2p5ge_priv *priv, u32 offset, u32 clr, + u32 set) +{ + clrsetbits_le32(priv->reg_base + offset, clr, set); +} + +static int mt798x_i2p5ge_download_fw(struct mtk_i2p5ge_priv *priv, + size_t fwsize, const void *fwdata) +{ + u32 __iomem *fwmem = priv->reg_base + MTK_2P5GPHY_PMB_FW; + const u32 *fw; + u32 i; + + /* Assume fw data is 4-byte aligned */ + fw = fwdata; + + for (i = 0; i < (fwsize >> 2); i++) + writel(fw[i], &fwmem[i]); + + return 0; +} + +static int mt798x_i2p5ge_phy_probe(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv; + + priv = malloc(sizeof(*priv)); + if (!priv) + return -ENOMEM; + + priv->reg_base = ioremap(PBUS_BASE, PBUS_SIZE); + if (!priv->reg_base) { + free(priv); + return -ENODEV; + } + + phydev->priv = priv; + + return 0; +} + +static int mt798x_i2p5ge_phy_config(struct phy_device *phydev) +{ + + /* Setup LED */ + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_LINK10 | + MTK_PHY_LED_ON_LINK100 | + MTK_PHY_LED_ON_LINK1000 | + MTK_PHY_LED_ON_LINK2500); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX); + + /* Switch pinctrl after setting polarity to avoid bogus blinking */ + pinctrl_select_state(phydev->dev, "i2p5gbe-led"); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LPI_PCS_DSP_CTRL, + MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK, 0); + + /* Enable 16-bit next page exchange bit if 1000-BT isn't advertising */ + mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, AUTO_NP_10XEN, + FIELD_PREP(AUTO_NP_10XEN, 0x1)); + + /* Set HW auto downshift */ + mtk_phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_AUX_CTRL_AND_STATUS, + MTK_PHY_ENABLE_DOWNSHIFT); + mtk_phy_restore_page(phydev); + + /* Configure parallel detction functionality */ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_PMA_PMD_SPEED_ABILITY, + CAP_10T_HDX | CAP_100X_HDX); + phy_clear_bits_mmd(phydev, MDIO_DEVAD_NONE, + MII_ADVERTISE, + ADVERTISE_10HALF | ADVERTISE_100HALF); + + return 0; +} + +static void mt798x_i2p5ge_print_fw_info(const void *fwdata, size_t fwsize) +{ + const struct mtk_i2p5ge_fw_info *info; + u8 ver_minor, ver_patch; + + info = (const void *)((uintptr_t)fwdata + fwsize - sizeof(*info)); + + ver_minor = info->ver[1] >> 4; + ver_patch = info->ver[1] & 0xf; + + printf("Firmware loaded, date %02x%02x/%02x/%02x, ver %x.%u.%u\n", + info->datecode[0], info->datecode[1], info->datecode[2], + info->datecode[3], info->ver[0], ver_minor, ver_patch); +} + +int __weak mt7987_i2p5ge_get_fw(void **fw, size_t *fwsize, + void **dspfw, size_t *dspfwsize) +{ + void *pmb, *dsp; + int ret; + + pmb = malloc(MT7987_2P5GE_PMB_FW_SIZE); + if (!pmb) + return -ENOMEM; + + ret = request_firmware_into_buf_via_script( + pmb, MT7987_2P5GE_PMB_FW_SIZE, + "mt7987_i2p5ge_load_pmb_firmware", fwsize); + if (ret) { + free(pmb); + return ret; + } + + dsp = malloc(MT7987_2P5GE_DSPBITTB_SIZE); + if (!dsp) { + free(pmb); + return -ENOMEM; + } + + ret = request_firmware_into_buf_via_script( + dsp, MT7987_2P5GE_DSPBITTB_SIZE, + "mt7987_i2p5ge_load_dspbit_firmware", dspfwsize); + if (ret) { + free(pmb); + free(dsp); + return ret; + } + + *fw = pmb; + *dspfw = dsp; + + return 1; +} + +static int mt7987_i2p5ge_download_dspfw(struct mtk_i2p5ge_priv *priv, + const void *dspfw_data) +{ + const u32 *dspfw; + u32 i; + + pbus_rmw(priv, SMEM_CTRL, 0, SMEM_HW_RDATA_ZERO); + + pbus_rmw(priv, PHY_CTRL_CONFIG, 0, BIT(16)); + + /* Initialize data memory */ + pbus_rmw(priv, DM_CTRL_P01, 0, BIT(28)); + pbus_rmw(priv, DM_CTRL_P23, 0, BIT(28)); + + /* Initialize coefficient memory */ + pbus_rmw(priv, CM_CTRL_P01, 0, BIT(28)); + pbus_rmw(priv, CM_CTRL_P23, 0, BIT(28)); + + /* Initialize PM offset */ + pbus_write(priv, SMEM_ADDR_REF_ADDR, 0); + + /* Assume DSP bit data is 4-byte aligned */ + dspfw = dspfw_data; + + for (i = 0; i < (MT7987_2P5GE_DSPBITTB_SIZE >> 2); i += 4) { + pbus_write(priv, SMEM_WDAT0, dspfw[i]); + pbus_write(priv, SMEM_WDAT1, dspfw[i + 1]); + pbus_write(priv, SMEM_WDAT2, dspfw[i + 2]); + pbus_write(priv, SMEM_WDAT3, dspfw[i + 3]); + } + + pbus_rmw(priv, DM_CTRL_P01, BIT(28), 0); + pbus_rmw(priv, DM_CTRL_P23, BIT(28), 0); + + pbus_rmw(priv, CM_CTRL_P01, BIT(28), 0); + pbus_rmw(priv, CM_CTRL_P23, BIT(28), 0); + + return 0; +} + +static int mt7987_i2p5ge_phy_load_fw(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv = phydev->priv; + size_t fw_size, dspfw_size; + void __iomem *apb_base; + void *fw, *dspfw; + int ret, fwrc; + u32 reg; + + apb_base = ioremap(MTK_2P5GPHY_APB_BASE, MTK_2P5GPHY_APB_SIZE); + if (!apb_base) + return -ENODEV; + + fwrc = mt7987_i2p5ge_get_fw(&fw, &fw_size, &dspfw, &dspfw_size); + if (fwrc < 0) { + dev_err(phydev->dev, "Failed to get firmware data\n"); + return -EINVAL; + } + + if (fw_size != MT7987_2P5GE_PMB_FW_SIZE) { + dev_err(phydev->dev, + "PMB firmware size mismatch (0x%zx != 0x%x)\n", + fw_size, MT7987_2P5GE_PMB_FW_SIZE); + ret = -EINVAL; + goto cleanup; + } + + if (dspfw_size != MT7987_2P5GE_DSPBITTB_SIZE) { + dev_err(phydev->dev, + "DSP code size mismatch (0x%zx != 0x%x)\n", + dspfw_size, MT7987_2P5GE_DSPBITTB_SIZE); + ret = -EINVAL; + goto cleanup; + } + + /* Force 2.5Gphy back to AN state */ + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + mdelay(5); + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_PDOWN); + + clrbits_le16(apb_base + SW_RESET, MD32_RESTART_EN_CLEAR); + setbits_le16(apb_base + SW_RESET, MD32_RESTART_EN_CLEAR); + clrbits_le16(apb_base + SW_RESET, MD32_RESTART_EN_CLEAR); + + pbus_rmw(priv, MD32_EN_CFG, MD32_EN, 0); + + ret = mt798x_i2p5ge_download_fw(priv, MT7987_2P5GE_PMB_FW_SIZE, fw); + if (ret) + goto cleanup; + + /* Enable 100Mbps module clock. */ + pbus_rmw(priv, FNPLL_PWR_CTRL1, RG_SPEED_MASK, RG_SPEED_100); + + /* Check if 100Mbps module clock is ready. */ + ret = readl_poll_timeout(priv->reg_base + FNPLL_PWR_CTRL_STATUS, reg, + reg & RG_SPEED_100_STABLE, 10000); + if (ret) { + dev_err(phydev->dev, + "Timed out enabling 100Mbps module clock\n"); + } + + /* Enable 2.5Gbps module clock. */ + pbus_rmw(priv, FNPLL_PWR_CTRL1, RG_SPEED_MASK, RG_SPEED_2500); + + /* Check if 2.5Gbps module clock is ready. */ + ret = readl_poll_timeout(priv->reg_base + FNPLL_PWR_CTRL_STATUS, reg, + reg & RG_SPEED_2500_STABLE, 10000); + + if (ret) { + dev_err(phydev->dev, + "Timed out enabling 2.5Gbps module clock\n"); + } + + /* Disable AN */ + phy_clear_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_ANENABLE); + + /* Force to run at 2.5G speed */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_AN_FORCE_SPEED_REG, + MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK, + MTK_PHY_MASTER_FORCE_SPEED_SEL_EN | + FIELD_PREP(MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK, 0x1b)); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_RELATED, + MTK_PHY_BYPASS_LINK_STATUS_OK | + MTK_PHY_FORCE_LINK_STATUS_HCD); + + /* Set xbz, pma and rx as "do not reset" in order to input DSP code. */ + pbus_rmw(priv, DO_NOT_RESET, 0, + DO_NOT_RESET_XBZ | DO_NOT_RESET_PMA | DO_NOT_RESET_RX); + + pbus_rmw(priv, SYS_SW_RESET, RESET_RST_CNT, 0); + + pbus_write(priv, PMU_WP, WRITE_PROTECT_KEY); + + pbus_rmw(priv, PMU_PMA_AUTO_CFG, 0, + PMU_AUTO_MODE_EN | POWER_ON_AUTO_MODE); + + /* Check if clock in auto mode is disabled. */ + ret = readl_poll_timeout(priv->reg_base + PMU_PMA_STATUS, reg, + (reg & CLK_IS_DISABLED) == 0x0, 100000); + if (ret) + dev_err(phydev->dev, "Timed out enabling clock auto mode\n"); + + ret = mt7987_i2p5ge_download_dspfw(priv, dspfw); + if (ret) + goto cleanup; + + pbus_rmw(priv, MD32_EN_CFG, 0, MD32_EN); + + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + /* We need a delay here to stabilize initialization of MCU */ + mdelay(8); + + mt798x_i2p5ge_print_fw_info(fw, fw_size); + +cleanup: + if (fwrc > 0) { + free(fw); + free(dspfw); + } + + iounmap(apb_base); + + return ret; +} + +static int mt7987_i2p5ge_phy_config(struct phy_device *phydev) +{ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_POLARITY); + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MT7987_OPTIONS, + NORMAL_RETRAIN_DISABLE); + + return mt798x_i2p5ge_phy_config(phydev); +} + +static int mt7987_i2p5ge_phy_probe(struct phy_device *phydev) +{ + int ret; + + ret = mt798x_i2p5ge_phy_probe(phydev); + if (ret) + return ret; + + return mt7987_i2p5ge_phy_load_fw(phydev); +} + +int __weak mt7988_i2p5ge_get_fw(void **fw, size_t *size) +{ + void *pmb; + int ret; + + pmb = malloc(MT7988_2P5GE_PMB_FW_SIZE); + if (!pmb) + return -ENOMEM; + + ret = request_firmware_into_buf_via_script( + pmb, MT7988_2P5GE_PMB_FW_SIZE, + "mt7988_i2p5ge_load_pmb_firmware", size); + if (ret) { + free(pmb); + return ret; + } + + *fw = pmb; + + return 1; +} + +static int mt7988_i2p5ge_phy_load_fw(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv = phydev->priv; + size_t fw_size; + int ret, fwrc; + void *fw; + + fwrc = mt7988_i2p5ge_get_fw(&fw, &fw_size); + if (fwrc < 0) { + dev_err(phydev->dev, "Failed to get firmware data\n"); + return -EINVAL; + } + + if (fw_size != MT7988_2P5GE_PMB_FW_SIZE) { + dev_err(phydev->dev, "Firmware size mismatch (0x%zx != 0x%x)\n", + fw_size, MT7988_2P5GE_PMB_FW_SIZE); + ret = -EINVAL; + goto cleanup; + } + + ret = mt798x_i2p5ge_download_fw(priv, MT7988_2P5GE_PMB_FW_SIZE, fw); + if (ret) + goto cleanup; + + pbus_rmw(priv, MD32_EN_CFG, MD32_EN, 0); + pbus_rmw(priv, MD32_EN_CFG, 0, MD32_EN); + + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + /* We need a delay here to stabilize initialization of MCU */ + mdelay(8); + + mt798x_i2p5ge_print_fw_info(fw, fw_size); + +cleanup: + if (fwrc > 0) + free(fw); + + return ret; +} + +static int mt7988_i2p5ge_phy_config(struct phy_device *phydev) +{ + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_POLARITY); + + return mt798x_i2p5ge_phy_config(phydev); +} + +static int mt7988_i2p5ge_phy_probe(struct phy_device *phydev) +{ + int ret; + + ret = mt798x_i2p5ge_phy_probe(phydev); + if (ret) + return ret; + + return mt7988_i2p5ge_phy_load_fw(phydev); +} + +static int mt798x_i2p5ge_phy_startup(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv = phydev->priv; + int ret; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + /* Initialize speed/duplex */ + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (phydev->link) { + ret = phy_read(phydev, MDIO_DEVAD_NONE, PHY_AUX_CTRL_STATUS); + if (ret < 0) + return ret; + + switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) { + case PHY_AUX_SPD_10: + phydev->speed = SPEED_10; + break; + + case PHY_AUX_SPD_100: + phydev->speed = SPEED_100; + break; + + case PHY_AUX_SPD_1000: + pbus_rmw(priv, FIFO_CTRL, + TX_SFIFO_IDLE_CNT_MASK | TX_SFIFO_DEL_IPG_WM_MASK, + FIELD_PREP(TX_SFIFO_IDLE_CNT_MASK, 0x1) | + FIELD_PREP(TX_SFIFO_DEL_IPG_WM_MASK, 0x10)); + pbus_rmw(priv, MIN_IPG_NUM, LS_MIN_IPG_NUM_MASK, + FIELD_PREP(LS_MIN_IPG_NUM_MASK, 0xa)); + pbus_rmw(priv, FC_LWM, TX_FC_LWM_MASK, + FIELD_PREP(TX_FC_LWM_MASK, 0x340)); + phydev->speed = SPEED_1000; + break; + + case PHY_AUX_SPD_2500: + phydev->speed = SPEED_2500; + break; + + default: + break; + } + + /* This PHY always operates in full duplex */ + phydev->duplex = DUPLEX_FULL; + } + + return 0; +} + +U_BOOT_PHY_DRIVER(mt7987_i2p5ge) = { + .name = "MediaTek MT7987 built-in 2.5GbE PHY", + .uid = MTK_2P5GPHY_ID_MT7987, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PCS | MDIO_MMD_AN | MDIO_MMD_VEND1 | MDIO_MMD_VEND2), + .probe = &mt7987_i2p5ge_phy_probe, + .config = &mt7987_i2p5ge_phy_config, + .startup = &mt798x_i2p5ge_phy_startup, + .shutdown = &genphy_shutdown, +}; + +U_BOOT_PHY_DRIVER(mt7988_i2p5ge) = { + .name = "MediaTek MT7988 built-in 2.5GbE PHY", + .uid = MTK_2P5GPHY_ID_MT7988, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PCS | MDIO_MMD_AN | MDIO_MMD_VEND1 | MDIO_MMD_VEND2), + .probe = &mt7988_i2p5ge_phy_probe, + .config = &mt7988_i2p5ge_phy_config, + .startup = &mt798x_i2p5ge_phy_startup, + .shutdown = &genphy_shutdown, +}; diff --git a/drivers/net/phy/mediatek/mtk-phy-lib.c b/drivers/net/phy/mediatek/mtk-phy-lib.c new file mode 100644 index 00000000000..55e7a6b6eec --- /dev/null +++ b/drivers/net/phy/mediatek/mtk-phy-lib.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. All Rights Reserved. + * + * Author: Sky Huang <SkyLake.Huang@mediatek.com> + */ +#include <dm/device_compat.h> +#include <phy.h> + +#include "mtk.h" + +void mtk_phy_select_page(struct phy_device *phydev, int page) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MTK_EXT_PAGE_ACCESS, page); +} + +void mtk_phy_restore_page(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MTK_EXT_PAGE_ACCESS, + MTK_PHY_PAGE_STANDARD); +} + +/* Difference between functions with mtk_tr* and __mtk_tr* prefixes is + * mtk_tr* functions: wrapped by page switching operations + * __mtk_tr* functions: no page switching operations + */ +static void __mtk_tr_access(struct phy_device *phydev, bool read, u8 ch_addr, + u8 node_addr, u8 data_addr) +{ + u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */ + + if (read) + tr_cmd |= BIT(13); + + tr_cmd |= (((ch_addr & 0x3) << 11) | + ((node_addr & 0xf) << 7) | + ((data_addr & 0x3f) << 1)); + dev_dbg(phydev->dev, "tr_cmd: 0x%x\n", tr_cmd); + phy_write(phydev, MDIO_DEVAD_NONE, 0x10, tr_cmd); +} + +static void __mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u16 *tr_high, u16 *tr_low) +{ + __mtk_tr_access(phydev, true, ch_addr, node_addr, data_addr); + *tr_low = phy_read(phydev, MDIO_DEVAD_NONE, 0x11); + *tr_high = phy_read(phydev, MDIO_DEVAD_NONE, 0x12); + dev_dbg(phydev->dev, "tr_high read: 0x%x, tr_low read: 0x%x\n", + *tr_high, *tr_low); +} + +u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr) +{ + u16 tr_high; + u16 tr_low; + + mtk_phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + __mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low); + mtk_phy_restore_page(phydev); + + return (tr_high << 16) | tr_low; +} + +static void __mtk_tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 tr_data) +{ + phy_write(phydev, MDIO_DEVAD_NONE, 0x11, tr_data & 0xffff); + phy_write(phydev, MDIO_DEVAD_NONE, 0x12, tr_data >> 16); + dev_dbg(phydev->dev, "tr_high write: 0x%x, tr_low write: 0x%x\n", + tr_data >> 16, tr_data & 0xffff); + __mtk_tr_access(phydev, false, ch_addr, node_addr, data_addr); +} + +void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set) +{ + u32 tr_data; + u16 tr_high; + u16 tr_low; + + __mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low); + tr_data = (tr_high << 16) | tr_low; + tr_data = (tr_data & ~mask) | set; + __mtk_tr_write(phydev, ch_addr, node_addr, data_addr, tr_data); +} + +void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set) +{ + mtk_phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, mask, set); + mtk_phy_restore_page(phydev); +} + +void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 set) +{ + __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, 0, set); +} + +void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 clr) +{ + __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, clr, 0); +} diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h new file mode 100644 index 00000000000..68044cbdb32 --- /dev/null +++ b/drivers/net/phy/mediatek/mtk.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 MediaTek Inc. All Rights Reserved. + * + * Author: Sky Huang <SkyLake.Huang@mediatek.com> + * + * Common definition for Mediatek Ethernet PHYs + */ + +#ifndef _MTK_EPHY_H_ +#define _MTK_EPHY_H_ + +#define MTK_EXT_PAGE_ACCESS 0x1f +#define MTK_PHY_PAGE_STANDARD 0x0000 +#define MTK_PHY_PAGE_EXTENDED_1 0x0001 +#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 +/* suprv_media_select_RefClk */ +#define MTK_PHY_LP_DETECTED_MASK GENMASK(7, 6) +#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) + +#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +/* Registers on Token Ring debug nodes */ +/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x2 */ +#define AN_STATE_MASK GENMASK(22, 19) +#define AN_STATE_SHIFT 19 +#define AN_STATE_TX_DISABLE 1 + +/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */ +#define AN_NEW_LP_CNT_LIMIT_MASK GENMASK(23, 20) +#define AUTO_NP_10XEN BIT(6) + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_LINK_STATUS_MISC (0xa2) +#define MTK_PHY_FINAL_SPEED_1000 BIT(3) + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_LED0_ON_CTRL 0x24 +#define MTK_PHY_LED1_ON_CTRL 0x26 +#define MTK_GPHY_LED_ON_MASK GENMASK(6, 0) +#define MTK_2P5GPHY_LED_ON_MASK GENMASK(7, 0) +#define MTK_PHY_LED_ON_LINK1000 BIT(0) +#define MTK_PHY_LED_ON_LINK100 BIT(1) +#define MTK_PHY_LED_ON_LINK10 BIT(2) +#define MTK_PHY_LED_ON_LINKDOWN BIT(3) +#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +#define MTK_PHY_LED_ON_FORCE_ON BIT(6) +#define MTK_PHY_LED_ON_LINK2500 BIT(7) +#define MTK_PHY_LED_ON_POLARITY BIT(14) +#define MTK_PHY_LED_ON_ENABLE BIT(15) + +#define MTK_PHY_LED0_BLINK_CTRL 0x25 +#define MTK_PHY_LED1_BLINK_CTRL 0x27 +#define MTK_PHY_LED_BLINK_1000TX BIT(0) +#define MTK_PHY_LED_BLINK_1000RX BIT(1) +#define MTK_PHY_LED_BLINK_100TX BIT(2) +#define MTK_PHY_LED_BLINK_100RX BIT(3) +#define MTK_PHY_LED_BLINK_10TX BIT(4) +#define MTK_PHY_LED_BLINK_10RX BIT(5) +#define MTK_PHY_LED_BLINK_COLLISION BIT(6) +#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) +#define MTK_PHY_LED_BLINK_2500TX BIT(10) +#define MTK_PHY_LED_BLINK_2500RX BIT(11) + +#define MTK_GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK1000 | \ + MTK_PHY_LED_ON_LINK100 | \ + MTK_PHY_LED_ON_LINK10) +#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ + MTK_PHY_LED_BLINK_100RX | \ + MTK_PHY_LED_BLINK_10RX) +#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ + MTK_PHY_LED_BLINK_100RX | \ + MTK_PHY_LED_BLINK_10RX) + +#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ + MTK_GPHY_LED_ON_SET) +#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ + MTK_GPHY_LED_RX_BLINK_SET) +#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ + MTK_GPHY_LED_TX_BLINK_SET) + +#define MTK_PHY_LED_STATE_FORCE_ON 0 +#define MTK_PHY_LED_STATE_FORCE_BLINK 1 +#define MTK_PHY_LED_STATE_NETDEV 2 + +void mtk_phy_select_page(struct phy_device *phydev, int page); +void mtk_phy_restore_page(struct phy_device *phydev); + +u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr); +void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set); +void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set); +void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 set); +void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 clr); + +#endif /* _MTK_EPHY_H_ */ diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig index 52267339de0..93c3a0c35f2 100644 --- a/drivers/net/ti/Kconfig +++ b/drivers/net/ti/Kconfig @@ -49,6 +49,7 @@ config TI_AM65_CPSW_NUSS imply MISC imply SYSCON imply MDIO_TI_CPSW + imply SPL_SYSCON select PHYLIB help This driver supports TI K3 MCU CPSW Nuss Ethernet controller diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c index 9b69f36d04d..7a88f76fd09 100644 --- a/drivers/net/ti/am65-cpsw-nuss.c +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -234,14 +234,11 @@ out: #define AM65_GMII_SEL_MODE_RGMII 2 #define AM65_GMII_SEL_MODE_SGMII 3 -#define AM65_GMII_SEL_RGMII_IDMODE BIT(4) - static int am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv, phy_interface_t phy_mode) { struct udevice *dev = priv->dev; u32 offset, reg, phandle; - bool rgmii_id = false; fdt_addr_t gmii_sel; u32 mode = 0; ofnode node; @@ -278,12 +275,6 @@ static int am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv, mode = AM65_GMII_SEL_MODE_RGMII; break; - case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII_TXID: - mode = AM65_GMII_SEL_MODE_RGMII; - rgmii_id = true; - break; - case PHY_INTERFACE_MODE_SGMII: mode = AM65_GMII_SEL_MODE_SGMII; break; @@ -298,9 +289,6 @@ static int am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv, break; }; - if (rgmii_id) - mode |= AM65_GMII_SEL_RGMII_IDMODE; - reg = mode; dev_dbg(dev, "gmii_sel PHY mode: %u, new gmii_sel: %08x\n", phy_mode, reg); @@ -628,9 +616,9 @@ static int am65_cpsw_phy_init(struct udevice *dev) struct eth_pdata *pdata = dev_get_plat(dev); struct phy_device *phydev; u32 supported = PHY_GBIT_FEATURES; - int ret; + int ret = 0; - phydev = dm_eth_phy_connect(dev); + phydev = dm_eth_phy_connect_interface(dev, pdata->phy_interface); if (!phydev) { dev_err(dev, "phy_connect() failed\n"); return -ENODEV; @@ -657,9 +645,28 @@ static int am65_cpsw_ofdata_parse_phy(struct udevice *dev) dev_read_u32(dev, "reg", &priv->port_id); pdata->phy_interface = dev_read_phy_mode(dev); - if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) { + + /* CPSW controllers supported by this driver have a fixed internal TX + * delay in RGMII mode. Fix up PHY mode to account for this and warn + * about Device Trees that claim to have a TX delay on the PCB. + */ + switch (pdata->phy_interface) { + case PHY_INTERFACE_MODE_RGMII_ID: + pdata->phy_interface = PHY_INTERFACE_MODE_RGMII_RXID; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + pdata->phy_interface = PHY_INTERFACE_MODE_RGMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_RXID: + dev_warn(dev, + "RGMII mode without internal TX delay unsupported; please fix your Device Tree\n"); + break; + case PHY_INTERFACE_MODE_NA: dev_err(dev, "Invalid PHY mode, port %u\n", priv->port_id); return -EINVAL; + default: + break; } dev_read_u32(dev, "max-speed", (u32 *)&pdata->max_speed); @@ -705,7 +712,6 @@ static int am65_cpsw_probe_nuss(struct udevice *dev) struct am65_cpsw_common *cpsw_common = dev_get_priv(dev); ofnode ports_np, node; int ret, i; - struct udevice *port_dev; cpsw_common->dev = dev; cpsw_common->ss_base = dev_read_addr(dev); @@ -732,6 +738,7 @@ static int am65_cpsw_probe_nuss(struct udevice *dev) ports_np = dev_read_subnode(dev, "ethernet-ports"); if (!ofnode_valid(ports_np)) { ret = -ENOENT; + dev_err(dev, "Invalid device tree node %d\n", ret); goto out; } @@ -763,12 +770,6 @@ static int am65_cpsw_probe_nuss(struct udevice *dev) continue; cpsw_common->ports[port_id].disabled = disabled; - if (disabled) - continue; - - ret = device_bind_driver_to_node(dev, "am65_cpsw_nuss_port", ofnode_get_name(node), node, &port_dev); - if (ret) - dev_err(dev, "Failed to bind to %s node\n", ofnode_get_name(node)); } for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) { @@ -798,6 +799,37 @@ out: return ret; } +static int am65_cpsw_nuss_bind(struct udevice *dev) +{ + struct uclass_driver *drv; + struct udevice *port_dev; + ofnode ports_np, node; + int ret; + + drv = lists_uclass_lookup(UCLASS_ETH); + if (!drv) { + puts("Cannot find eth driver"); + return -ENOENT; + } + + ports_np = dev_read_subnode(dev, "ethernet-ports"); + if (!ofnode_valid(ports_np)) + return -ENOENT; + + ofnode_for_each_subnode(node, ports_np) { + const char *node_name; + + node_name = ofnode_get_name(node); + + ret = device_bind_driver_to_node(dev, "am65_cpsw_nuss_port", node_name, node, + &port_dev); + if (ret) + dev_err(dev, "Failed to bind to %s node\n", node_name); + } + + return ret; +} + static const struct udevice_id am65_cpsw_nuss_ids[] = { { .compatible = "ti,am654-cpsw-nuss" }, { .compatible = "ti,j721e-cpsw-nuss" }, @@ -809,6 +841,7 @@ U_BOOT_DRIVER(am65_cpsw_nuss) = { .name = "am65_cpsw_nuss", .id = UCLASS_MISC, .of_match = am65_cpsw_nuss_ids, + .bind = am65_cpsw_nuss_bind, .probe = am65_cpsw_probe_nuss, .priv_auto = sizeof(struct am65_cpsw_common), }; diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c index d8df3c9afb0..12a162b9d68 100644 --- a/drivers/net/ti/icssg_prueth.c +++ b/drivers/net/ti/icssg_prueth.c @@ -647,8 +647,6 @@ static int prueth_probe(struct udevice *dev) return -EINVAL; } - if (port_id < 0) - continue; if (disabled) continue; |