diff options
-rw-r--r-- | drivers/net/sfc/qt202x_phy.c | 42 |
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 | ||
53 | struct qt202x_phy_data { | 53 | struct 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 | |||
60 | static int qt2025c_wait_reset(struct efx_nic *efx) | 64 | static 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 | ||
103 | static 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 | |||
99 | static int qt202x_reset_phy(struct efx_nic *efx) | 136 | static 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 | ||