[TG3]: add 5780 fiber support
Add 5780S support by adding a new tg3_setup_fiber_mii_phy() function and a timer function for parallel link detection. 5780S uses standard MII registers for 1000BaseX and runs in GMII mode as opposed to TBI mode on older serdes chips. Signed-off-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ef7f5ec0de
commit
747e8f8bff
|
@ -1100,7 +1100,7 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
|
|||
tp->link_config.orig_autoneg = tp->link_config.autoneg;
|
||||
}
|
||||
|
||||
if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
|
||||
if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)) {
|
||||
tp->link_config.speed = SPEED_10;
|
||||
tp->link_config.duplex = DUPLEX_HALF;
|
||||
tp->link_config.autoneg = AUTONEG_ENABLE;
|
||||
|
@ -1247,6 +1247,25 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv
|
|||
u32 old_tx_mode = tp->tx_mode;
|
||||
|
||||
if (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) {
|
||||
|
||||
/* Convert 1000BaseX flow control bits to 1000BaseT
|
||||
* bits before resolving flow control.
|
||||
*/
|
||||
if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) {
|
||||
local_adv &= ~(ADVERTISE_PAUSE_CAP |
|
||||
ADVERTISE_PAUSE_ASYM);
|
||||
remote_adv &= ~(LPA_PAUSE_CAP | LPA_PAUSE_ASYM);
|
||||
|
||||
if (local_adv & ADVERTISE_1000XPAUSE)
|
||||
local_adv |= ADVERTISE_PAUSE_CAP;
|
||||
if (local_adv & ADVERTISE_1000XPSE_ASYM)
|
||||
local_adv |= ADVERTISE_PAUSE_ASYM;
|
||||
if (remote_adv & LPA_1000XPAUSE)
|
||||
remote_adv |= LPA_PAUSE_CAP;
|
||||
if (remote_adv & LPA_1000XPAUSE_ASYM)
|
||||
remote_adv |= LPA_PAUSE_ASYM;
|
||||
}
|
||||
|
||||
if (local_adv & ADVERTISE_PAUSE_CAP) {
|
||||
if (local_adv & ADVERTISE_PAUSE_ASYM) {
|
||||
if (remote_adv & LPA_PAUSE_CAP)
|
||||
|
@ -2507,12 +2526,226 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
|
||||
{
|
||||
int current_link_up, err = 0;
|
||||
u32 bmsr, bmcr;
|
||||
u16 current_speed;
|
||||
u8 current_duplex;
|
||||
|
||||
tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
|
||||
tw32_f(MAC_MODE, tp->mac_mode);
|
||||
udelay(40);
|
||||
|
||||
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 (force_reset)
|
||||
tg3_phy_reset(tp);
|
||||
|
||||
current_link_up = 0;
|
||||
current_speed = SPEED_INVALID;
|
||||
current_duplex = DUPLEX_INVALID;
|
||||
|
||||
err |= tg3_readphy(tp, MII_BMSR, &bmsr);
|
||||
err |= tg3_readphy(tp, MII_BMSR, &bmsr);
|
||||
|
||||
err |= tg3_readphy(tp, MII_BMCR, &bmcr);
|
||||
|
||||
if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset &&
|
||||
(tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) {
|
||||
/* do nothing, just check for link up at the end */
|
||||
} else if (tp->link_config.autoneg == AUTONEG_ENABLE) {
|
||||
u32 adv, new_adv;
|
||||
|
||||
err |= tg3_readphy(tp, MII_ADVERTISE, &adv);
|
||||
new_adv = adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF |
|
||||
ADVERTISE_1000XPAUSE |
|
||||
ADVERTISE_1000XPSE_ASYM |
|
||||
ADVERTISE_SLCT);
|
||||
|
||||
/* Always advertise symmetric PAUSE just like copper */
|
||||
new_adv |= ADVERTISE_1000XPAUSE;
|
||||
|
||||
if (tp->link_config.advertising & ADVERTISED_1000baseT_Half)
|
||||
new_adv |= ADVERTISE_1000XHALF;
|
||||
if (tp->link_config.advertising & ADVERTISED_1000baseT_Full)
|
||||
new_adv |= ADVERTISE_1000XFULL;
|
||||
|
||||
if ((new_adv != adv) || !(bmcr & BMCR_ANENABLE)) {
|
||||
tg3_writephy(tp, MII_ADVERTISE, new_adv);
|
||||
bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
|
||||
tg3_writephy(tp, MII_BMCR, bmcr);
|
||||
|
||||
tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED);
|
||||
tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
|
||||
tp->tg3_flags2 &= ~TG3_FLG2_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 (netif_carrier_ok(tp->dev)) {
|
||||
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);
|
||||
netif_carrier_off(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);
|
||||
tp->tg3_flags2 &= ~TG3_FLG2_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;
|
||||
|
||||
if (bmcr & BMCR_ANENABLE) {
|
||||
u32 local_adv, remote_adv, 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;
|
||||
|
||||
tg3_setup_flow_control(tp, local_adv,
|
||||
remote_adv);
|
||||
}
|
||||
else
|
||||
current_link_up = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (current_link_up != netif_carrier_ok(tp->dev)) {
|
||||
if (current_link_up)
|
||||
netif_carrier_on(tp->dev);
|
||||
else {
|
||||
netif_carrier_off(tp->dev);
|
||||
tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
|
||||
}
|
||||
tg3_link_report(tp);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tg3_serdes_parallel_detect(struct tg3 *tp)
|
||||
{
|
||||
if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) {
|
||||
/* Give autoneg time to complete. */
|
||||
tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
|
||||
return;
|
||||
}
|
||||
if (!netif_carrier_ok(tp->dev) &&
|
||||
(tp->link_config.autoneg == AUTONEG_ENABLE)) {
|
||||
u32 bmcr;
|
||||
|
||||
tg3_readphy(tp, MII_BMCR, &bmcr);
|
||||
if (bmcr & BMCR_ANENABLE) {
|
||||
u32 phy1, phy2;
|
||||
|
||||
/* Select shadow register 0x1f */
|
||||
tg3_writephy(tp, 0x1c, 0x7c00);
|
||||
tg3_readphy(tp, 0x1c, &phy1);
|
||||
|
||||
/* Select expansion interrupt status register */
|
||||
tg3_writephy(tp, 0x17, 0x0f01);
|
||||
tg3_readphy(tp, 0x15, &phy2);
|
||||
tg3_readphy(tp, 0x15, &phy2);
|
||||
|
||||
if ((phy1 & 0x10) && !(phy2 & 0x20)) {
|
||||
/* We have signal detect and not receiving
|
||||
* config code words, link is up by parallel
|
||||
* detection.
|
||||
*/
|
||||
|
||||
bmcr &= ~BMCR_ANENABLE;
|
||||
bmcr |= BMCR_SPEED1000 | BMCR_FULLDPLX;
|
||||
tg3_writephy(tp, MII_BMCR, bmcr);
|
||||
tp->tg3_flags2 |= TG3_FLG2_PARALLEL_DETECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (netif_carrier_ok(tp->dev) &&
|
||||
(tp->link_config.autoneg == AUTONEG_ENABLE) &&
|
||||
(tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) {
|
||||
u32 phy2;
|
||||
|
||||
/* Select expansion interrupt status register */
|
||||
tg3_writephy(tp, 0x17, 0x0f01);
|
||||
tg3_readphy(tp, 0x15, &phy2);
|
||||
if (phy2 & 0x20) {
|
||||
u32 bmcr;
|
||||
|
||||
/* Config code words received, turn on autoneg. */
|
||||
tg3_readphy(tp, MII_BMCR, &bmcr);
|
||||
tg3_writephy(tp, MII_BMCR, bmcr | BMCR_ANENABLE);
|
||||
|
||||
tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int tg3_setup_phy(struct tg3 *tp, int force_reset)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) {
|
||||
err = tg3_setup_fiber_phy(tp, force_reset);
|
||||
} else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) {
|
||||
err = tg3_setup_fiber_mii_phy(tp, force_reset);
|
||||
} else {
|
||||
err = tg3_setup_copper_phy(tp, force_reset);
|
||||
}
|
||||
|
@ -4127,6 +4360,9 @@ static int tg3_chip_reset(struct tg3 *tp)
|
|||
if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) {
|
||||
tp->mac_mode = MAC_MODE_PORT_MODE_TBI;
|
||||
tw32_f(MAC_MODE, tp->mac_mode);
|
||||
} else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) {
|
||||
tp->mac_mode = MAC_MODE_PORT_MODE_GMII;
|
||||
tw32_f(MAC_MODE, tp->mac_mode);
|
||||
} else
|
||||
tw32_f(MAC_MODE, 0);
|
||||
udelay(40);
|
||||
|
@ -5911,7 +6147,8 @@ static void tg3_timer(unsigned long __opaque)
|
|||
udelay(40);
|
||||
tg3_setup_phy(tp, 0);
|
||||
}
|
||||
}
|
||||
} else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
|
||||
tg3_serdes_parallel_detect(tp);
|
||||
|
||||
tp->timer_counter = tp->timer_multiplier;
|
||||
}
|
||||
|
@ -8615,8 +8852,12 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
|
|||
eeprom_phy_id = 0;
|
||||
|
||||
tp->phy_id = eeprom_phy_id;
|
||||
if (eeprom_phy_serdes)
|
||||
tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES;
|
||||
if (eeprom_phy_serdes) {
|
||||
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780)
|
||||
tp->tg3_flags2 |= TG3_FLG2_MII_SERDES;
|
||||
else
|
||||
tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES;
|
||||
}
|
||||
|
||||
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS)
|
||||
led_cfg = cfg2 & (NIC_SRAM_DATA_CFG_LED_MODE_MASK |
|
||||
|
@ -8751,7 +8992,7 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
|
|||
}
|
||||
}
|
||||
|
||||
if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) &&
|
||||
if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) &&
|
||||
!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
|
||||
u32 bmsr, adv_reg, tg3_ctrl;
|
||||
|
||||
|
@ -8804,7 +9045,7 @@ skip_phy_reset:
|
|||
err = tg3_init_5401phy_dsp(tp);
|
||||
}
|
||||
|
||||
if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
|
||||
if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)
|
||||
tp->link_config.advertising =
|
||||
(ADVERTISED_1000baseT_Half |
|
||||
ADVERTISED_1000baseT_Full |
|
||||
|
@ -9153,7 +9394,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
|
|||
if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) ||
|
||||
((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
|
||||
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) &&
|
||||
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)))
|
||||
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)) ||
|
||||
(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES))
|
||||
tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED;
|
||||
|
||||
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5703_AX ||
|
||||
|
|
|
@ -2161,6 +2161,10 @@ struct tg3 {
|
|||
#define TG3_FLG2_PROTECTED_NVRAM 0x00100000
|
||||
#define TG3_FLG2_USING_MSI 0x00200000
|
||||
#define TG3_FLG2_JUMBO_CAPABLE 0x00400000
|
||||
#define TG3_FLG2_MII_SERDES 0x00800000
|
||||
#define TG3_FLG2_ANY_SERDES (TG3_FLG2_PHY_SERDES | \
|
||||
TG3_FLG2_MII_SERDES)
|
||||
#define TG3_FLG2_PARALLEL_DETECT 0x01000000
|
||||
|
||||
u32 split_mode_max_reqs;
|
||||
#define SPLIT_MODE_5704_MAX_REQ 3
|
||||
|
|
Loading…
Reference in New Issue