diff options
author | Michael Chan <mchan@broadcom.com> | 2008-01-21 20:07:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:10:15 -0500 |
commit | b2fadeae1334008c1bb4d87bc507141cb7aaf0e8 (patch) | |
tree | 26fbaeefce8860a40e54852783280446c4c95847 /drivers/net/bnx2.c | |
parent | 1097f5e92107ca3950fabf5e1d724faa80c91e7f (diff) |
[BNX2]: Add link-down workaround on 5706 serdes.
In some blade systems using the 5706 serdes, the hardware sometimes
does not properly generate link down interrupts. We add a workaround
in the driver's timer to force a link-down when some PHY registers
report loss of SYNC.
The parallel detect logic is cleaned up slightly to better integrate
the workaround.
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bnx2.c')
-rw-r--r-- | drivers/net/bnx2.c | 96 |
1 files changed, 80 insertions, 16 deletions
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 18ed8068dc2d..c03f67735bf4 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c | |||
@@ -1186,6 +1186,19 @@ bnx2_disable_forced_2g5(struct bnx2 *bp) | |||
1186 | bnx2_write_phy(bp, bp->mii_bmcr, bmcr); | 1186 | bnx2_write_phy(bp, bp->mii_bmcr, bmcr); |
1187 | } | 1187 | } |
1188 | 1188 | ||
1189 | static void | ||
1190 | bnx2_5706s_force_link_dn(struct bnx2 *bp, int start) | ||
1191 | { | ||
1192 | u32 val; | ||
1193 | |||
1194 | bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_EXPAND_SERDES_CTL); | ||
1195 | bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val); | ||
1196 | if (start) | ||
1197 | bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val & 0xff0f); | ||
1198 | else | ||
1199 | bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val | 0xc0); | ||
1200 | } | ||
1201 | |||
1189 | static int | 1202 | static int |
1190 | bnx2_set_link(struct bnx2 *bp) | 1203 | bnx2_set_link(struct bnx2 *bp) |
1191 | { | 1204 | { |
@@ -1211,6 +1224,10 @@ bnx2_set_link(struct bnx2 *bp) | |||
1211 | (CHIP_NUM(bp) == CHIP_NUM_5706)) { | 1224 | (CHIP_NUM(bp) == CHIP_NUM_5706)) { |
1212 | u32 val; | 1225 | u32 val; |
1213 | 1226 | ||
1227 | if (bp->phy_flags & PHY_FORCED_DOWN_FLAG) { | ||
1228 | bnx2_5706s_force_link_dn(bp, 0); | ||
1229 | bp->phy_flags &= ~PHY_FORCED_DOWN_FLAG; | ||
1230 | } | ||
1214 | val = REG_RD(bp, BNX2_EMAC_STATUS); | 1231 | val = REG_RD(bp, BNX2_EMAC_STATUS); |
1215 | if (val & BNX2_EMAC_STATUS_LINK) | 1232 | if (val & BNX2_EMAC_STATUS_LINK) |
1216 | bmsr |= BMSR_LSTATUS; | 1233 | bmsr |= BMSR_LSTATUS; |
@@ -1239,7 +1256,15 @@ bnx2_set_link(struct bnx2 *bp) | |||
1239 | (bp->autoneg & AUTONEG_SPEED)) | 1256 | (bp->autoneg & AUTONEG_SPEED)) |
1240 | bnx2_disable_forced_2g5(bp); | 1257 | bnx2_disable_forced_2g5(bp); |
1241 | 1258 | ||
1242 | bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; | 1259 | if (bp->phy_flags & PHY_PARALLEL_DETECT_FLAG) { |
1260 | u32 bmcr; | ||
1261 | |||
1262 | bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); | ||
1263 | bmcr |= BMCR_ANENABLE; | ||
1264 | bnx2_write_phy(bp, bp->mii_bmcr, bmcr); | ||
1265 | |||
1266 | bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; | ||
1267 | } | ||
1243 | bp->link_up = 0; | 1268 | bp->link_up = 0; |
1244 | } | 1269 | } |
1245 | 1270 | ||
@@ -5276,13 +5301,51 @@ bnx2_test_intr(struct bnx2 *bp) | |||
5276 | return -ENODEV; | 5301 | return -ENODEV; |
5277 | } | 5302 | } |
5278 | 5303 | ||
5304 | static int | ||
5305 | bnx2_5706_serdes_has_link(struct bnx2 *bp) | ||
5306 | { | ||
5307 | u32 mode_ctl, an_dbg, exp; | ||
5308 | |||
5309 | bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_MODE_CTL); | ||
5310 | bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &mode_ctl); | ||
5311 | |||
5312 | if (!(mode_ctl & MISC_SHDW_MODE_CTL_SIG_DET)) | ||
5313 | return 0; | ||
5314 | |||
5315 | bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG); | ||
5316 | bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); | ||
5317 | bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); | ||
5318 | |||
5319 | if (an_dbg & MISC_SHDW_AN_DBG_NOSYNC) | ||
5320 | return 0; | ||
5321 | |||
5322 | bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_EXPAND_REG1); | ||
5323 | bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &exp); | ||
5324 | bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &exp); | ||
5325 | |||
5326 | if (exp & MII_EXPAND_REG1_RUDI_C) /* receiving CONFIG */ | ||
5327 | return 0; | ||
5328 | |||
5329 | return 1; | ||
5330 | } | ||
5331 | |||
5279 | static void | 5332 | static void |
5280 | bnx2_5706_serdes_timer(struct bnx2 *bp) | 5333 | bnx2_5706_serdes_timer(struct bnx2 *bp) |
5281 | { | 5334 | { |
5335 | int check_link = 1; | ||
5336 | |||
5282 | spin_lock(&bp->phy_lock); | 5337 | spin_lock(&bp->phy_lock); |
5283 | if (bp->serdes_an_pending) | 5338 | if (bp->phy_flags & PHY_FORCED_DOWN_FLAG) { |
5339 | bnx2_5706s_force_link_dn(bp, 0); | ||
5340 | bp->phy_flags &= ~PHY_FORCED_DOWN_FLAG; | ||
5341 | spin_unlock(&bp->phy_lock); | ||
5342 | return; | ||
5343 | } | ||
5344 | |||
5345 | if (bp->serdes_an_pending) { | ||
5284 | bp->serdes_an_pending--; | 5346 | bp->serdes_an_pending--; |
5285 | else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) { | 5347 | check_link = 0; |
5348 | } else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) { | ||
5286 | u32 bmcr; | 5349 | u32 bmcr; |
5287 | 5350 | ||
5288 | bp->current_interval = bp->timer_interval; | 5351 | bp->current_interval = bp->timer_interval; |
@@ -5290,19 +5353,7 @@ bnx2_5706_serdes_timer(struct bnx2 *bp) | |||
5290 | bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); | 5353 | bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); |
5291 | 5354 | ||
5292 | if (bmcr & BMCR_ANENABLE) { | 5355 | if (bmcr & BMCR_ANENABLE) { |
5293 | u32 phy1, phy2; | 5356 | if (bnx2_5706_serdes_has_link(bp)) { |
5294 | |||
5295 | bnx2_write_phy(bp, 0x1c, 0x7c00); | ||
5296 | bnx2_read_phy(bp, 0x1c, &phy1); | ||
5297 | |||
5298 | bnx2_write_phy(bp, 0x17, 0x0f01); | ||
5299 | bnx2_read_phy(bp, 0x15, &phy2); | ||
5300 | bnx2_write_phy(bp, 0x17, 0x0f01); | ||
5301 | bnx2_read_phy(bp, 0x15, &phy2); | ||
5302 | |||
5303 | if ((phy1 & 0x10) && /* SIGNAL DETECT */ | ||
5304 | !(phy2 & 0x20)) { /* no CONFIG */ | ||
5305 | |||
5306 | bmcr &= ~BMCR_ANENABLE; | 5357 | bmcr &= ~BMCR_ANENABLE; |
5307 | bmcr |= BMCR_SPEED1000 | BMCR_FULLDPLX; | 5358 | bmcr |= BMCR_SPEED1000 | BMCR_FULLDPLX; |
5308 | bnx2_write_phy(bp, bp->mii_bmcr, bmcr); | 5359 | bnx2_write_phy(bp, bp->mii_bmcr, bmcr); |
@@ -5314,6 +5365,7 @@ bnx2_5706_serdes_timer(struct bnx2 *bp) | |||
5314 | (bp->phy_flags & PHY_PARALLEL_DETECT_FLAG)) { | 5365 | (bp->phy_flags & PHY_PARALLEL_DETECT_FLAG)) { |
5315 | u32 phy2; | 5366 | u32 phy2; |
5316 | 5367 | ||
5368 | check_link = 0; | ||
5317 | bnx2_write_phy(bp, 0x17, 0x0f01); | 5369 | bnx2_write_phy(bp, 0x17, 0x0f01); |
5318 | bnx2_read_phy(bp, 0x15, &phy2); | 5370 | bnx2_read_phy(bp, 0x15, &phy2); |
5319 | if (phy2 & 0x20) { | 5371 | if (phy2 & 0x20) { |
@@ -5328,6 +5380,18 @@ bnx2_5706_serdes_timer(struct bnx2 *bp) | |||
5328 | } else | 5380 | } else |
5329 | bp->current_interval = bp->timer_interval; | 5381 | bp->current_interval = bp->timer_interval; |
5330 | 5382 | ||
5383 | if (bp->link_up && (bp->autoneg & AUTONEG_SPEED) && check_link) { | ||
5384 | u32 val; | ||
5385 | |||
5386 | bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG); | ||
5387 | bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &val); | ||
5388 | bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &val); | ||
5389 | |||
5390 | if (val & MISC_SHDW_AN_DBG_NOSYNC) { | ||
5391 | bnx2_5706s_force_link_dn(bp, 1); | ||
5392 | bp->phy_flags |= PHY_FORCED_DOWN_FLAG; | ||
5393 | } | ||
5394 | } | ||
5331 | spin_unlock(&bp->phy_lock); | 5395 | spin_unlock(&bp->phy_lock); |
5332 | } | 5396 | } |
5333 | 5397 | ||