diff options
Diffstat (limited to 'drivers/net/benet/be_ethtool.c')
-rw-r--r-- | drivers/net/benet/be_ethtool.c | 294 |
1 files changed, 287 insertions, 7 deletions
diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c index f0fd95b43c07..51e1065e7897 100644 --- a/drivers/net/benet/be_ethtool.c +++ b/drivers/net/benet/be_ethtool.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2005 - 2009 ServerEngines | 2 | * Copyright (C) 2005 - 2010 ServerEngines |
3 | * All rights reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or | 5 | * This program is free software; you can redistribute it and/or |
@@ -55,7 +55,7 @@ static const struct be_ethtool_stat et_stats[] = { | |||
55 | {DRVSTAT_INFO(be_tx_stops)}, | 55 | {DRVSTAT_INFO(be_tx_stops)}, |
56 | {DRVSTAT_INFO(be_fwd_reqs)}, | 56 | {DRVSTAT_INFO(be_fwd_reqs)}, |
57 | {DRVSTAT_INFO(be_tx_wrbs)}, | 57 | {DRVSTAT_INFO(be_tx_wrbs)}, |
58 | {DRVSTAT_INFO(be_polls)}, | 58 | {DRVSTAT_INFO(be_rx_polls)}, |
59 | {DRVSTAT_INFO(be_tx_events)}, | 59 | {DRVSTAT_INFO(be_tx_events)}, |
60 | {DRVSTAT_INFO(be_rx_events)}, | 60 | {DRVSTAT_INFO(be_rx_events)}, |
61 | {DRVSTAT_INFO(be_tx_compl)}, | 61 | {DRVSTAT_INFO(be_tx_compl)}, |
@@ -107,6 +107,20 @@ static const struct be_ethtool_stat et_stats[] = { | |||
107 | }; | 107 | }; |
108 | #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats) | 108 | #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats) |
109 | 109 | ||
110 | static const char et_self_tests[][ETH_GSTRING_LEN] = { | ||
111 | "MAC Loopback test", | ||
112 | "PHY Loopback test", | ||
113 | "External Loopback test", | ||
114 | "DDR DMA test" | ||
115 | "Link test" | ||
116 | }; | ||
117 | |||
118 | #define ETHTOOL_TESTS_NUM ARRAY_SIZE(et_self_tests) | ||
119 | #define BE_MAC_LOOPBACK 0x0 | ||
120 | #define BE_PHY_LOOPBACK 0x1 | ||
121 | #define BE_ONE_PORT_EXT_LOOPBACK 0x2 | ||
122 | #define BE_NO_LOOPBACK 0xff | ||
123 | |||
110 | static void | 124 | static void |
111 | be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) | 125 | be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) |
112 | { | 126 | { |
@@ -234,7 +248,7 @@ be_get_ethtool_stats(struct net_device *netdev, | |||
234 | struct be_rxf_stats *rxf_stats = &hw_stats->rxf; | 248 | struct be_rxf_stats *rxf_stats = &hw_stats->rxf; |
235 | struct be_port_rxf_stats *port_stats = | 249 | struct be_port_rxf_stats *port_stats = |
236 | &rxf_stats->port[adapter->port_num]; | 250 | &rxf_stats->port[adapter->port_num]; |
237 | struct net_device_stats *net_stats = &adapter->stats.net_stats; | 251 | struct net_device_stats *net_stats = &netdev->stats; |
238 | struct be_erx_stats *erx_stats = &hw_stats->erx; | 252 | struct be_erx_stats *erx_stats = &hw_stats->erx; |
239 | void *p = NULL; | 253 | void *p = NULL; |
240 | int i; | 254 | int i; |
@@ -278,19 +292,100 @@ be_get_stat_strings(struct net_device *netdev, uint32_t stringset, | |||
278 | data += ETH_GSTRING_LEN; | 292 | data += ETH_GSTRING_LEN; |
279 | } | 293 | } |
280 | break; | 294 | break; |
295 | case ETH_SS_TEST: | ||
296 | for (i = 0; i < ETHTOOL_TESTS_NUM; i++) { | ||
297 | memcpy(data, et_self_tests[i], ETH_GSTRING_LEN); | ||
298 | data += ETH_GSTRING_LEN; | ||
299 | } | ||
300 | break; | ||
281 | } | 301 | } |
282 | } | 302 | } |
283 | 303 | ||
284 | static int be_get_stats_count(struct net_device *netdev) | 304 | static int be_get_sset_count(struct net_device *netdev, int stringset) |
285 | { | 305 | { |
286 | return ETHTOOL_STATS_NUM; | 306 | switch (stringset) { |
307 | case ETH_SS_TEST: | ||
308 | return ETHTOOL_TESTS_NUM; | ||
309 | case ETH_SS_STATS: | ||
310 | return ETHTOOL_STATS_NUM; | ||
311 | default: | ||
312 | return -EINVAL; | ||
313 | } | ||
287 | } | 314 | } |
288 | 315 | ||
289 | static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) | 316 | static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) |
290 | { | 317 | { |
291 | ecmd->speed = SPEED_10000; | 318 | struct be_adapter *adapter = netdev_priv(netdev); |
319 | u8 mac_speed = 0, connector = 0; | ||
320 | u16 link_speed = 0; | ||
321 | bool link_up = false; | ||
322 | int status; | ||
323 | |||
324 | if (adapter->link_speed < 0) { | ||
325 | status = be_cmd_link_status_query(adapter, &link_up, | ||
326 | &mac_speed, &link_speed); | ||
327 | |||
328 | /* link_speed is in units of 10 Mbps */ | ||
329 | if (link_speed) { | ||
330 | ecmd->speed = link_speed*10; | ||
331 | } else { | ||
332 | switch (mac_speed) { | ||
333 | case PHY_LINK_SPEED_1GBPS: | ||
334 | ecmd->speed = SPEED_1000; | ||
335 | break; | ||
336 | case PHY_LINK_SPEED_10GBPS: | ||
337 | ecmd->speed = SPEED_10000; | ||
338 | break; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | status = be_cmd_read_port_type(adapter, adapter->port_num, | ||
343 | &connector); | ||
344 | if (!status) { | ||
345 | switch (connector) { | ||
346 | case 7: | ||
347 | ecmd->port = PORT_FIBRE; | ||
348 | ecmd->transceiver = XCVR_EXTERNAL; | ||
349 | break; | ||
350 | case 0: | ||
351 | ecmd->port = PORT_TP; | ||
352 | ecmd->transceiver = XCVR_EXTERNAL; | ||
353 | break; | ||
354 | default: | ||
355 | ecmd->port = PORT_TP; | ||
356 | ecmd->transceiver = XCVR_INTERNAL; | ||
357 | break; | ||
358 | } | ||
359 | } else { | ||
360 | ecmd->port = PORT_AUI; | ||
361 | ecmd->transceiver = XCVR_INTERNAL; | ||
362 | } | ||
363 | |||
364 | /* Save for future use */ | ||
365 | adapter->link_speed = ecmd->speed; | ||
366 | adapter->port_type = ecmd->port; | ||
367 | adapter->transceiver = ecmd->transceiver; | ||
368 | } else { | ||
369 | ecmd->speed = adapter->link_speed; | ||
370 | ecmd->port = adapter->port_type; | ||
371 | ecmd->transceiver = adapter->transceiver; | ||
372 | } | ||
373 | |||
292 | ecmd->duplex = DUPLEX_FULL; | 374 | ecmd->duplex = DUPLEX_FULL; |
293 | ecmd->autoneg = AUTONEG_DISABLE; | 375 | ecmd->autoneg = AUTONEG_DISABLE; |
376 | ecmd->phy_address = adapter->port_num; | ||
377 | switch (ecmd->port) { | ||
378 | case PORT_FIBRE: | ||
379 | ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); | ||
380 | break; | ||
381 | case PORT_TP: | ||
382 | ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_TP); | ||
383 | break; | ||
384 | case PORT_AUI: | ||
385 | ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_AUI); | ||
386 | break; | ||
387 | } | ||
388 | |||
294 | return 0; | 389 | return 0; |
295 | } | 390 | } |
296 | 391 | ||
@@ -335,6 +430,142 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd) | |||
335 | } | 430 | } |
336 | 431 | ||
337 | static int | 432 | static int |
433 | be_phys_id(struct net_device *netdev, u32 data) | ||
434 | { | ||
435 | struct be_adapter *adapter = netdev_priv(netdev); | ||
436 | int status; | ||
437 | u32 cur; | ||
438 | |||
439 | be_cmd_get_beacon_state(adapter, adapter->port_num, &cur); | ||
440 | |||
441 | if (cur == BEACON_STATE_ENABLED) | ||
442 | return 0; | ||
443 | |||
444 | if (data < 2) | ||
445 | data = 2; | ||
446 | |||
447 | status = be_cmd_set_beacon_state(adapter, adapter->port_num, 0, 0, | ||
448 | BEACON_STATE_ENABLED); | ||
449 | set_current_state(TASK_INTERRUPTIBLE); | ||
450 | schedule_timeout(data*HZ); | ||
451 | |||
452 | status = be_cmd_set_beacon_state(adapter, adapter->port_num, 0, 0, | ||
453 | BEACON_STATE_DISABLED); | ||
454 | |||
455 | return status; | ||
456 | } | ||
457 | |||
458 | static void | ||
459 | be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) | ||
460 | { | ||
461 | struct be_adapter *adapter = netdev_priv(netdev); | ||
462 | |||
463 | wol->supported = WAKE_MAGIC; | ||
464 | if (adapter->wol) | ||
465 | wol->wolopts = WAKE_MAGIC; | ||
466 | else | ||
467 | wol->wolopts = 0; | ||
468 | memset(&wol->sopass, 0, sizeof(wol->sopass)); | ||
469 | return; | ||
470 | } | ||
471 | |||
472 | static int | ||
473 | be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) | ||
474 | { | ||
475 | struct be_adapter *adapter = netdev_priv(netdev); | ||
476 | |||
477 | if (wol->wolopts & ~WAKE_MAGIC) | ||
478 | return -EINVAL; | ||
479 | |||
480 | if (wol->wolopts & WAKE_MAGIC) | ||
481 | adapter->wol = true; | ||
482 | else | ||
483 | adapter->wol = false; | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int | ||
489 | be_test_ddr_dma(struct be_adapter *adapter) | ||
490 | { | ||
491 | int ret, i; | ||
492 | struct be_dma_mem ddrdma_cmd; | ||
493 | u64 pattern[2] = {0x5a5a5a5a5a5a5a5aULL, 0xa5a5a5a5a5a5a5a5ULL}; | ||
494 | |||
495 | ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test); | ||
496 | ddrdma_cmd.va = pci_alloc_consistent(adapter->pdev, ddrdma_cmd.size, | ||
497 | &ddrdma_cmd.dma); | ||
498 | if (!ddrdma_cmd.va) { | ||
499 | dev_err(&adapter->pdev->dev, "Memory allocation failure \n"); | ||
500 | return -ENOMEM; | ||
501 | } | ||
502 | |||
503 | for (i = 0; i < 2; i++) { | ||
504 | ret = be_cmd_ddr_dma_test(adapter, pattern[i], | ||
505 | 4096, &ddrdma_cmd); | ||
506 | if (ret != 0) | ||
507 | goto err; | ||
508 | } | ||
509 | |||
510 | err: | ||
511 | pci_free_consistent(adapter->pdev, ddrdma_cmd.size, | ||
512 | ddrdma_cmd.va, ddrdma_cmd.dma); | ||
513 | return ret; | ||
514 | } | ||
515 | |||
516 | static u64 be_loopback_test(struct be_adapter *adapter, u8 loopback_type, | ||
517 | u64 *status) | ||
518 | { | ||
519 | be_cmd_set_loopback(adapter, adapter->port_num, | ||
520 | loopback_type, 1); | ||
521 | *status = be_cmd_loopback_test(adapter, adapter->port_num, | ||
522 | loopback_type, 1500, | ||
523 | 2, 0xabc); | ||
524 | be_cmd_set_loopback(adapter, adapter->port_num, | ||
525 | BE_NO_LOOPBACK, 1); | ||
526 | return *status; | ||
527 | } | ||
528 | |||
529 | static void | ||
530 | be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data) | ||
531 | { | ||
532 | struct be_adapter *adapter = netdev_priv(netdev); | ||
533 | bool link_up; | ||
534 | u8 mac_speed = 0; | ||
535 | u16 qos_link_speed = 0; | ||
536 | |||
537 | memset(data, 0, sizeof(u64) * ETHTOOL_TESTS_NUM); | ||
538 | |||
539 | if (test->flags & ETH_TEST_FL_OFFLINE) { | ||
540 | if (be_loopback_test(adapter, BE_MAC_LOOPBACK, | ||
541 | &data[0]) != 0) { | ||
542 | test->flags |= ETH_TEST_FL_FAILED; | ||
543 | } | ||
544 | if (be_loopback_test(adapter, BE_PHY_LOOPBACK, | ||
545 | &data[1]) != 0) { | ||
546 | test->flags |= ETH_TEST_FL_FAILED; | ||
547 | } | ||
548 | if (be_loopback_test(adapter, BE_ONE_PORT_EXT_LOOPBACK, | ||
549 | &data[2]) != 0) { | ||
550 | test->flags |= ETH_TEST_FL_FAILED; | ||
551 | } | ||
552 | } | ||
553 | |||
554 | if (be_test_ddr_dma(adapter) != 0) { | ||
555 | data[3] = 1; | ||
556 | test->flags |= ETH_TEST_FL_FAILED; | ||
557 | } | ||
558 | |||
559 | if (be_cmd_link_status_query(adapter, &link_up, &mac_speed, | ||
560 | &qos_link_speed) != 0) { | ||
561 | test->flags |= ETH_TEST_FL_FAILED; | ||
562 | data[4] = -1; | ||
563 | } else if (mac_speed) { | ||
564 | data[4] = 1; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | static int | ||
338 | be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) | 569 | be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) |
339 | { | 570 | { |
340 | struct be_adapter *adapter = netdev_priv(netdev); | 571 | struct be_adapter *adapter = netdev_priv(netdev); |
@@ -348,10 +579,57 @@ be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) | |||
348 | return be_load_fw(adapter, file_name); | 579 | return be_load_fw(adapter, file_name); |
349 | } | 580 | } |
350 | 581 | ||
582 | static int | ||
583 | be_get_eeprom_len(struct net_device *netdev) | ||
584 | { | ||
585 | return BE_READ_SEEPROM_LEN; | ||
586 | } | ||
587 | |||
588 | static int | ||
589 | be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, | ||
590 | uint8_t *data) | ||
591 | { | ||
592 | struct be_adapter *adapter = netdev_priv(netdev); | ||
593 | struct be_dma_mem eeprom_cmd; | ||
594 | struct be_cmd_resp_seeprom_read *resp; | ||
595 | int status; | ||
596 | |||
597 | if (!eeprom->len) | ||
598 | return -EINVAL; | ||
599 | |||
600 | eeprom->magic = BE_VENDOR_ID | (adapter->pdev->device<<16); | ||
601 | |||
602 | memset(&eeprom_cmd, 0, sizeof(struct be_dma_mem)); | ||
603 | eeprom_cmd.size = sizeof(struct be_cmd_req_seeprom_read); | ||
604 | eeprom_cmd.va = pci_alloc_consistent(adapter->pdev, eeprom_cmd.size, | ||
605 | &eeprom_cmd.dma); | ||
606 | |||
607 | if (!eeprom_cmd.va) { | ||
608 | dev_err(&adapter->pdev->dev, | ||
609 | "Memory allocation failure. Could not read eeprom\n"); | ||
610 | return -ENOMEM; | ||
611 | } | ||
612 | |||
613 | status = be_cmd_get_seeprom_data(adapter, &eeprom_cmd); | ||
614 | |||
615 | if (!status) { | ||
616 | resp = (struct be_cmd_resp_seeprom_read *) eeprom_cmd.va; | ||
617 | memcpy(data, resp->seeprom_data + eeprom->offset, eeprom->len); | ||
618 | } | ||
619 | pci_free_consistent(adapter->pdev, eeprom_cmd.size, eeprom_cmd.va, | ||
620 | eeprom_cmd.dma); | ||
621 | |||
622 | return status; | ||
623 | } | ||
624 | |||
351 | const struct ethtool_ops be_ethtool_ops = { | 625 | const struct ethtool_ops be_ethtool_ops = { |
352 | .get_settings = be_get_settings, | 626 | .get_settings = be_get_settings, |
353 | .get_drvinfo = be_get_drvinfo, | 627 | .get_drvinfo = be_get_drvinfo, |
628 | .get_wol = be_get_wol, | ||
629 | .set_wol = be_set_wol, | ||
354 | .get_link = ethtool_op_get_link, | 630 | .get_link = ethtool_op_get_link, |
631 | .get_eeprom_len = be_get_eeprom_len, | ||
632 | .get_eeprom = be_read_eeprom, | ||
355 | .get_coalesce = be_get_coalesce, | 633 | .get_coalesce = be_get_coalesce, |
356 | .set_coalesce = be_set_coalesce, | 634 | .set_coalesce = be_set_coalesce, |
357 | .get_ringparam = be_get_ringparam, | 635 | .get_ringparam = be_get_ringparam, |
@@ -366,7 +644,9 @@ const struct ethtool_ops be_ethtool_ops = { | |||
366 | .get_tso = ethtool_op_get_tso, | 644 | .get_tso = ethtool_op_get_tso, |
367 | .set_tso = ethtool_op_set_tso, | 645 | .set_tso = ethtool_op_set_tso, |
368 | .get_strings = be_get_stat_strings, | 646 | .get_strings = be_get_stat_strings, |
369 | .get_stats_count = be_get_stats_count, | 647 | .phys_id = be_phys_id, |
648 | .get_sset_count = be_get_sset_count, | ||
370 | .get_ethtool_stats = be_get_ethtool_stats, | 649 | .get_ethtool_stats = be_get_ethtool_stats, |
371 | .flash_device = be_do_flash, | 650 | .flash_device = be_do_flash, |
651 | .self_test = be_self_test, | ||
372 | }; | 652 | }; |