diff options
author | Donald Skidmore <donald.c.skidmore@intel.com> | 2008-11-21 00:11:42 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-21 00:11:42 -0500 |
commit | c4900be053d376dfe4f603d000aa5e4c60745dec (patch) | |
tree | f5658e8d4f2345e0f15346020fe3aeb2adb48905 /drivers/net/ixgbe/ixgbe_phy.c | |
parent | 859ee3c43812051e21816c6d6d4cc04fb7ce9b2e (diff) |
ixgbe: add SFP+ driver support
This patch adds support for SFP+ PHY in the following device ID's (10DB,
10F1, 10E1). These SFP+ PHY's are accessed via an I2C interface so the
patch also includes functions to support this.
Another feature of note is that the PHY is pluggable and some
rearchitecting was needed to support this.
Signed-off-by: Donald Skidmore <donald.c.skidmore@intel.com>
Signed-off-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ixgbe/ixgbe_phy.c')
-rw-r--r-- | drivers/net/ixgbe/ixgbe_phy.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c index 981e6d849592..5a8669aedf64 100644 --- a/drivers/net/ixgbe/ixgbe_phy.c +++ b/drivers/net/ixgbe/ixgbe_phy.c | |||
@@ -127,6 +127,9 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) | |||
127 | case QT2022_PHY_ID: | 127 | case QT2022_PHY_ID: |
128 | phy_type = ixgbe_phy_qt; | 128 | phy_type = ixgbe_phy_qt; |
129 | break; | 129 | break; |
130 | case ATH_PHY_ID: | ||
131 | phy_type = ixgbe_phy_nl; | ||
132 | break; | ||
130 | default: | 133 | default: |
131 | phy_type = ixgbe_phy_unknown; | 134 | phy_type = ixgbe_phy_unknown; |
132 | break; | 135 | break; |
@@ -430,6 +433,261 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, | |||
430 | } | 433 | } |
431 | 434 | ||
432 | /** | 435 | /** |
436 | * ixgbe_reset_phy_nl - Performs a PHY reset | ||
437 | * @hw: pointer to hardware structure | ||
438 | **/ | ||
439 | s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) | ||
440 | { | ||
441 | u16 phy_offset, control, eword, edata, block_crc; | ||
442 | bool end_data = false; | ||
443 | u16 list_offset, data_offset; | ||
444 | u16 phy_data = 0; | ||
445 | s32 ret_val = 0; | ||
446 | u32 i; | ||
447 | |||
448 | hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, | ||
449 | IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data); | ||
450 | |||
451 | /* reset the PHY and poll for completion */ | ||
452 | hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, | ||
453 | IXGBE_MDIO_PHY_XS_DEV_TYPE, | ||
454 | (phy_data | IXGBE_MDIO_PHY_XS_RESET)); | ||
455 | |||
456 | for (i = 0; i < 100; i++) { | ||
457 | hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, | ||
458 | IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data); | ||
459 | if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) == 0) | ||
460 | break; | ||
461 | msleep(10); | ||
462 | } | ||
463 | |||
464 | if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) != 0) { | ||
465 | hw_dbg(hw, "PHY reset did not complete.\n"); | ||
466 | ret_val = IXGBE_ERR_PHY; | ||
467 | goto out; | ||
468 | } | ||
469 | |||
470 | /* Get init offsets */ | ||
471 | ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset, | ||
472 | &data_offset); | ||
473 | if (ret_val != 0) | ||
474 | goto out; | ||
475 | |||
476 | ret_val = hw->eeprom.ops.read(hw, data_offset, &block_crc); | ||
477 | data_offset++; | ||
478 | while (!end_data) { | ||
479 | /* | ||
480 | * Read control word from PHY init contents offset | ||
481 | */ | ||
482 | ret_val = hw->eeprom.ops.read(hw, data_offset, &eword); | ||
483 | control = (eword & IXGBE_CONTROL_MASK_NL) >> | ||
484 | IXGBE_CONTROL_SHIFT_NL; | ||
485 | edata = eword & IXGBE_DATA_MASK_NL; | ||
486 | switch (control) { | ||
487 | case IXGBE_DELAY_NL: | ||
488 | data_offset++; | ||
489 | hw_dbg(hw, "DELAY: %d MS\n", edata); | ||
490 | msleep(edata); | ||
491 | break; | ||
492 | case IXGBE_DATA_NL: | ||
493 | hw_dbg(hw, "DATA: \n"); | ||
494 | data_offset++; | ||
495 | hw->eeprom.ops.read(hw, data_offset++, | ||
496 | &phy_offset); | ||
497 | for (i = 0; i < edata; i++) { | ||
498 | hw->eeprom.ops.read(hw, data_offset, &eword); | ||
499 | hw->phy.ops.write_reg(hw, phy_offset, | ||
500 | IXGBE_TWINAX_DEV, eword); | ||
501 | hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword, | ||
502 | phy_offset); | ||
503 | data_offset++; | ||
504 | phy_offset++; | ||
505 | } | ||
506 | break; | ||
507 | case IXGBE_CONTROL_NL: | ||
508 | data_offset++; | ||
509 | hw_dbg(hw, "CONTROL: \n"); | ||
510 | if (edata == IXGBE_CONTROL_EOL_NL) { | ||
511 | hw_dbg(hw, "EOL\n"); | ||
512 | end_data = true; | ||
513 | } else if (edata == IXGBE_CONTROL_SOL_NL) { | ||
514 | hw_dbg(hw, "SOL\n"); | ||
515 | } else { | ||
516 | hw_dbg(hw, "Bad control value\n"); | ||
517 | ret_val = IXGBE_ERR_PHY; | ||
518 | goto out; | ||
519 | } | ||
520 | break; | ||
521 | default: | ||
522 | hw_dbg(hw, "Bad control type\n"); | ||
523 | ret_val = IXGBE_ERR_PHY; | ||
524 | goto out; | ||
525 | } | ||
526 | } | ||
527 | |||
528 | out: | ||
529 | return ret_val; | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * ixgbe_identify_sfp_module_generic - Identifies SFP module and assigns | ||
534 | * the PHY type. | ||
535 | * @hw: pointer to hardware structure | ||
536 | * | ||
537 | * Searches for and indentifies the SFP module. Assings appropriate PHY type. | ||
538 | **/ | ||
539 | s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) | ||
540 | { | ||
541 | s32 status = IXGBE_ERR_PHY_ADDR_INVALID; | ||
542 | u32 vendor_oui = 0; | ||
543 | u8 identifier = 0; | ||
544 | u8 comp_codes_1g = 0; | ||
545 | u8 comp_codes_10g = 0; | ||
546 | u8 oui_bytes[4] = {0, 0, 0, 0}; | ||
547 | u8 transmission_media = 0; | ||
548 | |||
549 | status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER, | ||
550 | &identifier); | ||
551 | |||
552 | if (status == IXGBE_ERR_SFP_NOT_PRESENT) { | ||
553 | hw->phy.sfp_type = ixgbe_sfp_type_not_present; | ||
554 | goto out; | ||
555 | } | ||
556 | |||
557 | if (identifier == IXGBE_SFF_IDENTIFIER_SFP) { | ||
558 | hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_1GBE_COMP_CODES, | ||
559 | &comp_codes_1g); | ||
560 | hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_10GBE_COMP_CODES, | ||
561 | &comp_codes_10g); | ||
562 | hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_TRANSMISSION_MEDIA, | ||
563 | &transmission_media); | ||
564 | |||
565 | /* ID Module | ||
566 | * ========= | ||
567 | * 0 SFP_DA_CU | ||
568 | * 1 SFP_SR | ||
569 | * 2 SFP_LR | ||
570 | */ | ||
571 | if (transmission_media & IXGBE_SFF_TWIN_AX_CAPABLE) | ||
572 | hw->phy.sfp_type = ixgbe_sfp_type_da_cu; | ||
573 | else if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE) | ||
574 | hw->phy.sfp_type = ixgbe_sfp_type_sr; | ||
575 | else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE) | ||
576 | hw->phy.sfp_type = ixgbe_sfp_type_lr; | ||
577 | else | ||
578 | hw->phy.sfp_type = ixgbe_sfp_type_unknown; | ||
579 | |||
580 | /* Determine PHY vendor */ | ||
581 | if (hw->phy.type == ixgbe_phy_unknown) { | ||
582 | hw->phy.id = identifier; | ||
583 | hw->phy.ops.read_i2c_eeprom(hw, | ||
584 | IXGBE_SFF_VENDOR_OUI_BYTE0, | ||
585 | &oui_bytes[0]); | ||
586 | hw->phy.ops.read_i2c_eeprom(hw, | ||
587 | IXGBE_SFF_VENDOR_OUI_BYTE1, | ||
588 | &oui_bytes[1]); | ||
589 | hw->phy.ops.read_i2c_eeprom(hw, | ||
590 | IXGBE_SFF_VENDOR_OUI_BYTE2, | ||
591 | &oui_bytes[2]); | ||
592 | |||
593 | vendor_oui = | ||
594 | ((oui_bytes[0] << IXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) | | ||
595 | (oui_bytes[1] << IXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) | | ||
596 | (oui_bytes[2] << IXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT)); | ||
597 | |||
598 | switch (vendor_oui) { | ||
599 | case IXGBE_SFF_VENDOR_OUI_TYCO: | ||
600 | if (transmission_media & | ||
601 | IXGBE_SFF_TWIN_AX_CAPABLE) | ||
602 | hw->phy.type = ixgbe_phy_tw_tyco; | ||
603 | break; | ||
604 | case IXGBE_SFF_VENDOR_OUI_FTL: | ||
605 | hw->phy.type = ixgbe_phy_sfp_ftl; | ||
606 | break; | ||
607 | case IXGBE_SFF_VENDOR_OUI_AVAGO: | ||
608 | hw->phy.type = ixgbe_phy_sfp_avago; | ||
609 | break; | ||
610 | default: | ||
611 | if (transmission_media & | ||
612 | IXGBE_SFF_TWIN_AX_CAPABLE) | ||
613 | hw->phy.type = ixgbe_phy_tw_unknown; | ||
614 | else | ||
615 | hw->phy.type = ixgbe_phy_sfp_unknown; | ||
616 | break; | ||
617 | } | ||
618 | } | ||
619 | status = 0; | ||
620 | } | ||
621 | |||
622 | out: | ||
623 | return status; | ||
624 | } | ||
625 | |||
626 | /** | ||
627 | * ixgbe_get_sfp_init_sequence_offsets - Checks the MAC's EEPROM to see | ||
628 | * if it supports a given SFP+ module type, if so it returns the offsets to the | ||
629 | * phy init sequence block. | ||
630 | * @hw: pointer to hardware structure | ||
631 | * @list_offset: offset to the SFP ID list | ||
632 | * @data_offset: offset to the SFP data block | ||
633 | **/ | ||
634 | s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, | ||
635 | u16 *list_offset, | ||
636 | u16 *data_offset) | ||
637 | { | ||
638 | u16 sfp_id; | ||
639 | |||
640 | if (hw->phy.sfp_type == ixgbe_sfp_type_unknown) | ||
641 | return IXGBE_ERR_SFP_NOT_SUPPORTED; | ||
642 | |||
643 | if (hw->phy.sfp_type == ixgbe_sfp_type_not_present) | ||
644 | return IXGBE_ERR_SFP_NOT_PRESENT; | ||
645 | |||
646 | if ((hw->device_id == IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM) && | ||
647 | (hw->phy.sfp_type == ixgbe_sfp_type_da_cu)) | ||
648 | return IXGBE_ERR_SFP_NOT_SUPPORTED; | ||
649 | |||
650 | /* Read offset to PHY init contents */ | ||
651 | hw->eeprom.ops.read(hw, IXGBE_PHY_INIT_OFFSET_NL, list_offset); | ||
652 | |||
653 | if ((!*list_offset) || (*list_offset == 0xFFFF)) | ||
654 | return IXGBE_ERR_PHY; | ||
655 | |||
656 | /* Shift offset to first ID word */ | ||
657 | (*list_offset)++; | ||
658 | |||
659 | /* | ||
660 | * Find the matching SFP ID in the EEPROM | ||
661 | * and program the init sequence | ||
662 | */ | ||
663 | hw->eeprom.ops.read(hw, *list_offset, &sfp_id); | ||
664 | |||
665 | while (sfp_id != IXGBE_PHY_INIT_END_NL) { | ||
666 | if (sfp_id == hw->phy.sfp_type) { | ||
667 | (*list_offset)++; | ||
668 | hw->eeprom.ops.read(hw, *list_offset, data_offset); | ||
669 | if ((!*data_offset) || (*data_offset == 0xFFFF)) { | ||
670 | hw_dbg(hw, "SFP+ module not supported\n"); | ||
671 | return IXGBE_ERR_SFP_NOT_SUPPORTED; | ||
672 | } else { | ||
673 | break; | ||
674 | } | ||
675 | } else { | ||
676 | (*list_offset) += 2; | ||
677 | if (hw->eeprom.ops.read(hw, *list_offset, &sfp_id)) | ||
678 | return IXGBE_ERR_PHY; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | if (sfp_id == IXGBE_PHY_INIT_END_NL) { | ||
683 | hw_dbg(hw, "No matching SFP+ module found\n"); | ||
684 | return IXGBE_ERR_SFP_NOT_SUPPORTED; | ||
685 | } | ||
686 | |||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | /** | ||
433 | * ixgbe_check_phy_link_tnx - Determine link and speed status | 691 | * ixgbe_check_phy_link_tnx - Determine link and speed status |
434 | * @hw: pointer to hardware structure | 692 | * @hw: pointer to hardware structure |
435 | * | 693 | * |