blob: d89d6da82e22ac9cb68cd1244f9a0b2a22e2cabc [file] [log] [blame]
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* MSM EMAC Ethernet Controller Hardware support
*/
#include <linux/crc32.h>
#include <linux/if_vlan.h>
#include <linux/jiffies.h>
#include <linux/phy.h>
#include "emac_hw.h"
#include "emac_ptp.h"
#define RFD_PREF_LOW_TH 0x10
#define RFD_PREF_UP_TH 0x10
#define JUMBO_1KAH 0x4
#define RXF_DOF_TH 0x0be
#define RXF_UOF_TH 0x1a0
#define RXD_TH 0x100
static int emac_hw_sgmii_setup_link(struct emac_hw *hw, u32 speed,
bool autoneg, bool fc);
/* RGMII specific macros */
#define EMAC_RGMII_PLL_LOCK_TIMEOUT (HZ / 1000) /* 1ms */
#define EMAC_RGMII_CORE_IE_C 0x2001
#define EMAC_RGMII_PLL_L_VAL 0x14
#define EMAC_RGMII_PHY_MODE 0
/* REG */
u32 emac_reg_r32(struct emac_hw *hw, u8 base, u32 reg)
{
return readl_relaxed(hw->reg_addr[base] + reg);
}
void emac_reg_w32(struct emac_hw *hw, u8 base, u32 reg, u32 val)
{
writel_relaxed(val, hw->reg_addr[base] + reg);
}
void emac_reg_update32(struct emac_hw *hw, u8 base, u32 reg, u32 mask, u32 val)
{
u32 data;
data = emac_reg_r32(hw, base, reg);
emac_reg_w32(hw, base, reg, ((data & ~mask) | val));
}
u32 emac_reg_field_r32(struct emac_hw *hw, u8 base, u32 reg,
u32 mask, u32 shift)
{
u32 data;
data = emac_reg_r32(hw, base, reg);
return (data & mask) >> shift;
}
/* PHY */
static int emac_disable_mdio_autopoll(struct emac_hw *hw)
{
u32 i, val;
emac_reg_update32(hw, EMAC, EMAC_MDIO_CTRL, MDIO_AP_EN, 0);
wmb(); /* ensure mdio autopoll disable is requested */
/* wait for any mdio polling to complete */
for (i = 0; i < MDIO_WAIT_TIMES; i++) {
val = emac_reg_r32(hw, EMAC, EMAC_MDIO_CTRL);
if (!(val & MDIO_BUSY))
return 0;
udelay(100);
}
/* failed to disable; ensure it is enabled before returning */
emac_reg_update32(hw, EMAC, EMAC_MDIO_CTRL, 0, MDIO_AP_EN);
wmb(); /* ensure mdio autopoll is enabled */
return -EBUSY;
}
static void emac_enable_mdio_autopoll(struct emac_hw *hw)
{
emac_reg_update32(hw, EMAC, EMAC_MDIO_CTRL, 0, MDIO_AP_EN);
wmb(); /* ensure mdio autopoll is enabled */
}
int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast,
u16 reg_addr, u16 *phy_data)
{
u32 i, clk_sel, val = 0;
int retval = 0;
*phy_data = 0;
clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28;
if (emac_hw_get_adap(hw)->no_ephy == false) {
retval = emac_disable_mdio_autopoll(hw);
if (retval)
return retval;
}
emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK,
(dev << PHY_ADDR_SHFT));
wmb(); /* ensure PHY address is set before we proceed */
if (ext) {
val = ((dev << DEVAD_SHFT) & DEVAD_BMSK) |
((reg_addr << EX_REG_ADDR_SHFT) & EX_REG_ADDR_BMSK);
emac_reg_w32(hw, EMAC, EMAC_MDIO_EX_CTRL, val);
wmb(); /* ensure proper address is set before proceeding */
val = SUP_PREAMBLE |
((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
MDIO_START | MDIO_MODE | MDIO_RD_NWR;
} else {
val = val & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK |
MDIO_MODE | MDIO_PR);
val = SUP_PREAMBLE |
((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
((reg_addr << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
MDIO_START | MDIO_RD_NWR;
}
emac_reg_w32(hw, EMAC, EMAC_MDIO_CTRL, val);
mb(); /* ensure hw starts the operation before we check for result */
for (i = 0; i < MDIO_WAIT_TIMES; i++) {
val = emac_reg_r32(hw, EMAC, EMAC_MDIO_CTRL);
if (!(val & (MDIO_START | MDIO_BUSY))) {
*phy_data = (u16)((val >> MDIO_DATA_SHFT) &
MDIO_DATA_BMSK);
break;
}
udelay(100);
}
if (i == MDIO_WAIT_TIMES)
retval = -EIO;
if (emac_hw_get_adap(hw)->no_ephy == false)
emac_enable_mdio_autopoll(hw);
return retval;
}
int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev,
bool fast, u16 reg_addr, u16 phy_data)
{
u32 i, clk_sel, val = 0;
int retval = 0;
clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28;
if (emac_hw_get_adap(hw)->no_ephy == false) {
retval = emac_disable_mdio_autopoll(hw);
if (retval)
return retval;
}
emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK,
(dev << PHY_ADDR_SHFT));
wmb(); /* ensure PHY address is set before we proceed */
if (ext) {
val = ((dev << DEVAD_SHFT) & DEVAD_BMSK) |
((reg_addr << EX_REG_ADDR_SHFT) & EX_REG_ADDR_BMSK);
emac_reg_w32(hw, EMAC, EMAC_MDIO_EX_CTRL, val);
wmb(); /* ensure proper address is set before proceeding */
val = SUP_PREAMBLE |
((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
((phy_data << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
MDIO_START | MDIO_MODE;
} else {
val = val & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK |
MDIO_DATA_BMSK | MDIO_MODE | MDIO_PR);
val = SUP_PREAMBLE |
((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
((reg_addr << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
((phy_data << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
MDIO_START;
}
emac_reg_w32(hw, EMAC, EMAC_MDIO_CTRL, val);
mb(); /* ensure hw starts the operation before we check for result */
for (i = 0; i < MDIO_WAIT_TIMES; i++) {
val = emac_reg_r32(hw, EMAC, EMAC_MDIO_CTRL);
if (!(val & (MDIO_START | MDIO_BUSY)))
break;
udelay(100);
}
if (i == MDIO_WAIT_TIMES)
retval = -EIO;
if (emac_hw_get_adap(hw)->no_ephy == false)
emac_enable_mdio_autopoll(hw);
return retval;
}
int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr,
u16 reg_addr, u16 *phy_data)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
unsigned long flags;
int retval;
spin_lock_irqsave(&hw->mdio_lock, flags);
retval = emac_hw_read_phy_reg(hw, false, phy_addr, true,
reg_addr, phy_data);
spin_unlock_irqrestore(&hw->mdio_lock, flags);
if (retval)
emac_err(adpt, "error reading phy reg 0x%02x\n", reg_addr);
else
emac_dbg(adpt, hw, "EMAC PHY RD: 0x%02x -> 0x%04x\n", reg_addr,
*phy_data);
return retval;
}
int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr,
u16 reg_addr, u16 phy_data)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
unsigned long flags;
int retval;
spin_lock_irqsave(&hw->mdio_lock, flags);
retval = emac_hw_write_phy_reg(hw, false, phy_addr, true,
reg_addr, phy_data);
spin_unlock_irqrestore(&hw->mdio_lock, flags);
if (retval)
emac_err(adpt, "error writing phy reg 0x%02x\n", reg_addr);
else
emac_dbg(adpt, hw, "EMAC PHY WR: 0x%02x <- 0x%04x\n", reg_addr,
phy_data);
return retval;
}
int emac_hw_init_sgmii(struct emac_hw *hw)
{
int i;
emac_hw_sgmii_setup_link(hw, hw->autoneg_advertised,
hw->autoneg, !hw->disable_fc_autoneg);
/* PCS programming */
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_CDR_CTRL0,
SGMII_CDR_MAX_CNT);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_CMN_PWR_CTRL,
BIAS_EN | SYSCLK_EN | CLKBUF_L_EN |
PLL_TXCLK_EN | PLL_RXCLK_EN);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_TX_PWR_CTRL,
L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_RX_PWR_CTRL,
L0_RX_SIGDET_EN |
(1 << L0_RX_TERM_MODE_SHFT) | L0_RX_I_EN);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_CMN_PWR_CTRL,
BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN |
PLL_TXCLK_EN | PLL_RXCLK_EN);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_LANE_CTRL1,
L0_RX_EQ_EN | L0_RESET_TSYNC_EN | L0_DRV_LVL_BMSK);
wmb();
/* sysclk/refclk setting */
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_SYSCLK_EN_SEL,
SYSCLK_SEL_CMOS);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_SYS_CLK_CTRL,
SYSCLK_CM | SYSCLK_AC_COUPLE);
/* PLL setting */
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLL_IP_SETI,
QSERDES_PLL_IPSETI);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLL_CP_SETI,
QSERDES_PLL_CP_SETI);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLL_IP_SETP,
QSERDES_PLL_IP_SETP);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLL_CP_SETP,
QSERDES_PLL_CP_SETP);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLL_CRCTRL,
QSERDES_PLL_CRCTRL);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLL_CNTRL,
OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_DEC_START1,
DEC_START1_MUX | QSERDES_PLL_DEC);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_DEC_START2,
DEC_START2_MUX | DEC_START2);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_DIV_FRAC_START1,
DIV_FRAC_START1_MUX | QSERDES_PLL_DIV_FRAC_START1);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_DIV_FRAC_START2,
DIV_FRAC_START2_MUX | QSERDES_PLL_DIV_FRAC_START2);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_DIV_FRAC_START3,
DIV_FRAC_START3_MUX | QSERDES_PLL_DIV_FRAC_START3);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLLLOCK_CMP1,
QSERDES_PLL_LOCK_CMP1);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLLLOCK_CMP2,
QSERDES_PLL_LOCK_CMP2);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLLLOCK_CMP3,
QSERDES_PLL_LOCK_CMP3);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_PLLLOCK_CMP_EN,
PLLLOCK_CMP_EN);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_RESETSM_CNTRL,
FRQ_TUNE_MODE);
/* CDR setting */
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_RX_CDR_CONTROL,
SECONDORDERENABLE |
(QSERDES_RX_CDR_CTRL1_THRESH << FIRSTORDER_THRESH_SHFT) |
(QSERDES_RX_CDR_CTRL1_GAIN << SECONDORDERGAIN_SHFT));
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_RX_CDR_CONTROL2,
SECONDORDERENABLE |
(QSERDES_RX_CDR_CTRL2_THRESH << FIRSTORDER_THRESH_SHFT) |
(QSERDES_RX_CDR_CTRL2_GAIN << SECONDORDERGAIN_SHFT));
/* TX/RX setting */
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_TX_BIST_MODE_LANENO,
QSERDES_TX_BIST_MODE_LANENO);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_TX_TX_DRV_LVL,
TX_DRV_LVL_MUX | (QSERDES_TX_DRV_LVL << TX_DRV_LVL_SHFT));
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN,
EMP_EN_MUX | EMP_EN);
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_TX_TX_EMP_POST1_LVL,
TX_EMP_POST1_LVL_MUX |
(QSERDES_TX_EMP_POST1_LVL << TX_EMP_POST1_LVL_SHFT));
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_RX_RX_EQ_GAIN12,
(QSERDES_RX_EQ_GAIN2 << RX_EQ_GAIN2_SHFT) |
(QSERDES_RX_EQ_GAIN1 << RX_EQ_GAIN1_SHFT));
emac_reg_w32(hw, EMAC_QSERDES, EMAC_QSERDES_TX_LANE_MODE,
QSERDES_TX_LANE_MODE);
wmb();
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_SERDES_START,
SERDES_START);
wmb();
for (i = 0; i < SERDES_START_WAIT_TIMES; i++) {
if (emac_reg_r32(hw, EMAC_QSERDES, EMAC_QSERDES_COM_RESET_SM) &
QSERDES_READY)
break;
usleep_range(100, 200);
}
if (i == SERDES_START_WAIT_TIMES) {
emac_err(emac_hw_get_adap(hw), "serdes failed to start\n");
return -EIO;
}
/* Mask out all the SGMII Interrupt */
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_INTERRUPT_MASK, 0);
wmb();
emac_hw_clear_sgmii_intr_status(hw, SGMII_PHY_INTERRUPT_ERR);
return 0;
}
int emac_hw_reset_sgmii(struct emac_hw *hw)
{
/* It may take about 100ms to reset the SGMII PHY*/
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
PHY_RESET, PHY_RESET);
wmb();
msleep(50);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, PHY_RESET, 0);
wmb();
msleep(50);
return emac_hw_init_sgmii(hw);
}
/* initialize RGMII PHY */
static int emac_hw_init_rgmii(struct emac_hw *hw)
{
u32 val;
unsigned long timeout;
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1, 0, FREQ_MODE);
emac_reg_w32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR18,
EMAC_RGMII_CORE_IE_C);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
RGMII_PHY_MODE_BMSK,
(EMAC_RGMII_PHY_MODE << RGMII_PHY_MODE_SHFT));
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, PHY_RESET, 0);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3,
PLL_L_VAL_5_0_BMSK,
(EMAC_RGMII_PLL_L_VAL << PLL_L_VAL_5_0_SHFT));
/* reset PHY PLL and ensure PLL is reset */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3, 0, PLL_RESET);
wmb();
udelay(10);
/* power down analog sections of PLL and ensure the same */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3, 0, BYPASSNL);
wmb();
udelay(10);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, CKEDGE_SEL);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
TX_ID_EN_L, RX_ID_EN_L);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
HDRIVE_BMSK, (0x0 << HDRIVE_SHFT));
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, WOL_EN, 0);
/* reset PHY and ensure reset is complete */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, PHY_RESET);
wmb();
udelay(10);
/* pull PHY out of reset and ensure PHY is normal */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, PHY_RESET, 0);
wmb();
udelay(1000);
/* pull PHY PLL out of reset and ensure PLL is working */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3, PLL_RESET, 0);
wmb();
udelay(10);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR5,
0, RMII_125_CLK_EN);
wmb();
/* wait for PLL to lock */
timeout = jiffies + EMAC_RGMII_PLL_LOCK_TIMEOUT;
do {
val = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_STATUS);
if (val & PLL_LOCK_DET)
break;
udelay(100);
} while (time_after_eq(timeout, jiffies));
if (time_after(jiffies, timeout)) {
emac_err(emac_hw_get_adap(hw), "PHY PLL lock failed\n");
return -EIO;
}
return 0;
}
/* initialize phy */
int emac_hw_init_phy(struct emac_hw *hw)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
int retval = 0;
spin_lock_init(&hw->mdio_lock);
hw->autoneg = true;
hw->autoneg_advertised = EMAC_LINK_SPEED_DEFAULT;
if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII)
retval = emac_hw_init_sgmii(hw);
else if (adpt->phy_mode == PHY_INTERFACE_MODE_RGMII)
retval = emac_hw_init_rgmii(hw);
return retval;
}
/* initialize external phy */
int emac_hw_init_ephy(struct emac_hw *hw)
{
u16 val, phy_id[2];
int retval = 0;
if (emac_hw_get_adap(hw)->no_ephy == false) {
retval = emac_read_phy_reg(hw, hw->phy_addr,
MII_PHYSID1, &phy_id[0]);
if (retval)
return retval;
retval = emac_read_phy_reg(hw, hw->phy_addr,
MII_PHYSID2, &phy_id[1]);
if (retval)
return retval;
hw->phy_id[0] = phy_id[0];
hw->phy_id[1] = phy_id[1];
} else {
emac_disable_mdio_autopoll(hw);
}
/* disable hibernation in case of rgmii phy */
if (emac_hw_get_adap(hw)->phy_mode == PHY_INTERFACE_MODE_RGMII) {
retval = emac_write_phy_reg(hw, hw->phy_addr,
MII_DBG_ADDR, HIBERNATE_CTRL_REG);
if (retval)
return retval;
retval = emac_read_phy_reg(hw, hw->phy_addr,
MII_DBG_DATA, &val);
if (retval)
return retval;
val &= ~HIBERNATE_EN;
retval = emac_write_phy_reg(hw, hw->phy_addr,
MII_DBG_DATA, val);
}
return retval;
}
/* LINK */
static int emac_hw_sgmii_setup_link(struct emac_hw *hw, u32 speed,
bool autoneg, bool fc)
{
u32 val;
u32 speed_cfg = 0;
val = emac_reg_r32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_AUTONEG_CFG2);
if (autoneg) {
val &= ~(FORCE_AN_RX_CFG | FORCE_AN_TX_CFG);
val |= AN_ENABLE;
emac_reg_w32(hw, EMAC_SGMII_PHY,
EMAC_SGMII_PHY_AUTONEG_CFG2, val);
wmb();
} else {
switch (speed) {
case EMAC_LINK_SPEED_10_HALF:
speed_cfg = SPDMODE_10;
break;
case EMAC_LINK_SPEED_10_FULL:
speed_cfg = SPDMODE_10 | DUPLEX_MODE;
break;
case EMAC_LINK_SPEED_100_HALF:
speed_cfg = SPDMODE_100;
break;
case EMAC_LINK_SPEED_100_FULL:
speed_cfg = SPDMODE_100 | DUPLEX_MODE;
break;
case EMAC_LINK_SPEED_1GB_FULL:
speed_cfg = SPDMODE_1000 | DUPLEX_MODE;
break;
default:
return -EINVAL;
}
val &= ~AN_ENABLE;
emac_reg_w32(hw, EMAC_SGMII_PHY,
EMAC_SGMII_PHY_SPEED_CFG1, speed_cfg);
emac_reg_w32(hw, EMAC_SGMII_PHY,
EMAC_SGMII_PHY_AUTONEG_CFG2, val);
wmb();
}
return 0;
}
static int emac_hw_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg,
bool fc)
{
u16 adv, bmcr, ctrl1000 = 0;
int retval = 0;
if (autoneg) {
switch (hw->req_fc_mode) {
case emac_fc_full:
case emac_fc_rx_pause:
adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
break;
case emac_fc_tx_pause:
adv = ADVERTISE_PAUSE_ASYM;
break;
default:
adv = 0;
break;
}
if (!fc)
adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
if (speed & EMAC_LINK_SPEED_10_HALF)
adv |= ADVERTISE_10HALF;
if (speed & EMAC_LINK_SPEED_10_FULL)
adv |= ADVERTISE_10HALF | ADVERTISE_10FULL;
if (speed & EMAC_LINK_SPEED_100_HALF)
adv |= ADVERTISE_100HALF;
if (speed & EMAC_LINK_SPEED_100_FULL)
adv |= ADVERTISE_100HALF | ADVERTISE_100FULL;
if (speed & EMAC_LINK_SPEED_1GB_FULL)
ctrl1000 |= ADVERTISE_1000FULL;
retval |= emac_write_phy_reg(hw, hw->phy_addr,
MII_ADVERTISE, adv);
retval |= emac_write_phy_reg(hw, hw->phy_addr,
MII_CTRL1000, ctrl1000);
bmcr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART;
retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr);
} else {
bmcr = BMCR_RESET;
switch (speed) {
case EMAC_LINK_SPEED_10_HALF:
bmcr |= BMCR_SPEED10;
break;
case EMAC_LINK_SPEED_10_FULL:
bmcr |= BMCR_SPEED10 | BMCR_FULLDPLX;
break;
case EMAC_LINK_SPEED_100_HALF:
bmcr |= BMCR_SPEED100;
break;
case EMAC_LINK_SPEED_100_FULL:
bmcr |= BMCR_SPEED100 | BMCR_FULLDPLX;
break;
default:
return -EINVAL;
}
retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr);
}
return retval;
}
int emac_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg, bool fc)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
int retval = 0;
if (adpt->no_ephy == true) {
if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII) {
hw->autoneg = autoneg;
hw->autoneg_advertised = speed;
/* The AN_ENABLE and SPEED_CFG can't change on fly.
The SGMII_PHY has to be re-initialized.
*/
return emac_hw_reset_sgmii(hw);
} else {
emac_err(adpt, "can't setup phy link without ephy\n");
return -ENOTSUPP;
}
}
if (emac_hw_setup_phy_link(hw, speed, autoneg, fc)) {
emac_err(adpt, "error when init phy speed and fc\n");
retval = -EINVAL;
} else {
hw->autoneg = autoneg;
}
return retval;
}
int emac_setup_phy_link_speed(struct emac_hw *hw, u32 speed,
bool autoneg, bool fc)
{
/* update speed based on input link speed */
hw->autoneg_advertised = speed & EMAC_LINK_SPEED_DEFAULT;
return emac_setup_phy_link(hw, hw->autoneg_advertised, autoneg, fc);
}
int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
u16 bmsr, pssr;
int retval;
if (adpt->no_ephy == true) {
if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII) {
return emac_check_sgmii_link(hw, speed, link_up);
} else {
emac_err(adpt, "can't check phy link without ephy\n");
return -ENOTSUPP;
}
}
retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr);
if (retval)
return retval;
if (!(bmsr & BMSR_LSTATUS)) {
*link_up = false;
*speed = EMAC_LINK_SPEED_UNKNOWN;
return 0;
}
*link_up = true;
retval = emac_read_phy_reg(hw, hw->phy_addr, MII_PSSR, &pssr);
if (retval)
return retval;
if (!(pssr & PSSR_SPD_DPLX_RESOLVED)) {
emac_err(adpt, "error for speed duplex resolved\n");
return -EINVAL;
}
switch (pssr & PSSR_SPEED) {
case PSSR_1000MBS:
if (pssr & PSSR_DPLX)
*speed = EMAC_LINK_SPEED_1GB_FULL;
else
emac_err(adpt, "1000M half duplex is invalid");
break;
case PSSR_100MBS:
if (pssr & PSSR_DPLX)
*speed = EMAC_LINK_SPEED_100_FULL;
else
*speed = EMAC_LINK_SPEED_100_HALF;
break;
case PSSR_10MBS:
if (pssr & PSSR_DPLX)
*speed = EMAC_LINK_SPEED_10_FULL;
else
*speed = EMAC_LINK_SPEED_10_HALF;
break;
default:
*speed = EMAC_LINK_SPEED_UNKNOWN;
retval = -EINVAL;
break;
}
return retval;
}
int emac_hw_get_lpa_speed(struct emac_hw *hw, u32 *speed)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
int retval;
u16 lpa, stat1000;
bool link;
if (adpt->no_ephy == true) {
if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII) {
return emac_check_sgmii_link(hw, speed, &link);
} else {
emac_err(adpt, "can't get lpa speed without ephy\n");
return -ENOTSUPP;
}
}
retval = emac_read_phy_reg(hw, hw->phy_addr, MII_LPA, &lpa);
retval |= emac_read_phy_reg(hw, hw->phy_addr, MII_STAT1000, &stat1000);
if (retval)
return retval;
*speed = EMAC_LINK_SPEED_10_HALF;
if (lpa & LPA_10FULL)
*speed = EMAC_LINK_SPEED_10_FULL;
else if (lpa & LPA_10HALF)
*speed = EMAC_LINK_SPEED_10_HALF;
else if (lpa & LPA_100FULL)
*speed = EMAC_LINK_SPEED_100_FULL;
else if (lpa & LPA_100HALF)
*speed = EMAC_LINK_SPEED_100_HALF;
else if (stat1000 & LPA_1000FULL)
*speed = EMAC_LINK_SPEED_1GB_FULL;
return 0;
}
int emac_hw_clear_sgmii_intr_status(struct emac_hw *hw, u32 irq_bits)
{
u32 status;
int i;
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_INTERRUPT_CLEAR,
irq_bits);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_IRQ_CMD,
IRQ_GLOBAL_CLEAR);
wmb();
/* After set the IRQ_GLOBAL_CLEAR bit, the status clearing must
* be confirmed before clear the bits in other registers.
* It takes a few cycles for hw to clear the interrupt status.
*/
for (i = 0; i < SGMII_PHY_IRQ_CLR_WAIT_TIME; i++) {
udelay(1);
status = emac_reg_r32(hw, EMAC_SGMII_PHY,
EMAC_SGMII_PHY_INTERRUPT_STATUS);
if (!(status & irq_bits))
break;
}
if (status & irq_bits) {
emac_err(emac_hw_get_adap(hw),
"failed to clear SGMII irq: status 0x%x bits 0x%x\n",
status, irq_bits);
return -EIO;
}
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_IRQ_CMD, 0);
emac_reg_w32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_INTERRUPT_CLEAR, 0);
wmb();
return 0;
}
int emac_check_sgmii_link(struct emac_hw *hw, u32 *speed, bool *link_up)
{
u32 val;
val = emac_reg_r32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_AUTONEG_CFG2);
if (val & AN_ENABLE)
return emac_check_sgmii_autoneg(hw, speed, link_up);
val = emac_reg_r32(hw, EMAC_SGMII_PHY, EMAC_SGMII_PHY_SPEED_CFG1);
val &= DUPLEX_MODE | SPDMODE_BMSK;
switch (val) {
case DUPLEX_MODE | SPDMODE_1000:
*speed = EMAC_LINK_SPEED_1GB_FULL;
break;
case DUPLEX_MODE | SPDMODE_100:
*speed = EMAC_LINK_SPEED_100_FULL;
break;
case SPDMODE_100:
*speed = EMAC_LINK_SPEED_100_HALF;
break;
case DUPLEX_MODE | SPDMODE_10:
*speed = EMAC_LINK_SPEED_10_FULL;
break;
case SPDMODE_10:
*speed = EMAC_LINK_SPEED_10_HALF;
break;
default:
*speed = EMAC_LINK_SPEED_UNKNOWN;
break;
}
*link_up = true;
return 0;
}
int emac_check_sgmii_autoneg(struct emac_hw *hw, u32 *speed, bool *link_up)
{
u32 status;
status = emac_reg_r32(hw, EMAC_SGMII_PHY,
EMAC_SGMII_PHY_AUTONEG1_STATUS) & 0xff;
status <<= 8;
status |= emac_reg_r32(hw, EMAC_SGMII_PHY,
EMAC_SGMII_PHY_AUTONEG0_STATUS) & 0xff;
if (!(status & TXCFG_LINK)) {
*link_up = false;
*speed = EMAC_LINK_SPEED_UNKNOWN;
return 0;
}
*link_up = true;
switch (status & TXCFG_MODE_BMSK) {
case TXCFG_1000_FULL:
*speed = EMAC_LINK_SPEED_1GB_FULL;
break;
case TXCFG_100_FULL:
*speed = EMAC_LINK_SPEED_100_FULL;
break;
case TXCFG_100_HALF:
*speed = EMAC_LINK_SPEED_100_HALF;
break;
case TXCFG_10_FULL:
*speed = EMAC_LINK_SPEED_10_FULL;
break;
case TXCFG_10_HALF:
*speed = EMAC_LINK_SPEED_10_HALF;
break;
default:
*speed = EMAC_LINK_SPEED_UNKNOWN;
break;
}
return 0;
}
/* INTR */
void emac_hw_enable_intr(struct emac_hw *hw)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
int i;
for (i = 0; i < EMAC_NUM_CORE_IRQ; i++) {
struct emac_irq_per_dev *irq = &adpt->irq[i];
const struct emac_irq_common *irq_cmn = &emac_irq_cmn_tbl[i];
emac_reg_w32(hw, EMAC, irq_cmn->status_reg, ~DIS_INT);
emac_reg_w32(hw, EMAC, irq_cmn->mask_reg, irq->mask);
}
if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII)
emac_reg_w32(hw, EMAC_SGMII_PHY,
emac_irq_cmn_tbl[EMAC_SGMII_PHY_IRQ].mask_reg,
adpt->irq[EMAC_SGMII_PHY_IRQ].mask);
if (adpt->tstamp_en)
emac_reg_w32(hw, EMAC_1588, EMAC_P1588_PTP_EXPANDED_INT_MASK,
hw->ptp_intr_mask);
wmb();
}
void emac_hw_disable_intr(struct emac_hw *hw)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
int i;
for (i = 0; i < EMAC_NUM_CORE_IRQ; i++) {
const struct emac_irq_common *irq_cmn = &emac_irq_cmn_tbl[i];
emac_reg_w32(hw, EMAC, irq_cmn->status_reg, DIS_INT);
emac_reg_w32(hw, EMAC, irq_cmn->mask_reg, 0);
}
if (adpt->tstamp_en)
emac_reg_w32(hw, EMAC_1588, EMAC_P1588_PTP_EXPANDED_INT_MASK,
0);
if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII)
emac_reg_w32(hw, EMAC_SGMII_PHY,
emac_irq_cmn_tbl[EMAC_SGMII_PHY_IRQ].mask_reg, 0);
wmb();
}
/* MC */
void emac_hw_set_mc_addr(struct emac_hw *hw, u8 *addr)
{
u32 crc32, bit, reg, mta;
/* Calculate the CRC of the MAC address */
crc32 = ether_crc(ETH_ALEN, addr);
/* The HASH Table is an array of 2 32-bit registers. It is
* treated like an array of 64 bits (BitArray[hash_value]).
* Use the upper 6 bits of the above CRC as the hash value.
*/
reg = (crc32 >> 31) & 0x1;
bit = (crc32 >> 26) & 0x1F;
mta = emac_reg_r32(hw, EMAC, EMAC_HASH_TAB_REG0 + (reg << 2));
mta |= (0x1 << bit);
emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG0 + (reg << 2), mta);
wmb();
}
void emac_hw_clear_mc_addr(struct emac_hw *hw)
{
emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG0, 0);
emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG1, 0);
wmb();
}
/* definitions for RSS */
#define EMAC_RSS_KEY(_i, _type) \
(EMAC_RSS_KEY0 + ((_i) * sizeof(_type)))
#define EMAC_RSS_TBL(_i, _type) \
(EMAC_IDT_TABLE0 + ((_i) * sizeof(_type)))
/* RSS */
void emac_hw_config_rss(struct emac_hw *hw)
{
int key_len_by_u32 = sizeof(hw->rss_key) / sizeof(u32);
int idt_len_by_u32 = sizeof(hw->rss_idt) / sizeof(u32);
u32 rxq0;
int i;
/* Fill out hash function keys */
for (i = 0; i < key_len_by_u32; i++) {
u32 key, idx_base;
idx_base = (key_len_by_u32 - i) * 4;
key = ((hw->rss_key[idx_base - 1]) |
(hw->rss_key[idx_base - 2] << 8) |
(hw->rss_key[idx_base - 3] << 16) |
(hw->rss_key[idx_base - 4] << 24));
emac_reg_w32(hw, EMAC, EMAC_RSS_KEY(i, u32), key);
}
/* Fill out redirection table */
for (i = 0; i < idt_len_by_u32; i++)
emac_reg_w32(hw, EMAC, EMAC_RSS_TBL(i, u32), hw->rss_idt[i]);
emac_reg_w32(hw, EMAC, EMAC_BASE_CPU_NUMBER, hw->rss_base_cpu);
rxq0 = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_0);
if (hw->rss_hstype & EMAC_RSS_HSTYP_IPV4_EN)
rxq0 |= RXQ0_RSS_HSTYP_IPV4_EN;
else
rxq0 &= ~RXQ0_RSS_HSTYP_IPV4_EN;
if (hw->rss_hstype & EMAC_RSS_HSTYP_TCP4_EN)
rxq0 |= RXQ0_RSS_HSTYP_IPV4_TCP_EN;
else
rxq0 &= ~RXQ0_RSS_HSTYP_IPV4_TCP_EN;
if (hw->rss_hstype & EMAC_RSS_HSTYP_IPV6_EN)
rxq0 |= RXQ0_RSS_HSTYP_IPV6_EN;
else
rxq0 &= ~RXQ0_RSS_HSTYP_IPV6_EN;
if (hw->rss_hstype & EMAC_RSS_HSTYP_TCP6_EN)
rxq0 |= RXQ0_RSS_HSTYP_IPV6_TCP_EN;
else
rxq0 &= ~RXQ0_RSS_HSTYP_IPV6_TCP_EN;
rxq0 |= ((hw->rss_idt_size << IDT_TABLE_SIZE_SHFT) &
IDT_TABLE_SIZE_BMSK);
rxq0 |= RSS_HASH_EN;
wmb(); /* ensure all parameters are written before we enable RSS */
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_0, rxq0);
wmb();
}
/* Config MAC modes */
void emac_hw_config_mac_ctrl(struct emac_hw *hw)
{
u32 mac;
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
if (TEST_FLAG(hw, HW_VLANSTRIP_EN))
mac |= VLAN_STRIP;
else
mac &= ~VLAN_STRIP;
if (TEST_FLAG(hw, HW_PROMISC_EN))
mac |= PROM_MODE;
else
mac &= ~PROM_MODE;
if (TEST_FLAG(hw, HW_MULTIALL_EN))
mac |= MULTI_ALL;
else
mac &= ~MULTI_ALL;
if (TEST_FLAG(hw, HW_LOOPBACK_EN))
mac |= MAC_LP_EN;
else
mac &= ~MAC_LP_EN;
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
wmb();
}
/* Wake On LAN (WOL) */
void emac_hw_config_wol(struct emac_hw *hw, u32 wufc)
{
u32 wol = 0;
/* turn on magic packet event */
if (wufc & EMAC_WOL_MAGIC)
wol |= MG_FRAME_EN | MG_FRAME_PME | WK_FRAME_EN;
/* turn on link up event */
if (wufc & EMAC_WOL_PHY)
wol |= LK_CHG_EN | LK_CHG_PME;
emac_reg_w32(hw, EMAC, EMAC_WOL_CTRL0, wol);
wmb();
}
/* Power Management */
void emac_hw_config_pow_save(struct emac_hw *hw, u32 speed,
bool wol_en, bool rx_en)
{
u32 dma_mas, mac;
dma_mas = emac_reg_r32(hw, EMAC, EMAC_DMA_MAS_CTRL);
dma_mas &= ~LPW_CLK_SEL;
dma_mas |= LPW_STATE;
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
mac &= ~(FULLD | RXEN | TXEN);
mac = (mac & ~SPEED_BMSK) |
(((u32)emac_mac_speed_10_100 << SPEED_SHFT) & SPEED_BMSK);
if (wol_en) {
if (rx_en)
mac |= (RXEN | BROAD_EN);
/* If WOL is enabled, set link speed/duplex for mac */
if (EMAC_LINK_SPEED_1GB_FULL == speed)
mac = (mac & ~SPEED_BMSK) |
(((u32)emac_mac_speed_1000 << SPEED_SHFT) &
SPEED_BMSK);
if (EMAC_LINK_SPEED_10_FULL == speed ||
EMAC_LINK_SPEED_100_FULL == speed ||
EMAC_LINK_SPEED_1GB_FULL == speed)
mac |= FULLD;
} else {
/* select lower clock speed if WOL is disabled */
dma_mas |= LPW_CLK_SEL;
}
emac_reg_w32(hw, EMAC, EMAC_DMA_MAS_CTRL, dma_mas);
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
wmb();
}
/* Config descriptor rings */
static void emac_hw_config_ring_ctrl(struct emac_hw *hw)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
if (adpt->tstamp_en) {
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1,
0, ENABLE_RRD_TIMESTAMP);
}
/* TPD */
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_1,
EMAC_DMA_ADDR_HI(adpt->tx_queue[0].tpd.tpdma));
switch (adpt->num_txques) {
case 4:
emac_reg_w32(hw, EMAC, EMAC_H3TPD_BASE_ADDR_LO,
EMAC_DMA_ADDR_LO(adpt->tx_queue[3].tpd.tpdma));
case 3:
emac_reg_w32(hw, EMAC, EMAC_H2TPD_BASE_ADDR_LO,
EMAC_DMA_ADDR_LO(adpt->tx_queue[2].tpd.tpdma));
case 2:
emac_reg_w32(hw, EMAC, EMAC_H1TPD_BASE_ADDR_LO,
EMAC_DMA_ADDR_LO(adpt->tx_queue[1].tpd.tpdma));
case 1:
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_8,
EMAC_DMA_ADDR_LO(adpt->tx_queue[0].tpd.tpdma));
break;
default:
emac_err(adpt, "Invalid number of TX queues (%d)\n",
adpt->num_txques);
return;
}
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_9,
adpt->tx_queue[0].tpd.count & TPD_RING_SIZE_BMSK);
/* RFD & RRD */
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_0,
EMAC_DMA_ADDR_HI(adpt->rx_queue[0].rfd.rfdma));
switch (adpt->num_rxques) {
case 4:
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_13,
EMAC_DMA_ADDR_LO(adpt->rx_queue[3].rfd.rfdma));
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_16,
EMAC_DMA_ADDR_LO(adpt->rx_queue[3].rrd.rrdma));
case 3:
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_12,
EMAC_DMA_ADDR_LO(adpt->rx_queue[2].rfd.rfdma));
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_15,
EMAC_DMA_ADDR_LO(adpt->rx_queue[2].rrd.rrdma));
case 2:
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_10,
EMAC_DMA_ADDR_LO(adpt->rx_queue[1].rfd.rfdma));
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_14,
EMAC_DMA_ADDR_LO(adpt->rx_queue[1].rrd.rrdma));
case 1:
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_2,
EMAC_DMA_ADDR_LO(adpt->rx_queue[0].rfd.rfdma));
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_5,
EMAC_DMA_ADDR_LO(adpt->rx_queue[0].rrd.rrdma));
break;
default:
emac_err(adpt, "Invalid number of RX queues (%d)\n",
adpt->num_rxques);
return;
}
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_3,
adpt->rx_queue[0].rfd.count & RFD_RING_SIZE_BMSK);
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_6,
adpt->rx_queue[0].rrd.count & RRD_RING_SIZE_BMSK);
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_4,
adpt->rxbuf_size & RX_BUFFER_SIZE_BMSK);
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_11, 0);
wmb(); /* ensure all parameters are written before we enable them */
/* Load all of base address above */
emac_reg_w32(hw, EMAC, EMAC_INTER_SRAM_PART9, 1);
wmb();
}
/* Config transmit parameters */
static void emac_hw_config_tx_ctrl(struct emac_hw *hw)
{
u16 tx_offload_thresh = EMAC_MAX_TX_OFFLOAD_THRESH;
u32 val;
emac_reg_w32(hw, EMAC, EMAC_TXQ_CTRL_1,
(tx_offload_thresh >> 3) & JUMBO_TASK_OFFLOAD_THRESHOLD_BMSK);
val = (hw->tpd_burst << NUM_TPD_BURST_PREF_SHFT) &
NUM_TPD_BURST_PREF_BMSK;
val |= (TXQ_MODE | LS_8023_SP);
val |= (0x0100 << NUM_TXF_BURST_PREF_SHFT) &
NUM_TXF_BURST_PREF_BMSK;
emac_reg_w32(hw, EMAC, EMAC_TXQ_CTRL_0, val);
emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_2,
(TXF_HWM_BMSK | TXF_LWM_BMSK), 0);
wmb();
}
/* Config receive parameters */
static void emac_hw_config_rx_ctrl(struct emac_hw *hw)
{
u32 val;
val = ((hw->rfd_burst << NUM_RFD_BURST_PREF_SHFT) &
NUM_RFD_BURST_PREF_BMSK);
val |= (SP_IPV6 | CUT_THRU_EN);
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_0, val);
val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_1);
val &= ~(JUMBO_1KAH_BMSK | RFD_PREF_LOW_THRESHOLD_BMSK |
RFD_PREF_UP_THRESHOLD_BMSK);
val |= (JUMBO_1KAH << JUMBO_1KAH_SHFT) |
(RFD_PREF_LOW_TH << RFD_PREF_LOW_THRESHOLD_SHFT) |
(RFD_PREF_UP_TH << RFD_PREF_UP_THRESHOLD_SHFT);
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_1, val);
val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_2);
val &= ~(RXF_DOF_THRESHOLD_BMSK | RXF_UOF_THRESHOLD_BMSK);
val |= (RXF_DOF_TH << RXF_DOF_THRESHOLD_SHFT) |
(RXF_UOF_TH << RXF_UOF_THRESHOLD_SHFT);
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_2, val);
val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_3);
val &= ~(RXD_TIMER_BMSK | RXD_THRESHOLD_BMSK);
val |= RXD_TH << RXD_THRESHOLD_SHFT;
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_3, val);
wmb();
}
/* Config dma */
static void emac_hw_config_dma_ctrl(struct emac_hw *hw)
{
u32 dma_ctrl;
dma_ctrl = DMAR_REQ_PRI;
switch (hw->dma_order) {
case emac_dma_ord_in:
dma_ctrl |= IN_ORDER_MODE;
break;
case emac_dma_ord_enh:
dma_ctrl |= ENH_ORDER_MODE;
break;
case emac_dma_ord_out:
dma_ctrl |= OUT_ORDER_MODE;
break;
default:
break;
}
dma_ctrl |= (((u32)hw->dmar_block) << REGRDBLEN_SHFT) &
REGRDBLEN_BMSK;
dma_ctrl |= (((u32)hw->dmaw_block) << REGWRBLEN_SHFT) &
REGWRBLEN_BMSK;
dma_ctrl |= (((u32)hw->dmar_dly_cnt) << DMAR_DLY_CNT_SHFT) &
DMAR_DLY_CNT_BMSK;
dma_ctrl |= (((u32) hw->dmaw_dly_cnt) << DMAW_DLY_CNT_SHFT) &
DMAW_DLY_CNT_BMSK;
emac_reg_w32(hw, EMAC, EMAC_DMA_CTRL, dma_ctrl);
wmb();
}
/* Flow Control (fc) */
static int emac_get_fc_mode(struct emac_hw *hw, enum emac_fc_mode *mode)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
u16 i, bmsr = 0, pssr = 0;
int retval = 0;
for (i = 0; i < EMAC_MAX_SETUP_LNK_CYCLE; i++) {
retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr);
if (retval)
return retval;
if (bmsr & BMSR_LSTATUS) {
retval = emac_read_phy_reg(hw, hw->phy_addr,
MII_PSSR, &pssr);
if (retval)
return retval;
if (!(pssr & PSSR_SPD_DPLX_RESOLVED)) {
emac_err(adpt,
"error for speed duplex resolved\n");
return -EINVAL;
}
if ((pssr & PSSR_FC_TXEN) &&
(pssr & PSSR_FC_RXEN)) {
*mode = (hw->req_fc_mode == emac_fc_full) ?
emac_fc_full : emac_fc_rx_pause;
} else if (pssr & PSSR_FC_TXEN) {
*mode = emac_fc_tx_pause;
} else if (pssr & PSSR_FC_RXEN) {
*mode = emac_fc_rx_pause;
} else {
*mode = emac_fc_none;
}
break;
}
msleep(100); /* link can take upto few seconds to come up */
}
if (i == EMAC_MAX_SETUP_LNK_CYCLE) {
emac_err(adpt, "error when get flow control mode\n");
retval = -EINVAL;
}
return retval;
}
int emac_hw_config_fc(struct emac_hw *hw)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
u32 mac;
int retval;
if (hw->disable_fc_autoneg || adpt->no_ephy) {
hw->cur_fc_mode = hw->req_fc_mode;
} else {
retval = emac_get_fc_mode(hw, &hw->cur_fc_mode);
if (retval)
return retval;
}
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
switch (hw->cur_fc_mode) {
case emac_fc_none:
mac &= ~(RXFC | TXFC);
break;
case emac_fc_rx_pause:
mac &= ~TXFC;
mac |= RXFC;
break;
case emac_fc_tx_pause:
mac |= TXFC;
mac &= ~RXFC;
break;
case emac_fc_full:
case emac_fc_default:
mac |= (TXFC | RXFC);
break;
default:
emac_err(adpt, "flow control param set incorrectly\n");
return -EINVAL;
}
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
wmb();
return 0;
}
/* Configure MAC */
void emac_hw_config_mac(struct emac_hw *hw)
{
u32 val;
emac_hw_set_mac_addr(hw, hw->mac_addr);
emac_hw_config_ring_ctrl(hw);
emac_reg_w32(hw, EMAC, EMAC_MAX_FRAM_LEN_CTRL,
hw->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);
emac_hw_config_tx_ctrl(hw);
emac_hw_config_rx_ctrl(hw);
emac_hw_config_dma_ctrl(hw);
if (TEST_FLAG(hw, HW_PTP_CAP))
emac_ptp_config(hw);
val = emac_reg_r32(hw, EMAC, EMAC_AXI_MAST_CTRL);
val &= ~(DATA_BYTE_SWAP | MAX_BOUND);
val |= MAX_BTYPE;
emac_reg_w32(hw, EMAC, EMAC_AXI_MAST_CTRL, val);
emac_reg_w32(hw, EMAC, EMAC_CLK_GATE_CTRL, 0);
emac_reg_w32(hw, EMAC, EMAC_MISC_CTRL, RX_UNCPL_INT_EN);
wmb();
}
/* Reset MAC */
void emac_hw_reset_mac(struct emac_hw *hw)
{
emac_reg_w32(hw, EMAC, EMAC_INT_MASK, 0);
emac_reg_w32(hw, EMAC, EMAC_INT_STATUS, DIS_INT);
emac_hw_stop_mac(hw);
emac_reg_update32(hw, EMAC, EMAC_DMA_MAS_CTRL, 0, SOFT_RST);
wmb(); /* ensure mac is fully reset */
udelay(100);
emac_reg_update32(hw, EMAC, EMAC_DMA_MAS_CTRL, 0, INT_RD_CLR_EN);
wmb();
}
/* Start MAC */
void emac_hw_start_mac(struct emac_hw *hw)
{
struct emac_adapter *adpt = emac_hw_get_adap(hw);
u32 mac, csr1;
/* enable tx queue */
if (adpt->num_txques && (adpt->num_txques <= EMAC_MAX_TX_QUEUES))
emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_0, 0, TXQ_EN);
/* enable rx queue */
if (adpt->num_rxques && (adpt->num_rxques <= EMAC_MAX_RX_QUEUES))
emac_reg_update32(hw, EMAC, EMAC_RXQ_CTRL_0, 0, RXQ_EN);
/* enable mac control */
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
csr1 = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1);
mac |= TXEN | RXEN; /* enable RX/TX */
/* enable RX/TX Flow Control */
switch (hw->cur_fc_mode) {
case emac_fc_full:
mac |= (TXFC | RXFC);
break;
case emac_fc_rx_pause:
mac |= RXFC;
break;
case emac_fc_tx_pause:
mac |= TXFC;
break;
default:
break;
}
/* setup link speed */
mac &= ~SPEED_BMSK;
switch (hw->link_speed) {
case EMAC_LINK_SPEED_1GB_FULL:
mac |= ((emac_mac_speed_1000 << SPEED_SHFT) & SPEED_BMSK);
csr1 |= FREQ_MODE;
break;
default:
mac |= ((emac_mac_speed_10_100 << SPEED_SHFT) & SPEED_BMSK);
csr1 &= ~FREQ_MODE;
break;
}
switch (hw->link_speed) {
case EMAC_LINK_SPEED_1GB_FULL:
case EMAC_LINK_SPEED_100_FULL:
case EMAC_LINK_SPEED_10_FULL:
mac |= FULLD;
break;
default:
mac &= ~FULLD;
}
/* other parameters */
mac |= (CRCE | PCRCE);
mac |= ((hw->preamble << PRLEN_SHFT) & PRLEN_BMSK);
mac |= BROAD_EN;
mac |= FLCHK;
mac &= ~RX_CHKSUM_EN;
mac &= ~(HUGEN | VLAN_STRIP | TPAUSE | SIMR | HUGE | MULTI_ALL |
DEBUG_MODE | SINGLE_PAUSE_MODE);
emac_reg_w32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1, csr1);
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
/* enable interrupt read clear, low power sleep mode and
the irq moderators
*/
emac_reg_w32(hw, EMAC, EMAC_IRQ_MOD_TIM_INIT, hw->irq_mod);
emac_reg_w32(hw, EMAC, EMAC_DMA_MAS_CTRL,
(INT_RD_CLR_EN | LPW_MODE |
IRQ_MODERATOR_EN | IRQ_MODERATOR2_EN));
if (TEST_FLAG(hw, HW_PTP_CAP))
emac_ptp_set_linkspeed(hw, hw->link_speed);
emac_hw_config_mac_ctrl(hw);
emac_reg_update32(hw, EMAC, EMAC_ATHR_HEADER_CTRL,
(HEADER_ENABLE | HEADER_CNT_EN), 0);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, WOL_EN);
wmb();
}
/* Stop MAC */
void emac_hw_stop_mac(struct emac_hw *hw)
{
emac_reg_update32(hw, EMAC, EMAC_RXQ_CTRL_0, RXQ_EN, 0);
emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_0, TXQ_EN, 0);
emac_reg_update32(hw, EMAC, EMAC_MAC_CTRL, (TXEN | RXEN), 0);
wmb(); /* make sure mac is stopped before we proceede */
udelay(1000);
}
/* set MAC address */
void emac_hw_set_mac_addr(struct emac_hw *hw, u8 *addr)
{
u32 sta;
/* for example: 00-A0-C6-11-22-33
* 0<-->C6112233, 1<-->00A0.
*/
/* low dword */
sta = (((u32)addr[2]) << 24) | (((u32)addr[3]) << 16) |
(((u32)addr[4]) << 8) | (((u32)addr[5]));
emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR0, sta);
/* hight dword */
sta = (((u32)addr[0]) << 8) | (((u32)addr[1]));
emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR1, sta);
wmb();
}
/* Read one entry from the HW tx timestamp FIFO */
bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts)
{
u32 ts_idx;
ts_idx = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_INX);
if (ts_idx & EMAC_WRAPPER_TX_TS_EMPTY)
return false;
ts->ns = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_LO);
ts->sec = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_HI);
ts->ts_idx = ts_idx & EMAC_WRAPPER_TX_TS_INX_BMSK;
return true;
}