Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: pcs: add C37 SGMII AN support for intel mGbE controller

XPCS IP supports C37 SGMII AN process and it is used in intel multi-GbE
controller as MAC-side SGMII.

Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ong Boon Leong and committed by
David S. Miller
b97b5331 07a4bc51

+167 -1
+166 -1
drivers/net/pcs/pcs-xpcs.c
··· 15 15 #define SYNOPSYS_XPCS_USXGMII_ID 0x7996ced0 16 16 #define SYNOPSYS_XPCS_10GKR_ID 0x7996ced0 17 17 #define SYNOPSYS_XPCS_XLGMII_ID 0x7996ced0 18 + #define SYNOPSYS_XPCS_SGMII_ID 0x7996ced0 18 19 #define SYNOPSYS_XPCS_MASK 0xffffffff 19 20 20 21 /* Vendor regs access */ ··· 57 56 /* AN_LP_ABL3 */ 58 57 #define DW_C73_2500KX BIT(0) 59 58 #define DW_C73_5000KR BIT(1) 59 + 60 + /* Clause 37 Defines */ 61 + /* VR MII MMD registers offsets */ 62 + #define DW_VR_MII_DIG_CTRL1 0x8000 63 + #define DW_VR_MII_AN_CTRL 0x8001 64 + #define DW_VR_MII_AN_INTR_STS 0x8002 65 + 66 + /* VR_MII_DIG_CTRL1 */ 67 + #define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9) 68 + 69 + /* VR_MII_AN_CTRL */ 70 + #define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3 71 + #define DW_VR_MII_TX_CONFIG_MASK BIT(3) 72 + #define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1 73 + #define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0 74 + #define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1 75 + #define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1) 76 + #define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0 77 + #define DW_VR_MII_PCS_MODE_C37_SGMII 0x2 78 + 79 + /* VR_MII_AN_INTR_STS */ 80 + #define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1) 81 + #define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2 82 + #define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2) 83 + #define DW_VR_MII_C37_ANSGM_SP_10 0x0 84 + #define DW_VR_MII_C37_ANSGM_SP_100 0x1 85 + #define DW_VR_MII_C37_ANSGM_SP_1000 0x2 86 + #define DW_VR_MII_C37_ANSGM_SP_LNKSTS BIT(4) 60 87 61 88 static const int xpcs_usxgmii_features[] = { 62 89 ETHTOOL_LINK_MODE_Pause_BIT, ··· 134 105 __ETHTOOL_LINK_MODE_MASK_NBITS, 135 106 }; 136 107 108 + static const int xpcs_sgmii_features[] = { 109 + ETHTOOL_LINK_MODE_10baseT_Half_BIT, 110 + ETHTOOL_LINK_MODE_10baseT_Full_BIT, 111 + ETHTOOL_LINK_MODE_100baseT_Half_BIT, 112 + ETHTOOL_LINK_MODE_100baseT_Full_BIT, 113 + ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 114 + ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 115 + __ETHTOOL_LINK_MODE_MASK_NBITS, 116 + }; 117 + 137 118 static const phy_interface_t xpcs_usxgmii_interfaces[] = { 138 119 PHY_INTERFACE_MODE_USXGMII, 139 120 PHY_INTERFACE_MODE_MAX, ··· 156 117 157 118 static const phy_interface_t xpcs_xlgmii_interfaces[] = { 158 119 PHY_INTERFACE_MODE_XLGMII, 120 + PHY_INTERFACE_MODE_MAX, 121 + }; 122 + 123 + static const phy_interface_t xpcs_sgmii_interfaces[] = { 124 + PHY_INTERFACE_MODE_SGMII, 159 125 PHY_INTERFACE_MODE_MAX, 160 126 }; 161 127 ··· 189 145 .supported = xpcs_xlgmii_features, 190 146 .interface = xpcs_xlgmii_interfaces, 191 147 .an_mode = DW_AN_C73, 148 + }, { 149 + .id = SYNOPSYS_XPCS_SGMII_ID, 150 + .mask = SYNOPSYS_XPCS_MASK, 151 + .supported = xpcs_sgmii_features, 152 + .interface = xpcs_sgmii_interfaces, 153 + .an_mode = DW_AN_C37_SGMII, 192 154 }, 193 155 }; 194 156 ··· 256 206 switch (xpcs->an_mode) { 257 207 case DW_AN_C73: 258 208 dev = MDIO_MMD_PCS; 209 + break; 210 + case DW_AN_C37_SGMII: 211 + dev = MDIO_MMD_VEND2; 259 212 break; 260 213 default: 261 214 return -1; ··· 650 597 return 0; 651 598 } 652 599 600 + static int xpcs_config_aneg_c37_sgmii(struct mdio_xpcs_args *xpcs) 601 + { 602 + int ret; 603 + 604 + /* For AN for C37 SGMII mode, the settings are :- 605 + * 1) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN) 606 + * 2) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII) 607 + * DW xPCS used with DW EQoS MAC is always MAC side SGMII. 608 + * 3) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic 609 + * speed/duplex mode change by HW after SGMII AN complete) 610 + * 611 + * Note: Since it is MAC side SGMII, there is no need to set 612 + * SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from 613 + * PHY about the link state change after C28 AN is completed 614 + * between PHY and Link Partner. There is also no need to 615 + * trigger AN restart for MAC-side SGMII. 616 + */ 617 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); 618 + if (ret < 0) 619 + return ret; 620 + 621 + ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); 622 + ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << 623 + DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & 624 + DW_VR_MII_PCS_MODE_MASK); 625 + ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII << 626 + DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & 627 + DW_VR_MII_TX_CONFIG_MASK); 628 + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); 629 + if (ret < 0) 630 + return ret; 631 + 632 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); 633 + if (ret < 0) 634 + return ret; 635 + 636 + ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 637 + 638 + return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); 639 + } 640 + 653 641 static int xpcs_config(struct mdio_xpcs_args *xpcs, 654 642 const struct phylink_link_state *state) 655 643 { ··· 703 609 if (ret) 704 610 return ret; 705 611 } 612 + break; 613 + case DW_AN_C37_SGMII: 614 + ret = xpcs_config_aneg_c37_sgmii(xpcs); 615 + if (ret) 616 + return ret; 706 617 break; 707 618 default: 708 619 return -1; ··· 749 650 return 0; 750 651 } 751 652 653 + static int xpcs_get_state_c37_sgmii(struct mdio_xpcs_args *xpcs, 654 + struct phylink_link_state *state) 655 + { 656 + int ret; 657 + 658 + /* Reset link_state */ 659 + state->link = false; 660 + state->speed = SPEED_UNKNOWN; 661 + state->duplex = DUPLEX_UNKNOWN; 662 + state->pause = 0; 663 + 664 + /* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link 665 + * status, speed and duplex. 666 + */ 667 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); 668 + if (ret < 0) 669 + return false; 670 + 671 + if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { 672 + int speed_value; 673 + 674 + state->link = true; 675 + 676 + speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >> 677 + DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT; 678 + if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000) 679 + state->speed = SPEED_1000; 680 + else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100) 681 + state->speed = SPEED_100; 682 + else 683 + state->speed = SPEED_10; 684 + 685 + if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD) 686 + state->duplex = DUPLEX_FULL; 687 + else 688 + state->duplex = DUPLEX_HALF; 689 + } 690 + 691 + return 0; 692 + } 693 + 752 694 static int xpcs_get_state(struct mdio_xpcs_args *xpcs, 753 695 struct phylink_link_state *state) 754 696 { ··· 798 658 switch (xpcs->an_mode) { 799 659 case DW_AN_C73: 800 660 ret = xpcs_get_state_c73(xpcs, state); 661 + if (ret) 662 + return ret; 663 + break; 664 + case DW_AN_C37_SGMII: 665 + ret = xpcs_get_state_c37_sgmii(xpcs, state); 801 666 if (ret) 802 667 return ret; 803 668 break; ··· 827 682 int ret; 828 683 u32 id; 829 684 685 + /* First, search C73 PCS using PCS MMD */ 830 686 ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); 831 687 if (ret < 0) 832 688 return 0xffffffff; ··· 838 692 if (ret < 0) 839 693 return 0xffffffff; 840 694 841 - return id | ret; 695 + /* If Device IDs are not all zeros, we found C73 AN-type device */ 696 + if (id | ret) 697 + return id | ret; 698 + 699 + /* Next, search C37 PCS using Vendor-Specific MII MMD */ 700 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1); 701 + if (ret < 0) 702 + return 0xffffffff; 703 + 704 + id = ret << 16; 705 + 706 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2); 707 + if (ret < 0) 708 + return 0xffffffff; 709 + 710 + /* If Device IDs are not all zeros, we found C37 AN-type device */ 711 + if (id | ret) 712 + return id | ret; 713 + 714 + return 0xffffffff; 842 715 } 843 716 844 717 static bool xpcs_check_features(struct mdio_xpcs_args *xpcs,
+1
include/linux/pcs/pcs-xpcs.h
··· 12 12 13 13 /* AN mode */ 14 14 #define DW_AN_C73 1 15 + #define DW_AN_C37_SGMII 2 15 16 16 17 struct mdio_xpcs_args { 17 18 __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);