|
@@ -1874,6 +1874,20 @@ static void tg3_link_report(struct tg3 *tp)
|
|
|
tp->link_up = netif_carrier_ok(tp->dev);
|
|
|
}
|
|
|
|
|
|
+static u32 tg3_decode_flowctrl_1000T(u32 adv)
|
|
|
+{
|
|
|
+ u32 flowctrl = 0;
|
|
|
+
|
|
|
+ if (adv & ADVERTISE_PAUSE_CAP) {
|
|
|
+ flowctrl |= FLOW_CTRL_RX;
|
|
|
+ if (!(adv & ADVERTISE_PAUSE_ASYM))
|
|
|
+ flowctrl |= FLOW_CTRL_TX;
|
|
|
+ } else if (adv & ADVERTISE_PAUSE_ASYM)
|
|
|
+ flowctrl |= FLOW_CTRL_TX;
|
|
|
+
|
|
|
+ return flowctrl;
|
|
|
+}
|
|
|
+
|
|
|
static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl)
|
|
|
{
|
|
|
u16 miireg;
|
|
@@ -1890,6 +1904,20 @@ static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl)
|
|
|
return miireg;
|
|
|
}
|
|
|
|
|
|
+static u32 tg3_decode_flowctrl_1000X(u32 adv)
|
|
|
+{
|
|
|
+ u32 flowctrl = 0;
|
|
|
+
|
|
|
+ if (adv & ADVERTISE_1000XPAUSE) {
|
|
|
+ flowctrl |= FLOW_CTRL_RX;
|
|
|
+ if (!(adv & ADVERTISE_1000XPSE_ASYM))
|
|
|
+ flowctrl |= FLOW_CTRL_TX;
|
|
|
+ } else if (adv & ADVERTISE_1000XPSE_ASYM)
|
|
|
+ flowctrl |= FLOW_CTRL_TX;
|
|
|
+
|
|
|
+ return flowctrl;
|
|
|
+}
|
|
|
+
|
|
|
static u8 tg3_resolve_flowctrl_1000X(u16 lcladv, u16 rmtadv)
|
|
|
{
|
|
|
u8 cap = 0;
|
|
@@ -4347,6 +4375,103 @@ static void tg3_phy_copper_begin(struct tg3 *tp)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int tg3_phy_pull_config(struct tg3 *tp)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ err = tg3_readphy(tp, MII_BMCR, &val);
|
|
|
+ if (err)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ if (!(val & BMCR_ANENABLE)) {
|
|
|
+ tp->link_config.autoneg = AUTONEG_DISABLE;
|
|
|
+ tp->link_config.advertising = 0;
|
|
|
+ tg3_flag_clear(tp, PAUSE_AUTONEG);
|
|
|
+
|
|
|
+ err = -EIO;
|
|
|
+
|
|
|
+ switch (val & (BMCR_SPEED1000 | BMCR_SPEED100)) {
|
|
|
+ case 0:
|
|
|
+ if (tp->phy_flags & TG3_PHYFLG_ANY_SERDES)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ tp->link_config.speed = SPEED_10;
|
|
|
+ break;
|
|
|
+ case BMCR_SPEED100:
|
|
|
+ if (tp->phy_flags & TG3_PHYFLG_ANY_SERDES)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ tp->link_config.speed = SPEED_100;
|
|
|
+ break;
|
|
|
+ case BMCR_SPEED1000:
|
|
|
+ if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
|
|
|
+ tp->link_config.speed = SPEED_1000;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* Fall through */
|
|
|
+ default:
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (val & BMCR_FULLDPLX)
|
|
|
+ tp->link_config.duplex = DUPLEX_FULL;
|
|
|
+ else
|
|
|
+ tp->link_config.duplex = DUPLEX_HALF;
|
|
|
+
|
|
|
+ tp->link_config.flowctrl = FLOW_CTRL_RX | FLOW_CTRL_TX;
|
|
|
+
|
|
|
+ err = 0;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ tp->link_config.autoneg = AUTONEG_ENABLE;
|
|
|
+ tp->link_config.advertising = ADVERTISED_Autoneg;
|
|
|
+ tg3_flag_set(tp, PAUSE_AUTONEG);
|
|
|
+
|
|
|
+ if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES)) {
|
|
|
+ u32 adv;
|
|
|
+
|
|
|
+ err = tg3_readphy(tp, MII_ADVERTISE, &val);
|
|
|
+ if (err)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ adv = mii_adv_to_ethtool_adv_t(val & ADVERTISE_ALL);
|
|
|
+ tp->link_config.advertising |= adv | ADVERTISED_TP;
|
|
|
+
|
|
|
+ tp->link_config.flowctrl = tg3_decode_flowctrl_1000T(val);
|
|
|
+ } else {
|
|
|
+ tp->link_config.advertising |= ADVERTISED_FIBRE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
|
|
|
+ u32 adv;
|
|
|
+
|
|
|
+ if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES)) {
|
|
|
+ err = tg3_readphy(tp, MII_CTRL1000, &val);
|
|
|
+ if (err)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ adv = mii_ctrl1000_to_ethtool_adv_t(val);
|
|
|
+ } else {
|
|
|
+ err = tg3_readphy(tp, MII_ADVERTISE, &val);
|
|
|
+ if (err)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ adv = tg3_decode_flowctrl_1000X(val);
|
|
|
+ tp->link_config.flowctrl = adv;
|
|
|
+
|
|
|
+ val &= (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL);
|
|
|
+ adv = mii_adv_to_ethtool_adv_x(val);
|
|
|
+ }
|
|
|
+
|
|
|
+ tp->link_config.advertising |= adv;
|
|
|
+ }
|
|
|
+
|
|
|
+done:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int tg3_init_5401phy_dsp(struct tg3 *tp)
|
|
|
{
|
|
|
int err;
|
|
@@ -9313,6 +9438,12 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
|
|
|
TG3_CPMU_DBTMR2_TXIDXEQ_2047US);
|
|
|
}
|
|
|
|
|
|
+ if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
|
|
|
+ !(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) {
|
|
|
+ tg3_phy_pull_config(tp);
|
|
|
+ tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
|
|
|
+ }
|
|
|
+
|
|
|
if (reset_phy)
|
|
|
tg3_phy_reset(tp);
|
|
|
|
|
@@ -11640,6 +11771,8 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
tp->link_config.duplex = cmd->duplex;
|
|
|
}
|
|
|
|
|
|
+ tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
|
|
|
+
|
|
|
tg3_warn_mgmt_link_flap(tp);
|
|
|
|
|
|
if (netif_running(dev))
|
|
@@ -11931,6 +12064,8 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
|
|
|
tg3_full_unlock(tp);
|
|
|
}
|
|
|
|
|
|
+ tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
|
|
|
+
|
|
|
return err;
|
|
|
}
|
|
|
|