diff options
author | Matt Carlson <mcarlson@broadcom.com> | 2007-12-20 23:10:01 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:59:34 -0500 |
commit | ef167e27039eeaea6d3cdd5c547b082e89840bdd (patch) | |
tree | fd7d1b76a867472dd26ce3a866eea59e30422983 /drivers | |
parent | 5be73b471bbed9ca61ddfd952a2cb7701f94f034 (diff) |
[TG3]: Fix supporting flowctrl code
This patch does three things. It modifies tg3_setup_flow_control() to
use the administrator requested flow control settings if
autonegotiation is turned off. It slightly modifies the
tg3_setup_fiber_mii_phy() function to account for this new use case.
And finally, it does the same for tg3_setup_copper_phy().
The copper modifications are more than a small multi-line change. The
new code makes an attempt to avoid a link renegotiation if the link is
active at half duplex and the only difference between the current
advertised settings and requested advertised settings is the
flow control advertisements.
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/tg3.c | 107 |
1 files changed, 64 insertions, 43 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index cdade05bdd07..b2f505d6ea94 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c | |||
@@ -1694,7 +1694,8 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv | |||
1694 | u32 old_rx_mode = tp->rx_mode; | 1694 | u32 old_rx_mode = tp->rx_mode; |
1695 | u32 old_tx_mode = tp->tx_mode; | 1695 | u32 old_tx_mode = tp->tx_mode; |
1696 | 1696 | ||
1697 | if (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) { | 1697 | if (tp->link_config.autoneg == AUTONEG_ENABLE && |
1698 | (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG)) { | ||
1698 | if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) | 1699 | if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) |
1699 | new_tg3_flags = tg3_resolve_flowctrl_1000X(local_adv, | 1700 | new_tg3_flags = tg3_resolve_flowctrl_1000X(local_adv, |
1700 | remote_adv); | 1701 | remote_adv); |
@@ -1975,10 +1976,44 @@ static int tg3_copper_is_advertising_all(struct tg3 *tp, u32 mask) | |||
1975 | return 1; | 1976 | return 1; |
1976 | } | 1977 | } |
1977 | 1978 | ||
1979 | static int tg3_adv_1000T_flowctrl_ok(struct tg3 *tp, u32 *lcladv, u32 *rmtadv) | ||
1980 | { | ||
1981 | u32 curadv, reqadv; | ||
1982 | |||
1983 | if (tg3_readphy(tp, MII_ADVERTISE, lcladv)) | ||
1984 | return 1; | ||
1985 | |||
1986 | curadv = *lcladv & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); | ||
1987 | reqadv = tg3_advert_flowctrl_1000T(tp->link_config.flowctrl); | ||
1988 | |||
1989 | if (tp->link_config.active_duplex == DUPLEX_FULL) { | ||
1990 | if (curadv != reqadv) | ||
1991 | return 0; | ||
1992 | |||
1993 | if (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) | ||
1994 | tg3_readphy(tp, MII_LPA, rmtadv); | ||
1995 | } else { | ||
1996 | /* Reprogram the advertisement register, even if it | ||
1997 | * does not affect the current link. If the link | ||
1998 | * gets renegotiated in the future, we can save an | ||
1999 | * additional renegotiation cycle by advertising | ||
2000 | * it correctly in the first place. | ||
2001 | */ | ||
2002 | if (curadv != reqadv) { | ||
2003 | *lcladv &= ~(ADVERTISE_PAUSE_CAP | | ||
2004 | ADVERTISE_PAUSE_ASYM); | ||
2005 | tg3_writephy(tp, MII_ADVERTISE, *lcladv | reqadv); | ||
2006 | } | ||
2007 | } | ||
2008 | |||
2009 | return 1; | ||
2010 | } | ||
2011 | |||
1978 | static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) | 2012 | static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) |
1979 | { | 2013 | { |
1980 | int current_link_up; | 2014 | int current_link_up; |
1981 | u32 bmsr, dummy; | 2015 | u32 bmsr, dummy; |
2016 | u32 lcl_adv, rmt_adv; | ||
1982 | u16 current_speed; | 2017 | u16 current_speed; |
1983 | u8 current_duplex; | 2018 | u8 current_duplex; |
1984 | int i, err; | 2019 | int i, err; |
@@ -2121,54 +2156,35 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) | |||
2121 | udelay(10); | 2156 | udelay(10); |
2122 | } | 2157 | } |
2123 | 2158 | ||
2124 | if (tp->link_config.autoneg == AUTONEG_ENABLE) { | 2159 | lcl_adv = 0; |
2125 | if (bmcr & BMCR_ANENABLE) { | 2160 | rmt_adv = 0; |
2126 | current_link_up = 1; | ||
2127 | 2161 | ||
2128 | /* Force autoneg restart if we are exiting | 2162 | tp->link_config.active_speed = current_speed; |
2129 | * low power mode. | 2163 | tp->link_config.active_duplex = current_duplex; |
2130 | */ | 2164 | |
2131 | if (!tg3_copper_is_advertising_all(tp, | 2165 | if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
2132 | tp->link_config.advertising)) | 2166 | if ((bmcr & BMCR_ANENABLE) && |
2133 | current_link_up = 0; | 2167 | tg3_copper_is_advertising_all(tp, |
2134 | } else { | 2168 | tp->link_config.advertising)) { |
2135 | current_link_up = 0; | 2169 | if (tg3_adv_1000T_flowctrl_ok(tp, &lcl_adv, |
2170 | &rmt_adv)) | ||
2171 | current_link_up = 1; | ||
2136 | } | 2172 | } |
2137 | } else { | 2173 | } else { |
2138 | if (!(bmcr & BMCR_ANENABLE) && | 2174 | if (!(bmcr & BMCR_ANENABLE) && |
2139 | tp->link_config.speed == current_speed && | 2175 | tp->link_config.speed == current_speed && |
2140 | tp->link_config.duplex == current_duplex) { | 2176 | tp->link_config.duplex == current_duplex && |
2177 | tp->link_config.flowctrl == | ||
2178 | tp->link_config.active_flowctrl) { | ||
2141 | current_link_up = 1; | 2179 | current_link_up = 1; |
2142 | } else { | ||
2143 | current_link_up = 0; | ||
2144 | } | 2180 | } |
2145 | } | 2181 | } |
2146 | 2182 | ||
2147 | tp->link_config.active_speed = current_speed; | 2183 | if (current_link_up == 1 && |
2148 | tp->link_config.active_duplex = current_duplex; | 2184 | tp->link_config.active_duplex == DUPLEX_FULL) |
2185 | tg3_setup_flow_control(tp, lcl_adv, rmt_adv); | ||
2149 | } | 2186 | } |
2150 | 2187 | ||
2151 | if (current_link_up == 1 && | ||
2152 | (tp->link_config.active_duplex == DUPLEX_FULL) && | ||
2153 | (tp->link_config.autoneg == AUTONEG_ENABLE)) { | ||
2154 | u32 local_adv, remote_adv; | ||
2155 | |||
2156 | if (tg3_readphy(tp, MII_ADVERTISE, &local_adv)) | ||
2157 | local_adv = 0; | ||
2158 | |||
2159 | if (tg3_readphy(tp, MII_LPA, &remote_adv)) | ||
2160 | remote_adv = 0; | ||
2161 | |||
2162 | /* If we are not advertising what has been requested, | ||
2163 | * bring the link down and reconfigure. | ||
2164 | */ | ||
2165 | if (local_adv != | ||
2166 | tg3_advert_flowctrl_1000T(tp->link_config.flowctrl)) { | ||
2167 | current_link_up = 0; | ||
2168 | } else { | ||
2169 | tg3_setup_flow_control(tp, local_adv, remote_adv); | ||
2170 | } | ||
2171 | } | ||
2172 | relink: | 2188 | relink: |
2173 | if (current_link_up == 0 || tp->link_config.phy_is_low_power) { | 2189 | if (current_link_up == 0 || tp->link_config.phy_is_low_power) { |
2174 | u32 tmp; | 2190 | u32 tmp; |
@@ -2981,6 +2997,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) | |||
2981 | u32 bmsr, bmcr; | 2997 | u32 bmsr, bmcr; |
2982 | u16 current_speed; | 2998 | u16 current_speed; |
2983 | u8 current_duplex; | 2999 | u8 current_duplex; |
3000 | u32 local_adv, remote_adv; | ||
2984 | 3001 | ||
2985 | tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; | 3002 | tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; |
2986 | tw32_f(MAC_MODE, tp->mac_mode); | 3003 | tw32_f(MAC_MODE, tp->mac_mode); |
@@ -3014,7 +3031,8 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) | |||
3014 | err |= tg3_readphy(tp, MII_BMCR, &bmcr); | 3031 | err |= tg3_readphy(tp, MII_BMCR, &bmcr); |
3015 | 3032 | ||
3016 | if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && | 3033 | if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && |
3017 | (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) { | 3034 | (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT) && |
3035 | tp->link_config.flowctrl == tp->link_config.active_flowctrl) { | ||
3018 | /* do nothing, just check for link up at the end */ | 3036 | /* do nothing, just check for link up at the end */ |
3019 | } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { | 3037 | } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
3020 | u32 adv, new_adv; | 3038 | u32 adv, new_adv; |
@@ -3096,8 +3114,11 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) | |||
3096 | else | 3114 | else |
3097 | current_duplex = DUPLEX_HALF; | 3115 | current_duplex = DUPLEX_HALF; |
3098 | 3116 | ||
3117 | local_adv = 0; | ||
3118 | remote_adv = 0; | ||
3119 | |||
3099 | if (bmcr & BMCR_ANENABLE) { | 3120 | if (bmcr & BMCR_ANENABLE) { |
3100 | u32 local_adv, remote_adv, common; | 3121 | u32 common; |
3101 | 3122 | ||
3102 | err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); | 3123 | err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); |
3103 | err |= tg3_readphy(tp, MII_LPA, &remote_adv); | 3124 | err |= tg3_readphy(tp, MII_LPA, &remote_adv); |
@@ -3108,15 +3129,15 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) | |||
3108 | current_duplex = DUPLEX_FULL; | 3129 | current_duplex = DUPLEX_FULL; |
3109 | else | 3130 | else |
3110 | current_duplex = DUPLEX_HALF; | 3131 | current_duplex = DUPLEX_HALF; |
3111 | |||
3112 | tg3_setup_flow_control(tp, local_adv, | ||
3113 | remote_adv); | ||
3114 | } | 3132 | } |
3115 | else | 3133 | else |
3116 | current_link_up = 0; | 3134 | current_link_up = 0; |
3117 | } | 3135 | } |
3118 | } | 3136 | } |
3119 | 3137 | ||
3138 | if (current_link_up == 1 && current_duplex == DUPLEX_FULL) | ||
3139 | tg3_setup_flow_control(tp, local_adv, remote_adv); | ||
3140 | |||
3120 | tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; | 3141 | tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; |
3121 | if (tp->link_config.active_duplex == DUPLEX_HALF) | 3142 | if (tp->link_config.active_duplex == DUPLEX_HALF) |
3122 | tp->mac_mode |= MAC_MODE_HALF_DUPLEX; | 3143 | tp->mac_mode |= MAC_MODE_HALF_DUPLEX; |