Commit 747e8f8b authored by Michael Chan's avatar Michael Chan Committed by David S. Miller

[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: default avatarMichael Chan <mchan@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ef7f5ec0
......@@ -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 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment