diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2008-12-13 00:50:46 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-12-13 00:58:17 -0500 |
commit | 04cc8cacb01c09fba2297faf1477cd570ba43f0b (patch) | |
tree | f17dbd584b072d14f1500c6f6d659be993ae35c7 /drivers/net/sfc/tenxpress.c | |
parent | 177dfcd80f28f8fbc3e22c2d8b24d21cb86f1d97 (diff) |
sfc: Implement auto-negotiation
Add infrastructure for auto-negotiation of speed, duplex and flow
control.
When using 10Xpress, auto-negotiate flow control. While we're
at it, clean up the code to warn when partner is not 10GBASE-T
capable.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc/tenxpress.c')
-rw-r--r-- | drivers/net/sfc/tenxpress.c | 138 |
1 files changed, 72 insertions, 66 deletions
diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index d60353bb40b6..634ff9198823 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c | |||
@@ -17,10 +17,10 @@ | |||
17 | #include "boards.h" | 17 | #include "boards.h" |
18 | 18 | ||
19 | /* We expect these MMDs to be in the package */ | 19 | /* We expect these MMDs to be in the package */ |
20 | /* AN not here as mdio_check_mmds() requires STAT2 support */ | ||
21 | #define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD | \ | 20 | #define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD | \ |
22 | MDIO_MMDREG_DEVS_PCS | \ | 21 | MDIO_MMDREG_DEVS_PCS | \ |
23 | MDIO_MMDREG_DEVS_PHYXS) | 22 | MDIO_MMDREG_DEVS_PHYXS | \ |
23 | MDIO_MMDREG_DEVS_AN) | ||
24 | 24 | ||
25 | #define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ | 25 | #define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ |
26 | (1 << LOOPBACK_PCS) | \ | 26 | (1 << LOOPBACK_PCS) | \ |
@@ -57,6 +57,7 @@ | |||
57 | #define PMA_PMD_LED_ON (1) | 57 | #define PMA_PMD_LED_ON (1) |
58 | #define PMA_PMD_LED_OFF (2) | 58 | #define PMA_PMD_LED_OFF (2) |
59 | #define PMA_PMD_LED_FLASH (3) | 59 | #define PMA_PMD_LED_FLASH (3) |
60 | #define PMA_PMD_LED_MASK 3 | ||
60 | /* All LEDs under hardware control */ | 61 | /* All LEDs under hardware control */ |
61 | #define PMA_PMD_LED_FULL_AUTO (0) | 62 | #define PMA_PMD_LED_FULL_AUTO (0) |
62 | /* Green and Amber under hardware control, Red off */ | 63 | /* Green and Amber under hardware control, Red off */ |
@@ -242,78 +243,60 @@ unlock: | |||
242 | return rc; | 243 | return rc; |
243 | } | 244 | } |
244 | 245 | ||
245 | static void tenxpress_set_bad_lp(struct efx_nic *efx, bool bad_lp) | 246 | static void tenxpress_check_bad_lp(struct efx_nic *efx, bool link_ok) |
246 | { | 247 | { |
247 | struct tenxpress_phy_data *pd = efx->phy_data; | 248 | struct tenxpress_phy_data *pd = efx->phy_data; |
249 | int phy_id = efx->mii.phy_id; | ||
250 | bool bad_lp; | ||
248 | int reg; | 251 | int reg; |
249 | 252 | ||
253 | if (link_ok) { | ||
254 | bad_lp = false; | ||
255 | } else { | ||
256 | /* Check that AN has started but not completed. */ | ||
257 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, | ||
258 | MDIO_AN_STATUS); | ||
259 | if (!(reg & (1 << MDIO_AN_STATUS_LP_AN_CAP_LBN))) | ||
260 | return; /* LP status is unknown */ | ||
261 | bad_lp = !(reg & (1 << MDIO_AN_STATUS_AN_DONE_LBN)); | ||
262 | if (bad_lp) | ||
263 | pd->bad_lp_tries++; | ||
264 | } | ||
265 | |||
250 | /* Nothing to do if all is well and was previously so. */ | 266 | /* Nothing to do if all is well and was previously so. */ |
251 | if (!(bad_lp || pd->bad_lp_tries)) | 267 | if (!pd->bad_lp_tries) |
252 | return; | 268 | return; |
253 | 269 | ||
254 | reg = mdio_clause45_read(efx, efx->mii.phy_id, | 270 | /* Use the RX (red) LED as an error indicator once we've seen AN |
255 | MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG); | 271 | * failure several times in a row, and also log a message. */ |
256 | 272 | if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) { | |
257 | if (bad_lp) | 273 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, |
258 | pd->bad_lp_tries++; | 274 | PMA_PMD_LED_OVERR_REG); |
259 | else | 275 | reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN); |
260 | pd->bad_lp_tries = 0; | 276 | if (!bad_lp) { |
261 | 277 | reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN; | |
262 | if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) { | 278 | } else { |
263 | pd->bad_lp_tries = 0; /* Restart count */ | 279 | reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN; |
264 | reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); | 280 | EFX_ERR(efx, "appears to be plugged into a port" |
265 | reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); | 281 | " that is not 10GBASE-T capable. The PHY" |
266 | EFX_ERR(efx, "This NIC appears to be plugged into" | 282 | " supports 10GBASE-T ONLY, so no link can" |
267 | " a port that is not 10GBASE-T capable.\n" | 283 | " be established\n"); |
268 | " This PHY is 10GBASE-T ONLY, so no link can" | 284 | } |
269 | " be established.\n"); | 285 | mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, |
270 | } else { | 286 | PMA_PMD_LED_OVERR_REG, reg); |
271 | reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN); | 287 | pd->bad_lp_tries = bad_lp; |
272 | } | 288 | } |
273 | mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, | ||
274 | PMA_PMD_LED_OVERR_REG, reg); | ||
275 | } | 289 | } |
276 | 290 | ||
277 | /* Check link status and return a boolean OK value. If the link is NOT | 291 | static bool tenxpress_link_ok(struct efx_nic *efx) |
278 | * OK we have a quick rummage round to see if we appear to be plugged | ||
279 | * into a non-10GBT port and if so warn the user that they won't get | ||
280 | * link any time soon as we are 10GBT only, unless caller specified | ||
281 | * not to do this check (it isn't useful in loopback) */ | ||
282 | static bool tenxpress_link_ok(struct efx_nic *efx, bool check_lp) | ||
283 | { | 292 | { |
284 | bool ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS); | 293 | if (efx->loopback_mode == LOOPBACK_NONE) |
285 | 294 | return mdio_clause45_links_ok(efx, MDIO_MMDREG_DEVS_AN); | |
286 | if (ok) { | 295 | else |
287 | tenxpress_set_bad_lp(efx, false); | 296 | return mdio_clause45_links_ok(efx, |
288 | } else if (check_lp) { | 297 | MDIO_MMDREG_DEVS_PMAPMD | |
289 | /* Are we plugged into the wrong sort of link? */ | 298 | MDIO_MMDREG_DEVS_PCS | |
290 | bool bad_lp = false; | 299 | MDIO_MMDREG_DEVS_PHYXS); |
291 | int phy_id = efx->mii.phy_id; | ||
292 | int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, | ||
293 | MDIO_AN_STATUS); | ||
294 | int xphy_stat = mdio_clause45_read(efx, phy_id, | ||
295 | MDIO_MMD_PMAPMD, | ||
296 | PMA_PMD_XSTATUS_REG); | ||
297 | /* Are we plugged into anything that sends FLPs? If | ||
298 | * not we can't distinguish between not being plugged | ||
299 | * in and being plugged into a non-AN antique. The FLP | ||
300 | * bit has the advantage of not clearing when autoneg | ||
301 | * restarts. */ | ||
302 | if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) { | ||
303 | tenxpress_set_bad_lp(efx, false); | ||
304 | return ok; | ||
305 | } | ||
306 | |||
307 | /* If it can do 10GBT it must be XNP capable */ | ||
308 | bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN)); | ||
309 | if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) { | ||
310 | bad_lp = !(mdio_clause45_read(efx, phy_id, | ||
311 | MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) & | ||
312 | (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)); | ||
313 | } | ||
314 | tenxpress_set_bad_lp(efx, bad_lp); | ||
315 | } | ||
316 | return ok; | ||
317 | } | 300 | } |
318 | 301 | ||
319 | static void tenxpress_phyxs_loopback(struct efx_nic *efx) | 302 | static void tenxpress_phyxs_loopback(struct efx_nic *efx) |
@@ -359,9 +342,10 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) | |||
359 | 342 | ||
360 | phy_data->loopback_mode = efx->loopback_mode; | 343 | phy_data->loopback_mode = efx->loopback_mode; |
361 | phy_data->phy_mode = efx->phy_mode; | 344 | phy_data->phy_mode = efx->phy_mode; |
362 | efx->link_up = tenxpress_link_ok(efx, false); | 345 | efx->link_up = tenxpress_link_ok(efx); |
363 | efx->link_speed = 10000; | 346 | efx->link_speed = 10000; |
364 | efx->link_fd = true; | 347 | efx->link_fd = true; |
348 | efx->link_fc = mdio_clause45_get_pause(efx); | ||
365 | } | 349 | } |
366 | 350 | ||
367 | static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) | 351 | static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) |
@@ -377,7 +361,8 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx) | |||
377 | bool link_ok; | 361 | bool link_ok; |
378 | int rc = 0; | 362 | int rc = 0; |
379 | 363 | ||
380 | link_ok = tenxpress_link_ok(efx, true); | 364 | link_ok = tenxpress_link_ok(efx); |
365 | tenxpress_check_bad_lp(efx, link_ok); | ||
381 | 366 | ||
382 | if (link_ok != efx->link_up) | 367 | if (link_ok != efx->link_up) |
383 | falcon_sim_phy_event(efx); | 368 | falcon_sim_phy_event(efx); |
@@ -451,6 +436,27 @@ static int tenxpress_phy_test(struct efx_nic *efx) | |||
451 | return tenxpress_special_reset(efx); | 436 | return tenxpress_special_reset(efx); |
452 | } | 437 | } |
453 | 438 | ||
439 | static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx) | ||
440 | { | ||
441 | int phy = efx->mii.phy_id; | ||
442 | u32 lpa = 0; | ||
443 | int reg; | ||
444 | |||
445 | reg = mdio_clause45_read(efx, phy, MDIO_MMD_AN, MDIO_AN_10GBT_STATUS); | ||
446 | if (reg & (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)) | ||
447 | lpa |= ADVERTISED_10000baseT_Full; | ||
448 | return lpa; | ||
449 | } | ||
450 | |||
451 | static void | ||
452 | tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) | ||
453 | { | ||
454 | mdio_clause45_get_settings_ext(efx, ecmd, ADVERTISED_10000baseT_Full, | ||
455 | tenxpress_get_xnp_lpa(efx)); | ||
456 | ecmd->supported |= SUPPORTED_10000baseT_Full; | ||
457 | ecmd->advertising |= ADVERTISED_10000baseT_Full; | ||
458 | } | ||
459 | |||
454 | struct efx_phy_operations falcon_tenxpress_phy_ops = { | 460 | struct efx_phy_operations falcon_tenxpress_phy_ops = { |
455 | .macs = EFX_XMAC, | 461 | .macs = EFX_XMAC, |
456 | .init = tenxpress_phy_init, | 462 | .init = tenxpress_phy_init, |
@@ -459,7 +465,7 @@ struct efx_phy_operations falcon_tenxpress_phy_ops = { | |||
459 | .fini = tenxpress_phy_fini, | 465 | .fini = tenxpress_phy_fini, |
460 | .clear_interrupt = tenxpress_phy_clear_interrupt, | 466 | .clear_interrupt = tenxpress_phy_clear_interrupt, |
461 | .test = tenxpress_phy_test, | 467 | .test = tenxpress_phy_test, |
462 | .get_settings = mdio_clause45_get_settings, | 468 | .get_settings = tenxpress_get_settings, |
463 | .set_settings = mdio_clause45_set_settings, | 469 | .set_settings = mdio_clause45_set_settings, |
464 | .mmds = TENXPRESS_REQUIRED_DEVS, | 470 | .mmds = TENXPRESS_REQUIRED_DEVS, |
465 | .loopbacks = TENXPRESS_LOOPBACKS, | 471 | .loopbacks = TENXPRESS_LOOPBACKS, |