mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
dc39a78b3c
Add msi-x support to the alx driver. This is in preparation for multi queue support. msi-x interrupts are disabled by default because without multi queue support there is no advantage over msi interrupts. The performance numbers observed with iperf stay the same. Based on information of the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery <tobias.regnery@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1125 lines
32 KiB
C
1125 lines
32 KiB
C
/*
|
|
* Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net>
|
|
*
|
|
* This file is free software: you may copy, redistribute and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This file 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* This file incorporates work covered by the following copyright and
|
|
* permission notice:
|
|
*
|
|
* Copyright (c) 2012 Qualcomm Atheros, Inc.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/mdio.h>
|
|
#include "reg.h"
|
|
#include "hw.h"
|
|
|
|
static inline bool alx_is_rev_a(u8 rev)
|
|
{
|
|
return rev == ALX_REV_A0 || rev == ALX_REV_A1;
|
|
}
|
|
|
|
static int alx_wait_mdio_idle(struct alx_hw *hw)
|
|
{
|
|
u32 val;
|
|
int i;
|
|
|
|
for (i = 0; i < ALX_MDIO_MAX_AC_TO; i++) {
|
|
val = alx_read_mem32(hw, ALX_MDIO);
|
|
if (!(val & ALX_MDIO_BUSY))
|
|
return 0;
|
|
udelay(10);
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
static int alx_read_phy_core(struct alx_hw *hw, bool ext, u8 dev,
|
|
u16 reg, u16 *phy_data)
|
|
{
|
|
u32 val, clk_sel;
|
|
int err;
|
|
|
|
*phy_data = 0;
|
|
|
|
/* use slow clock when it's in hibernation status */
|
|
clk_sel = hw->link_speed != SPEED_UNKNOWN ?
|
|
ALX_MDIO_CLK_SEL_25MD4 :
|
|
ALX_MDIO_CLK_SEL_25MD128;
|
|
|
|
if (ext) {
|
|
val = dev << ALX_MDIO_EXTN_DEVAD_SHIFT |
|
|
reg << ALX_MDIO_EXTN_REG_SHIFT;
|
|
alx_write_mem32(hw, ALX_MDIO_EXTN, val);
|
|
|
|
val = ALX_MDIO_SPRES_PRMBL | ALX_MDIO_START |
|
|
ALX_MDIO_MODE_EXT | ALX_MDIO_OP_READ |
|
|
clk_sel << ALX_MDIO_CLK_SEL_SHIFT;
|
|
} else {
|
|
val = ALX_MDIO_SPRES_PRMBL |
|
|
clk_sel << ALX_MDIO_CLK_SEL_SHIFT |
|
|
reg << ALX_MDIO_REG_SHIFT |
|
|
ALX_MDIO_START | ALX_MDIO_OP_READ;
|
|
}
|
|
alx_write_mem32(hw, ALX_MDIO, val);
|
|
|
|
err = alx_wait_mdio_idle(hw);
|
|
if (err)
|
|
return err;
|
|
val = alx_read_mem32(hw, ALX_MDIO);
|
|
*phy_data = ALX_GET_FIELD(val, ALX_MDIO_DATA);
|
|
return 0;
|
|
}
|
|
|
|
static int alx_write_phy_core(struct alx_hw *hw, bool ext, u8 dev,
|
|
u16 reg, u16 phy_data)
|
|
{
|
|
u32 val, clk_sel;
|
|
|
|
/* use slow clock when it's in hibernation status */
|
|
clk_sel = hw->link_speed != SPEED_UNKNOWN ?
|
|
ALX_MDIO_CLK_SEL_25MD4 :
|
|
ALX_MDIO_CLK_SEL_25MD128;
|
|
|
|
if (ext) {
|
|
val = dev << ALX_MDIO_EXTN_DEVAD_SHIFT |
|
|
reg << ALX_MDIO_EXTN_REG_SHIFT;
|
|
alx_write_mem32(hw, ALX_MDIO_EXTN, val);
|
|
|
|
val = ALX_MDIO_SPRES_PRMBL |
|
|
clk_sel << ALX_MDIO_CLK_SEL_SHIFT |
|
|
phy_data << ALX_MDIO_DATA_SHIFT |
|
|
ALX_MDIO_START | ALX_MDIO_MODE_EXT;
|
|
} else {
|
|
val = ALX_MDIO_SPRES_PRMBL |
|
|
clk_sel << ALX_MDIO_CLK_SEL_SHIFT |
|
|
reg << ALX_MDIO_REG_SHIFT |
|
|
phy_data << ALX_MDIO_DATA_SHIFT |
|
|
ALX_MDIO_START;
|
|
}
|
|
alx_write_mem32(hw, ALX_MDIO, val);
|
|
|
|
return alx_wait_mdio_idle(hw);
|
|
}
|
|
|
|
static int __alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data)
|
|
{
|
|
return alx_read_phy_core(hw, false, 0, reg, phy_data);
|
|
}
|
|
|
|
static int __alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data)
|
|
{
|
|
return alx_write_phy_core(hw, false, 0, reg, phy_data);
|
|
}
|
|
|
|
static int __alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata)
|
|
{
|
|
return alx_read_phy_core(hw, true, dev, reg, pdata);
|
|
}
|
|
|
|
static int __alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data)
|
|
{
|
|
return alx_write_phy_core(hw, true, dev, reg, data);
|
|
}
|
|
|
|
static int __alx_read_phy_dbg(struct alx_hw *hw, u16 reg, u16 *pdata)
|
|
{
|
|
int err;
|
|
|
|
err = __alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, reg);
|
|
if (err)
|
|
return err;
|
|
|
|
return __alx_read_phy_reg(hw, ALX_MII_DBG_DATA, pdata);
|
|
}
|
|
|
|
static int __alx_write_phy_dbg(struct alx_hw *hw, u16 reg, u16 data)
|
|
{
|
|
int err;
|
|
|
|
err = __alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, reg);
|
|
if (err)
|
|
return err;
|
|
|
|
return __alx_write_phy_reg(hw, ALX_MII_DBG_DATA, data);
|
|
}
|
|
|
|
int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data)
|
|
{
|
|
int err;
|
|
|
|
spin_lock(&hw->mdio_lock);
|
|
err = __alx_read_phy_reg(hw, reg, phy_data);
|
|
spin_unlock(&hw->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data)
|
|
{
|
|
int err;
|
|
|
|
spin_lock(&hw->mdio_lock);
|
|
err = __alx_write_phy_reg(hw, reg, phy_data);
|
|
spin_unlock(&hw->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata)
|
|
{
|
|
int err;
|
|
|
|
spin_lock(&hw->mdio_lock);
|
|
err = __alx_read_phy_ext(hw, dev, reg, pdata);
|
|
spin_unlock(&hw->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data)
|
|
{
|
|
int err;
|
|
|
|
spin_lock(&hw->mdio_lock);
|
|
err = __alx_write_phy_ext(hw, dev, reg, data);
|
|
spin_unlock(&hw->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int alx_read_phy_dbg(struct alx_hw *hw, u16 reg, u16 *pdata)
|
|
{
|
|
int err;
|
|
|
|
spin_lock(&hw->mdio_lock);
|
|
err = __alx_read_phy_dbg(hw, reg, pdata);
|
|
spin_unlock(&hw->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int alx_write_phy_dbg(struct alx_hw *hw, u16 reg, u16 data)
|
|
{
|
|
int err;
|
|
|
|
spin_lock(&hw->mdio_lock);
|
|
err = __alx_write_phy_dbg(hw, reg, data);
|
|
spin_unlock(&hw->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
static u16 alx_get_phy_config(struct alx_hw *hw)
|
|
{
|
|
u32 val;
|
|
u16 phy_val;
|
|
|
|
val = alx_read_mem32(hw, ALX_PHY_CTRL);
|
|
/* phy in reset */
|
|
if ((val & ALX_PHY_CTRL_DSPRST_OUT) == 0)
|
|
return ALX_DRV_PHY_UNKNOWN;
|
|
|
|
val = alx_read_mem32(hw, ALX_DRV);
|
|
val = ALX_GET_FIELD(val, ALX_DRV_PHY);
|
|
if (ALX_DRV_PHY_UNKNOWN == val)
|
|
return ALX_DRV_PHY_UNKNOWN;
|
|
|
|
alx_read_phy_reg(hw, ALX_MII_DBG_ADDR, &phy_val);
|
|
if (ALX_PHY_INITED == phy_val)
|
|
return val;
|
|
|
|
return ALX_DRV_PHY_UNKNOWN;
|
|
}
|
|
|
|
static bool alx_wait_reg(struct alx_hw *hw, u32 reg, u32 wait, u32 *val)
|
|
{
|
|
u32 read;
|
|
int i;
|
|
|
|
for (i = 0; i < ALX_SLD_MAX_TO; i++) {
|
|
read = alx_read_mem32(hw, reg);
|
|
if ((read & wait) == 0) {
|
|
if (val)
|
|
*val = read;
|
|
return true;
|
|
}
|
|
mdelay(1);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool alx_read_macaddr(struct alx_hw *hw, u8 *addr)
|
|
{
|
|
u32 mac0, mac1;
|
|
|
|
mac0 = alx_read_mem32(hw, ALX_STAD0);
|
|
mac1 = alx_read_mem32(hw, ALX_STAD1);
|
|
|
|
/* addr should be big-endian */
|
|
put_unaligned(cpu_to_be32(mac0), (__be32 *)(addr + 2));
|
|
put_unaligned(cpu_to_be16(mac1), (__be16 *)addr);
|
|
|
|
return is_valid_ether_addr(addr);
|
|
}
|
|
|
|
int alx_get_perm_macaddr(struct alx_hw *hw, u8 *addr)
|
|
{
|
|
u32 val;
|
|
|
|
/* try to get it from register first */
|
|
if (alx_read_macaddr(hw, addr))
|
|
return 0;
|
|
|
|
/* try to load from efuse */
|
|
if (!alx_wait_reg(hw, ALX_SLD, ALX_SLD_STAT | ALX_SLD_START, &val))
|
|
return -EIO;
|
|
alx_write_mem32(hw, ALX_SLD, val | ALX_SLD_START);
|
|
if (!alx_wait_reg(hw, ALX_SLD, ALX_SLD_START, NULL))
|
|
return -EIO;
|
|
if (alx_read_macaddr(hw, addr))
|
|
return 0;
|
|
|
|
/* try to load from flash/eeprom (if present) */
|
|
val = alx_read_mem32(hw, ALX_EFLD);
|
|
if (val & (ALX_EFLD_F_EXIST | ALX_EFLD_E_EXIST)) {
|
|
if (!alx_wait_reg(hw, ALX_EFLD,
|
|
ALX_EFLD_STAT | ALX_EFLD_START, &val))
|
|
return -EIO;
|
|
alx_write_mem32(hw, ALX_EFLD, val | ALX_EFLD_START);
|
|
if (!alx_wait_reg(hw, ALX_EFLD, ALX_EFLD_START, NULL))
|
|
return -EIO;
|
|
if (alx_read_macaddr(hw, addr))
|
|
return 0;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
void alx_set_macaddr(struct alx_hw *hw, const u8 *addr)
|
|
{
|
|
u32 val;
|
|
|
|
/* for example: 00-0B-6A-F6-00-DC * STAD0=6AF600DC, STAD1=000B */
|
|
val = be32_to_cpu(get_unaligned((__be32 *)(addr + 2)));
|
|
alx_write_mem32(hw, ALX_STAD0, val);
|
|
val = be16_to_cpu(get_unaligned((__be16 *)addr));
|
|
alx_write_mem32(hw, ALX_STAD1, val);
|
|
}
|
|
|
|
static void alx_reset_osc(struct alx_hw *hw, u8 rev)
|
|
{
|
|
u32 val, val2;
|
|
|
|
/* clear Internal OSC settings, switching OSC by hw itself */
|
|
val = alx_read_mem32(hw, ALX_MISC3);
|
|
alx_write_mem32(hw, ALX_MISC3,
|
|
(val & ~ALX_MISC3_25M_BY_SW) |
|
|
ALX_MISC3_25M_NOTO_INTNL);
|
|
|
|
/* 25M clk from chipset may be unstable 1s after de-assert of
|
|
* PERST, driver need re-calibrate before enter Sleep for WoL
|
|
*/
|
|
val = alx_read_mem32(hw, ALX_MISC);
|
|
if (rev >= ALX_REV_B0) {
|
|
/* restore over current protection def-val,
|
|
* this val could be reset by MAC-RST
|
|
*/
|
|
ALX_SET_FIELD(val, ALX_MISC_PSW_OCP, ALX_MISC_PSW_OCP_DEF);
|
|
/* a 0->1 change will update the internal val of osc */
|
|
val &= ~ALX_MISC_INTNLOSC_OPEN;
|
|
alx_write_mem32(hw, ALX_MISC, val);
|
|
alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN);
|
|
/* hw will automatically dis OSC after cab. */
|
|
val2 = alx_read_mem32(hw, ALX_MSIC2);
|
|
val2 &= ~ALX_MSIC2_CALB_START;
|
|
alx_write_mem32(hw, ALX_MSIC2, val2);
|
|
alx_write_mem32(hw, ALX_MSIC2, val2 | ALX_MSIC2_CALB_START);
|
|
} else {
|
|
val &= ~ALX_MISC_INTNLOSC_OPEN;
|
|
/* disable isolate for rev A devices */
|
|
if (alx_is_rev_a(rev))
|
|
val &= ~ALX_MISC_ISO_EN;
|
|
|
|
alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN);
|
|
alx_write_mem32(hw, ALX_MISC, val);
|
|
}
|
|
|
|
udelay(20);
|
|
}
|
|
|
|
static int alx_stop_mac(struct alx_hw *hw)
|
|
{
|
|
u32 rxq, txq, val;
|
|
u16 i;
|
|
|
|
rxq = alx_read_mem32(hw, ALX_RXQ0);
|
|
alx_write_mem32(hw, ALX_RXQ0, rxq & ~ALX_RXQ0_EN);
|
|
txq = alx_read_mem32(hw, ALX_TXQ0);
|
|
alx_write_mem32(hw, ALX_TXQ0, txq & ~ALX_TXQ0_EN);
|
|
|
|
udelay(40);
|
|
|
|
hw->rx_ctrl &= ~(ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_TX_EN);
|
|
alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
|
|
|
|
for (i = 0; i < ALX_DMA_MAC_RST_TO; i++) {
|
|
val = alx_read_mem32(hw, ALX_MAC_STS);
|
|
if (!(val & ALX_MAC_STS_IDLE))
|
|
return 0;
|
|
udelay(10);
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
int alx_reset_mac(struct alx_hw *hw)
|
|
{
|
|
u32 val, pmctrl;
|
|
int i, ret;
|
|
u8 rev;
|
|
bool a_cr;
|
|
|
|
pmctrl = 0;
|
|
rev = alx_hw_revision(hw);
|
|
a_cr = alx_is_rev_a(rev) && alx_hw_with_cr(hw);
|
|
|
|
/* disable all interrupts, RXQ/TXQ */
|
|
alx_write_mem32(hw, ALX_MSIX_MASK, 0xFFFFFFFF);
|
|
alx_write_mem32(hw, ALX_IMR, 0);
|
|
alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS);
|
|
|
|
ret = alx_stop_mac(hw);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* mac reset workaroud */
|
|
alx_write_mem32(hw, ALX_RFD_PIDX, 1);
|
|
|
|
/* dis l0s/l1 before mac reset */
|
|
if (a_cr) {
|
|
pmctrl = alx_read_mem32(hw, ALX_PMCTRL);
|
|
if (pmctrl & (ALX_PMCTRL_L1_EN | ALX_PMCTRL_L0S_EN))
|
|
alx_write_mem32(hw, ALX_PMCTRL,
|
|
pmctrl & ~(ALX_PMCTRL_L1_EN |
|
|
ALX_PMCTRL_L0S_EN));
|
|
}
|
|
|
|
/* reset whole mac safely */
|
|
val = alx_read_mem32(hw, ALX_MASTER);
|
|
alx_write_mem32(hw, ALX_MASTER,
|
|
val | ALX_MASTER_DMA_MAC_RST | ALX_MASTER_OOB_DIS);
|
|
|
|
/* make sure it's real idle */
|
|
udelay(10);
|
|
for (i = 0; i < ALX_DMA_MAC_RST_TO; i++) {
|
|
val = alx_read_mem32(hw, ALX_RFD_PIDX);
|
|
if (val == 0)
|
|
break;
|
|
udelay(10);
|
|
}
|
|
for (; i < ALX_DMA_MAC_RST_TO; i++) {
|
|
val = alx_read_mem32(hw, ALX_MASTER);
|
|
if ((val & ALX_MASTER_DMA_MAC_RST) == 0)
|
|
break;
|
|
udelay(10);
|
|
}
|
|
if (i == ALX_DMA_MAC_RST_TO)
|
|
return -EIO;
|
|
udelay(10);
|
|
|
|
if (a_cr) {
|
|
alx_write_mem32(hw, ALX_MASTER, val | ALX_MASTER_PCLKSEL_SRDS);
|
|
/* restore l0s / l1 */
|
|
if (pmctrl & (ALX_PMCTRL_L1_EN | ALX_PMCTRL_L0S_EN))
|
|
alx_write_mem32(hw, ALX_PMCTRL, pmctrl);
|
|
}
|
|
|
|
alx_reset_osc(hw, rev);
|
|
|
|
/* clear Internal OSC settings, switching OSC by hw itself,
|
|
* disable isolate for rev A devices
|
|
*/
|
|
val = alx_read_mem32(hw, ALX_MISC3);
|
|
alx_write_mem32(hw, ALX_MISC3,
|
|
(val & ~ALX_MISC3_25M_BY_SW) |
|
|
ALX_MISC3_25M_NOTO_INTNL);
|
|
val = alx_read_mem32(hw, ALX_MISC);
|
|
val &= ~ALX_MISC_INTNLOSC_OPEN;
|
|
if (alx_is_rev_a(rev))
|
|
val &= ~ALX_MISC_ISO_EN;
|
|
alx_write_mem32(hw, ALX_MISC, val);
|
|
udelay(20);
|
|
|
|
/* driver control speed/duplex, hash-alg */
|
|
alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
|
|
|
|
val = alx_read_mem32(hw, ALX_SERDES);
|
|
alx_write_mem32(hw, ALX_SERDES,
|
|
val | ALX_SERDES_MACCLK_SLWDWN |
|
|
ALX_SERDES_PHYCLK_SLWDWN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void alx_reset_phy(struct alx_hw *hw)
|
|
{
|
|
int i;
|
|
u32 val;
|
|
u16 phy_val;
|
|
|
|
/* (DSP)reset PHY core */
|
|
val = alx_read_mem32(hw, ALX_PHY_CTRL);
|
|
val &= ~(ALX_PHY_CTRL_DSPRST_OUT | ALX_PHY_CTRL_IDDQ |
|
|
ALX_PHY_CTRL_GATE_25M | ALX_PHY_CTRL_POWER_DOWN |
|
|
ALX_PHY_CTRL_CLS);
|
|
val |= ALX_PHY_CTRL_RST_ANALOG;
|
|
|
|
val |= (ALX_PHY_CTRL_HIB_PULSE | ALX_PHY_CTRL_HIB_EN);
|
|
alx_write_mem32(hw, ALX_PHY_CTRL, val);
|
|
udelay(10);
|
|
alx_write_mem32(hw, ALX_PHY_CTRL, val | ALX_PHY_CTRL_DSPRST_OUT);
|
|
|
|
for (i = 0; i < ALX_PHY_CTRL_DSPRST_TO; i++)
|
|
udelay(10);
|
|
|
|
/* phy power saving & hib */
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_LEGCYPS, ALX_LEGCYPS_DEF);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_SYSMODCTRL,
|
|
ALX_SYSMODCTRL_IECHOADJ_DEF);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_VDRVBIAS,
|
|
ALX_VDRVBIAS_DEF);
|
|
|
|
/* EEE advertisement */
|
|
val = alx_read_mem32(hw, ALX_LPI_CTRL);
|
|
alx_write_mem32(hw, ALX_LPI_CTRL, val & ~ALX_LPI_CTRL_EN);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_LOCAL_EEEADV, 0);
|
|
|
|
/* phy power saving */
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_TST10BTCFG, ALX_TST10BTCFG_DEF);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_SRDSYSMOD, ALX_SRDSYSMOD_DEF);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_TST100BTCFG, ALX_TST100BTCFG_DEF);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_ANACTRL, ALX_ANACTRL_DEF);
|
|
alx_read_phy_dbg(hw, ALX_MIIDBG_GREENCFG2, &phy_val);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_GREENCFG2,
|
|
phy_val & ~ALX_GREENCFG2_GATE_DFSE_EN);
|
|
/* rtl8139c, 120m issue */
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_NLP78,
|
|
ALX_MIIEXT_NLP78_120M_DEF);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_S3DIG10,
|
|
ALX_MIIEXT_S3DIG10_DEF);
|
|
|
|
if (hw->lnk_patch) {
|
|
/* Turn off half amplitude */
|
|
alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL3,
|
|
&phy_val);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL3,
|
|
phy_val | ALX_CLDCTRL3_BP_CABLE1TH_DET_GT);
|
|
/* Turn off Green feature */
|
|
alx_read_phy_dbg(hw, ALX_MIIDBG_GREENCFG2, &phy_val);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_GREENCFG2,
|
|
phy_val | ALX_GREENCFG2_BP_GREEN);
|
|
/* Turn off half Bias */
|
|
alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL5,
|
|
&phy_val);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL5,
|
|
phy_val | ALX_CLDCTRL5_BP_VD_HLFBIAS);
|
|
}
|
|
|
|
/* set phy interrupt mask */
|
|
alx_write_phy_reg(hw, ALX_MII_IER, ALX_IER_LINK_UP | ALX_IER_LINK_DOWN);
|
|
}
|
|
|
|
#define ALX_PCI_CMD (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
|
|
|
|
void alx_reset_pcie(struct alx_hw *hw)
|
|
{
|
|
u8 rev = alx_hw_revision(hw);
|
|
u32 val;
|
|
u16 val16;
|
|
|
|
/* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
|
|
pci_read_config_word(hw->pdev, PCI_COMMAND, &val16);
|
|
if (!(val16 & ALX_PCI_CMD) || (val16 & PCI_COMMAND_INTX_DISABLE)) {
|
|
val16 = (val16 | ALX_PCI_CMD) & ~PCI_COMMAND_INTX_DISABLE;
|
|
pci_write_config_word(hw->pdev, PCI_COMMAND, val16);
|
|
}
|
|
|
|
/* clear WoL setting/status */
|
|
val = alx_read_mem32(hw, ALX_WOL0);
|
|
alx_write_mem32(hw, ALX_WOL0, 0);
|
|
|
|
val = alx_read_mem32(hw, ALX_PDLL_TRNS1);
|
|
alx_write_mem32(hw, ALX_PDLL_TRNS1, val & ~ALX_PDLL_TRNS1_D3PLLOFF_EN);
|
|
|
|
/* mask some pcie error bits */
|
|
val = alx_read_mem32(hw, ALX_UE_SVRT);
|
|
val &= ~(ALX_UE_SVRT_DLPROTERR | ALX_UE_SVRT_FCPROTERR);
|
|
alx_write_mem32(hw, ALX_UE_SVRT, val);
|
|
|
|
/* wol 25M & pclk */
|
|
val = alx_read_mem32(hw, ALX_MASTER);
|
|
if (alx_is_rev_a(rev) && alx_hw_with_cr(hw)) {
|
|
if ((val & ALX_MASTER_WAKEN_25M) == 0 ||
|
|
(val & ALX_MASTER_PCLKSEL_SRDS) == 0)
|
|
alx_write_mem32(hw, ALX_MASTER,
|
|
val | ALX_MASTER_PCLKSEL_SRDS |
|
|
ALX_MASTER_WAKEN_25M);
|
|
} else {
|
|
if ((val & ALX_MASTER_WAKEN_25M) == 0 ||
|
|
(val & ALX_MASTER_PCLKSEL_SRDS) != 0)
|
|
alx_write_mem32(hw, ALX_MASTER,
|
|
(val & ~ALX_MASTER_PCLKSEL_SRDS) |
|
|
ALX_MASTER_WAKEN_25M);
|
|
}
|
|
|
|
/* ASPM setting */
|
|
alx_enable_aspm(hw, true, true);
|
|
|
|
udelay(10);
|
|
}
|
|
|
|
void alx_start_mac(struct alx_hw *hw)
|
|
{
|
|
u32 mac, txq, rxq;
|
|
|
|
rxq = alx_read_mem32(hw, ALX_RXQ0);
|
|
alx_write_mem32(hw, ALX_RXQ0, rxq | ALX_RXQ0_EN);
|
|
txq = alx_read_mem32(hw, ALX_TXQ0);
|
|
alx_write_mem32(hw, ALX_TXQ0, txq | ALX_TXQ0_EN);
|
|
|
|
mac = hw->rx_ctrl;
|
|
if (hw->duplex == DUPLEX_FULL)
|
|
mac |= ALX_MAC_CTRL_FULLD;
|
|
else
|
|
mac &= ~ALX_MAC_CTRL_FULLD;
|
|
ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED,
|
|
hw->link_speed == SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 :
|
|
ALX_MAC_CTRL_SPEED_10_100);
|
|
mac |= ALX_MAC_CTRL_TX_EN | ALX_MAC_CTRL_RX_EN;
|
|
hw->rx_ctrl = mac;
|
|
alx_write_mem32(hw, ALX_MAC_CTRL, mac);
|
|
}
|
|
|
|
void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc)
|
|
{
|
|
if (fc & ALX_FC_RX)
|
|
hw->rx_ctrl |= ALX_MAC_CTRL_RXFC_EN;
|
|
else
|
|
hw->rx_ctrl &= ~ALX_MAC_CTRL_RXFC_EN;
|
|
|
|
if (fc & ALX_FC_TX)
|
|
hw->rx_ctrl |= ALX_MAC_CTRL_TXFC_EN;
|
|
else
|
|
hw->rx_ctrl &= ~ALX_MAC_CTRL_TXFC_EN;
|
|
|
|
alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl);
|
|
}
|
|
|
|
void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en)
|
|
{
|
|
u32 pmctrl;
|
|
u8 rev = alx_hw_revision(hw);
|
|
|
|
pmctrl = alx_read_mem32(hw, ALX_PMCTRL);
|
|
|
|
ALX_SET_FIELD(pmctrl, ALX_PMCTRL_LCKDET_TIMER,
|
|
ALX_PMCTRL_LCKDET_TIMER_DEF);
|
|
pmctrl |= ALX_PMCTRL_RCVR_WT_1US |
|
|
ALX_PMCTRL_L1_CLKSW_EN |
|
|
ALX_PMCTRL_L1_SRDSRX_PWD;
|
|
ALX_SET_FIELD(pmctrl, ALX_PMCTRL_L1REQ_TO, ALX_PMCTRL_L1REG_TO_DEF);
|
|
ALX_SET_FIELD(pmctrl, ALX_PMCTRL_L1_TIMER, ALX_PMCTRL_L1_TIMER_16US);
|
|
pmctrl &= ~(ALX_PMCTRL_L1_SRDS_EN |
|
|
ALX_PMCTRL_L1_SRDSPLL_EN |
|
|
ALX_PMCTRL_L1_BUFSRX_EN |
|
|
ALX_PMCTRL_SADLY_EN |
|
|
ALX_PMCTRL_HOTRST_WTEN|
|
|
ALX_PMCTRL_L0S_EN |
|
|
ALX_PMCTRL_L1_EN |
|
|
ALX_PMCTRL_ASPM_FCEN |
|
|
ALX_PMCTRL_TXL1_AFTER_L0S |
|
|
ALX_PMCTRL_RXL1_AFTER_L0S);
|
|
if (alx_is_rev_a(rev) && alx_hw_with_cr(hw))
|
|
pmctrl |= ALX_PMCTRL_L1_SRDS_EN | ALX_PMCTRL_L1_SRDSPLL_EN;
|
|
|
|
if (l0s_en)
|
|
pmctrl |= (ALX_PMCTRL_L0S_EN | ALX_PMCTRL_ASPM_FCEN);
|
|
if (l1_en)
|
|
pmctrl |= (ALX_PMCTRL_L1_EN | ALX_PMCTRL_ASPM_FCEN);
|
|
|
|
alx_write_mem32(hw, ALX_PMCTRL, pmctrl);
|
|
}
|
|
|
|
|
|
static u32 ethadv_to_hw_cfg(struct alx_hw *hw, u32 ethadv_cfg)
|
|
{
|
|
u32 cfg = 0;
|
|
|
|
if (ethadv_cfg & ADVERTISED_Autoneg) {
|
|
cfg |= ALX_DRV_PHY_AUTO;
|
|
if (ethadv_cfg & ADVERTISED_10baseT_Half)
|
|
cfg |= ALX_DRV_PHY_10;
|
|
if (ethadv_cfg & ADVERTISED_10baseT_Full)
|
|
cfg |= ALX_DRV_PHY_10 | ALX_DRV_PHY_DUPLEX;
|
|
if (ethadv_cfg & ADVERTISED_100baseT_Half)
|
|
cfg |= ALX_DRV_PHY_100;
|
|
if (ethadv_cfg & ADVERTISED_100baseT_Full)
|
|
cfg |= ALX_DRV_PHY_100 | ALX_DRV_PHY_DUPLEX;
|
|
if (ethadv_cfg & ADVERTISED_1000baseT_Half)
|
|
cfg |= ALX_DRV_PHY_1000;
|
|
if (ethadv_cfg & ADVERTISED_1000baseT_Full)
|
|
cfg |= ALX_DRV_PHY_100 | ALX_DRV_PHY_DUPLEX;
|
|
if (ethadv_cfg & ADVERTISED_Pause)
|
|
cfg |= ADVERTISE_PAUSE_CAP;
|
|
if (ethadv_cfg & ADVERTISED_Asym_Pause)
|
|
cfg |= ADVERTISE_PAUSE_ASYM;
|
|
} else {
|
|
switch (ethadv_cfg) {
|
|
case ADVERTISED_10baseT_Half:
|
|
cfg |= ALX_DRV_PHY_10;
|
|
break;
|
|
case ADVERTISED_100baseT_Half:
|
|
cfg |= ALX_DRV_PHY_100;
|
|
break;
|
|
case ADVERTISED_10baseT_Full:
|
|
cfg |= ALX_DRV_PHY_10 | ALX_DRV_PHY_DUPLEX;
|
|
break;
|
|
case ADVERTISED_100baseT_Full:
|
|
cfg |= ALX_DRV_PHY_100 | ALX_DRV_PHY_DUPLEX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return cfg;
|
|
}
|
|
|
|
int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl)
|
|
{
|
|
u16 adv, giga, cr;
|
|
u32 val;
|
|
int err = 0;
|
|
|
|
alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, 0);
|
|
val = alx_read_mem32(hw, ALX_DRV);
|
|
ALX_SET_FIELD(val, ALX_DRV_PHY, 0);
|
|
|
|
if (ethadv & ADVERTISED_Autoneg) {
|
|
adv = ADVERTISE_CSMA;
|
|
adv |= ethtool_adv_to_mii_adv_t(ethadv);
|
|
|
|
if (flowctrl & ALX_FC_ANEG) {
|
|
if (flowctrl & ALX_FC_RX) {
|
|
adv |= ADVERTISED_Pause;
|
|
if (!(flowctrl & ALX_FC_TX))
|
|
adv |= ADVERTISED_Asym_Pause;
|
|
} else if (flowctrl & ALX_FC_TX) {
|
|
adv |= ADVERTISED_Asym_Pause;
|
|
}
|
|
}
|
|
giga = 0;
|
|
if (alx_hw_giga(hw))
|
|
giga = ethtool_adv_to_mii_ctrl1000_t(ethadv);
|
|
|
|
cr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART;
|
|
|
|
if (alx_write_phy_reg(hw, MII_ADVERTISE, adv) ||
|
|
alx_write_phy_reg(hw, MII_CTRL1000, giga) ||
|
|
alx_write_phy_reg(hw, MII_BMCR, cr))
|
|
err = -EBUSY;
|
|
} else {
|
|
cr = BMCR_RESET;
|
|
if (ethadv == ADVERTISED_100baseT_Half ||
|
|
ethadv == ADVERTISED_100baseT_Full)
|
|
cr |= BMCR_SPEED100;
|
|
if (ethadv == ADVERTISED_10baseT_Full ||
|
|
ethadv == ADVERTISED_100baseT_Full)
|
|
cr |= BMCR_FULLDPLX;
|
|
|
|
err = alx_write_phy_reg(hw, MII_BMCR, cr);
|
|
}
|
|
|
|
if (!err) {
|
|
alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, ALX_PHY_INITED);
|
|
val |= ethadv_to_hw_cfg(hw, ethadv);
|
|
}
|
|
|
|
alx_write_mem32(hw, ALX_DRV, val);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
void alx_post_phy_link(struct alx_hw *hw)
|
|
{
|
|
u16 phy_val, len, agc;
|
|
u8 revid = alx_hw_revision(hw);
|
|
bool adj_th = revid == ALX_REV_B0;
|
|
|
|
if (revid != ALX_REV_B0 && !alx_is_rev_a(revid))
|
|
return;
|
|
|
|
/* 1000BT/AZ, wrong cable length */
|
|
if (hw->link_speed != SPEED_UNKNOWN) {
|
|
alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL6,
|
|
&phy_val);
|
|
len = ALX_GET_FIELD(phy_val, ALX_CLDCTRL6_CAB_LEN);
|
|
alx_read_phy_dbg(hw, ALX_MIIDBG_AGC, &phy_val);
|
|
agc = ALX_GET_FIELD(phy_val, ALX_AGC_2_VGA);
|
|
|
|
if ((hw->link_speed == SPEED_1000 &&
|
|
(len > ALX_CLDCTRL6_CAB_LEN_SHORT1G ||
|
|
(len == 0 && agc > ALX_AGC_LONG1G_LIMT))) ||
|
|
(hw->link_speed == SPEED_100 &&
|
|
(len > ALX_CLDCTRL6_CAB_LEN_SHORT100M ||
|
|
(len == 0 && agc > ALX_AGC_LONG100M_LIMT)))) {
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT,
|
|
ALX_AZ_ANADECT_LONG);
|
|
alx_read_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE,
|
|
&phy_val);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE,
|
|
phy_val | ALX_AFE_10BT_100M_TH);
|
|
} else {
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT,
|
|
ALX_AZ_ANADECT_DEF);
|
|
alx_read_phy_ext(hw, ALX_MIIEXT_ANEG,
|
|
ALX_MIIEXT_AFE, &phy_val);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE,
|
|
phy_val & ~ALX_AFE_10BT_100M_TH);
|
|
}
|
|
|
|
/* threshold adjust */
|
|
if (adj_th && hw->lnk_patch) {
|
|
if (hw->link_speed == SPEED_100) {
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB,
|
|
ALX_MSE16DB_UP);
|
|
} else if (hw->link_speed == SPEED_1000) {
|
|
/*
|
|
* Giga link threshold, raise the tolerance of
|
|
* noise 50%
|
|
*/
|
|
alx_read_phy_dbg(hw, ALX_MIIDBG_MSE20DB,
|
|
&phy_val);
|
|
ALX_SET_FIELD(phy_val, ALX_MSE20DB_TH,
|
|
ALX_MSE20DB_TH_HI);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_MSE20DB,
|
|
phy_val);
|
|
}
|
|
}
|
|
} else {
|
|
alx_read_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE,
|
|
&phy_val);
|
|
alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE,
|
|
phy_val & ~ALX_AFE_10BT_100M_TH);
|
|
|
|
if (adj_th && hw->lnk_patch) {
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB,
|
|
ALX_MSE16DB_DOWN);
|
|
alx_read_phy_dbg(hw, ALX_MIIDBG_MSE20DB, &phy_val);
|
|
ALX_SET_FIELD(phy_val, ALX_MSE20DB_TH,
|
|
ALX_MSE20DB_TH_DEF);
|
|
alx_write_phy_dbg(hw, ALX_MIIDBG_MSE20DB, phy_val);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool alx_phy_configured(struct alx_hw *hw)
|
|
{
|
|
u32 cfg, hw_cfg;
|
|
|
|
cfg = ethadv_to_hw_cfg(hw, hw->adv_cfg);
|
|
cfg = ALX_GET_FIELD(cfg, ALX_DRV_PHY);
|
|
hw_cfg = alx_get_phy_config(hw);
|
|
|
|
if (hw_cfg == ALX_DRV_PHY_UNKNOWN)
|
|
return false;
|
|
|
|
return cfg == hw_cfg;
|
|
}
|
|
|
|
int alx_read_phy_link(struct alx_hw *hw)
|
|
{
|
|
struct pci_dev *pdev = hw->pdev;
|
|
u16 bmsr, giga;
|
|
int err;
|
|
|
|
err = alx_read_phy_reg(hw, MII_BMSR, &bmsr);
|
|
if (err)
|
|
return err;
|
|
|
|
err = alx_read_phy_reg(hw, MII_BMSR, &bmsr);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!(bmsr & BMSR_LSTATUS)) {
|
|
hw->link_speed = SPEED_UNKNOWN;
|
|
hw->duplex = DUPLEX_UNKNOWN;
|
|
return 0;
|
|
}
|
|
|
|
/* speed/duplex result is saved in PHY Specific Status Register */
|
|
err = alx_read_phy_reg(hw, ALX_MII_GIGA_PSSR, &giga);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!(giga & ALX_GIGA_PSSR_SPD_DPLX_RESOLVED))
|
|
goto wrong_speed;
|
|
|
|
switch (giga & ALX_GIGA_PSSR_SPEED) {
|
|
case ALX_GIGA_PSSR_1000MBS:
|
|
hw->link_speed = SPEED_1000;
|
|
break;
|
|
case ALX_GIGA_PSSR_100MBS:
|
|
hw->link_speed = SPEED_100;
|
|
break;
|
|
case ALX_GIGA_PSSR_10MBS:
|
|
hw->link_speed = SPEED_10;
|
|
break;
|
|
default:
|
|
goto wrong_speed;
|
|
}
|
|
|
|
hw->duplex = (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF;
|
|
return 0;
|
|
|
|
wrong_speed:
|
|
dev_err(&pdev->dev, "invalid PHY speed/duplex: 0x%x\n", giga);
|
|
return -EINVAL;
|
|
}
|
|
|
|
int alx_clear_phy_intr(struct alx_hw *hw)
|
|
{
|
|
u16 isr;
|
|
|
|
/* clear interrupt status by reading it */
|
|
return alx_read_phy_reg(hw, ALX_MII_ISR, &isr);
|
|
}
|
|
|
|
void alx_disable_rss(struct alx_hw *hw)
|
|
{
|
|
u32 ctrl = alx_read_mem32(hw, ALX_RXQ0);
|
|
|
|
ctrl &= ~ALX_RXQ0_RSS_HASH_EN;
|
|
alx_write_mem32(hw, ALX_RXQ0, ctrl);
|
|
}
|
|
|
|
void alx_configure_basic(struct alx_hw *hw)
|
|
{
|
|
u32 val, raw_mtu, max_payload;
|
|
u16 val16;
|
|
u8 chip_rev = alx_hw_revision(hw);
|
|
|
|
alx_set_macaddr(hw, hw->mac_addr);
|
|
|
|
alx_write_mem32(hw, ALX_CLK_GATE, ALX_CLK_GATE_ALL);
|
|
|
|
/* idle timeout to switch clk_125M */
|
|
if (chip_rev >= ALX_REV_B0)
|
|
alx_write_mem32(hw, ALX_IDLE_DECISN_TIMER,
|
|
ALX_IDLE_DECISN_TIMER_DEF);
|
|
|
|
alx_write_mem32(hw, ALX_SMB_TIMER, hw->smb_timer * 500UL);
|
|
|
|
val = alx_read_mem32(hw, ALX_MASTER);
|
|
val |= ALX_MASTER_IRQMOD2_EN |
|
|
ALX_MASTER_IRQMOD1_EN |
|
|
ALX_MASTER_SYSALVTIMER_EN;
|
|
alx_write_mem32(hw, ALX_MASTER, val);
|
|
alx_write_mem32(hw, ALX_IRQ_MODU_TIMER,
|
|
(hw->imt >> 1) << ALX_IRQ_MODU_TIMER1_SHIFT);
|
|
/* intr re-trig timeout */
|
|
alx_write_mem32(hw, ALX_INT_RETRIG, ALX_INT_RETRIG_TO);
|
|
/* tpd threshold to trig int */
|
|
alx_write_mem32(hw, ALX_TINT_TPD_THRSHLD, hw->ith_tpd);
|
|
alx_write_mem32(hw, ALX_TINT_TIMER, hw->imt);
|
|
|
|
raw_mtu = ALX_RAW_MTU(hw->mtu);
|
|
alx_write_mem32(hw, ALX_MTU, raw_mtu);
|
|
if (raw_mtu > (ALX_MTU_JUMBO_TH + ETH_FCS_LEN + VLAN_HLEN))
|
|
hw->rx_ctrl &= ~ALX_MAC_CTRL_FAST_PAUSE;
|
|
|
|
if (raw_mtu < ALX_TXQ1_JUMBO_TSO_TH)
|
|
val = (raw_mtu + 7) >> 3;
|
|
else
|
|
val = ALX_TXQ1_JUMBO_TSO_TH >> 3;
|
|
alx_write_mem32(hw, ALX_TXQ1, val | ALX_TXQ1_ERRLGPKT_DROP_EN);
|
|
|
|
max_payload = pcie_get_readrq(hw->pdev) >> 8;
|
|
/*
|
|
* if BIOS had changed the default dma read max length,
|
|
* restore it to default value
|
|
*/
|
|
if (max_payload < ALX_DEV_CTRL_MAXRRS_MIN)
|
|
pcie_set_readrq(hw->pdev, 128 << ALX_DEV_CTRL_MAXRRS_MIN);
|
|
|
|
val = ALX_TXQ_TPD_BURSTPREF_DEF << ALX_TXQ0_TPD_BURSTPREF_SHIFT |
|
|
ALX_TXQ0_MODE_ENHANCE | ALX_TXQ0_LSO_8023_EN |
|
|
ALX_TXQ0_SUPT_IPOPT |
|
|
ALX_TXQ_TXF_BURST_PREF_DEF << ALX_TXQ0_TXF_BURST_PREF_SHIFT;
|
|
alx_write_mem32(hw, ALX_TXQ0, val);
|
|
val = ALX_TXQ_TPD_BURSTPREF_DEF << ALX_HQTPD_Q1_NUMPREF_SHIFT |
|
|
ALX_TXQ_TPD_BURSTPREF_DEF << ALX_HQTPD_Q2_NUMPREF_SHIFT |
|
|
ALX_TXQ_TPD_BURSTPREF_DEF << ALX_HQTPD_Q3_NUMPREF_SHIFT |
|
|
ALX_HQTPD_BURST_EN;
|
|
alx_write_mem32(hw, ALX_HQTPD, val);
|
|
|
|
/* rxq, flow control */
|
|
val = alx_read_mem32(hw, ALX_SRAM5);
|
|
val = ALX_GET_FIELD(val, ALX_SRAM_RXF_LEN) << 3;
|
|
if (val > ALX_SRAM_RXF_LEN_8K) {
|
|
val16 = ALX_MTU_STD_ALGN >> 3;
|
|
val = (val - ALX_RXQ2_RXF_FLOW_CTRL_RSVD) >> 3;
|
|
} else {
|
|
val16 = ALX_MTU_STD_ALGN >> 3;
|
|
val = (val - ALX_MTU_STD_ALGN) >> 3;
|
|
}
|
|
alx_write_mem32(hw, ALX_RXQ2,
|
|
val16 << ALX_RXQ2_RXF_XOFF_THRESH_SHIFT |
|
|
val << ALX_RXQ2_RXF_XON_THRESH_SHIFT);
|
|
val = ALX_RXQ0_NUM_RFD_PREF_DEF << ALX_RXQ0_NUM_RFD_PREF_SHIFT |
|
|
ALX_RXQ0_RSS_MODE_DIS << ALX_RXQ0_RSS_MODE_SHIFT |
|
|
ALX_RXQ0_IDT_TBL_SIZE_DEF << ALX_RXQ0_IDT_TBL_SIZE_SHIFT |
|
|
ALX_RXQ0_RSS_HSTYP_ALL | ALX_RXQ0_RSS_HASH_EN |
|
|
ALX_RXQ0_IPV6_PARSE_EN;
|
|
|
|
if (alx_hw_giga(hw))
|
|
ALX_SET_FIELD(val, ALX_RXQ0_ASPM_THRESH,
|
|
ALX_RXQ0_ASPM_THRESH_100M);
|
|
|
|
alx_write_mem32(hw, ALX_RXQ0, val);
|
|
|
|
val = alx_read_mem32(hw, ALX_DMA);
|
|
val = ALX_DMA_RORDER_MODE_OUT << ALX_DMA_RORDER_MODE_SHIFT |
|
|
ALX_DMA_RREQ_PRI_DATA |
|
|
max_payload << ALX_DMA_RREQ_BLEN_SHIFT |
|
|
ALX_DMA_WDLY_CNT_DEF << ALX_DMA_WDLY_CNT_SHIFT |
|
|
ALX_DMA_RDLY_CNT_DEF << ALX_DMA_RDLY_CNT_SHIFT |
|
|
(hw->dma_chnl - 1) << ALX_DMA_RCHNL_SEL_SHIFT;
|
|
alx_write_mem32(hw, ALX_DMA, val);
|
|
|
|
/* default multi-tx-q weights */
|
|
val = ALX_WRR_PRI_RESTRICT_NONE << ALX_WRR_PRI_SHIFT |
|
|
4 << ALX_WRR_PRI0_SHIFT |
|
|
4 << ALX_WRR_PRI1_SHIFT |
|
|
4 << ALX_WRR_PRI2_SHIFT |
|
|
4 << ALX_WRR_PRI3_SHIFT;
|
|
alx_write_mem32(hw, ALX_WRR, val);
|
|
}
|
|
|
|
void alx_mask_msix(struct alx_hw *hw, int index, bool mask)
|
|
{
|
|
u32 reg, val;
|
|
|
|
reg = ALX_MSIX_ENTRY_BASE + index * PCI_MSIX_ENTRY_SIZE +
|
|
PCI_MSIX_ENTRY_VECTOR_CTRL;
|
|
|
|
val = mask ? PCI_MSIX_ENTRY_CTRL_MASKBIT : 0;
|
|
|
|
alx_write_mem32(hw, reg, val);
|
|
alx_post_write(hw);
|
|
}
|
|
|
|
|
|
bool alx_get_phy_info(struct alx_hw *hw)
|
|
{
|
|
u16 devs1, devs2;
|
|
|
|
if (alx_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id[0]) ||
|
|
alx_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id[1]))
|
|
return false;
|
|
|
|
/* since we haven't PMA/PMD status2 register, we can't
|
|
* use mdio45_probe function for prtad and mmds.
|
|
* use fixed MMD3 to get mmds.
|
|
*/
|
|
if (alx_read_phy_ext(hw, 3, MDIO_DEVS1, &devs1) ||
|
|
alx_read_phy_ext(hw, 3, MDIO_DEVS2, &devs2))
|
|
return false;
|
|
hw->mdio.mmds = devs1 | devs2 << 16;
|
|
|
|
return true;
|
|
}
|
|
|
|
void alx_update_hw_stats(struct alx_hw *hw)
|
|
{
|
|
/* RX stats */
|
|
hw->stats.rx_ok += alx_read_mem32(hw, ALX_MIB_RX_OK);
|
|
hw->stats.rx_bcast += alx_read_mem32(hw, ALX_MIB_RX_BCAST);
|
|
hw->stats.rx_mcast += alx_read_mem32(hw, ALX_MIB_RX_MCAST);
|
|
hw->stats.rx_pause += alx_read_mem32(hw, ALX_MIB_RX_PAUSE);
|
|
hw->stats.rx_ctrl += alx_read_mem32(hw, ALX_MIB_RX_CTRL);
|
|
hw->stats.rx_fcs_err += alx_read_mem32(hw, ALX_MIB_RX_FCS_ERR);
|
|
hw->stats.rx_len_err += alx_read_mem32(hw, ALX_MIB_RX_LEN_ERR);
|
|
hw->stats.rx_byte_cnt += alx_read_mem32(hw, ALX_MIB_RX_BYTE_CNT);
|
|
hw->stats.rx_runt += alx_read_mem32(hw, ALX_MIB_RX_RUNT);
|
|
hw->stats.rx_frag += alx_read_mem32(hw, ALX_MIB_RX_FRAG);
|
|
hw->stats.rx_sz_64B += alx_read_mem32(hw, ALX_MIB_RX_SZ_64B);
|
|
hw->stats.rx_sz_127B += alx_read_mem32(hw, ALX_MIB_RX_SZ_127B);
|
|
hw->stats.rx_sz_255B += alx_read_mem32(hw, ALX_MIB_RX_SZ_255B);
|
|
hw->stats.rx_sz_511B += alx_read_mem32(hw, ALX_MIB_RX_SZ_511B);
|
|
hw->stats.rx_sz_1023B += alx_read_mem32(hw, ALX_MIB_RX_SZ_1023B);
|
|
hw->stats.rx_sz_1518B += alx_read_mem32(hw, ALX_MIB_RX_SZ_1518B);
|
|
hw->stats.rx_sz_max += alx_read_mem32(hw, ALX_MIB_RX_SZ_MAX);
|
|
hw->stats.rx_ov_sz += alx_read_mem32(hw, ALX_MIB_RX_OV_SZ);
|
|
hw->stats.rx_ov_rxf += alx_read_mem32(hw, ALX_MIB_RX_OV_RXF);
|
|
hw->stats.rx_ov_rrd += alx_read_mem32(hw, ALX_MIB_RX_OV_RRD);
|
|
hw->stats.rx_align_err += alx_read_mem32(hw, ALX_MIB_RX_ALIGN_ERR);
|
|
hw->stats.rx_bc_byte_cnt += alx_read_mem32(hw, ALX_MIB_RX_BCCNT);
|
|
hw->stats.rx_mc_byte_cnt += alx_read_mem32(hw, ALX_MIB_RX_MCCNT);
|
|
hw->stats.rx_err_addr += alx_read_mem32(hw, ALX_MIB_RX_ERRADDR);
|
|
|
|
/* TX stats */
|
|
hw->stats.tx_ok += alx_read_mem32(hw, ALX_MIB_TX_OK);
|
|
hw->stats.tx_bcast += alx_read_mem32(hw, ALX_MIB_TX_BCAST);
|
|
hw->stats.tx_mcast += alx_read_mem32(hw, ALX_MIB_TX_MCAST);
|
|
hw->stats.tx_pause += alx_read_mem32(hw, ALX_MIB_TX_PAUSE);
|
|
hw->stats.tx_exc_defer += alx_read_mem32(hw, ALX_MIB_TX_EXC_DEFER);
|
|
hw->stats.tx_ctrl += alx_read_mem32(hw, ALX_MIB_TX_CTRL);
|
|
hw->stats.tx_defer += alx_read_mem32(hw, ALX_MIB_TX_DEFER);
|
|
hw->stats.tx_byte_cnt += alx_read_mem32(hw, ALX_MIB_TX_BYTE_CNT);
|
|
hw->stats.tx_sz_64B += alx_read_mem32(hw, ALX_MIB_TX_SZ_64B);
|
|
hw->stats.tx_sz_127B += alx_read_mem32(hw, ALX_MIB_TX_SZ_127B);
|
|
hw->stats.tx_sz_255B += alx_read_mem32(hw, ALX_MIB_TX_SZ_255B);
|
|
hw->stats.tx_sz_511B += alx_read_mem32(hw, ALX_MIB_TX_SZ_511B);
|
|
hw->stats.tx_sz_1023B += alx_read_mem32(hw, ALX_MIB_TX_SZ_1023B);
|
|
hw->stats.tx_sz_1518B += alx_read_mem32(hw, ALX_MIB_TX_SZ_1518B);
|
|
hw->stats.tx_sz_max += alx_read_mem32(hw, ALX_MIB_TX_SZ_MAX);
|
|
hw->stats.tx_single_col += alx_read_mem32(hw, ALX_MIB_TX_SINGLE_COL);
|
|
hw->stats.tx_multi_col += alx_read_mem32(hw, ALX_MIB_TX_MULTI_COL);
|
|
hw->stats.tx_late_col += alx_read_mem32(hw, ALX_MIB_TX_LATE_COL);
|
|
hw->stats.tx_abort_col += alx_read_mem32(hw, ALX_MIB_TX_ABORT_COL);
|
|
hw->stats.tx_underrun += alx_read_mem32(hw, ALX_MIB_TX_UNDERRUN);
|
|
hw->stats.tx_trd_eop += alx_read_mem32(hw, ALX_MIB_TX_TRD_EOP);
|
|
hw->stats.tx_len_err += alx_read_mem32(hw, ALX_MIB_TX_LEN_ERR);
|
|
hw->stats.tx_trunc += alx_read_mem32(hw, ALX_MIB_TX_TRUNC);
|
|
hw->stats.tx_bc_byte_cnt += alx_read_mem32(hw, ALX_MIB_TX_BCCNT);
|
|
hw->stats.tx_mc_byte_cnt += alx_read_mem32(hw, ALX_MIB_TX_MCCNT);
|
|
|
|
hw->stats.update += alx_read_mem32(hw, ALX_MIB_UPDATE);
|
|
}
|