| |
| #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 |
| |
| struct tg3_fiber_aneginfo { |
| int state; |
| #define ANEG_STATE_UNKNOWN 0 |
| #define ANEG_STATE_AN_ENABLE 1 |
| #define ANEG_STATE_RESTART_INIT 2 |
| #define ANEG_STATE_RESTART 3 |
| #define ANEG_STATE_DISABLE_LINK_OK 4 |
| #define ANEG_STATE_ABILITY_DETECT_INIT 5 |
| #define ANEG_STATE_ABILITY_DETECT 6 |
| #define ANEG_STATE_ACK_DETECT_INIT 7 |
| #define ANEG_STATE_ACK_DETECT 8 |
| #define ANEG_STATE_COMPLETE_ACK_INIT 9 |
| #define ANEG_STATE_COMPLETE_ACK 10 |
| #define ANEG_STATE_IDLE_DETECT_INIT 11 |
| #define ANEG_STATE_IDLE_DETECT 12 |
| #define ANEG_STATE_LINK_OK 13 |
| #define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14 |
| #define ANEG_STATE_NEXT_PAGE_WAIT 15 |
| |
| u32 flags; |
| #define MR_AN_ENABLE 0x00000001 |
| #define MR_RESTART_AN 0x00000002 |
| #define MR_AN_COMPLETE 0x00000004 |
| #define MR_PAGE_RX 0x00000008 |
| #define MR_NP_LOADED 0x00000010 |
| #define MR_TOGGLE_TX 0x00000020 |
| #define MR_LP_ADV_FULL_DUPLEX 0x00000040 |
| #define MR_LP_ADV_HALF_DUPLEX 0x00000080 |
| #define MR_LP_ADV_SYM_PAUSE 0x00000100 |
| #define MR_LP_ADV_ASYM_PAUSE 0x00000200 |
| #define MR_LP_ADV_REMOTE_FAULT1 0x00000400 |
| #define MR_LP_ADV_REMOTE_FAULT2 0x00000800 |
| #define MR_LP_ADV_NEXT_PAGE 0x00001000 |
| #define MR_TOGGLE_RX 0x00002000 |
| #define MR_NP_RX 0x00004000 |
| |
| #define MR_LINK_OK 0x80000000 |
| |
| unsigned long link_time, cur_time; |
| |
| u32 ability_match_cfg; |
| int ability_match_count; |
| |
| char ability_match, idle_match, ack_match; |
| |
| u32 txconfig, rxconfig; |
| #define ANEG_CFG_NP 0x00000080 |
| #define ANEG_CFG_ACK 0x00000040 |
| #define ANEG_CFG_RF2 0x00000020 |
| #define ANEG_CFG_RF1 0x00000010 |
| #define ANEG_CFG_PS2 0x00000001 |
| #define ANEG_CFG_PS1 0x00008000 |
| #define ANEG_CFG_HD 0x00004000 |
| #define ANEG_CFG_FD 0x00002000 |
| #define ANEG_CFG_INVAL 0x00001f06 |
| |
| }; |
| #define ANEG_OK 0 |
| #define ANEG_DONE 1 |
| #define ANEG_TIMER_ENAB 2 |
| #define ANEG_FAILED -1 |
| |
| #define ANEG_STATE_SETTLE_TIME 10000 |
| |
| static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl) |
| { |
| u16 miireg; |
| |
| if ((flow_ctrl & FLOW_CTRL_TX) && (flow_ctrl & FLOW_CTRL_RX)) |
| miireg = ADVERTISE_1000XPAUSE; |
| else if (flow_ctrl & FLOW_CTRL_TX) |
| miireg = ADVERTISE_1000XPSE_ASYM; |
| else if (flow_ctrl & FLOW_CTRL_RX) |
| miireg = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; |
| else |
| miireg = 0; |
| |
| return miireg; |
| } |
| |
| static void tg3_init_bcm8002(struct tg3 *tp) |
| { |
| u32 mac_status = tr32(MAC_STATUS); |
| int i; |
| |
| /* Reset when initting first time or we have a link. */ |
| if (tg3_flag(tp, INIT_COMPLETE) && |
| !(mac_status & MAC_STATUS_PCS_SYNCED)) |
| return; |
| |
| /* Set PLL lock range. */ |
| tg3_writephy(tp, 0x16, 0x8007); |
| |
| /* SW reset */ |
| tg3_writephy(tp, MII_BMCR, BMCR_RESET); |
| |
| /* Wait for reset to complete. */ |
| /* XXX schedule_timeout() ... */ |
| for (i = 0; i < 500; i++) |
| udelay(10); |
| |
| /* Config mode; select PMA/Ch 1 regs. */ |
| tg3_writephy(tp, 0x10, 0x8411); |
| |
| /* Enable auto-lock and comdet, select txclk for tx. */ |
| tg3_writephy(tp, 0x11, 0x0a10); |
| |
| tg3_writephy(tp, 0x18, 0x00a0); |
| tg3_writephy(tp, 0x16, 0x41ff); |
| |
| /* Assert and deassert POR. */ |
| tg3_writephy(tp, 0x13, 0x0400); |
| udelay(40); |
| tg3_writephy(tp, 0x13, 0x0000); |
| |
| tg3_writephy(tp, 0x11, 0x0a50); |
| udelay(40); |
| tg3_writephy(tp, 0x11, 0x0a10); |
| |
| /* Wait for signal to stabilize */ |
| /* XXX schedule_timeout() ... */ |
| for (i = 0; i < 15000; i++) |
| udelay(10); |
| |
| /* Deselect the channel register so we can read the PHYID |
| * later. |
| */ |
| tg3_writephy(tp, 0x10, 0x8011); |
| } |
| |
| static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) |
| { |
| u16 flowctrl; |
| int current_link_up; |
| u32 sg_dig_ctrl, sg_dig_status; |
| u32 serdes_cfg, expected_sg_dig_ctrl; |
| int workaround, port_a; |
| |
| serdes_cfg = 0; |
| expected_sg_dig_ctrl = 0; |
| workaround = 0; |
| port_a = 1; |
| current_link_up = 0; |
| |
| if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && |
| tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { |
| workaround = 1; |
| if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) |
| port_a = 0; |
| |
| /* preserve bits 0-11,13,14 for signal pre-emphasis */ |
| /* preserve bits 20-23 for voltage regulator */ |
| serdes_cfg = tr32(MAC_SERDES_CFG) & 0x00f06fff; |
| } |
| |
| sg_dig_ctrl = tr32(SG_DIG_CTRL); |
| |
| if (tp->link_config.autoneg != AUTONEG_ENABLE) { |
| if (sg_dig_ctrl & SG_DIG_USING_HW_AUTONEG) { |
| if (workaround) { |
| u32 val = serdes_cfg; |
| |
| if (port_a) |
| val |= 0xc010000; |
| else |
| val |= 0x4010000; |
| tw32_f(MAC_SERDES_CFG, val); |
| } |
| |
| tw32_f(SG_DIG_CTRL, SG_DIG_COMMON_SETUP); |
| } |
| if (mac_status & MAC_STATUS_PCS_SYNCED) { |
| tg3_setup_flow_control(tp, 0, 0); |
| current_link_up = 1; |
| } |
| goto out; |
| } |
| |
| /* Want auto-negotiation. */ |
| expected_sg_dig_ctrl = SG_DIG_USING_HW_AUTONEG | SG_DIG_COMMON_SETUP; |
| |
| flowctrl = tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); |
| if (flowctrl & ADVERTISE_1000XPAUSE) |
| expected_sg_dig_ctrl |= SG_DIG_PAUSE_CAP; |
| if (flowctrl & ADVERTISE_1000XPSE_ASYM) |
| expected_sg_dig_ctrl |= SG_DIG_ASYM_PAUSE; |
| |
| if (sg_dig_ctrl != expected_sg_dig_ctrl) { |
| if ((tp->phy_flags & TG3_PHYFLG_PARALLEL_DETECT) && |
| tp->serdes_counter && |
| ((mac_status & (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_RCVD_CFG)) == |
| MAC_STATUS_PCS_SYNCED)) { |
| tp->serdes_counter--; |
| current_link_up = 1; |
| goto out; |
| } |
| restart_autoneg: |
| if (workaround) |
| tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000); |
| tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | SG_DIG_SOFT_RESET); |
| udelay(5); |
| tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); |
| |
| tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; |
| tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; |
| } else if (mac_status & (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_SIGNAL_DET)) { |
| sg_dig_status = tr32(SG_DIG_STATUS); |
| mac_status = tr32(MAC_STATUS); |
| |
| if ((sg_dig_status & SG_DIG_AUTONEG_COMPLETE) && |
| (mac_status & MAC_STATUS_PCS_SYNCED)) { |
| u32 local_adv = 0, remote_adv = 0; |
| |
| if (sg_dig_ctrl & SG_DIG_PAUSE_CAP) |
| local_adv |= ADVERTISE_1000XPAUSE; |
| if (sg_dig_ctrl & SG_DIG_ASYM_PAUSE) |
| local_adv |= ADVERTISE_1000XPSE_ASYM; |
| |
| if (sg_dig_status & SG_DIG_PARTNER_PAUSE_CAPABLE) |
| remote_adv |= LPA_1000XPAUSE; |
| if (sg_dig_status & SG_DIG_PARTNER_ASYM_PAUSE) |
| remote_adv |= LPA_1000XPAUSE_ASYM; |
| |
| tp->link_config.rmt_adv = |
| mii_adv_to_ethtool_adv_x(remote_adv); |
| |
| tg3_setup_flow_control(tp, local_adv, remote_adv); |
| current_link_up = 1; |
| tp->serdes_counter = 0; |
| tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; |
| } else if (!(sg_dig_status & SG_DIG_AUTONEG_COMPLETE)) { |
| if (tp->serdes_counter) |
| tp->serdes_counter--; |
| else { |
| if (workaround) { |
| u32 val = serdes_cfg; |
| |
| if (port_a) |
| val |= 0xc010000; |
| else |
| val |= 0x4010000; |
| |
| tw32_f(MAC_SERDES_CFG, val); |
| } |
| |
| tw32_f(SG_DIG_CTRL, SG_DIG_COMMON_SETUP); |
| udelay(40); |
| |
| /* Link parallel detection - link is up */ |
| /* only if we have PCS_SYNC and not */ |
| /* receiving config code words */ |
| mac_status = tr32(MAC_STATUS); |
| if ((mac_status & MAC_STATUS_PCS_SYNCED) && |
| !(mac_status & MAC_STATUS_RCVD_CFG)) { |
| tg3_setup_flow_control(tp, 0, 0); |
| current_link_up = 1; |
| tp->phy_flags |= |
| TG3_PHYFLG_PARALLEL_DETECT; |
| tp->serdes_counter = |
| SERDES_PARALLEL_DET_TIMEOUT; |
| } else |
| goto restart_autoneg; |
| } |
| } |
| } else { |
| tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; |
| tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; |
| } |
| |
| out: |
| return current_link_up; |
| } |
| |
| static int tg3_fiber_aneg_smachine(struct tg3 *tp, |
| struct tg3_fiber_aneginfo *ap) |
| { |
| u16 flowctrl; |
| unsigned long delta; |
| u32 rx_cfg_reg; |
| int ret; |
| |
| if (ap->state == ANEG_STATE_UNKNOWN) { |
| ap->rxconfig = 0; |
| ap->link_time = 0; |
| ap->cur_time = 0; |
| ap->ability_match_cfg = 0; |
| ap->ability_match_count = 0; |
| ap->ability_match = 0; |
| ap->idle_match = 0; |
| ap->ack_match = 0; |
| } |
| ap->cur_time++; |
| |
| if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { |
| rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); |
| |
| if (rx_cfg_reg != ap->ability_match_cfg) { |
| ap->ability_match_cfg = rx_cfg_reg; |
| ap->ability_match = 0; |
| ap->ability_match_count = 0; |
| } else { |
| if (++ap->ability_match_count > 1) { |
| ap->ability_match = 1; |
| ap->ability_match_cfg = rx_cfg_reg; |
| } |
| } |
| if (rx_cfg_reg & ANEG_CFG_ACK) |
| ap->ack_match = 1; |
| else |
| ap->ack_match = 0; |
| |
| ap->idle_match = 0; |
| } else { |
| ap->idle_match = 1; |
| ap->ability_match_cfg = 0; |
| ap->ability_match_count = 0; |
| ap->ability_match = 0; |
| ap->ack_match = 0; |
| |
| rx_cfg_reg = 0; |
| } |
| |
| ap->rxconfig = rx_cfg_reg; |
| ret = ANEG_OK; |
| |
| switch (ap->state) { |
| case ANEG_STATE_UNKNOWN: |
| if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) |
| ap->state = ANEG_STATE_AN_ENABLE; |
| |
| /* fallthru */ |
| case ANEG_STATE_AN_ENABLE: |
| ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); |
| if (ap->flags & MR_AN_ENABLE) { |
| ap->link_time = 0; |
| ap->cur_time = 0; |
| ap->ability_match_cfg = 0; |
| ap->ability_match_count = 0; |
| ap->ability_match = 0; |
| ap->idle_match = 0; |
| ap->ack_match = 0; |
| |
| ap->state = ANEG_STATE_RESTART_INIT; |
| } else { |
| ap->state = ANEG_STATE_DISABLE_LINK_OK; |
| } |
| break; |
| |
| case ANEG_STATE_RESTART_INIT: |
| ap->link_time = ap->cur_time; |
| ap->flags &= ~(MR_NP_LOADED); |
| ap->txconfig = 0; |
| tw32(MAC_TX_AUTO_NEG, 0); |
| tp->mac_mode |= MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ret = ANEG_TIMER_ENAB; |
| ap->state = ANEG_STATE_RESTART; |
| |
| /* fallthru */ |
| case ANEG_STATE_RESTART: |
| delta = ap->cur_time - ap->link_time; |
| if (delta > ANEG_STATE_SETTLE_TIME) |
| ap->state = ANEG_STATE_ABILITY_DETECT_INIT; |
| else |
| ret = ANEG_TIMER_ENAB; |
| break; |
| |
| case ANEG_STATE_DISABLE_LINK_OK: |
| ret = ANEG_DONE; |
| break; |
| |
| case ANEG_STATE_ABILITY_DETECT_INIT: |
| ap->flags &= ~(MR_TOGGLE_TX); |
| ap->txconfig = ANEG_CFG_FD; |
| flowctrl = tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); |
| if (flowctrl & ADVERTISE_1000XPAUSE) |
| ap->txconfig |= ANEG_CFG_PS1; |
| if (flowctrl & ADVERTISE_1000XPSE_ASYM) |
| ap->txconfig |= ANEG_CFG_PS2; |
| tw32(MAC_TX_AUTO_NEG, ap->txconfig); |
| tp->mac_mode |= MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ap->state = ANEG_STATE_ABILITY_DETECT; |
| break; |
| |
| case ANEG_STATE_ABILITY_DETECT: |
| if (ap->ability_match != 0 && ap->rxconfig != 0) |
| ap->state = ANEG_STATE_ACK_DETECT_INIT; |
| break; |
| |
| case ANEG_STATE_ACK_DETECT_INIT: |
| ap->txconfig |= ANEG_CFG_ACK; |
| tw32(MAC_TX_AUTO_NEG, ap->txconfig); |
| tp->mac_mode |= MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ap->state = ANEG_STATE_ACK_DETECT; |
| |
| /* fallthru */ |
| case ANEG_STATE_ACK_DETECT: |
| if (ap->ack_match != 0) { |
| if ((ap->rxconfig & ~ANEG_CFG_ACK) == |
| (ap->ability_match_cfg & ~ANEG_CFG_ACK)) { |
| ap->state = ANEG_STATE_COMPLETE_ACK_INIT; |
| } else { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| } |
| } else if (ap->ability_match != 0 && |
| ap->rxconfig == 0) { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| } |
| break; |
| |
| case ANEG_STATE_COMPLETE_ACK_INIT: |
| if (ap->rxconfig & ANEG_CFG_INVAL) { |
| ret = ANEG_FAILED; |
| break; |
| } |
| ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | |
| MR_LP_ADV_HALF_DUPLEX | |
| MR_LP_ADV_SYM_PAUSE | |
| MR_LP_ADV_ASYM_PAUSE | |
| MR_LP_ADV_REMOTE_FAULT1 | |
| MR_LP_ADV_REMOTE_FAULT2 | |
| MR_LP_ADV_NEXT_PAGE | |
| MR_TOGGLE_RX | |
| MR_NP_RX); |
| if (ap->rxconfig & ANEG_CFG_FD) |
| ap->flags |= MR_LP_ADV_FULL_DUPLEX; |
| if (ap->rxconfig & ANEG_CFG_HD) |
| ap->flags |= MR_LP_ADV_HALF_DUPLEX; |
| if (ap->rxconfig & ANEG_CFG_PS1) |
| ap->flags |= MR_LP_ADV_SYM_PAUSE; |
| if (ap->rxconfig & ANEG_CFG_PS2) |
| ap->flags |= MR_LP_ADV_ASYM_PAUSE; |
| if (ap->rxconfig & ANEG_CFG_RF1) |
| ap->flags |= MR_LP_ADV_REMOTE_FAULT1; |
| if (ap->rxconfig & ANEG_CFG_RF2) |
| ap->flags |= MR_LP_ADV_REMOTE_FAULT2; |
| if (ap->rxconfig & ANEG_CFG_NP) |
| ap->flags |= MR_LP_ADV_NEXT_PAGE; |
| |
| ap->link_time = ap->cur_time; |
| |
| ap->flags ^= (MR_TOGGLE_TX); |
| if (ap->rxconfig & 0x0008) |
| ap->flags |= MR_TOGGLE_RX; |
| if (ap->rxconfig & ANEG_CFG_NP) |
| ap->flags |= MR_NP_RX; |
| ap->flags |= MR_PAGE_RX; |
| |
| ap->state = ANEG_STATE_COMPLETE_ACK; |
| ret = ANEG_TIMER_ENAB; |
| break; |
| |
| case ANEG_STATE_COMPLETE_ACK: |
| if (ap->ability_match != 0 && |
| ap->rxconfig == 0) { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| break; |
| } |
| delta = ap->cur_time - ap->link_time; |
| if (delta > ANEG_STATE_SETTLE_TIME) { |
| if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { |
| ap->state = ANEG_STATE_IDLE_DETECT_INIT; |
| } else { |
| if ((ap->txconfig & ANEG_CFG_NP) == 0 && |
| !(ap->flags & MR_NP_RX)) { |
| ap->state = ANEG_STATE_IDLE_DETECT_INIT; |
| } else { |
| ret = ANEG_FAILED; |
| } |
| } |
| } |
| break; |
| |
| case ANEG_STATE_IDLE_DETECT_INIT: |
| ap->link_time = ap->cur_time; |
| tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ap->state = ANEG_STATE_IDLE_DETECT; |
| ret = ANEG_TIMER_ENAB; |
| break; |
| |
| case ANEG_STATE_IDLE_DETECT: |
| if (ap->ability_match != 0 && |
| ap->rxconfig == 0) { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| break; |
| } |
| delta = ap->cur_time - ap->link_time; |
| if (delta > ANEG_STATE_SETTLE_TIME) { |
| /* XXX another gem from the Broadcom driver :( */ |
| ap->state = ANEG_STATE_LINK_OK; |
| } |
| break; |
| |
| case ANEG_STATE_LINK_OK: |
| ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); |
| ret = ANEG_DONE; |
| break; |
| |
| case ANEG_STATE_NEXT_PAGE_WAIT_INIT: |
| /* ??? unimplemented */ |
| break; |
| |
| case ANEG_STATE_NEXT_PAGE_WAIT: |
| /* ??? unimplemented */ |
| break; |
| |
| default: |
| ret = ANEG_FAILED; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static int fiber_autoneg(struct tg3 *tp, u32 *txflags, u32 *rxflags) |
| { |
| int res = 0; |
| struct tg3_fiber_aneginfo aninfo; |
| int status = ANEG_FAILED; |
| unsigned int tick; |
| u32 tmp; |
| |
| tw32_f(MAC_TX_AUTO_NEG, 0); |
| |
| tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; |
| tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); |
| udelay(40); |
| |
| tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); |
| udelay(40); |
| |
| memset(&aninfo, 0, sizeof(aninfo)); |
| aninfo.flags |= MR_AN_ENABLE; |
| aninfo.state = ANEG_STATE_UNKNOWN; |
| aninfo.cur_time = 0; |
| tick = 0; |
| while (++tick < 195000) { |
| status = tg3_fiber_aneg_smachine(tp, &aninfo); |
| if (status == ANEG_DONE || status == ANEG_FAILED) |
| break; |
| |
| udelay(1); |
| } |
| |
| tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| *txflags = aninfo.txconfig; |
| *rxflags = aninfo.flags; |
| |
| if (status == ANEG_DONE && |
| (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | |
| MR_LP_ADV_FULL_DUPLEX))) |
| res = 1; |
| |
| return res; |
| } |
| |
| static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status) |
| { |
| int current_link_up = 0; |
| |
| if (!(mac_status & MAC_STATUS_PCS_SYNCED)) |
| goto out; |
| |
| if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
| u32 txflags, rxflags; |
| int i; |
| |
| if (fiber_autoneg(tp, &txflags, &rxflags)) { |
| u32 local_adv = 0, remote_adv = 0; |
| |
| if (txflags & ANEG_CFG_PS1) |
| local_adv |= ADVERTISE_1000XPAUSE; |
| if (txflags & ANEG_CFG_PS2) |
| local_adv |= ADVERTISE_1000XPSE_ASYM; |
| |
| if (rxflags & MR_LP_ADV_SYM_PAUSE) |
| remote_adv |= LPA_1000XPAUSE; |
| if (rxflags & MR_LP_ADV_ASYM_PAUSE) |
| remote_adv |= LPA_1000XPAUSE_ASYM; |
| |
| tp->link_config.rmt_adv = |
| mii_adv_to_ethtool_adv_x(remote_adv); |
| |
| tg3_setup_flow_control(tp, local_adv, remote_adv); |
| |
| current_link_up = 1; |
| } |
| for (i = 0; i < 30; i++) { |
| udelay(20); |
| tw32_f(MAC_STATUS, |
| (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| udelay(40); |
| if ((tr32(MAC_STATUS) & |
| (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)) == 0) |
| break; |
| } |
| |
| mac_status = tr32(MAC_STATUS); |
| if (!current_link_up && |
| (mac_status & MAC_STATUS_PCS_SYNCED) && |
| !(mac_status & MAC_STATUS_RCVD_CFG)) |
| current_link_up = 1; |
| } else { |
| tg3_setup_flow_control(tp, 0, 0); |
| |
| /* Forcing 1000FD link up. */ |
| current_link_up = 1; |
| |
| tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); |
| udelay(40); |
| |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| } |
| |
| out: |
| return current_link_up; |
| } |
| |
| static int tg3_test_and_report_link_chg(struct tg3 *tp, int curr_link_up) |
| { |
| if (curr_link_up != tp->link_up) { |
| if (curr_link_up) { |
| netdev_link_up(tp->dev); |
| } else { |
| netdev_link_down(tp->dev); |
| if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) |
| tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; |
| } |
| |
| tg3_link_report(tp); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void tg3_clear_mac_status(struct tg3 *tp) |
| { |
| 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); |
| } |
| |
| static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) |
| { |
| u32 orig_pause_cfg; |
| u16 orig_active_speed; |
| u8 orig_active_duplex; |
| u32 mac_status; |
| int current_link_up = force_reset; |
| int i; |
| |
| orig_pause_cfg = tp->link_config.active_flowctrl; |
| orig_active_speed = tp->link_config.active_speed; |
| orig_active_duplex = tp->link_config.active_duplex; |
| |
| if (!tg3_flag(tp, HW_AUTONEG) && |
| tp->link_up && |
| tg3_flag(tp, INIT_COMPLETE)) { |
| mac_status = tr32(MAC_STATUS); |
| mac_status &= (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_SIGNAL_DET | |
| MAC_STATUS_CFG_CHANGED | |
| MAC_STATUS_RCVD_CFG); |
| if (mac_status == (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_SIGNAL_DET)) { |
| tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| return 0; |
| } |
| } |
| |
| tw32_f(MAC_TX_AUTO_NEG, 0); |
| |
| tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); |
| tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| if (tp->phy_id == TG3_PHY_ID_BCM8002) |
| tg3_init_bcm8002(tp); |
| |
| /* Enable link change event even when serdes polling. */ |
| tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); |
| udelay(40); |
| |
| current_link_up = 0; |
| tp->link_config.rmt_adv = 0; |
| mac_status = tr32(MAC_STATUS); |
| |
| if (tg3_flag(tp, HW_AUTONEG)) |
| current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); |
| else |
| current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); |
| |
| tp->hw_status->status = |
| (SD_STATUS_UPDATED | |
| (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); |
| |
| for (i = 0; i < 100; i++) { |
| tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| udelay(5); |
| if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED | |
| MAC_STATUS_LNKSTATE_CHANGED)) == 0) |
| break; |
| } |
| |
| mac_status = tr32(MAC_STATUS); |
| if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { |
| current_link_up = 0; |
| if (tp->link_config.autoneg == AUTONEG_ENABLE && |
| tp->serdes_counter == 0) { |
| tw32_f(MAC_MODE, (tp->mac_mode | |
| MAC_MODE_SEND_CONFIGS)); |
| udelay(1); |
| tw32_f(MAC_MODE, tp->mac_mode); |
| } |
| } |
| |
| if (current_link_up) { |
| tp->link_config.active_speed = SPEED_1000; |
| tp->link_config.active_duplex = DUPLEX_FULL; |
| tw32(MAC_LED_CTRL, (tp->led_ctrl | |
| LED_CTRL_LNKLED_OVERRIDE | |
| LED_CTRL_1000MBPS_ON)); |
| } else { |
| tp->link_config.active_speed = SPEED_UNKNOWN; |
| tp->link_config.active_duplex = DUPLEX_UNKNOWN; |
| tw32(MAC_LED_CTRL, (tp->led_ctrl | |
| LED_CTRL_LNKLED_OVERRIDE | |
| LED_CTRL_TRAFFIC_OVERRIDE)); |
| } |
| |
| if (!tg3_test_and_report_link_chg(tp, current_link_up)) { |
| u32 now_pause_cfg = tp->link_config.active_flowctrl; |
| if (orig_pause_cfg != now_pause_cfg || |
| orig_active_speed != tp->link_config.active_speed || |
| orig_active_duplex != tp->link_config.active_duplex) |
| tg3_link_report(tp); |
| } |
| |
| return 0; |
| } |
| |
| static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) |
| { |
| int err = 0; |
| u32 bmsr, bmcr; |
| u16 current_speed = SPEED_UNKNOWN; |
| u8 current_duplex = DUPLEX_UNKNOWN; |
| int current_link_up = 0; |
| u32 local_adv, remote_adv, sgsr; |
| |
| if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || |
| GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) && |
| !tg3_readphy(tp, SERDES_TG3_1000X_STATUS, &sgsr) && |
| (sgsr & SERDES_TG3_SGMII_MODE)) { |
| |
| if (force_reset) |
| tg3_phy_reset(tp); |
| |
| tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; |
| |
| if (!(sgsr & SERDES_TG3_LINK_UP)) { |
| tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; |
| } else { |
| current_link_up = 1; |
| if (sgsr & SERDES_TG3_SPEED_1000) { |
| current_speed = SPEED_1000; |
| tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; |
| } else if (sgsr & SERDES_TG3_SPEED_100) { |
| current_speed = SPEED_100; |
| tp->mac_mode |= MAC_MODE_PORT_MODE_MII; |
| } else { |
| current_speed = SPEED_10; |
| tp->mac_mode |= MAC_MODE_PORT_MODE_MII; |
| } |
| |
| if (sgsr & SERDES_TG3_FULL_DUPLEX) |
| current_duplex = DUPLEX_FULL; |
| else |
| current_duplex = DUPLEX_HALF; |
| } |
| |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| tg3_clear_mac_status(tp); |
| |
| goto fiber_setup_done; |
| } |
| |
| tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| tg3_clear_mac_status(tp); |
| |
| if (force_reset) |
| tg3_phy_reset(tp); |
| |
| tp->link_config.rmt_adv = 0; |
| |
| err |= tg3_readphy(tp, MII_BMSR, &bmsr); |
| err |= tg3_readphy(tp, MII_BMSR, &bmsr); |
| if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { |
| if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) |
| bmsr |= BMSR_LSTATUS; |
| else |
| bmsr &= ~BMSR_LSTATUS; |
| } |
| |
| err |= tg3_readphy(tp, MII_BMCR, &bmcr); |
| |
| if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && |
| (tp->phy_flags & TG3_PHYFLG_PARALLEL_DETECT)) { |
| /* do nothing, just check for link up at the end */ |
| } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
| u32 adv, newadv; |
| |
| err |= tg3_readphy(tp, MII_ADVERTISE, &adv); |
| newadv = adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | |
| ADVERTISE_1000XPAUSE | |
| ADVERTISE_1000XPSE_ASYM | |
| ADVERTISE_SLCT); |
| |
| newadv |= tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); |
| newadv |= ethtool_adv_to_mii_adv_x(tp->link_config.advertising); |
| |
| if ((newadv != adv) || !(bmcr & BMCR_ANENABLE)) { |
| tg3_writephy(tp, MII_ADVERTISE, newadv); |
| bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; |
| tg3_writephy(tp, MII_BMCR, bmcr); |
| |
| tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); |
| tp->serdes_counter = SERDES_AN_TIMEOUT_5714S; |
| tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; |
| |
| return err; |
| } |
| } else { |
| u32 new_bmcr; |
| |
| bmcr &= ~BMCR_SPEED1000; |
| new_bmcr = bmcr & ~(BMCR_ANENABLE | BMCR_FULLDPLX); |
| |
| if (tp->link_config.duplex == DUPLEX_FULL) |
| new_bmcr |= BMCR_FULLDPLX; |
| |
| if (new_bmcr != bmcr) { |
| /* BMCR_SPEED1000 is a reserved bit that needs |
| * to be set on write. |
| */ |
| new_bmcr |= BMCR_SPEED1000; |
| |
| /* Force a linkdown */ |
| if (tp->link_up) { |
| u32 adv; |
| |
| err |= tg3_readphy(tp, MII_ADVERTISE, &adv); |
| adv &= ~(ADVERTISE_1000XFULL | |
| ADVERTISE_1000XHALF | |
| ADVERTISE_SLCT); |
| tg3_writephy(tp, MII_ADVERTISE, adv); |
| tg3_writephy(tp, MII_BMCR, bmcr | |
| BMCR_ANRESTART | |
| BMCR_ANENABLE); |
| udelay(10); |
| netdev_link_down(tp->dev); |
| } |
| tg3_writephy(tp, MII_BMCR, new_bmcr); |
| bmcr = new_bmcr; |
| err |= tg3_readphy(tp, MII_BMSR, &bmsr); |
| err |= tg3_readphy(tp, MII_BMSR, &bmsr); |
| if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { |
| if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) |
| bmsr |= BMSR_LSTATUS; |
| else |
| bmsr &= ~BMSR_LSTATUS; |
| } |
| tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; |
| } |
| } |
| |
| if (bmsr & BMSR_LSTATUS) { |
| current_speed = SPEED_1000; |
| current_link_up = 1; |
| if (bmcr & BMCR_FULLDPLX) |
| current_duplex = DUPLEX_FULL; |
| else |
| current_duplex = DUPLEX_HALF; |
| |
| local_adv = 0; |
| remote_adv = 0; |
| |
| if (bmcr & BMCR_ANENABLE) { |
| u32 common; |
| |
| err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); |
| err |= tg3_readphy(tp, MII_LPA, &remote_adv); |
| common = local_adv & remote_adv; |
| if (common & (ADVERTISE_1000XHALF | |
| ADVERTISE_1000XFULL)) { |
| if (common & ADVERTISE_1000XFULL) |
| current_duplex = DUPLEX_FULL; |
| else |
| current_duplex = DUPLEX_HALF; |
| |
| tp->link_config.rmt_adv = |
| mii_adv_to_ethtool_adv_x(remote_adv); |
| } else if (!tg3_flag(tp, 5780_CLASS)) { |
| /* Link is up via parallel detect */ |
| } else { |
| current_link_up = 0; |
| } |
| } |
| } |
| |
| fiber_setup_done: |
| if (current_link_up && current_duplex == DUPLEX_FULL) |
| tg3_setup_flow_control(tp, local_adv, remote_adv); |
| |
| tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; |
| if (tp->link_config.active_duplex == DUPLEX_HALF) |
| tp->mac_mode |= MAC_MODE_HALF_DUPLEX; |
| |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); |
| |
| tp->link_config.active_speed = current_speed; |
| tp->link_config.active_duplex = current_duplex; |
| |
| tg3_test_and_report_link_chg(tp, current_link_up); |
| return err; |
| } |
| |
| 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 (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 |
| 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; |
| } |