summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornp <np@FreeBSD.org>2015-01-18 20:38:38 +0000
committernp <np@FreeBSD.org>2015-01-18 20:38:38 +0000
commit4c190cd51e944f0c9765ff16440dc2e5258946c7 (patch)
tree762f5cd0d8d3c8ef6ebc366720415217ce95a74f
parent7fe8fbec77343dc12cd90833d80eb7f721248bc7 (diff)
downloadFreeBSD-src-4c190cd51e944f0c9765ff16440dc2e5258946c7.zip
FreeBSD-src-4c190cd51e944f0c9765ff16440dc2e5258946c7.tar.gz
MFC r276959:
cxgb: replace r273280 with a more comprehensive fix. Poll for link state when the link is down, even for interrupt capable PHYs. Allow PHYs to report a dubious "partial" link. If this state is seen 3 consecutive times (each check is ~1s apart) then reset the PHY. This is a workaround for a situation where repeatedly toggling the link from the peer gets the AEL2005 PHY into a state where it never establishes a PCS block lock even when everything is in order.
-rw-r--r--sys/dev/cxgb/common/cxgb_ael1002.c35
-rw-r--r--sys/dev/cxgb/common/cxgb_aq100x.c6
-rw-r--r--sys/dev/cxgb/common/cxgb_common.h9
-rw-r--r--sys/dev/cxgb/common/cxgb_mv88e1xxx.c7
-rw-r--r--sys/dev/cxgb/common/cxgb_t3_hw.c11
-rw-r--r--sys/dev/cxgb/common/cxgb_tn1010.c7
-rw-r--r--sys/dev/cxgb/common/cxgb_vsc8211.c14
-rw-r--r--sys/dev/cxgb/cxgb_main.c3
8 files changed, 59 insertions, 33 deletions
diff --git a/sys/dev/cxgb/common/cxgb_ael1002.c b/sys/dev/cxgb/common/cxgb_ael1002.c
index bcd2728..e1a3fbe 100644
--- a/sys/dev/cxgb/common/cxgb_ael1002.c
+++ b/sys/dev/cxgb/common/cxgb_ael1002.c
@@ -283,10 +283,10 @@ static int ael1002_intr_noop(struct cphy *phy)
/*
* Get link status for a 10GBASE-R device.
*/
-static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
+static int get_link_status_r(struct cphy *phy, int *link_state, int *speed,
int *duplex, int *fc)
{
- if (link_ok) {
+ if (link_state) {
unsigned int stat0, stat1, stat2;
int err = mdio_read(phy, MDIO_DEV_PMA_PMD, PMD_RSD, &stat0);
@@ -296,10 +296,16 @@ static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
err = mdio_read(phy, MDIO_DEV_XGXS, XS_LN_STAT, &stat2);
if (err)
return err;
- *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
- if (*link_ok == 0)
- return (0);
+ stat0 &= 1;
+ stat1 &= 1;
+ stat2 = (stat2 >> 12) & 1;
+ if (stat0 & stat1 & stat2)
+ *link_state = PHY_LINK_UP;
+ else if (stat0 == 1 && stat1 == 0 && stat2 == 1)
+ *link_state = PHY_LINK_PARTIAL;
+ else
+ *link_state = PHY_LINK_DOWN;
}
if (speed)
*speed = SPEED_10000;
@@ -1345,10 +1351,8 @@ static int ael2005_intr_handler(struct cphy *phy)
return ret;
ret |= cause;
- if (!ret) {
- (void) ael2005_reset(phy, 0);
+ if (!ret)
ret |= cphy_cause_link_change;
- }
return ret;
}
@@ -2156,10 +2160,10 @@ int t3_ael2020_phy_prep(pinfo_t *pinfo, int phy_addr,
/*
* Get link status for a 10GBASE-X device.
*/
-static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
+static int get_link_status_x(struct cphy *phy, int *link_state, int *speed,
int *duplex, int *fc)
{
- if (link_ok) {
+ if (link_state) {
unsigned int stat0, stat1, stat2;
int err = mdio_read(phy, MDIO_DEV_PMA_PMD, PMD_RSD, &stat0);
@@ -2169,7 +2173,10 @@ static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
err = mdio_read(phy, MDIO_DEV_XGXS, XS_LN_STAT, &stat2);
if (err)
return err;
- *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;
+ if ((stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1)
+ *link_state = PHY_LINK_UP;
+ else
+ *link_state = PHY_LINK_DOWN;
}
if (speed)
*speed = SPEED_10000;
@@ -2230,10 +2237,10 @@ static int xaui_direct_reset(struct cphy *phy, int wait)
return 0;
}
-static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
+static int xaui_direct_get_link_status(struct cphy *phy, int *link_state,
int *speed, int *duplex, int *fc)
{
- if (link_ok) {
+ if (link_state) {
unsigned int status;
adapter_t *adapter = phy->adapter;
@@ -2245,7 +2252,7 @@ static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
XGM_REG(A_XGM_SERDES_STAT2, phy->addr)) |
t3_read_reg(adapter,
XGM_REG(A_XGM_SERDES_STAT3, phy->addr));
- *link_ok = !(status & F_LOWSIG0);
+ *link_state = status & F_LOWSIG0 ? PHY_LINK_DOWN : PHY_LINK_UP;
}
if (speed)
*speed = SPEED_10000;
diff --git a/sys/dev/cxgb/common/cxgb_aq100x.c b/sys/dev/cxgb/common/cxgb_aq100x.c
index d36d07a..b253e67 100644
--- a/sys/dev/cxgb/common/cxgb_aq100x.c
+++ b/sys/dev/cxgb/common/cxgb_aq100x.c
@@ -350,7 +350,7 @@ aq100x_set_speed_duplex(struct cphy *phy, int speed, int duplex)
}
static int
-aq100x_get_link_status(struct cphy *phy, int *link_ok, int *speed, int *duplex,
+aq100x_get_link_status(struct cphy *phy, int *link_state, int *speed, int *duplex,
int *fc)
{
int err;
@@ -440,8 +440,8 @@ aq100x_get_link_status(struct cphy *phy, int *link_ok, int *speed, int *duplex,
link = 1;
done:
- if (link_ok)
- *link_ok = link;
+ if (link_state)
+ *link_state = link ? PHY_LINK_UP : PHY_LINK_DOWN;
return (0);
}
diff --git a/sys/dev/cxgb/common/cxgb_common.h b/sys/dev/cxgb/common/cxgb_common.h
index df4ddda..32f0a93 100644
--- a/sys/dev/cxgb/common/cxgb_common.h
+++ b/sys/dev/cxgb/common/cxgb_common.h
@@ -543,6 +543,12 @@ enum {
phy_modtype_unknown
};
+enum {
+ PHY_LINK_DOWN = 0,
+ PHY_LINK_UP,
+ PHY_LINK_PARTIAL
+};
+
/* PHY operations */
struct cphy_ops {
int (*reset)(struct cphy *phy, int wait);
@@ -558,7 +564,7 @@ struct cphy_ops {
int (*advertise)(struct cphy *phy, unsigned int advertise_map);
int (*set_loopback)(struct cphy *phy, int mmd, int dir, int enable);
int (*set_speed_duplex)(struct cphy *phy, int speed, int duplex);
- int (*get_link_status)(struct cphy *phy, int *link_ok, int *speed,
+ int (*get_link_status)(struct cphy *phy, int *link_state, int *speed,
int *duplex, int *fc);
int (*power_down)(struct cphy *phy, int enable);
};
@@ -567,6 +573,7 @@ struct cphy_ops {
struct cphy {
u8 addr; /* PHY address */
u8 modtype; /* PHY module type */
+ u8 rst;
unsigned int priv; /* scratch pad */
unsigned int caps; /* PHY capabilities */
adapter_t *adapter; /* associated adapter */
diff --git a/sys/dev/cxgb/common/cxgb_mv88e1xxx.c b/sys/dev/cxgb/common/cxgb_mv88e1xxx.c
index 6281ac8..4093aa7 100644
--- a/sys/dev/cxgb/common/cxgb_mv88e1xxx.c
+++ b/sys/dev/cxgb/common/cxgb_mv88e1xxx.c
@@ -185,7 +185,7 @@ static int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on)
on ? BMCR_LOOPBACK : 0);
}
-static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,
+static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_state,
int *speed, int *duplex, int *fc)
{
u32 status;
@@ -206,8 +206,9 @@ static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,
else
sp = SPEED_1000;
}
- if (link_ok)
- *link_ok = (status & V_PSSR_LINK) != 0;
+ if (link_state)
+ *link_state = status & V_PSSR_LINK ? PHY_LINK_UP :
+ PHY_LINK_DOWN;
if (speed)
*speed = sp;
if (duplex)
diff --git a/sys/dev/cxgb/common/cxgb_t3_hw.c b/sys/dev/cxgb/common/cxgb_t3_hw.c
index 32aab35..942571a 100644
--- a/sys/dev/cxgb/common/cxgb_t3_hw.c
+++ b/sys/dev/cxgb/common/cxgb_t3_hw.c
@@ -1520,7 +1520,7 @@ static void t3_clear_faults(adapter_t *adapter, int port_id)
*/
void t3_link_changed(adapter_t *adapter, int port_id)
{
- int link_ok, speed, duplex, fc, link_fault;
+ int link_ok, speed, duplex, fc, link_fault, link_state;
struct port_info *pi = adap2pinfo(adapter, port_id);
struct cphy *phy = &pi->phy;
struct cmac *mac = &pi->mac;
@@ -1532,7 +1532,14 @@ void t3_link_changed(adapter_t *adapter, int port_id)
fc = lc->fc;
link_fault = 0;
- phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
+ phy->ops->get_link_status(phy, &link_state, &speed, &duplex, &fc);
+ link_ok = (link_state == PHY_LINK_UP);
+ if (link_state != PHY_LINK_PARTIAL)
+ phy->rst = 0;
+ else if (++phy->rst == 3) {
+ phy->ops->reset(phy, 0);
+ phy->rst = 0;
+ }
if (link_ok == 0)
pi->link_fault = LF_NO;
diff --git a/sys/dev/cxgb/common/cxgb_tn1010.c b/sys/dev/cxgb/common/cxgb_tn1010.c
index 4575828..1e8bf0b 100644
--- a/sys/dev/cxgb/common/cxgb_tn1010.c
+++ b/sys/dev/cxgb/common/cxgb_tn1010.c
@@ -129,7 +129,7 @@ static int tn1010_advertise(struct cphy *phy, unsigned int advert)
ADVERTISE_LOOP_TIMING);
}
-static int tn1010_get_link_status(struct cphy *phy, int *link_ok,
+static int tn1010_get_link_status(struct cphy *phy, int *link_state,
int *speed, int *duplex, int *fc)
{
unsigned int status, lpa, adv;
@@ -139,8 +139,9 @@ static int tn1010_get_link_status(struct cphy *phy, int *link_ok,
if (err)
return err;
- if (link_ok)
- *link_ok = (status & F_LINK_STAT) != 0;
+ if (link_state)
+ *link_state = status & F_LINK_STAT ? PHY_LINK_UP :
+ PHY_LINK_DOWN;
if (G_ANEG_STAT(status) == ANEG_COMPLETE) {
sp = (status & F_ANEG_SPEED_1G) ? SPEED_1000 : SPEED_10000;
diff --git a/sys/dev/cxgb/common/cxgb_vsc8211.c b/sys/dev/cxgb/common/cxgb_vsc8211.c
index 9d83859..4928e4a 100644
--- a/sys/dev/cxgb/common/cxgb_vsc8211.c
+++ b/sys/dev/cxgb/common/cxgb_vsc8211.c
@@ -129,7 +129,7 @@ static int vsc8211_autoneg_restart(struct cphy *cphy)
BMCR_ANRESTART);
}
-static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
+static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
int *speed, int *duplex, int *fc)
{
unsigned int bmcr, status, lpa, adv;
@@ -141,7 +141,7 @@ static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
if (err)
return err;
- if (link_ok) {
+ if (link_state) {
/*
* BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
* once more to get the current link state.
@@ -150,7 +150,8 @@ static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
err = mdio_read(cphy, 0, MII_BMSR, &status);
if (err)
return err;
- *link_ok = (status & BMSR_LSTATUS) != 0;
+ *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
+ PHY_LINK_DOWN;
}
if (!(bmcr & BMCR_ANENABLE)) {
dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
@@ -201,7 +202,7 @@ static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
return 0;
}
-static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
+static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_state,
int *speed, int *duplex, int *fc)
{
unsigned int bmcr, status, lpa, adv;
@@ -213,7 +214,7 @@ static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
if (err)
return err;
- if (link_ok) {
+ if (link_state) {
/*
* BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
* once more to get the current link state.
@@ -222,7 +223,8 @@ static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
err = mdio_read(cphy, 0, MII_BMSR, &status);
if (err)
return err;
- *link_ok = (status & BMSR_LSTATUS) != 0;
+ *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
+ PHY_LINK_DOWN;
}
if (!(bmcr & BMCR_ANENABLE)) {
dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
diff --git a/sys/dev/cxgb/cxgb_main.c b/sys/dev/cxgb/cxgb_main.c
index 81e2f5a..293de3d 100644
--- a/sys/dev/cxgb/cxgb_main.c
+++ b/sys/dev/cxgb/cxgb_main.c
@@ -2226,7 +2226,8 @@ check_link_status(void *arg, int pending)
t3_link_changed(sc, pi->port_id);
- if (pi->link_fault || !(pi->phy.caps & SUPPORTED_LINK_IRQ))
+ if (pi->link_fault || !(pi->phy.caps & SUPPORTED_LINK_IRQ) ||
+ pi->link_config.link_ok == 0)
callout_reset(&pi->link_check_ch, hz, link_check_callout, pi);
}
OpenPOWER on IntegriCloud