mirror of https://github.com/ipxe/ipxe.git
1604 lines
40 KiB
C
1604 lines
40 KiB
C
|
|
#include <mii.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <byteswap.h>
|
|
#include <ipxe/pci.h>
|
|
|
|
#include "tg3.h"
|
|
|
|
static void tg3_link_report(struct tg3 *tp);
|
|
|
|
void tg3_mdio_init(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
if (tg3_flag(tp, 5717_PLUS)) {
|
|
u32 is_serdes;
|
|
|
|
tp->phy_addr = PCI_FUNC(tp->pdev->busdevfn) + 1;
|
|
|
|
if (tp->pci_chip_rev_id != CHIPREV_ID_5717_A0)
|
|
is_serdes = tr32(SG_DIG_STATUS) & SG_DIG_IS_SERDES;
|
|
else
|
|
is_serdes = tr32(TG3_CPMU_PHY_STRAP) &
|
|
TG3_CPMU_PHY_STRAP_IS_SERDES;
|
|
if (is_serdes)
|
|
tp->phy_addr += 7;
|
|
} else
|
|
tp->phy_addr = TG3_PHY_MII_ADDR;
|
|
}
|
|
|
|
static int tg3_issue_otp_command(struct tg3 *tp, u32 cmd)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int i;
|
|
u32 val;
|
|
|
|
tw32(OTP_CTRL, cmd | OTP_CTRL_OTP_CMD_START);
|
|
tw32(OTP_CTRL, cmd);
|
|
|
|
/* Wait for up to 1 ms for command to execute. */
|
|
for (i = 0; i < 100; i++) {
|
|
val = tr32(OTP_STATUS);
|
|
if (val & OTP_STATUS_CMD_DONE)
|
|
break;
|
|
udelay(10);
|
|
}
|
|
|
|
return (val & OTP_STATUS_CMD_DONE) ? 0 : -EBUSY;
|
|
}
|
|
|
|
/* Read the gphy configuration from the OTP region of the chip. The gphy
|
|
* configuration is a 32-bit value that straddles the alignment boundary.
|
|
* We do two 32-bit reads and then shift and merge the results.
|
|
*/
|
|
u32 tg3_read_otp_phycfg(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 bhalf_otp, thalf_otp;
|
|
|
|
tw32(OTP_MODE, OTP_MODE_OTP_THRU_GRC);
|
|
|
|
if (tg3_issue_otp_command(tp, OTP_CTRL_OTP_CMD_INIT))
|
|
return 0;
|
|
|
|
tw32(OTP_ADDRESS, OTP_ADDRESS_MAGIC1);
|
|
|
|
if (tg3_issue_otp_command(tp, OTP_CTRL_OTP_CMD_READ))
|
|
return 0;
|
|
|
|
thalf_otp = tr32(OTP_READ_DATA);
|
|
|
|
tw32(OTP_ADDRESS, OTP_ADDRESS_MAGIC2);
|
|
|
|
if (tg3_issue_otp_command(tp, OTP_CTRL_OTP_CMD_READ))
|
|
return 0;
|
|
|
|
bhalf_otp = tr32(OTP_READ_DATA);
|
|
|
|
return ((thalf_otp & 0x0000ffff) << 16) | (bhalf_otp >> 16);
|
|
}
|
|
|
|
#define PHY_BUSY_LOOPS 5000
|
|
|
|
int tg3_readphy(struct tg3 *tp, int reg, u32 *val)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 frame_val;
|
|
unsigned int loops;
|
|
int ret;
|
|
|
|
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
|
|
tw32_f(MAC_MI_MODE,
|
|
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
|
|
udelay(80);
|
|
}
|
|
|
|
*val = 0x0;
|
|
|
|
frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) &
|
|
MI_COM_PHY_ADDR_MASK);
|
|
frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
|
|
MI_COM_REG_ADDR_MASK);
|
|
frame_val |= (MI_COM_CMD_READ | MI_COM_START);
|
|
|
|
tw32_f(MAC_MI_COM, frame_val);
|
|
|
|
loops = PHY_BUSY_LOOPS;
|
|
while (loops != 0) {
|
|
udelay(10);
|
|
frame_val = tr32(MAC_MI_COM);
|
|
|
|
if ((frame_val & MI_COM_BUSY) == 0) {
|
|
udelay(5);
|
|
frame_val = tr32(MAC_MI_COM);
|
|
break;
|
|
}
|
|
loops -= 1;
|
|
}
|
|
|
|
ret = -EBUSY;
|
|
if (loops != 0) {
|
|
*val = frame_val & MI_COM_DATA_MASK;
|
|
ret = 0;
|
|
}
|
|
|
|
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
|
|
tw32_f(MAC_MI_MODE, tp->mi_mode);
|
|
udelay(80);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct subsys_tbl_ent {
|
|
u16 subsys_vendor, subsys_devid;
|
|
u32 phy_id;
|
|
};
|
|
|
|
static struct subsys_tbl_ent subsys_id_to_phy_id[] = {
|
|
/* Broadcom boards. */
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95700A6, TG3_PHY_ID_BCM5401 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95701A5, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95700T6, TG3_PHY_ID_BCM8002 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95700A9, 0 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95701T1, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95701T8, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95701A7, 0 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95701A10, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95701A12, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95703AX1, TG3_PHY_ID_BCM5703 },
|
|
{ TG3PCI_SUBVENDOR_ID_BROADCOM,
|
|
TG3PCI_SUBDEVICE_ID_BROADCOM_95703AX2, TG3_PHY_ID_BCM5703 },
|
|
|
|
/* 3com boards. */
|
|
{ TG3PCI_SUBVENDOR_ID_3COM,
|
|
TG3PCI_SUBDEVICE_ID_3COM_3C996T, TG3_PHY_ID_BCM5401 },
|
|
{ TG3PCI_SUBVENDOR_ID_3COM,
|
|
TG3PCI_SUBDEVICE_ID_3COM_3C996BT, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_3COM,
|
|
TG3PCI_SUBDEVICE_ID_3COM_3C996SX, 0 },
|
|
{ TG3PCI_SUBVENDOR_ID_3COM,
|
|
TG3PCI_SUBDEVICE_ID_3COM_3C1000T, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_3COM,
|
|
TG3PCI_SUBDEVICE_ID_3COM_3C940BR01, TG3_PHY_ID_BCM5701 },
|
|
|
|
/* DELL boards. */
|
|
{ TG3PCI_SUBVENDOR_ID_DELL,
|
|
TG3PCI_SUBDEVICE_ID_DELL_VIPER, TG3_PHY_ID_BCM5401 },
|
|
{ TG3PCI_SUBVENDOR_ID_DELL,
|
|
TG3PCI_SUBDEVICE_ID_DELL_JAGUAR, TG3_PHY_ID_BCM5401 },
|
|
{ TG3PCI_SUBVENDOR_ID_DELL,
|
|
TG3PCI_SUBDEVICE_ID_DELL_MERLOT, TG3_PHY_ID_BCM5411 },
|
|
{ TG3PCI_SUBVENDOR_ID_DELL,
|
|
TG3PCI_SUBDEVICE_ID_DELL_SLIM_MERLOT, TG3_PHY_ID_BCM5411 },
|
|
|
|
/* Compaq boards. */
|
|
{ TG3PCI_SUBVENDOR_ID_COMPAQ,
|
|
TG3PCI_SUBDEVICE_ID_COMPAQ_BANSHEE, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_COMPAQ,
|
|
TG3PCI_SUBDEVICE_ID_COMPAQ_BANSHEE_2, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_COMPAQ,
|
|
TG3PCI_SUBDEVICE_ID_COMPAQ_CHANGELING, 0 },
|
|
{ TG3PCI_SUBVENDOR_ID_COMPAQ,
|
|
TG3PCI_SUBDEVICE_ID_COMPAQ_NC7780, TG3_PHY_ID_BCM5701 },
|
|
{ TG3PCI_SUBVENDOR_ID_COMPAQ,
|
|
TG3PCI_SUBDEVICE_ID_COMPAQ_NC7780_2, TG3_PHY_ID_BCM5701 },
|
|
|
|
/* IBM boards. */
|
|
{ TG3PCI_SUBVENDOR_ID_IBM,
|
|
TG3PCI_SUBDEVICE_ID_IBM_5703SAX2, 0 }
|
|
};
|
|
|
|
static struct subsys_tbl_ent *tg3_lookup_by_subsys(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int i;
|
|
|
|
DBGC(tp->dev, "Matching with: %x:%x\n", tp->subsystem_vendor, tp->subsystem_device);
|
|
|
|
for (i = 0; i < (int) ARRAY_SIZE(subsys_id_to_phy_id); i++) {
|
|
if ((subsys_id_to_phy_id[i].subsys_vendor ==
|
|
tp->subsystem_vendor) &&
|
|
(subsys_id_to_phy_id[i].subsys_devid ==
|
|
tp->subsystem_device))
|
|
return &subsys_id_to_phy_id[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int tg3_writephy(struct tg3 *tp, int reg, u32 val)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 frame_val;
|
|
unsigned int loops;
|
|
int ret;
|
|
|
|
if ((tp->phy_flags & TG3_PHYFLG_IS_FET) &&
|
|
(reg == MII_TG3_CTRL || reg == MII_TG3_AUX_CTRL))
|
|
return 0;
|
|
|
|
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
|
|
tw32_f(MAC_MI_MODE,
|
|
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
|
|
udelay(80);
|
|
}
|
|
|
|
frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) &
|
|
MI_COM_PHY_ADDR_MASK);
|
|
frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
|
|
MI_COM_REG_ADDR_MASK);
|
|
frame_val |= (val & MI_COM_DATA_MASK);
|
|
frame_val |= (MI_COM_CMD_WRITE | MI_COM_START);
|
|
|
|
tw32_f(MAC_MI_COM, frame_val);
|
|
|
|
loops = PHY_BUSY_LOOPS;
|
|
while (loops != 0) {
|
|
udelay(10);
|
|
frame_val = tr32(MAC_MI_COM);
|
|
if ((frame_val & MI_COM_BUSY) == 0) {
|
|
udelay(5);
|
|
frame_val = tr32(MAC_MI_COM);
|
|
break;
|
|
}
|
|
loops -= 1;
|
|
}
|
|
|
|
ret = -EBUSY;
|
|
if (loops != 0)
|
|
ret = 0;
|
|
|
|
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
|
|
tw32_f(MAC_MI_MODE, tp->mi_mode);
|
|
udelay(80);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tg3_bmcr_reset(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 phy_control;
|
|
int limit, err;
|
|
|
|
/* OK, reset it, and poll the BMCR_RESET bit until it
|
|
* clears or we time out.
|
|
*/
|
|
phy_control = BMCR_RESET;
|
|
err = tg3_writephy(tp, MII_BMCR, phy_control);
|
|
if (err != 0)
|
|
return -EBUSY;
|
|
|
|
limit = 5000;
|
|
while (limit--) {
|
|
err = tg3_readphy(tp, MII_BMCR, &phy_control);
|
|
if (err != 0)
|
|
return -EBUSY;
|
|
|
|
if ((phy_control & BMCR_RESET) == 0) {
|
|
udelay(40);
|
|
break;
|
|
}
|
|
udelay(10);
|
|
}
|
|
if (limit < 0)
|
|
return -EBUSY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tg3_wait_macro_done(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int limit = 100;
|
|
|
|
while (limit--) {
|
|
u32 tmp32;
|
|
|
|
if (!tg3_readphy(tp, MII_TG3_DSP_CONTROL, &tmp32)) {
|
|
if ((tmp32 & 0x1000) == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (limit < 0)
|
|
return -EBUSY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
static const u32 test_pat[4][6] = {
|
|
{ 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456, 0x00000003 },
|
|
{ 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a, 0x00000005 },
|
|
{ 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd, 0x00000003 },
|
|
{ 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1, 0x00000005 }
|
|
};
|
|
int chan;
|
|
|
|
for (chan = 0; chan < 4; chan++) {
|
|
int i;
|
|
|
|
tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
|
|
(chan * 0x2000) | 0x0200);
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0002);
|
|
|
|
for (i = 0; i < 6; i++)
|
|
tg3_writephy(tp, MII_TG3_DSP_RW_PORT,
|
|
test_pat[chan][i]);
|
|
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0202);
|
|
if (tg3_wait_macro_done(tp)) {
|
|
*resetp = 1;
|
|
return -EBUSY;
|
|
}
|
|
|
|
tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
|
|
(chan * 0x2000) | 0x0200);
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0082);
|
|
if (tg3_wait_macro_done(tp)) {
|
|
*resetp = 1;
|
|
return -EBUSY;
|
|
}
|
|
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0802);
|
|
if (tg3_wait_macro_done(tp)) {
|
|
*resetp = 1;
|
|
return -EBUSY;
|
|
}
|
|
|
|
for (i = 0; i < 6; i += 2) {
|
|
u32 low, high;
|
|
|
|
if (tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low) ||
|
|
tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high) ||
|
|
tg3_wait_macro_done(tp)) {
|
|
*resetp = 1;
|
|
return -EBUSY;
|
|
}
|
|
low &= 0x7fff;
|
|
high &= 0x000f;
|
|
if (low != test_pat[chan][i] ||
|
|
high != test_pat[chan][i+1]) {
|
|
tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b);
|
|
tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001);
|
|
tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005);
|
|
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tg3_phy_reset_chanpat(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int chan;
|
|
|
|
for (chan = 0; chan < 4; chan++) {
|
|
int i;
|
|
|
|
tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
|
|
(chan * 0x2000) | 0x0200);
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0002);
|
|
for (i = 0; i < 6; i++)
|
|
tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000);
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0202);
|
|
if (tg3_wait_macro_done(tp))
|
|
return -EBUSY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tg3_phydsp_write(struct tg3 *tp, u32 reg, u32 val)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int err;
|
|
|
|
err = tg3_writephy(tp, MII_TG3_DSP_ADDRESS, reg);
|
|
if (!err)
|
|
err = tg3_writephy(tp, MII_TG3_DSP_RW_PORT, val);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int tg3_phy_auxctl_write(struct tg3 *tp, int reg, u32 set)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
if (reg == MII_TG3_AUXCTL_SHDWSEL_MISC)
|
|
set |= MII_TG3_AUXCTL_MISC_WREN;
|
|
|
|
return tg3_writephy(tp, MII_TG3_AUX_CTRL, set | reg);
|
|
}
|
|
|
|
#define TG3_PHY_AUXCTL_SMDSP_ENABLE(tp) \
|
|
tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, \
|
|
MII_TG3_AUXCTL_ACTL_SMDSP_ENA | \
|
|
MII_TG3_AUXCTL_ACTL_TX_6DB)
|
|
|
|
#define TG3_PHY_AUXCTL_SMDSP_DISABLE(tp) \
|
|
tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, \
|
|
MII_TG3_AUXCTL_ACTL_TX_6DB);
|
|
|
|
static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 reg32, phy9_orig;
|
|
int retries, do_phy_reset, err;
|
|
|
|
retries = 10;
|
|
do_phy_reset = 1;
|
|
do {
|
|
if (do_phy_reset) {
|
|
err = tg3_bmcr_reset(tp);
|
|
if (err)
|
|
return err;
|
|
do_phy_reset = 0;
|
|
}
|
|
|
|
/* Disable transmitter and interrupt. */
|
|
if (tg3_readphy(tp, MII_TG3_EXT_CTRL, ®32))
|
|
continue;
|
|
|
|
reg32 |= 0x3000;
|
|
tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
|
|
|
|
/* Set full-duplex, 1000 mbps. */
|
|
tg3_writephy(tp, MII_BMCR,
|
|
BMCR_FULLDPLX | TG3_BMCR_SPEED1000);
|
|
|
|
/* Set to master mode. */
|
|
if (tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig))
|
|
continue;
|
|
|
|
tg3_writephy(tp, MII_TG3_CTRL,
|
|
(MII_TG3_CTRL_AS_MASTER |
|
|
MII_TG3_CTRL_ENABLE_AS_MASTER));
|
|
|
|
err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Block the PHY control access. */
|
|
tg3_phydsp_write(tp, 0x8005, 0x0800);
|
|
|
|
err = tg3_phy_write_and_check_testpat(tp, &do_phy_reset);
|
|
if (!err)
|
|
break;
|
|
} while (--retries);
|
|
|
|
err = tg3_phy_reset_chanpat(tp);
|
|
if (err)
|
|
return err;
|
|
|
|
tg3_phydsp_write(tp, 0x8005, 0x0000);
|
|
|
|
tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200);
|
|
tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0000);
|
|
|
|
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
|
|
|
|
tg3_writephy(tp, MII_TG3_CTRL, phy9_orig);
|
|
|
|
if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, ®32)) {
|
|
reg32 &= ~0x3000;
|
|
tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
|
|
} else if (!err)
|
|
err = -EBUSY;
|
|
|
|
return err;
|
|
}
|
|
|
|
static void tg3_phy_apply_otp(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 otp, phy;
|
|
|
|
if (!tp->phy_otp)
|
|
return;
|
|
|
|
otp = tp->phy_otp;
|
|
|
|
if (TG3_PHY_AUXCTL_SMDSP_ENABLE(tp))
|
|
return;
|
|
|
|
phy = ((otp & TG3_OTP_AGCTGT_MASK) >> TG3_OTP_AGCTGT_SHIFT);
|
|
phy |= MII_TG3_DSP_TAP1_AGCTGT_DFLT;
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_TAP1, phy);
|
|
|
|
phy = ((otp & TG3_OTP_HPFFLTR_MASK) >> TG3_OTP_HPFFLTR_SHIFT) |
|
|
((otp & TG3_OTP_HPFOVER_MASK) >> TG3_OTP_HPFOVER_SHIFT);
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_AADJ1CH0, phy);
|
|
|
|
phy = ((otp & TG3_OTP_LPFDIS_MASK) >> TG3_OTP_LPFDIS_SHIFT);
|
|
phy |= MII_TG3_DSP_AADJ1CH3_ADCCKADJ;
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_AADJ1CH3, phy);
|
|
|
|
phy = ((otp & TG3_OTP_VDAC_MASK) >> TG3_OTP_VDAC_SHIFT);
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_EXP75, phy);
|
|
|
|
phy = ((otp & TG3_OTP_10BTAMP_MASK) >> TG3_OTP_10BTAMP_SHIFT);
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_EXP96, phy);
|
|
|
|
phy = ((otp & TG3_OTP_ROFF_MASK) >> TG3_OTP_ROFF_SHIFT) |
|
|
((otp & TG3_OTP_RCOFF_MASK) >> TG3_OTP_RCOFF_SHIFT);
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_EXP97, phy);
|
|
|
|
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
|
|
}
|
|
|
|
static int tg3_phy_auxctl_read(struct tg3 *tp, int reg, u32 *val)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int err;
|
|
|
|
err = tg3_writephy(tp, MII_TG3_AUX_CTRL,
|
|
(reg << MII_TG3_AUXCTL_MISC_RDSEL_SHIFT) |
|
|
MII_TG3_AUXCTL_SHDWSEL_MISC);
|
|
if (!err)
|
|
err = tg3_readphy(tp, MII_TG3_AUX_CTRL, val);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void tg3_phy_toggle_automdix(struct tg3 *tp, int enable)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 phy;
|
|
|
|
if (!tg3_flag(tp, 5705_PLUS) ||
|
|
(tp->phy_flags & TG3_PHYFLG_ANY_SERDES))
|
|
return;
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_IS_FET) {
|
|
u32 ephy;
|
|
|
|
if (!tg3_readphy(tp, MII_TG3_FET_TEST, &ephy)) {
|
|
u32 reg = MII_TG3_FET_SHDW_MISCCTRL;
|
|
|
|
tg3_writephy(tp, MII_TG3_FET_TEST,
|
|
ephy | MII_TG3_FET_SHADOW_EN);
|
|
if (!tg3_readphy(tp, reg, &phy)) {
|
|
if (enable)
|
|
phy |= MII_TG3_FET_SHDW_MISCCTRL_MDIX;
|
|
else
|
|
phy &= ~MII_TG3_FET_SHDW_MISCCTRL_MDIX;
|
|
tg3_writephy(tp, reg, phy);
|
|
}
|
|
tg3_writephy(tp, MII_TG3_FET_TEST, ephy);
|
|
}
|
|
} else {
|
|
int ret;
|
|
|
|
ret = tg3_phy_auxctl_read(tp,
|
|
MII_TG3_AUXCTL_SHDWSEL_MISC, &phy);
|
|
if (!ret) {
|
|
if (enable)
|
|
phy |= MII_TG3_AUXCTL_MISC_FORCE_AMDIX;
|
|
else
|
|
phy &= ~MII_TG3_AUXCTL_MISC_FORCE_AMDIX;
|
|
tg3_phy_auxctl_write(tp,
|
|
MII_TG3_AUXCTL_SHDWSEL_MISC, phy);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void tg3_phy_set_wirespeed(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int ret;
|
|
u32 val;
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_NO_ETH_WIRE_SPEED)
|
|
return;
|
|
|
|
ret = tg3_phy_auxctl_read(tp, MII_TG3_AUXCTL_SHDWSEL_MISC, &val);
|
|
if (!ret)
|
|
tg3_phy_auxctl_write(tp, MII_TG3_AUXCTL_SHDWSEL_MISC,
|
|
val | MII_TG3_AUXCTL_MISC_WIRESPD_EN);
|
|
}
|
|
|
|
/* This will reset the tigon3 PHY if there is no valid
|
|
* link unless the FORCE argument is non-zero.
|
|
*/
|
|
int tg3_phy_reset(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 val, cpmuctrl;
|
|
int err;
|
|
|
|
DBGCP(&tp->pdev->dev, "%s\n", __func__);
|
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
|
|
val = tr32(GRC_MISC_CFG);
|
|
tw32_f(GRC_MISC_CFG, val & ~GRC_MISC_CFG_EPHY_IDDQ);
|
|
udelay(40);
|
|
}
|
|
err = tg3_readphy(tp, MII_BMSR, &val);
|
|
err |= tg3_readphy(tp, MII_BMSR, &val);
|
|
if (err != 0)
|
|
return -EBUSY;
|
|
|
|
netdev_link_down(tp->dev);
|
|
tg3_link_report(tp);
|
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 ||
|
|
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
|
|
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
|
|
err = tg3_phy_reset_5703_4_5(tp);
|
|
if (err)
|
|
return err;
|
|
goto out;
|
|
}
|
|
|
|
cpmuctrl = 0;
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
|
|
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) {
|
|
cpmuctrl = tr32(TG3_CPMU_CTRL);
|
|
if (cpmuctrl & CPMU_CTRL_GPHY_10MB_RXONLY)
|
|
tw32(TG3_CPMU_CTRL,
|
|
cpmuctrl & ~CPMU_CTRL_GPHY_10MB_RXONLY);
|
|
}
|
|
|
|
err = tg3_bmcr_reset(tp);
|
|
if (err)
|
|
return err;
|
|
|
|
if (cpmuctrl & CPMU_CTRL_GPHY_10MB_RXONLY) {
|
|
val = MII_TG3_DSP_EXP8_AEDW | MII_TG3_DSP_EXP8_REJ2MHz;
|
|
tg3_phydsp_write(tp, MII_TG3_DSP_EXP8, val);
|
|
|
|
tw32(TG3_CPMU_CTRL, cpmuctrl);
|
|
}
|
|
|
|
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX ||
|
|
GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5761_AX) {
|
|
val = tr32(TG3_CPMU_LSPD_1000MB_CLK);
|
|
if ((val & CPMU_LSPD_1000MB_MACCLK_MASK) ==
|
|
CPMU_LSPD_1000MB_MACCLK_12_5) {
|
|
val &= ~CPMU_LSPD_1000MB_MACCLK_MASK;
|
|
udelay(40);
|
|
tw32_f(TG3_CPMU_LSPD_1000MB_CLK, val);
|
|
}
|
|
}
|
|
|
|
if (tg3_flag(tp, 5717_PLUS) &&
|
|
(tp->phy_flags & TG3_PHYFLG_MII_SERDES))
|
|
return 0;
|
|
|
|
tg3_phy_apply_otp(tp);
|
|
|
|
out:
|
|
if ((tp->phy_flags & TG3_PHYFLG_ADC_BUG) &&
|
|
!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
|
|
tg3_phydsp_write(tp, 0x201f, 0x2aaa);
|
|
tg3_phydsp_write(tp, 0x000a, 0x0323);
|
|
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
|
|
}
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_5704_A0_BUG) {
|
|
tg3_writephy(tp, MII_TG3_MISC_SHDW, 0x8d68);
|
|
tg3_writephy(tp, MII_TG3_MISC_SHDW, 0x8d68);
|
|
}
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_BER_BUG) {
|
|
if (!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
|
|
tg3_phydsp_write(tp, 0x000a, 0x310b);
|
|
tg3_phydsp_write(tp, 0x201f, 0x9506);
|
|
tg3_phydsp_write(tp, 0x401f, 0x14e2);
|
|
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
|
|
}
|
|
} else if (tp->phy_flags & TG3_PHYFLG_JITTER_BUG) {
|
|
if (!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
|
|
tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a);
|
|
if (tp->phy_flags & TG3_PHYFLG_ADJUST_TRIM) {
|
|
tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x110b);
|
|
tg3_writephy(tp, MII_TG3_TEST1,
|
|
MII_TG3_TEST1_TRIM_EN | 0x4);
|
|
} else
|
|
tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x010b);
|
|
|
|
TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
|
|
}
|
|
}
|
|
|
|
if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM5401) {
|
|
/* Cannot do read-modify-write on 5401 */
|
|
tg3_phy_auxctl_write(tp, MII_TG3_AUXCTL_SHDWSEL_AUXCTL, 0x4c20);
|
|
}
|
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
|
|
/* adjust output voltage */
|
|
tg3_writephy(tp, MII_TG3_FET_PTEST, 0x12);
|
|
}
|
|
|
|
tg3_phy_toggle_automdix(tp, 1);
|
|
tg3_phy_set_wirespeed(tp);
|
|
return 0;
|
|
}
|
|
|
|
static int tg3_copper_is_advertising_all(struct tg3 *tp, u32 mask)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 adv_reg, all_mask = 0;
|
|
|
|
if (mask & ADVERTISED_10baseT_Half)
|
|
all_mask |= ADVERTISE_10HALF;
|
|
if (mask & ADVERTISED_10baseT_Full)
|
|
all_mask |= ADVERTISE_10FULL;
|
|
if (mask & ADVERTISED_100baseT_Half)
|
|
all_mask |= ADVERTISE_100HALF;
|
|
if (mask & ADVERTISED_100baseT_Full)
|
|
all_mask |= ADVERTISE_100FULL;
|
|
|
|
if (tg3_readphy(tp, MII_ADVERTISE, &adv_reg))
|
|
return 0;
|
|
|
|
if ((adv_reg & all_mask) != all_mask)
|
|
return 0;
|
|
if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
|
|
u32 tg3_ctrl;
|
|
|
|
all_mask = 0;
|
|
if (mask & ADVERTISED_1000baseT_Half)
|
|
all_mask |= ADVERTISE_1000HALF;
|
|
if (mask & ADVERTISED_1000baseT_Full)
|
|
all_mask |= ADVERTISE_1000FULL;
|
|
|
|
if (tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl))
|
|
return 0;
|
|
|
|
if ((tg3_ctrl & all_mask) != all_mask)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static u16 tg3_advert_flowctrl_1000T(u8 flow_ctrl)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u16 miireg;
|
|
|
|
if ((flow_ctrl & FLOW_CTRL_TX) && (flow_ctrl & FLOW_CTRL_RX))
|
|
miireg = ADVERTISE_PAUSE_CAP;
|
|
else if (flow_ctrl & FLOW_CTRL_TX)
|
|
miireg = ADVERTISE_PAUSE_ASYM;
|
|
else if (flow_ctrl & FLOW_CTRL_RX)
|
|
miireg = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
|
|
else
|
|
miireg = 0;
|
|
|
|
return miireg;
|
|
}
|
|
|
|
static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int err = 0;
|
|
u32 val __unused, new_adv;
|
|
|
|
new_adv = ADVERTISE_CSMA;
|
|
if (advertise & ADVERTISED_10baseT_Half)
|
|
new_adv |= ADVERTISE_10HALF;
|
|
if (advertise & ADVERTISED_10baseT_Full)
|
|
new_adv |= ADVERTISE_10FULL;
|
|
if (advertise & ADVERTISED_100baseT_Half)
|
|
new_adv |= ADVERTISE_100HALF;
|
|
if (advertise & ADVERTISED_100baseT_Full)
|
|
new_adv |= ADVERTISE_100FULL;
|
|
|
|
new_adv |= tg3_advert_flowctrl_1000T(flowctrl);
|
|
|
|
err = tg3_writephy(tp, MII_ADVERTISE, new_adv);
|
|
if (err)
|
|
goto done;
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
|
|
goto done;
|
|
|
|
new_adv = 0;
|
|
if (advertise & ADVERTISED_1000baseT_Half)
|
|
new_adv |= MII_TG3_CTRL_ADV_1000_HALF;
|
|
if (advertise & ADVERTISED_1000baseT_Full)
|
|
new_adv |= MII_TG3_CTRL_ADV_1000_FULL;
|
|
|
|
if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
|
|
tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)
|
|
new_adv |= (MII_TG3_CTRL_AS_MASTER |
|
|
MII_TG3_CTRL_ENABLE_AS_MASTER);
|
|
|
|
err = tg3_writephy(tp, MII_TG3_CTRL, new_adv);
|
|
if (err)
|
|
goto done;
|
|
|
|
if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP))
|
|
goto done;
|
|
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
static int tg3_init_5401phy_dsp(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int err;
|
|
|
|
/* Turn off tap power management. */
|
|
/* Set Extended packet length bit */
|
|
err = tg3_phy_auxctl_write(tp, MII_TG3_AUXCTL_SHDWSEL_AUXCTL, 0x4c20);
|
|
|
|
err |= tg3_phydsp_write(tp, 0x0012, 0x1804);
|
|
err |= tg3_phydsp_write(tp, 0x0013, 0x1204);
|
|
err |= tg3_phydsp_write(tp, 0x8006, 0x0132);
|
|
err |= tg3_phydsp_write(tp, 0x8006, 0x0232);
|
|
err |= tg3_phydsp_write(tp, 0x201f, 0x0a20);
|
|
|
|
udelay(40);
|
|
|
|
return err;
|
|
}
|
|
|
|
#define ADVERTISED_Autoneg (1 << 6)
|
|
#define ADVERTISED_Pause (1 << 13)
|
|
#define ADVERTISED_TP (1 << 7)
|
|
#define ADVERTISED_FIBRE (1 << 10)
|
|
|
|
#define AUTONEG_ENABLE 0x01
|
|
|
|
static void tg3_phy_init_link_config(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 adv = ADVERTISED_Autoneg |
|
|
ADVERTISED_Pause;
|
|
|
|
|
|
if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY))
|
|
adv |= ADVERTISED_1000baseT_Half |
|
|
ADVERTISED_1000baseT_Full;
|
|
if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES))
|
|
adv |= ADVERTISED_100baseT_Half |
|
|
ADVERTISED_100baseT_Full |
|
|
ADVERTISED_10baseT_Half |
|
|
ADVERTISED_10baseT_Full |
|
|
ADVERTISED_TP;
|
|
else
|
|
adv |= ADVERTISED_FIBRE;
|
|
|
|
tp->link_config.advertising = adv;
|
|
tp->link_config.speed = SPEED_INVALID;
|
|
tp->link_config.duplex = DUPLEX_INVALID;
|
|
tp->link_config.autoneg = AUTONEG_ENABLE;
|
|
tp->link_config.active_speed = SPEED_INVALID;
|
|
tp->link_config.active_duplex = DUPLEX_INVALID;
|
|
tp->link_config.orig_speed = SPEED_INVALID;
|
|
tp->link_config.orig_duplex = DUPLEX_INVALID;
|
|
tp->link_config.orig_autoneg = AUTONEG_INVALID;
|
|
}
|
|
|
|
int tg3_phy_probe(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 hw_phy_id_1, hw_phy_id_2;
|
|
u32 hw_phy_id, hw_phy_id_masked;
|
|
int err;
|
|
|
|
/* flow control autonegotiation is default behavior */
|
|
tg3_flag_set(tp, PAUSE_AUTONEG);
|
|
tp->link_config.flowctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
|
|
|
|
/* Reading the PHY ID register can conflict with ASF
|
|
* firmware access to the PHY hardware.
|
|
*/
|
|
err = 0;
|
|
if (tg3_flag(tp, ENABLE_ASF) || tg3_flag(tp, ENABLE_APE)) {
|
|
hw_phy_id = hw_phy_id_masked = TG3_PHY_ID_INVALID;
|
|
} else {
|
|
/* Now read the physical PHY_ID from the chip and verify
|
|
* that it is sane. If it doesn't look good, we fall back
|
|
* to either the hard-coded table based PHY_ID and failing
|
|
* that the value found in the eeprom area.
|
|
*/
|
|
err |= tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1);
|
|
err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2);
|
|
|
|
hw_phy_id = (hw_phy_id_1 & 0xffff) << 10;
|
|
hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16;
|
|
hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0;
|
|
|
|
hw_phy_id_masked = hw_phy_id & TG3_PHY_ID_MASK;
|
|
}
|
|
|
|
if (!err && TG3_KNOWN_PHY_ID(hw_phy_id_masked)) {
|
|
tp->phy_id = hw_phy_id;
|
|
if (hw_phy_id_masked == TG3_PHY_ID_BCM8002)
|
|
tp->phy_flags |= TG3_PHYFLG_PHY_SERDES;
|
|
else
|
|
tp->phy_flags &= ~TG3_PHYFLG_PHY_SERDES;
|
|
} else {
|
|
if (tp->phy_id != TG3_PHY_ID_INVALID) {
|
|
/* Do nothing, phy ID already set up in
|
|
* tg3_get_eeprom_hw_cfg().
|
|
*/
|
|
} else {
|
|
struct subsys_tbl_ent *p;
|
|
|
|
/* No eeprom signature? Try the hardcoded
|
|
* subsys device table.
|
|
*/
|
|
p = tg3_lookup_by_subsys(tp);
|
|
if (!p) {
|
|
DBGC(&tp->pdev->dev, "lookup by subsys failed\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
tp->phy_id = p->phy_id;
|
|
if (!tp->phy_id ||
|
|
tp->phy_id == TG3_PHY_ID_BCM8002)
|
|
tp->phy_flags |= TG3_PHYFLG_PHY_SERDES;
|
|
}
|
|
}
|
|
|
|
if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
|
|
((tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 &&
|
|
tp->pci_chip_rev_id != CHIPREV_ID_5717_A0) ||
|
|
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765 &&
|
|
tp->pci_chip_rev_id != CHIPREV_ID_57765_A0)))
|
|
tp->phy_flags |= TG3_PHYFLG_EEE_CAP;
|
|
|
|
tg3_phy_init_link_config(tp);
|
|
|
|
if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES) &&
|
|
!tg3_flag(tp, ENABLE_APE) &&
|
|
!tg3_flag(tp, ENABLE_ASF)) {
|
|
u32 bmsr;
|
|
u32 mask;
|
|
|
|
tg3_readphy(tp, MII_BMSR, &bmsr);
|
|
if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&
|
|
(bmsr & BMSR_LSTATUS))
|
|
goto skip_phy_reset;
|
|
|
|
err = tg3_phy_reset(tp);
|
|
if (err)
|
|
return err;
|
|
|
|
tg3_phy_set_wirespeed(tp);
|
|
|
|
mask = (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
|
|
ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
|
|
ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full);
|
|
if (!tg3_copper_is_advertising_all(tp, mask)) {
|
|
tg3_phy_autoneg_cfg(tp, tp->link_config.advertising,
|
|
tp->link_config.flowctrl);
|
|
|
|
tg3_writephy(tp, MII_BMCR,
|
|
BMCR_ANENABLE | BMCR_ANRESTART);
|
|
}
|
|
}
|
|
|
|
skip_phy_reset:
|
|
if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM5401) {
|
|
err = tg3_init_5401phy_dsp(tp);
|
|
if (err)
|
|
return err;
|
|
|
|
err = tg3_init_5401phy_dsp(tp);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void tg3_poll_link(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
if (tp->hw_status->status & SD_STATUS_LINK_CHG) {
|
|
DBGC(tp->dev,"link_changed\n");
|
|
tp->hw_status->status &= ~SD_STATUS_LINK_CHG;
|
|
tg3_setup_phy(tp, 0);
|
|
}
|
|
}
|
|
|
|
static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
switch (val & MII_TG3_AUX_STAT_SPDMASK) {
|
|
case MII_TG3_AUX_STAT_10HALF:
|
|
*speed = SPEED_10;
|
|
*duplex = DUPLEX_HALF;
|
|
break;
|
|
|
|
case MII_TG3_AUX_STAT_10FULL:
|
|
*speed = SPEED_10;
|
|
*duplex = DUPLEX_FULL;
|
|
break;
|
|
|
|
case MII_TG3_AUX_STAT_100HALF:
|
|
*speed = SPEED_100;
|
|
*duplex = DUPLEX_HALF;
|
|
break;
|
|
|
|
case MII_TG3_AUX_STAT_100FULL:
|
|
*speed = SPEED_100;
|
|
*duplex = DUPLEX_FULL;
|
|
break;
|
|
|
|
case MII_TG3_AUX_STAT_1000HALF:
|
|
*speed = SPEED_1000;
|
|
*duplex = DUPLEX_HALF;
|
|
break;
|
|
|
|
case MII_TG3_AUX_STAT_1000FULL:
|
|
*speed = SPEED_1000;
|
|
*duplex = DUPLEX_FULL;
|
|
break;
|
|
|
|
default:
|
|
if (tp->phy_flags & TG3_PHYFLG_IS_FET) {
|
|
*speed = (val & MII_TG3_AUX_STAT_100) ? SPEED_100 :
|
|
SPEED_10;
|
|
*duplex = (val & MII_TG3_AUX_STAT_FULL) ? DUPLEX_FULL :
|
|
DUPLEX_HALF;
|
|
break;
|
|
}
|
|
*speed = SPEED_INVALID;
|
|
*duplex = DUPLEX_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int tg3_adv_1000T_flowctrl_ok(struct tg3 *tp, u32 *lcladv, u32 *rmtadv)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 curadv, reqadv;
|
|
|
|
if (tg3_readphy(tp, MII_ADVERTISE, lcladv))
|
|
return 1;
|
|
|
|
curadv = *lcladv & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
|
|
reqadv = tg3_advert_flowctrl_1000T(tp->link_config.flowctrl);
|
|
|
|
if (tp->link_config.active_duplex == DUPLEX_FULL) {
|
|
if (curadv != reqadv)
|
|
return 0;
|
|
|
|
if (tg3_flag(tp, PAUSE_AUTONEG))
|
|
tg3_readphy(tp, MII_LPA, rmtadv);
|
|
} else {
|
|
/* Reprogram the advertisement register, even if it
|
|
* does not affect the current link. If the link
|
|
* gets renegotiated in the future, we can save an
|
|
* additional renegotiation cycle by advertising
|
|
* it correctly in the first place.
|
|
*/
|
|
if (curadv != reqadv) {
|
|
*lcladv &= ~(ADVERTISE_PAUSE_CAP |
|
|
ADVERTISE_PAUSE_ASYM);
|
|
tg3_writephy(tp, MII_ADVERTISE, *lcladv | reqadv);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static u8 tg3_resolve_flowctrl_1000X(u16 lcladv, u16 rmtadv)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u8 cap = 0;
|
|
|
|
if (lcladv & ADVERTISE_1000XPAUSE) {
|
|
if (lcladv & ADVERTISE_1000XPSE_ASYM) {
|
|
if (rmtadv & LPA_1000XPAUSE)
|
|
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
|
|
else if (rmtadv & LPA_1000XPAUSE_ASYM)
|
|
cap = FLOW_CTRL_RX;
|
|
} else {
|
|
if (rmtadv & LPA_1000XPAUSE)
|
|
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
|
|
}
|
|
} else if (lcladv & ADVERTISE_1000XPSE_ASYM) {
|
|
if ((rmtadv & LPA_1000XPAUSE) && (rmtadv & LPA_1000XPAUSE_ASYM))
|
|
cap = FLOW_CTRL_TX;
|
|
}
|
|
|
|
return cap;
|
|
}
|
|
|
|
static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u8 flowctrl = 0;
|
|
u32 old_rx_mode = tp->rx_mode;
|
|
u32 old_tx_mode = tp->tx_mode;
|
|
|
|
if (tg3_flag(tp, PAUSE_AUTONEG)) {
|
|
if (tp->phy_flags & TG3_PHYFLG_ANY_SERDES)
|
|
flowctrl = tg3_resolve_flowctrl_1000X(lcladv, rmtadv);
|
|
else
|
|
flowctrl = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
|
|
} else
|
|
flowctrl = tp->link_config.flowctrl;
|
|
|
|
tp->link_config.active_flowctrl = flowctrl;
|
|
|
|
if (flowctrl & FLOW_CTRL_RX)
|
|
tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE;
|
|
else
|
|
tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE;
|
|
|
|
if (old_rx_mode != tp->rx_mode)
|
|
tw32_f(MAC_RX_MODE, tp->rx_mode);
|
|
|
|
if (flowctrl & FLOW_CTRL_TX)
|
|
tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE;
|
|
else
|
|
tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE;
|
|
|
|
if (old_tx_mode != tp->tx_mode)
|
|
tw32_f(MAC_TX_MODE, tp->tx_mode);
|
|
}
|
|
|
|
static void tg3_phy_copper_begin(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 new_adv;
|
|
|
|
if (tp->link_config.speed == SPEED_INVALID) {
|
|
if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
|
|
tp->link_config.advertising &=
|
|
~(ADVERTISED_1000baseT_Half |
|
|
ADVERTISED_1000baseT_Full);
|
|
|
|
tg3_phy_autoneg_cfg(tp, tp->link_config.advertising,
|
|
tp->link_config.flowctrl);
|
|
} else {
|
|
/* Asking for a specific link mode. */
|
|
if (tp->link_config.speed == SPEED_1000) {
|
|
if (tp->link_config.duplex == DUPLEX_FULL)
|
|
new_adv = ADVERTISED_1000baseT_Full;
|
|
else
|
|
new_adv = ADVERTISED_1000baseT_Half;
|
|
} else if (tp->link_config.speed == SPEED_100) {
|
|
if (tp->link_config.duplex == DUPLEX_FULL)
|
|
new_adv = ADVERTISED_100baseT_Full;
|
|
else
|
|
new_adv = ADVERTISED_100baseT_Half;
|
|
} else {
|
|
if (tp->link_config.duplex == DUPLEX_FULL)
|
|
new_adv = ADVERTISED_10baseT_Full;
|
|
else
|
|
new_adv = ADVERTISED_10baseT_Half;
|
|
}
|
|
|
|
tg3_phy_autoneg_cfg(tp, new_adv,
|
|
tp->link_config.flowctrl);
|
|
}
|
|
|
|
tg3_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
|
|
}
|
|
|
|
static int tg3_5700_link_polarity(struct tg3 *tp, u32 speed)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
if (tp->led_ctrl == LED_CTRL_MODE_PHY_2)
|
|
return 1;
|
|
else if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM5411) {
|
|
if (speed != SPEED_10)
|
|
return 1;
|
|
} else if (speed == SPEED_10)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 1
|
|
|
|
static void tg3_ump_link_report(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 reg;
|
|
u32 val;
|
|
|
|
if (!tg3_flag(tp, 5780_CLASS) || !tg3_flag(tp, ENABLE_ASF))
|
|
return;
|
|
|
|
tg3_wait_for_event_ack(tp);
|
|
|
|
tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_LINK_UPDATE);
|
|
|
|
tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 14);
|
|
|
|
val = 0;
|
|
if (!tg3_readphy(tp, MII_BMCR, ®))
|
|
val = reg << 16;
|
|
if (!tg3_readphy(tp, MII_BMSR, ®))
|
|
val |= (reg & 0xffff);
|
|
tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, val);
|
|
|
|
val = 0;
|
|
if (!tg3_readphy(tp, MII_ADVERTISE, ®))
|
|
val = reg << 16;
|
|
if (!tg3_readphy(tp, MII_LPA, ®))
|
|
val |= (reg & 0xffff);
|
|
tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 4, val);
|
|
|
|
val = 0;
|
|
if (!(tp->phy_flags & TG3_PHYFLG_MII_SERDES)) {
|
|
if (!tg3_readphy(tp, MII_CTRL1000, ®))
|
|
val = reg << 16;
|
|
if (!tg3_readphy(tp, MII_STAT1000, ®))
|
|
val |= (reg & 0xffff);
|
|
}
|
|
tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 8, val);
|
|
|
|
if (!tg3_readphy(tp, MII_PHYADDR, ®))
|
|
val = reg << 16;
|
|
else
|
|
val = 0;
|
|
tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 12, val);
|
|
|
|
tg3_generate_fw_event(tp);
|
|
}
|
|
|
|
/* NOTE: Debugging only code */
|
|
static void tg3_link_report(struct tg3 *tp)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
if (!netdev_link_ok(tp->dev)) {
|
|
DBGC(tp->dev, "Link is down\n");
|
|
tg3_ump_link_report(tp);
|
|
} else {
|
|
DBGC(tp->dev, "Link is up at %d Mbps, %s duplex\n",
|
|
(tp->link_config.active_speed == SPEED_1000 ?
|
|
1000 :
|
|
(tp->link_config.active_speed == SPEED_100 ?
|
|
100 : 10)),
|
|
(tp->link_config.active_duplex == DUPLEX_FULL ?
|
|
"full" : "half"));
|
|
|
|
DBGC(tp->dev, "Flow control is %s for TX and %s for RX\n",
|
|
(tp->link_config.active_flowctrl & FLOW_CTRL_TX) ?
|
|
"on" : "off",
|
|
(tp->link_config.active_flowctrl & FLOW_CTRL_RX) ?
|
|
"on" : "off");
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_EEE_CAP)
|
|
DBGC(tp->dev, "EEE is %s\n",
|
|
tp->setlpicnt ? "enabled" : "disabled");
|
|
|
|
tg3_ump_link_report(tp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
int current_link_up;
|
|
u32 bmsr, val;
|
|
u32 lcl_adv, rmt_adv;
|
|
u16 current_speed;
|
|
u8 current_duplex;
|
|
int i, err;
|
|
|
|
tw32(MAC_EVENT, 0);
|
|
|
|
tw32_f(MAC_STATUS,
|
|
(MAC_STATUS_SYNC_CHANGED |
|
|
MAC_STATUS_CFG_CHANGED |
|
|
MAC_STATUS_MI_COMPLETION |
|
|
MAC_STATUS_LNKSTATE_CHANGED));
|
|
udelay(40);
|
|
|
|
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
|
|
tw32_f(MAC_MI_MODE,
|
|
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
|
|
udelay(80);
|
|
}
|
|
|
|
tg3_phy_auxctl_write(tp, MII_TG3_AUXCTL_SHDWSEL_PWRCTL, 0);
|
|
|
|
/* Some third-party PHYs need to be reset on link going
|
|
* down.
|
|
*/
|
|
if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 ||
|
|
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
|
|
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
|
|
netdev_link_ok(tp->dev)) {
|
|
tg3_readphy(tp, MII_BMSR, &bmsr);
|
|
if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&
|
|
!(bmsr & BMSR_LSTATUS))
|
|
force_reset = 1;
|
|
}
|
|
if (force_reset)
|
|
tg3_phy_reset(tp);
|
|
|
|
if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM5401) {
|
|
tg3_readphy(tp, MII_BMSR, &bmsr);
|
|
if (tg3_readphy(tp, MII_BMSR, &bmsr) ||
|
|
!tg3_flag(tp, INIT_COMPLETE))
|
|
bmsr = 0;
|
|
|
|
if (!(bmsr & BMSR_LSTATUS)) {
|
|
err = tg3_init_5401phy_dsp(tp);
|
|
if (err)
|
|
return err;
|
|
|
|
tg3_readphy(tp, MII_BMSR, &bmsr);
|
|
for (i = 0; i < 1000; i++) {
|
|
udelay(10);
|
|
if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&
|
|
(bmsr & BMSR_LSTATUS)) {
|
|
udelay(40);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((tp->phy_id & TG3_PHY_ID_REV_MASK) ==
|
|
TG3_PHY_REV_BCM5401_B0 &&
|
|
!(bmsr & BMSR_LSTATUS) &&
|
|
tp->link_config.active_speed == SPEED_1000) {
|
|
err = tg3_phy_reset(tp);
|
|
if (!err)
|
|
err = tg3_init_5401phy_dsp(tp);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
} else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
|
|
tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) {
|
|
/* 5701 {A0,B0} CRC bug workaround */
|
|
tg3_writephy(tp, 0x15, 0x0a75);
|
|
tg3_writephy(tp, MII_TG3_MISC_SHDW, 0x8c68);
|
|
tg3_writephy(tp, MII_TG3_MISC_SHDW, 0x8d68);
|
|
tg3_writephy(tp, MII_TG3_MISC_SHDW, 0x8c68);
|
|
}
|
|
|
|
/* Clear pending interrupts... */
|
|
tg3_readphy(tp, MII_TG3_ISTAT, &val);
|
|
tg3_readphy(tp, MII_TG3_ISTAT, &val);
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_USE_MI_INTERRUPT)
|
|
tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG);
|
|
else if (!(tp->phy_flags & TG3_PHYFLG_IS_FET))
|
|
tg3_writephy(tp, MII_TG3_IMASK, ~0);
|
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
|
|
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
|
|
if (tp->led_ctrl == LED_CTRL_MODE_PHY_1)
|
|
tg3_writephy(tp, MII_TG3_EXT_CTRL,
|
|
MII_TG3_EXT_CTRL_LNK3_LED_MODE);
|
|
else
|
|
tg3_writephy(tp, MII_TG3_EXT_CTRL, 0);
|
|
}
|
|
|
|
current_link_up = 0;
|
|
current_speed = SPEED_INVALID;
|
|
current_duplex = DUPLEX_INVALID;
|
|
|
|
if (tp->phy_flags & TG3_PHYFLG_CAPACITIVE_COUPLING) {
|
|
err = tg3_phy_auxctl_read(tp,
|
|
MII_TG3_AUXCTL_SHDWSEL_MISCTEST,
|
|
&val);
|
|
if (!err && !(val & (1 << 10))) {
|
|
tg3_phy_auxctl_write(tp,
|
|
MII_TG3_AUXCTL_SHDWSEL_MISCTEST,
|
|
val | (1 << 10));
|
|
goto relink;
|
|
}
|
|
}
|
|
|
|
bmsr = 0;
|
|
for (i = 0; i < 100; i++) {
|
|
tg3_readphy(tp, MII_BMSR, &bmsr);
|
|
if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&
|
|
(bmsr & BMSR_LSTATUS))
|
|
break;
|
|
udelay(40);
|
|
}
|
|
|
|
if (bmsr & BMSR_LSTATUS) {
|
|
u32 aux_stat, bmcr;
|
|
|
|
tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat);
|
|
for (i = 0; i < 2000; i++) {
|
|
udelay(10);
|
|
if (!tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat) &&
|
|
aux_stat)
|
|
break;
|
|
}
|
|
|
|
tg3_aux_stat_to_speed_duplex(tp, aux_stat,
|
|
¤t_speed,
|
|
¤t_duplex);
|
|
|
|
bmcr = 0;
|
|
for (i = 0; i < 200; i++) {
|
|
tg3_readphy(tp, MII_BMCR, &bmcr);
|
|
if (tg3_readphy(tp, MII_BMCR, &bmcr))
|
|
continue;
|
|
if (bmcr && bmcr != 0x7fff)
|
|
break;
|
|
udelay(10);
|
|
}
|
|
|
|
lcl_adv = 0;
|
|
rmt_adv = 0;
|
|
|
|
tp->link_config.active_speed = current_speed;
|
|
tp->link_config.active_duplex = current_duplex;
|
|
|
|
if ((bmcr & BMCR_ANENABLE) &&
|
|
tg3_copper_is_advertising_all(tp,
|
|
tp->link_config.advertising)) {
|
|
if (tg3_adv_1000T_flowctrl_ok(tp, &lcl_adv,
|
|
&rmt_adv)) {
|
|
current_link_up = 1;
|
|
}
|
|
}
|
|
|
|
if (current_link_up == 1 &&
|
|
tp->link_config.active_duplex == DUPLEX_FULL)
|
|
tg3_setup_flow_control(tp, lcl_adv, rmt_adv);
|
|
}
|
|
|
|
relink:
|
|
if (current_link_up == 0) {
|
|
tg3_phy_copper_begin(tp);
|
|
|
|
tg3_readphy(tp, MII_BMSR, &bmsr);
|
|
if ((!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) ||
|
|
(tp->mac_mode & MAC_MODE_PORT_INT_LPBACK))
|
|
current_link_up = 1;
|
|
}
|
|
|
|
tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK;
|
|
if (current_link_up == 1) {
|
|
if (tp->link_config.active_speed == SPEED_100 ||
|
|
tp->link_config.active_speed == SPEED_10)
|
|
tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
|
|
else
|
|
tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
|
|
} else if (tp->phy_flags & TG3_PHYFLG_IS_FET)
|
|
tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
|
|
else
|
|
tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
|
|
|
|
tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX;
|
|
if (tp->link_config.active_duplex == DUPLEX_HALF)
|
|
tp->mac_mode |= MAC_MODE_HALF_DUPLEX;
|
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) {
|
|
if (current_link_up == 1 &&
|
|
tg3_5700_link_polarity(tp, tp->link_config.active_speed))
|
|
tp->mac_mode |= MAC_MODE_LINK_POLARITY;
|
|
else
|
|
tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
|
|
}
|
|
|
|
/* ??? Without this setting Netgear GA302T PHY does not
|
|
* ??? send/receive packets...
|
|
*/
|
|
if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM5411 &&
|
|
tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) {
|
|
tp->mi_mode |= MAC_MI_MODE_AUTO_POLL;
|
|
tw32_f(MAC_MI_MODE, tp->mi_mode);
|
|
udelay(80);
|
|
}
|
|
|
|
tw32_f(MAC_MODE, tp->mac_mode);
|
|
udelay(40);
|
|
|
|
/* Enabled attention when the link has changed state. */
|
|
tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED);
|
|
udelay(40);
|
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 &&
|
|
current_link_up == 1 &&
|
|
tp->link_config.active_speed == SPEED_1000 &&
|
|
(tg3_flag(tp, PCIX_MODE) || tg3_flag(tp, PCI_HIGH_SPEED))) {
|
|
udelay(120);
|
|
/* NOTE: this freezes for mdc? */
|
|
tw32_f(MAC_STATUS,
|
|
(MAC_STATUS_SYNC_CHANGED |
|
|
MAC_STATUS_CFG_CHANGED));
|
|
udelay(40);
|
|
tg3_write_mem(tp,
|
|
NIC_SRAM_FIRMWARE_MBOX,
|
|
NIC_SRAM_FIRMWARE_MBOX_MAGIC2);
|
|
}
|
|
|
|
/* Prevent send BD corruption. */
|
|
if (tg3_flag(tp, CLKREQ_BUG)) {
|
|
u16 oldlnkctl, newlnkctl;
|
|
|
|
pci_read_config_word(tp->pdev,
|
|
tp->pcie_cap + PCI_EXP_LNKCTL,
|
|
&oldlnkctl);
|
|
if (tp->link_config.active_speed == SPEED_100 ||
|
|
tp->link_config.active_speed == SPEED_10)
|
|
newlnkctl = oldlnkctl & ~PCI_EXP_LNKCTL_CLKREQ_EN;
|
|
else
|
|
newlnkctl = oldlnkctl | PCI_EXP_LNKCTL_CLKREQ_EN;
|
|
if (newlnkctl != oldlnkctl)
|
|
pci_write_config_word(tp->pdev,
|
|
tp->pcie_cap + PCI_EXP_LNKCTL,
|
|
newlnkctl);
|
|
}
|
|
|
|
if (current_link_up != netdev_link_ok(tp->dev)) {
|
|
if (current_link_up)
|
|
netdev_link_up(tp->dev);
|
|
else
|
|
netdev_link_down(tp->dev);
|
|
tg3_link_report(tp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tg3_setup_phy(struct tg3 *tp, int force_reset)
|
|
{ DBGP("%s\n", __func__);
|
|
|
|
u32 val;
|
|
int err;
|
|
|
|
#if 0
|
|
if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES)
|
|
err = tg3_setup_fiber_phy(tp, force_reset);
|
|
else if (tp->phy_flags & TG3_PHYFLG_MII_SERDES)
|
|
err = tg3_setup_fiber_mii_phy(tp, force_reset);
|
|
else
|
|
#endif
|
|
/* FIXME: add only copper phy variants for now */
|
|
err = tg3_setup_copper_phy(tp, force_reset);
|
|
|
|
val = (2 << TX_LENGTHS_IPG_CRS_SHIFT) |
|
|
(6 << TX_LENGTHS_IPG_SHIFT);
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
|
|
val |= tr32(MAC_TX_LENGTHS) &
|
|
(TX_LENGTHS_JMB_FRM_LEN_MSK |
|
|
TX_LENGTHS_CNT_DWN_VAL_MSK);
|
|
|
|
if (tp->link_config.active_speed == SPEED_1000 &&
|
|
tp->link_config.active_duplex == DUPLEX_HALF)
|
|
tw32(MAC_TX_LENGTHS, val |
|
|
(0xff << TX_LENGTHS_SLOT_TIME_SHIFT));
|
|
else
|
|
tw32(MAC_TX_LENGTHS, val |
|
|
(32 << TX_LENGTHS_SLOT_TIME_SHIFT));
|
|
|
|
if (!tg3_flag(tp, 5705_PLUS)) {
|
|
if (netdev_link_ok(tp->dev)) {
|
|
tw32(HOSTCC_STAT_COAL_TICKS, DEFAULT_STAT_COAL_TICKS);
|
|
} else {
|
|
tw32(HOSTCC_STAT_COAL_TICKS, 0);
|
|
}
|
|
}
|
|
|
|
val = tr32(PCIE_PWR_MGMT_THRESH);
|
|
if (!netdev_link_ok(tp->dev))
|
|
val = (val & ~PCIE_PWR_MGMT_L1_THRESH_MSK);
|
|
else
|
|
val |= PCIE_PWR_MGMT_L1_THRESH_MSK;
|
|
tw32(PCIE_PWR_MGMT_THRESH, val);
|
|
|
|
return err;
|
|
}
|