#include #include #include #include #include #include #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; }