/* * Copyright (c) 2013 Johannes Berg * * 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 . * * 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 #include #include #include #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); }