diff options
Diffstat (limited to 'drivers/net/mtk_eth.c')
-rw-r--r-- | drivers/net/mtk_eth.c | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c new file mode 100644 index 0000000000..cc09404830 --- /dev/null +++ b/drivers/net/mtk_eth.c @@ -0,0 +1,1175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <miiphy.h> +#include <regmap.h> +#include <reset.h> +#include <syscon.h> +#include <wait_bit.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <linux/mdio.h> +#include <linux/mii.h> + +#include "mtk_eth.h" + +#define NUM_TX_DESC 24 +#define NUM_RX_DESC 24 +#define TX_TOTAL_BUF_SIZE (NUM_TX_DESC * PKTSIZE_ALIGN) +#define RX_TOTAL_BUF_SIZE (NUM_RX_DESC * PKTSIZE_ALIGN) +#define TOTAL_PKT_BUF_SIZE (TX_TOTAL_BUF_SIZE + RX_TOTAL_BUF_SIZE) + +#define MT7530_NUM_PHYS 5 +#define MT7530_DFL_SMI_ADDR 31 + +#define MT7530_PHY_ADDR(base, addr) \ + (((base) + (addr)) & 0x1f) + +#define GDMA_FWD_TO_CPU \ + (0x20000000 | \ + GDM_ICS_EN | \ + GDM_TCS_EN | \ + GDM_UCS_EN | \ + STRP_CRC | \ + (DP_PDMA << MYMAC_DP_S) | \ + (DP_PDMA << BC_DP_S) | \ + (DP_PDMA << MC_DP_S) | \ + (DP_PDMA << UN_DP_S)) + +#define GDMA_FWD_DISCARD \ + (0x20000000 | \ + GDM_ICS_EN | \ + GDM_TCS_EN | \ + GDM_UCS_EN | \ + STRP_CRC | \ + (DP_DISCARD << MYMAC_DP_S) | \ + (DP_DISCARD << BC_DP_S) | \ + (DP_DISCARD << MC_DP_S) | \ + (DP_DISCARD << UN_DP_S)) + +struct pdma_rxd_info1 { + u32 PDP0; +}; + +struct pdma_rxd_info2 { + u32 PLEN1 : 14; + u32 LS1 : 1; + u32 UN_USED : 1; + u32 PLEN0 : 14; + u32 LS0 : 1; + u32 DDONE : 1; +}; + +struct pdma_rxd_info3 { + u32 PDP1; +}; + +struct pdma_rxd_info4 { + u32 FOE_ENTRY : 14; + u32 CRSN : 5; + u32 SP : 3; + u32 L4F : 1; + u32 L4VLD : 1; + u32 TACK : 1; + u32 IP4F : 1; + u32 IP4 : 1; + u32 IP6 : 1; + u32 UN_USED : 4; +}; + +struct pdma_rxdesc { + struct pdma_rxd_info1 rxd_info1; + struct pdma_rxd_info2 rxd_info2; + struct pdma_rxd_info3 rxd_info3; + struct pdma_rxd_info4 rxd_info4; +}; + +struct pdma_txd_info1 { + u32 SDP0; +}; + +struct pdma_txd_info2 { + u32 SDL1 : 14; + u32 LS1 : 1; + u32 BURST : 1; + u32 SDL0 : 14; + u32 LS0 : 1; + u32 DDONE : 1; +}; + +struct pdma_txd_info3 { + u32 SDP1; +}; + +struct pdma_txd_info4 { + u32 VLAN_TAG : 16; + u32 INS : 1; + u32 RESV : 2; + u32 UDF : 6; + u32 FPORT : 3; + u32 TSO : 1; + u32 TUI_CO : 3; +}; + +struct pdma_txdesc { + struct pdma_txd_info1 txd_info1; + struct pdma_txd_info2 txd_info2; + struct pdma_txd_info3 txd_info3; + struct pdma_txd_info4 txd_info4; +}; + +enum mtk_switch { + SW_NONE, + SW_MT7530 +}; + +enum mtk_soc { + SOC_MT7623, + SOC_MT7629 +}; + +struct mtk_eth_priv { + char pkt_pool[TOTAL_PKT_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); + + struct pdma_txdesc *tx_ring_noc; + struct pdma_rxdesc *rx_ring_noc; + + int rx_dma_owner_idx0; + int tx_cpu_owner_idx0; + + void __iomem *fe_base; + void __iomem *gmac_base; + void __iomem *ethsys_base; + + 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); + + enum mtk_soc soc; + int gmac_id; + int force_mode; + int speed; + int duplex; + + struct phy_device *phydev; + int phy_interface; + int phy_addr; + + enum mtk_switch sw; + int (*switch_init)(struct mtk_eth_priv *priv); + u32 mt7530_smi_addr; + u32 mt7530_phy_base; + + struct gpio_desc rst_gpio; + int mcm; + + struct reset_ctl rst_fe; + struct reset_ctl rst_mcm; +}; + +static void mtk_pdma_write(struct mtk_eth_priv *priv, u32 reg, u32 val) +{ + writel(val, priv->fe_base + PDMA_BASE + reg); +} + +static void mtk_pdma_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, + u32 set) +{ + clrsetbits_le32(priv->fe_base + PDMA_BASE + reg, clr, set); +} + +static void mtk_gdma_write(struct mtk_eth_priv *priv, int no, u32 reg, + u32 val) +{ + u32 gdma_base; + + if (no == 1) + gdma_base = GDMA2_BASE; + else + gdma_base = GDMA1_BASE; + + writel(val, priv->fe_base + gdma_base + reg); +} + +static u32 mtk_gmac_read(struct mtk_eth_priv *priv, u32 reg) +{ + return readl(priv->gmac_base + reg); +} + +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) +{ + clrsetbits_le32(priv->gmac_base + reg, clr, set); +} + +static void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, + u32 set) +{ + clrsetbits_le32(priv->ethsys_base + reg, clr, set); +} + +/* 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) +{ + int ret; + u32 val; + + val = (st << MDIO_ST_S) | + ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | + (((u32)phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | + (((u32)reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); + + if (cmd == MDIO_CMD_WRITE) + val |= data & MDIO_RW_DATA_M; + + mtk_gmac_write(priv, GMAC_PIAC_REG, val | PHY_ACS_ST); + + ret = wait_for_bit_le32(priv->gmac_base + GMAC_PIAC_REG, + PHY_ACS_ST, 0, 5000, 0); + if (ret) { + pr_warn("MDIO access timeout\n"); + return ret; + } + + if (cmd == MDIO_CMD_READ) { + val = mtk_gmac_read(priv, GMAC_PIAC_REG); + return val & MDIO_RW_DATA_M; + } + + return 0; +} + +/* Direct MDIO clause 22 read via SoC */ +static 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) +{ + 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 ret; + + ret = mtk_mii_rw(priv, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45); + if (ret) + return ret; + + return mtk_mii_rw(priv, addr, devad, 0, MDIO_CMD_READ_C45, + MDIO_ST_C45); +} + +/* 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 ret; + + ret = mtk_mii_rw(priv, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45); + if (ret) + return ret; + + return mtk_mii_rw(priv, addr, devad, val, MDIO_CMD_WRITE, + MDIO_ST_C45); +} + +/* 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 ret; + + ret = priv->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); + 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)); + if (ret) + return ret; + + return priv->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 ret; + + ret = priv->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); + 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)); + if (ret) + return ret; + + return priv->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) +{ + 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); +} + +static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + 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); +} + +static int mtk_mdio_register(struct udevice *dev) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + 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; + 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); + + mdio_bus->priv = (void *)priv; + + ret = mdio_register(mdio_bus); + + if (ret) + return ret; + + priv->mdio_bus = mdio_bus; + + return 0; +} + +/* + * 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 mt7530_reg_read(struct mtk_eth_priv *priv, u32 reg, u32 *data) +{ + int ret, low_word, high_word; + + /* Write page address */ + ret = mtk_mii_write(priv, priv->mt7530_smi_addr, 0x1f, reg >> 6); + if (ret) + return ret; + + /* Read low word */ + low_word = mtk_mii_read(priv, priv->mt7530_smi_addr, (reg >> 2) & 0xf); + if (low_word < 0) + return low_word; + + /* Read high word */ + high_word = mtk_mii_read(priv, priv->mt7530_smi_addr, 0x10); + if (high_word < 0) + return high_word; + + if (data) + *data = ((u32)high_word << 16) | (low_word & 0xffff); + + return 0; +} + +static int mt7530_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 data) +{ + int ret; + + /* Write page address */ + ret = mtk_mii_write(priv, priv->mt7530_smi_addr, 0x1f, reg >> 6); + if (ret) + return ret; + + /* Write low word */ + ret = mtk_mii_write(priv, priv->mt7530_smi_addr, (reg >> 2) & 0xf, + data & 0xffff); + if (ret) + return ret; + + /* Write high word */ + return mtk_mii_write(priv, priv->mt7530_smi_addr, 0x10, data >> 16); +} + +static void mt7530_reg_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, + u32 set) +{ + u32 val; + + mt7530_reg_read(priv, reg, &val); + val &= ~clr; + val |= set; + mt7530_reg_write(priv, reg, val); +} + +static void mt7530_core_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 val) +{ + u8 phy_addr = MT7530_PHY_ADDR(priv->mt7530_phy_base, 0); + + mtk_mmd_ind_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 */ + 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 int mt7530_setup(struct mtk_eth_priv *priv) +{ + u16 phy_addr, phy_val; + u32 val; + int i; + + /* Select 250MHz clk for RGMII mode */ + mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG, + ETHSYS_TRGMII_CLK_SEL362_5, 0); + + /* Global reset switch */ + if (priv->mcm) { + reset_assert(&priv->rst_mcm); + udelay(1000); + reset_deassert(&priv->rst_mcm); + mdelay(1000); + } 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(1000); + } + + /* Modify HWTRAP first to allow direct access to internal PHYs */ + mt7530_reg_read(priv, HWTRAP_REG, &val); + val |= CHG_TRAP; + val &= ~C_MDIO_BPS; + mt7530_reg_write(priv, MHWTRAP_REG, val); + + /* Calculate the phy base address */ + val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3; + priv->mt7530_phy_base = (val | 0x7) + 1; + + /* Turn off PHYs */ + for (i = 0; i < MT7530_NUM_PHYS; i++) { + phy_addr = MT7530_PHY_ADDR(priv->mt7530_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 */ + mt7530_reg_write(priv, PCMR_REG(5), FORCE_MODE); + mt7530_reg_write(priv, PCMR_REG(6), FORCE_MODE); + + /* MT7530 reset */ + mt7530_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); + udelay(100); + + val = (1 << 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 */ + mt7530_reg_write(priv, PCMR_REG(6), val); + + /* MT7530 Port5: Forced link down */ + mt7530_reg_write(priv, PCMR_REG(5), FORCE_MODE); + + /* MT7530 Port6: Set to RGMII */ + mt7530_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII); + + /* Hardware Trap: Enable Port6, Disable Port5 */ + mt7530_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); + mt7530_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++) + mt7530_reg_write(priv, MT7530_TRGMII_TD_ODT(i), + (8 << TD_DM_DRVP_S) | (8 << TD_DM_DRVN_S)); + + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mt7530_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16); + + /* Turn on PHYs */ + for (i = 0; i < MT7530_NUM_PHYS; i++) { + phy_addr = MT7530_PHY_ADDR(priv->mt7530_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); + } + + /* Set port isolation */ + for (i = 0; i < 8; i++) { + /* Set port matrix mode */ + if (i != 6) + mt7530_reg_write(priv, PCR_REG(i), + (0x40 << PORT_MATRIX_S)); + else + mt7530_reg_write(priv, PCR_REG(i), + (0x3f << PORT_MATRIX_S)); + + /* Set port mode to user port */ + mt7530_reg_write(priv, PVC_REG(i), + (0x8100 << STAG_VPID_S) | + (VLAN_ATTR_USER << VLAN_ATTR_S)); + } + + return 0; +} + +static void mtk_phy_link_adjust(struct mtk_eth_priv *priv) +{ + u16 lcl_adv = 0, rmt_adv = 0; + u8 flowctrl; + u32 mcr; + + mcr = (1 << IPG_CFG_S) | + (MAC_RX_PKT_LEN_1536 << MAC_RX_PKT_LEN_S) | + MAC_MODE | FORCE_MODE | + MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN; + + switch (priv->phydev->speed) { + case SPEED_10: + mcr |= (SPEED_10M << FORCE_SPD_S); + break; + case SPEED_100: + mcr |= (SPEED_100M << FORCE_SPD_S); + break; + case SPEED_1000: + mcr |= (SPEED_1000M << FORCE_SPD_S); + break; + }; + + if (priv->phydev->link) + mcr |= FORCE_LINK; + + if (priv->phydev->duplex) { + mcr |= FORCE_DPX; + + if (priv->phydev->pause) + rmt_adv = LPA_PAUSE_CAP; + if (priv->phydev->asym_pause) + rmt_adv |= LPA_PAUSE_ASYM; + + if (priv->phydev->advertising & ADVERTISED_Pause) + lcl_adv |= ADVERTISE_PAUSE_CAP; + if (priv->phydev->advertising & ADVERTISED_Asym_Pause) + lcl_adv |= ADVERTISE_PAUSE_ASYM; + + flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + + if (flowctrl & FLOW_CTRL_TX) + mcr |= FORCE_TX_FC; + if (flowctrl & FLOW_CTRL_RX) + mcr |= FORCE_RX_FC; + + debug("rx pause %s, tx pause %s\n", + flowctrl & FLOW_CTRL_RX ? "enabled" : "disabled", + flowctrl & FLOW_CTRL_TX ? "enabled" : "disabled"); + } + + mtk_gmac_write(priv, GMAC_PORT_MCR(priv->gmac_id), mcr); +} + +static int mtk_phy_start(struct mtk_eth_priv *priv) +{ + struct phy_device *phydev = priv->phydev; + int ret; + + ret = phy_startup(phydev); + + if (ret) { + debug("Could not initialize PHY %s\n", phydev->dev->name); + return ret; + } + + if (!phydev->link) { + debug("%s: link down.\n", phydev->dev->name); + return 0; + } + + mtk_phy_link_adjust(priv); + + debug("Speed: %d, %s duplex%s\n", phydev->speed, + (phydev->duplex) ? "full" : "half", + (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); + + return 0; +} + +static int mtk_phy_probe(struct udevice *dev) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + struct phy_device *phydev; + + phydev = phy_connect(priv->mdio_bus, priv->phy_addr, dev, + priv->phy_interface); + if (!phydev) + return -ENODEV; + + phydev->supported &= PHY_GBIT_FEATURES; + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + phy_config(phydev); + + return 0; +} + +static void mtk_mac_init(struct mtk_eth_priv *priv) +{ + int i, ge_mode = 0; + u32 mcr; + + switch (priv->phy_interface) { + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_SGMII: + ge_mode = GE_MODE_RGMII; + break; + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_GMII: + ge_mode = GE_MODE_MII; + break; + case PHY_INTERFACE_MODE_RMII: + ge_mode = GE_MODE_RMII; + break; + default: + break; + } + + /* set the gmac to the right mode */ + mtk_ethsys_rmw(priv, ETHSYS_SYSCFG0_REG, + SYSCFG0_GE_MODE_M << SYSCFG0_GE_MODE_S(priv->gmac_id), + ge_mode << SYSCFG0_GE_MODE_S(priv->gmac_id)); + + if (priv->force_mode) { + mcr = (1 << IPG_CFG_S) | + (MAC_RX_PKT_LEN_1536 << MAC_RX_PKT_LEN_S) | + MAC_MODE | FORCE_MODE | + MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + FORCE_LINK; + + switch (priv->speed) { + case SPEED_10: + mcr |= SPEED_10M << FORCE_SPD_S; + break; + case SPEED_100: + mcr |= SPEED_100M << FORCE_SPD_S; + break; + case SPEED_1000: + mcr |= SPEED_1000M << FORCE_SPD_S; + break; + } + + if (priv->duplex) + mcr |= FORCE_DPX; + + mtk_gmac_write(priv, GMAC_PORT_MCR(priv->gmac_id), mcr); + } + + if (priv->soc == SOC_MT7623) { + /* Lower Tx Driving for TRGMII path */ + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mtk_gmac_write(priv, GMAC_TRGMII_TD_ODT(i), + (8 << TD_DM_DRVP_S) | + (8 << TD_DM_DRVN_S)); + + mtk_gmac_rmw(priv, GMAC_TRGMII_RCK_CTRL, 0, + RX_RST | RXC_DQSISEL); + mtk_gmac_rmw(priv, GMAC_TRGMII_RCK_CTRL, RX_RST, 0); + } +} + +static void mtk_eth_fifo_init(struct mtk_eth_priv *priv) +{ + char *pkt_base = priv->pkt_pool; + int i; + + mtk_pdma_rmw(priv, PDMA_GLO_CFG_REG, 0xffff0000, 0); + udelay(500); + + memset(priv->tx_ring_noc, 0, NUM_TX_DESC * sizeof(struct pdma_txdesc)); + memset(priv->rx_ring_noc, 0, NUM_RX_DESC * sizeof(struct pdma_rxdesc)); + memset(priv->pkt_pool, 0, TOTAL_PKT_BUF_SIZE); + + flush_dcache_range((u32)pkt_base, (u32)(pkt_base + TOTAL_PKT_BUF_SIZE)); + + priv->rx_dma_owner_idx0 = 0; + priv->tx_cpu_owner_idx0 = 0; + + for (i = 0; i < NUM_TX_DESC; i++) { + priv->tx_ring_noc[i].txd_info2.LS0 = 1; + priv->tx_ring_noc[i].txd_info2.DDONE = 1; + priv->tx_ring_noc[i].txd_info4.FPORT = priv->gmac_id + 1; + + priv->tx_ring_noc[i].txd_info1.SDP0 = virt_to_phys(pkt_base); + pkt_base += PKTSIZE_ALIGN; + } + + for (i = 0; i < NUM_RX_DESC; i++) { + priv->rx_ring_noc[i].rxd_info2.PLEN0 = PKTSIZE_ALIGN; + priv->rx_ring_noc[i].rxd_info1.PDP0 = virt_to_phys(pkt_base); + pkt_base += PKTSIZE_ALIGN; + } + + mtk_pdma_write(priv, TX_BASE_PTR_REG(0), + virt_to_phys(priv->tx_ring_noc)); + mtk_pdma_write(priv, TX_MAX_CNT_REG(0), NUM_TX_DESC); + mtk_pdma_write(priv, TX_CTX_IDX_REG(0), priv->tx_cpu_owner_idx0); + + mtk_pdma_write(priv, RX_BASE_PTR_REG(0), + virt_to_phys(priv->rx_ring_noc)); + mtk_pdma_write(priv, RX_MAX_CNT_REG(0), NUM_RX_DESC); + mtk_pdma_write(priv, RX_CRX_IDX_REG(0), NUM_RX_DESC - 1); + + mtk_pdma_write(priv, PDMA_RST_IDX_REG, RST_DTX_IDX0 | RST_DRX_IDX0); +} + +static int mtk_eth_start(struct udevice *dev) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + int ret; + + /* Reset FE */ + reset_assert(&priv->rst_fe); + udelay(1000); + reset_deassert(&priv->rst_fe); + mdelay(10); + + /* Packets forward to PDMA */ + mtk_gdma_write(priv, priv->gmac_id, GDMA_IG_CTRL_REG, GDMA_FWD_TO_CPU); + + if (priv->gmac_id == 0) + mtk_gdma_write(priv, 1, GDMA_IG_CTRL_REG, GDMA_FWD_DISCARD); + else + mtk_gdma_write(priv, 0, GDMA_IG_CTRL_REG, GDMA_FWD_DISCARD); + + udelay(500); + + mtk_eth_fifo_init(priv); + + /* Start PHY */ + if (priv->sw == SW_NONE) { + ret = mtk_phy_start(priv); + if (ret) + return ret; + } + + mtk_pdma_rmw(priv, PDMA_GLO_CFG_REG, 0, + TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN); + udelay(500); + + return 0; +} + +static void mtk_eth_stop(struct udevice *dev) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + + mtk_pdma_rmw(priv, PDMA_GLO_CFG_REG, + TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN, 0); + udelay(500); + + wait_for_bit_le32(priv->fe_base + PDMA_BASE + PDMA_GLO_CFG_REG, + RX_DMA_BUSY | TX_DMA_BUSY, 0, 5000, 0); +} + +static int mtk_eth_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct mtk_eth_priv *priv = dev_get_priv(dev); + unsigned char *mac = pdata->enetaddr; + u32 macaddr_lsb, macaddr_msb; + + macaddr_msb = ((u32)mac[0] << 8) | (u32)mac[1]; + macaddr_lsb = ((u32)mac[2] << 24) | ((u32)mac[3] << 16) | + ((u32)mac[4] << 8) | (u32)mac[5]; + + mtk_gdma_write(priv, priv->gmac_id, GDMA_MAC_MSB_REG, macaddr_msb); + mtk_gdma_write(priv, priv->gmac_id, GDMA_MAC_LSB_REG, macaddr_lsb); + + return 0; +} + +static int mtk_eth_send(struct udevice *dev, void *packet, int length) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + u32 idx = priv->tx_cpu_owner_idx0; + void *pkt_base; + + if (!priv->tx_ring_noc[idx].txd_info2.DDONE) { + debug("mtk-eth: TX DMA descriptor ring is full\n"); + return -EPERM; + } + + pkt_base = (void *)phys_to_virt(priv->tx_ring_noc[idx].txd_info1.SDP0); + memcpy(pkt_base, packet, length); + flush_dcache_range((u32)pkt_base, (u32)pkt_base + + roundup(length, ARCH_DMA_MINALIGN)); + + priv->tx_ring_noc[idx].txd_info2.SDL0 = length; + priv->tx_ring_noc[idx].txd_info2.DDONE = 0; + + priv->tx_cpu_owner_idx0 = (priv->tx_cpu_owner_idx0 + 1) % NUM_TX_DESC; + mtk_pdma_write(priv, TX_CTX_IDX_REG(0), priv->tx_cpu_owner_idx0); + + return 0; +} + +static int mtk_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + u32 idx = priv->rx_dma_owner_idx0; + uchar *pkt_base; + u32 length; + + if (!priv->rx_ring_noc[idx].rxd_info2.DDONE) { + debug("mtk-eth: RX DMA descriptor ring is empty\n"); + return -EAGAIN; + } + + length = priv->rx_ring_noc[idx].rxd_info2.PLEN0; + pkt_base = (void *)phys_to_virt(priv->rx_ring_noc[idx].rxd_info1.PDP0); + invalidate_dcache_range((u32)pkt_base, (u32)pkt_base + + roundup(length, ARCH_DMA_MINALIGN)); + + if (packetp) + *packetp = pkt_base; + + return length; +} + +static int mtk_eth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + u32 idx = priv->rx_dma_owner_idx0; + + priv->rx_ring_noc[idx].rxd_info2.DDONE = 0; + priv->rx_ring_noc[idx].rxd_info2.LS0 = 0; + priv->rx_ring_noc[idx].rxd_info2.PLEN0 = PKTSIZE_ALIGN; + + mtk_pdma_write(priv, RX_CRX_IDX_REG(0), idx); + priv->rx_dma_owner_idx0 = (priv->rx_dma_owner_idx0 + 1) % NUM_RX_DESC; + + return 0; +} + +static int mtk_eth_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct mtk_eth_priv *priv = dev_get_priv(dev); + u32 iobase = pdata->iobase; + int ret; + + /* Frame Engine Register Base */ + priv->fe_base = (void *)iobase; + + /* GMAC Register Base */ + priv->gmac_base = (void *)(iobase + GMAC_BASE); + + /* MDIO register */ + ret = mtk_mdio_register(dev); + if (ret) + return ret; + + /* Prepare for tx/rx rings */ + priv->tx_ring_noc = (struct pdma_txdesc *) + noncached_alloc(sizeof(struct pdma_txdesc) * NUM_TX_DESC, + ARCH_DMA_MINALIGN); + priv->rx_ring_noc = (struct pdma_rxdesc *) + noncached_alloc(sizeof(struct pdma_rxdesc) * NUM_RX_DESC, + ARCH_DMA_MINALIGN); + + /* Set MAC mode */ + mtk_mac_init(priv); + + /* Probe phy if switch is not specified */ + if (priv->sw == SW_NONE) + return mtk_phy_probe(dev); + + /* Initialize switch */ + return priv->switch_init(priv); +} + +static int mtk_eth_remove(struct udevice *dev) +{ + struct mtk_eth_priv *priv = dev_get_priv(dev); + + /* MDIO unregister */ + mdio_unregister(priv->mdio_bus); + mdio_free(priv->mdio_bus); + + /* Stop possibly started DMA */ + mtk_eth_stop(dev); + + return 0; +} + +static int mtk_eth_ofdata_to_platdata(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(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; + + priv->soc = dev_get_driver_data(dev); + + pdata->iobase = devfdt_get_addr(dev); + + /* get corresponding ethsys phandle */ + ret = dev_read_phandle_with_args(dev, "mediatek,ethsys", NULL, 0, 0, + &args); + if (ret) + return ret; + + regmap = syscon_node_to_regmap(args.node); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + priv->ethsys_base = regmap_get_range(regmap, 0); + if (!priv->ethsys_base) { + dev_err(dev, "Unable to find ethsys\n"); + return -ENODEV; + } + + /* Reset controllers */ + ret = reset_get_by_name(dev, "fe", &priv->rst_fe); + if (ret) { + printf("error: Unable to get reset ctrl for frame engine\n"); + return ret; + } + + priv->gmac_id = dev_read_u32_default(dev, "mediatek,gmac-id", 0); + + /* Interface mode is required */ + str = dev_read_string(dev, "phy-mode"); + if (str) { + pdata->phy_interface = phy_get_interface_by_name(str); + priv->phy_interface = pdata->phy_interface; + } else { + printf("error: phy-mode is not set\n"); + return -EINVAL; + } + + /* Force mode or autoneg */ + subnode = ofnode_find_subnode(dev_ofnode(dev), "fixed-link"); + if (ofnode_valid(subnode)) { + priv->force_mode = 1; + priv->speed = ofnode_read_u32_default(subnode, "speed", 0); + priv->duplex = ofnode_read_bool(subnode, "full-duplex"); + + if (priv->speed != SPEED_10 && priv->speed != SPEED_100 && + priv->speed != SPEED_1000) { + printf("error: no valid speed set in fixed-link\n"); + return -EINVAL; + } + } + + /* check for switch first, otherwise phy will be used */ + priv->sw = SW_NONE; + priv->switch_init = NULL; + str = dev_read_string(dev, "mediatek,switch"); + + if (str) { + if (!strcmp(str, "mt7530")) { + priv->sw = SW_MT7530; + priv->switch_init = mt7530_setup; + priv->mt7530_smi_addr = MT7530_DFL_SMI_ADDR; + } else { + printf("error: unsupported switch\n"); + return -EINVAL; + } + + priv->mcm = dev_read_bool(dev, "mediatek,mcm"); + if (priv->mcm) { + ret = reset_get_by_name(dev, "mcm", &priv->rst_mcm); + if (ret) { + printf("error: no reset ctrl for mcm\n"); + return ret; + } + } else { + gpio_request_by_name(dev, "reset-gpios", 0, + &priv->rst_gpio, GPIOD_IS_OUT); + } + } else { + subnode = ofnode_find_subnode(dev_ofnode(dev), "phy-handle"); + if (!ofnode_valid(subnode)) { + printf("error: phy-handle is not specified\n"); + return ret; + } + + priv->phy_addr = ofnode_read_s32_default(subnode, "reg", -1); + if (priv->phy_addr < 0) { + printf("error: phy address is not specified\n"); + return ret; + } + } + + return 0; +} + +static const struct udevice_id mtk_eth_ids[] = { + { .compatible = "mediatek,mt7629-eth", .data = SOC_MT7629 }, + { .compatible = "mediatek,mt7623-eth", .data = SOC_MT7623 }, + {} +}; + +static const struct eth_ops mtk_eth_ops = { + .start = mtk_eth_start, + .stop = mtk_eth_stop, + .send = mtk_eth_send, + .recv = mtk_eth_recv, + .free_pkt = mtk_eth_free_pkt, + .write_hwaddr = mtk_eth_write_hwaddr, +}; + +U_BOOT_DRIVER(mtk_eth) = { + .name = "mtk-eth", + .id = UCLASS_ETH, + .of_match = mtk_eth_ids, + .ofdata_to_platdata = mtk_eth_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct eth_pdata), + .probe = mtk_eth_probe, + .remove = mtk_eth_remove, + .ops = &mtk_eth_ops, + .priv_auto_alloc_size = sizeof(struct mtk_eth_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; |