aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ixgbe/ixgbe_phy.c
diff options
context:
space:
mode:
authorDonald Skidmore <donald.c.skidmore@intel.com>2008-11-21 00:11:42 -0500
committerDavid S. Miller <davem@davemloft.net>2008-11-21 00:11:42 -0500
commitc4900be053d376dfe4f603d000aa5e4c60745dec (patch)
treef5658e8d4f2345e0f15346020fe3aeb2adb48905 /drivers/net/ixgbe/ixgbe_phy.c
parent859ee3c43812051e21816c6d6d4cc04fb7ce9b2e (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.c258
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 **/
439s32 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
528out:
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 **/
539s32 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
622out:
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 **/
634s32 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 *