aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Chan <mchan@broadcom.com>2008-01-21 20:07:06 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 18:10:15 -0500
commitb2fadeae1334008c1bb4d87bc507141cb7aaf0e8 (patch)
tree26fbaeefce8860a40e54852783280446c4c95847
parent1097f5e92107ca3950fabf5e1d724faa80c91e7f (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>
-rw-r--r--drivers/net/bnx2.c96
-rw-r--r--drivers/net/bnx2.h10
2 files changed, 90 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
1189static void
1190bnx2_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
1189static int 1202static int
1190bnx2_set_link(struct bnx2 *bp) 1203bnx2_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
5304static int
5305bnx2_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
5279static void 5332static void
5280bnx2_5706_serdes_timer(struct bnx2 *bp) 5333bnx2_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
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index c1ab30b0f87a..31a030a6e2a5 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -6344,6 +6344,15 @@ struct l2_fhdr {
6344#define MII_BNX2_DSP_RW_PORT 0x15 6344#define MII_BNX2_DSP_RW_PORT 0x15
6345#define MII_BNX2_DSP_ADDRESS 0x17 6345#define MII_BNX2_DSP_ADDRESS 0x17
6346#define MII_BNX2_DSP_EXPAND_REG 0x0f00 6346#define MII_BNX2_DSP_EXPAND_REG 0x0f00
6347#define MII_EXPAND_REG1 (MII_BNX2_DSP_EXPAND_REG | 1)
6348#define MII_EXPAND_REG1_RUDI_C 0x20
6349#define MII_EXPAND_SERDES_CTL (MII_BNX2_DSP_EXPAND_REG | 2)
6350
6351#define MII_BNX2_MISC_SHADOW 0x1c
6352#define MISC_SHDW_AN_DBG 0x6800
6353#define MISC_SHDW_AN_DBG_NOSYNC 0x0002
6354#define MISC_SHDW_MODE_CTL 0x7c00
6355#define MISC_SHDW_MODE_CTL_SIG_DET 0x0010
6347 6356
6348#define MII_BNX2_BLK_ADDR 0x1f 6357#define MII_BNX2_BLK_ADDR 0x1f
6349#define MII_BNX2_BLK_ADDR_IEEE0 0x0000 6358#define MII_BNX2_BLK_ADDR_IEEE0 0x0000
@@ -6643,6 +6652,7 @@ struct bnx2 {
6643#define PHY_INT_MODE_LINK_READY_FLAG 0x200 6652#define PHY_INT_MODE_LINK_READY_FLAG 0x200
6644#define PHY_DIS_EARLY_DAC_FLAG 0x400 6653#define PHY_DIS_EARLY_DAC_FLAG 0x400
6645#define REMOTE_PHY_CAP_FLAG 0x800 6654#define REMOTE_PHY_CAP_FLAG 0x800
6655#define PHY_FORCED_DOWN_FLAG 0x1000
6646 6656
6647 u32 mii_bmcr; 6657 u32 mii_bmcr;
6648 u32 mii_bmsr; 6658 u32 mii_bmsr;