aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc
diff options
context:
space:
mode:
authorMatthew Slattery <mslattery@solarflare.com>2009-12-23 08:47:37 -0500
committerDavid S. Miller <davem@davemloft.net>2009-12-23 22:09:06 -0500
commit17d6aeafe9d8268612d91edc0102659edb382282 (patch)
tree51367606075b11c19c8926a5f00e6811a685297e /drivers/net/sfc
parenta7ebd27a13757248863cd61e541af7fa9e7727ee (diff)
sfc: QT2025C: Work around PHY bug
If we see the PHY remaining stuck in a link-down state due to PCS being down while PMA/PMD is up, we briefly switch to PMA/PMD loopback and back, which usually unsticks it. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r--drivers/net/sfc/qt202x_phy.c42
1 files changed, 42 insertions, 0 deletions
diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c
index 7450e3afbf4d..e4590fb528a1 100644
--- a/drivers/net/sfc/qt202x_phy.c
+++ b/drivers/net/sfc/qt202x_phy.c
@@ -52,11 +52,15 @@ void falcon_qt202x_set_led(struct efx_nic *p, int led, int mode)
52 52
53struct qt202x_phy_data { 53struct qt202x_phy_data {
54 enum efx_phy_mode phy_mode; 54 enum efx_phy_mode phy_mode;
55 bool bug17190_in_bad_state;
56 unsigned long bug17190_timer;
55}; 57};
56 58
57#define QT2022C2_MAX_RESET_TIME 500 59#define QT2022C2_MAX_RESET_TIME 500
58#define QT2022C2_RESET_WAIT 10 60#define QT2022C2_RESET_WAIT 10
59 61
62#define BUG17190_INTERVAL (2 * HZ)
63
60static int qt2025c_wait_reset(struct efx_nic *efx) 64static int qt2025c_wait_reset(struct efx_nic *efx)
61{ 65{
62 unsigned long timeout = jiffies + 10 * HZ; 66 unsigned long timeout = jiffies + 10 * HZ;
@@ -96,6 +100,39 @@ static int qt2025c_wait_reset(struct efx_nic *efx)
96 return 0; 100 return 0;
97} 101}
98 102
103static void qt2025c_bug17190_workaround(struct efx_nic *efx)
104{
105 struct qt202x_phy_data *phy_data = efx->phy_data;
106
107 /* The PHY can get stuck in a state where it reports PHY_XS and PMA/PMD
108 * layers up, but PCS down (no block_lock). If we notice this state
109 * persisting for a couple of seconds, we switch PMA/PMD loopback
110 * briefly on and then off again, which is normally sufficient to
111 * recover it.
112 */
113 if (efx->link_state.up ||
114 !efx_mdio_links_ok(efx, MDIO_DEVS_PMAPMD | MDIO_DEVS_PHYXS)) {
115 phy_data->bug17190_in_bad_state = false;
116 return;
117 }
118
119 if (!phy_data->bug17190_in_bad_state) {
120 phy_data->bug17190_in_bad_state = true;
121 phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL;
122 return;
123 }
124
125 if (time_after_eq(jiffies, phy_data->bug17190_timer)) {
126 EFX_LOG(efx, "bashing QT2025C PMA/PMD\n");
127 efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1,
128 MDIO_PMA_CTRL1_LOOPBACK, true);
129 msleep(100);
130 efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1,
131 MDIO_PMA_CTRL1_LOOPBACK, false);
132 phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL;
133 }
134}
135
99static int qt202x_reset_phy(struct efx_nic *efx) 136static int qt202x_reset_phy(struct efx_nic *efx)
100{ 137{
101 int rc; 138 int rc;
@@ -144,6 +181,8 @@ static int qt202x_phy_probe(struct efx_nic *efx)
144 return -ENOMEM; 181 return -ENOMEM;
145 efx->phy_data = phy_data; 182 efx->phy_data = phy_data;
146 phy_data->phy_mode = efx->phy_mode; 183 phy_data->phy_mode = efx->phy_mode;
184 phy_data->bug17190_in_bad_state = false;
185 phy_data->bug17190_timer = 0;
147 186
148 efx->mdio.mmds = QT202X_REQUIRED_DEVS; 187 efx->mdio.mmds = QT202X_REQUIRED_DEVS;
149 efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; 188 efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
@@ -184,6 +223,9 @@ static bool qt202x_phy_poll(struct efx_nic *efx)
184 efx->link_state.fd = true; 223 efx->link_state.fd = true;
185 efx->link_state.fc = efx->wanted_fc; 224 efx->link_state.fc = efx->wanted_fc;
186 225
226 if (efx->phy_type == PHY_TYPE_QT2025C)
227 qt2025c_bug17190_workaround(efx);
228
187 return efx->link_state.up != was_up; 229 return efx->link_state.up != was_up;
188} 230}
189 231