diff options
author | Divy Le Ray <divy@chelsio.com> | 2007-03-18 16:10:12 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-03-23 01:48:34 -0400 |
commit | fc90664e3438c990d280f179ccb0642711d5c553 (patch) | |
tree | 8ece58a4eec002186f1c569385830f03336b50af /drivers/net/cxgb3/xgmac.c | |
parent | 2e2839627a957714808f98a802d137a7a2a1df46 (diff) |
cxgb3 - Fix potential MAC hang
Under rare conditions, the MAC might hang while generating a pause frame.
This patch fine tunes the MAC settings to avoid the issue, allows for
periodic MAC state check, and triggers a recovery if hung.
Also fix one MAC statistics counter for the rev board T3B2.
Signed-off-by: Divy Le Ray <divy@chelsio.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/cxgb3/xgmac.c')
-rw-r--r-- | drivers/net/cxgb3/xgmac.c | 133 |
1 files changed, 118 insertions, 15 deletions
diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/cxgb3/xgmac.c index 907a272ae32..2b42c13ba8e 100644 --- a/drivers/net/cxgb3/xgmac.c +++ b/drivers/net/cxgb3/xgmac.c | |||
@@ -124,9 +124,6 @@ int t3_mac_reset(struct cmac *mac) | |||
124 | xaui_serdes_reset(mac); | 124 | xaui_serdes_reset(mac); |
125 | } | 125 | } |
126 | 126 | ||
127 | if (adap->params.rev > 0) | ||
128 | t3_write_reg(adap, A_XGM_PAUSE_TIMER + oft, 0xf000); | ||
129 | |||
130 | val = F_MAC_RESET_; | 127 | val = F_MAC_RESET_; |
131 | if (is_10G(adap)) | 128 | if (is_10G(adap)) |
132 | val |= F_PCS_RESET_; | 129 | val |= F_PCS_RESET_; |
@@ -145,6 +142,58 @@ int t3_mac_reset(struct cmac *mac) | |||
145 | return 0; | 142 | return 0; |
146 | } | 143 | } |
147 | 144 | ||
145 | int t3b2_mac_reset(struct cmac *mac) | ||
146 | { | ||
147 | struct adapter *adap = mac->adapter; | ||
148 | unsigned int oft = mac->offset; | ||
149 | u32 val; | ||
150 | |||
151 | if (!macidx(mac)) | ||
152 | t3_set_reg_field(adap, A_MPS_CFG, F_PORT0ACTIVE, 0); | ||
153 | else | ||
154 | t3_set_reg_field(adap, A_MPS_CFG, F_PORT1ACTIVE, 0); | ||
155 | |||
156 | t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_); | ||
157 | t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ | ||
158 | |||
159 | msleep(10); | ||
160 | |||
161 | /* Check for xgm Rx fifo empty */ | ||
162 | if (t3_wait_op_done(adap, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT + oft, | ||
163 | 0x80000000, 1, 5, 2)) { | ||
164 | CH_ERR(adap, "MAC %d Rx fifo drain failed\n", | ||
165 | macidx(mac)); | ||
166 | return -1; | ||
167 | } | ||
168 | |||
169 | t3_write_reg(adap, A_XGM_RESET_CTRL + oft, 0); | ||
170 | t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ | ||
171 | |||
172 | val = F_MAC_RESET_; | ||
173 | if (is_10G(adap)) | ||
174 | val |= F_PCS_RESET_; | ||
175 | else if (uses_xaui(adap)) | ||
176 | val |= F_PCS_RESET_ | F_XG2G_RESET_; | ||
177 | else | ||
178 | val |= F_RGMII_RESET_ | F_XG2G_RESET_; | ||
179 | t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val); | ||
180 | t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ | ||
181 | if ((val & F_PCS_RESET_) && adap->params.rev) { | ||
182 | msleep(1); | ||
183 | t3b_pcs_reset(mac); | ||
184 | } | ||
185 | t3_write_reg(adap, A_XGM_RX_CFG + oft, | ||
186 | F_DISPAUSEFRAMES | F_EN1536BFRAMES | | ||
187 | F_RMFCS | F_ENJUMBO | F_ENHASHMCAST); | ||
188 | |||
189 | if (!macidx(mac)) | ||
190 | t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT0ACTIVE); | ||
191 | else | ||
192 | t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT1ACTIVE); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
148 | /* | 197 | /* |
149 | * Set the exact match register 'idx' to recognize the given Ethernet address. | 198 | * Set the exact match register 'idx' to recognize the given Ethernet address. |
150 | */ | 199 | */ |
@@ -251,9 +300,11 @@ int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu) | |||
251 | * Adjust the PAUSE frame watermarks. We always set the LWM, and the | 300 | * Adjust the PAUSE frame watermarks. We always set the LWM, and the |
252 | * HWM only if flow-control is enabled. | 301 | * HWM only if flow-control is enabled. |
253 | */ | 302 | */ |
254 | hwm = max(MAC_RXFIFO_SIZE - 3 * mtu, MAC_RXFIFO_SIZE / 2U); | 303 | hwm = max_t(unsigned int, MAC_RXFIFO_SIZE - 3 * mtu, |
255 | hwm = min(hwm, 3 * MAC_RXFIFO_SIZE / 4 + 1024); | 304 | MAC_RXFIFO_SIZE * 38 / 100); |
256 | lwm = hwm - 1024; | 305 | hwm = min(hwm, MAC_RXFIFO_SIZE - 8192); |
306 | lwm = min(3 * (int)mtu, MAC_RXFIFO_SIZE / 4); | ||
307 | |||
257 | v = t3_read_reg(adap, A_XGM_RXFIFO_CFG + mac->offset); | 308 | v = t3_read_reg(adap, A_XGM_RXFIFO_CFG + mac->offset); |
258 | v &= ~V_RXFIFOPAUSELWM(M_RXFIFOPAUSELWM); | 309 | v &= ~V_RXFIFOPAUSELWM(M_RXFIFOPAUSELWM); |
259 | v |= V_RXFIFOPAUSELWM(lwm / 8); | 310 | v |= V_RXFIFOPAUSELWM(lwm / 8); |
@@ -270,7 +321,15 @@ int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu) | |||
270 | thres = mtu > thres ? (mtu - thres + 7) / 8 : 0; | 321 | thres = mtu > thres ? (mtu - thres + 7) / 8 : 0; |
271 | thres = max(thres, 8U); /* need at least 8 */ | 322 | thres = max(thres, 8U); /* need at least 8 */ |
272 | t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + mac->offset, | 323 | t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + mac->offset, |
273 | V_TXFIFOTHRESH(M_TXFIFOTHRESH), V_TXFIFOTHRESH(thres)); | 324 | V_TXFIFOTHRESH(M_TXFIFOTHRESH) | V_TXIPG(M_TXIPG), |
325 | V_TXFIFOTHRESH(thres) | V_TXIPG(1)); | ||
326 | |||
327 | if (adap->params.rev > 0) | ||
328 | t3_write_reg(adap, A_XGM_PAUSE_TIMER + mac->offset, | ||
329 | (hwm - lwm) * 4 / 8); | ||
330 | t3_write_reg(adap, A_XGM_TX_PAUSE_QUANTA + mac->offset, | ||
331 | MAC_RXFIFO_SIZE * 4 * 8 / 512); | ||
332 | |||
274 | return 0; | 333 | return 0; |
275 | } | 334 | } |
276 | 335 | ||
@@ -298,12 +357,6 @@ int t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc) | |||
298 | V_PORTSPEED(M_PORTSPEED), val); | 357 | V_PORTSPEED(M_PORTSPEED), val); |
299 | } | 358 | } |
300 | 359 | ||
301 | val = t3_read_reg(adap, A_XGM_RXFIFO_CFG + oft); | ||
302 | val &= ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM); | ||
303 | if (fc & PAUSE_TX) | ||
304 | val |= V_RXFIFOPAUSEHWM(G_RXFIFOPAUSELWM(val) + 128); /* +1KB */ | ||
305 | t3_write_reg(adap, A_XGM_RXFIFO_CFG + oft, val); | ||
306 | |||
307 | t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN, | 360 | t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN, |
308 | (fc & PAUSE_RX) ? F_TXPAUSEEN : 0); | 361 | (fc & PAUSE_RX) ? F_TXPAUSEEN : 0); |
309 | return 0; | 362 | return 0; |
@@ -318,9 +371,17 @@ int t3_mac_enable(struct cmac *mac, int which) | |||
318 | if (which & MAC_DIRECTION_TX) { | 371 | if (which & MAC_DIRECTION_TX) { |
319 | t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN); | 372 | t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN); |
320 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); | 373 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); |
321 | t3_write_reg(adap, A_TP_PIO_DATA, 0xbf000001); | 374 | t3_write_reg(adap, A_TP_PIO_DATA, 0xc0ede401); |
322 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); | 375 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); |
323 | t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 1 << idx); | 376 | t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 1 << idx); |
377 | |||
378 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CNT_CH0 + idx); | ||
379 | mac->tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap, | ||
380 | A_TP_PIO_DATA))); | ||
381 | mac->xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap, | ||
382 | A_XGM_TX_SPI4_SOP_EOP_CNT))); | ||
383 | mac->txen = F_TXEN; | ||
384 | mac->toggle_cnt = 0; | ||
324 | } | 385 | } |
325 | if (which & MAC_DIRECTION_RX) | 386 | if (which & MAC_DIRECTION_RX) |
326 | t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN); | 387 | t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN); |
@@ -337,13 +398,50 @@ int t3_mac_disable(struct cmac *mac, int which) | |||
337 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); | 398 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); |
338 | t3_write_reg(adap, A_TP_PIO_DATA, 0xc000001f); | 399 | t3_write_reg(adap, A_TP_PIO_DATA, 0xc000001f); |
339 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); | 400 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); |
340 | t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 0); | 401 | t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 1 << idx); |
402 | mac->txen = 0; | ||
341 | } | 403 | } |
342 | if (which & MAC_DIRECTION_RX) | 404 | if (which & MAC_DIRECTION_RX) |
343 | t3_write_reg(adap, A_XGM_RX_CTRL + mac->offset, 0); | 405 | t3_write_reg(adap, A_XGM_RX_CTRL + mac->offset, 0); |
344 | return 0; | 406 | return 0; |
345 | } | 407 | } |
346 | 408 | ||
409 | int t3b2_mac_watchdog_task(struct cmac *mac) | ||
410 | { | ||
411 | struct adapter *adap = mac->adapter; | ||
412 | unsigned int tcnt, xcnt; | ||
413 | int status; | ||
414 | |||
415 | t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CNT_CH0 + macidx(mac)); | ||
416 | tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap, A_TP_PIO_DATA))); | ||
417 | xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap, | ||
418 | A_XGM_TX_SPI4_SOP_EOP_CNT + | ||
419 | mac->offset))); | ||
420 | |||
421 | if (tcnt != mac->tcnt && xcnt == 0 && mac->xcnt == 0) { | ||
422 | if (mac->toggle_cnt > 4) { | ||
423 | t3b2_mac_reset(mac); | ||
424 | mac->toggle_cnt = 0; | ||
425 | status = 2; | ||
426 | } else { | ||
427 | t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0); | ||
428 | t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset); | ||
429 | t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, | ||
430 | mac->txen); | ||
431 | t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset); | ||
432 | mac->toggle_cnt++; | ||
433 | status = 1; | ||
434 | } | ||
435 | } else { | ||
436 | mac->toggle_cnt = 0; | ||
437 | status = 0; | ||
438 | } | ||
439 | mac->tcnt = tcnt; | ||
440 | mac->xcnt = xcnt; | ||
441 | |||
442 | return status; | ||
443 | } | ||
444 | |||
347 | /* | 445 | /* |
348 | * This function is called periodically to accumulate the current values of the | 446 | * This function is called periodically to accumulate the current values of the |
349 | * RMON counters into the port statistics. Since the packet counters are only | 447 | * RMON counters into the port statistics. Since the packet counters are only |
@@ -375,6 +473,11 @@ const struct mac_stats *t3_mac_update_stats(struct cmac *mac) | |||
375 | RMON_UPDATE(mac, rx_too_long, RX_OVERSIZE_FRAMES); | 473 | RMON_UPDATE(mac, rx_too_long, RX_OVERSIZE_FRAMES); |
376 | mac->stats.rx_too_long += RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT); | 474 | mac->stats.rx_too_long += RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT); |
377 | 475 | ||
476 | v = RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT); | ||
477 | if (mac->adapter->params.rev == T3_REV_B2) | ||
478 | v &= 0x7fffffff; | ||
479 | mac->stats.rx_too_long += v; | ||
480 | |||
378 | RMON_UPDATE(mac, rx_frames_64, RX_64B_FRAMES); | 481 | RMON_UPDATE(mac, rx_frames_64, RX_64B_FRAMES); |
379 | RMON_UPDATE(mac, rx_frames_65_127, RX_65_127B_FRAMES); | 482 | RMON_UPDATE(mac, rx_frames_65_127, RX_65_127B_FRAMES); |
380 | RMON_UPDATE(mac, rx_frames_128_255, RX_128_255B_FRAMES); | 483 | RMON_UPDATE(mac, rx_frames_128_255, RX_128_255B_FRAMES); |