Skip to content

Commit

Permalink
net: pcs: add C37 SGMII AN support for intel mGbE controller
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ong Boon Leong authored and David S. Miller committed Mar 15, 2021
1 parent 07a4bc5 commit b97b533
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 1 deletion.
167 changes: 166 additions & 1 deletion drivers/net/pcs/pcs-xpcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define SYNOPSYS_XPCS_USXGMII_ID 0x7996ced0
#define SYNOPSYS_XPCS_10GKR_ID 0x7996ced0
#define SYNOPSYS_XPCS_XLGMII_ID 0x7996ced0
#define SYNOPSYS_XPCS_SGMII_ID 0x7996ced0
#define SYNOPSYS_XPCS_MASK 0xffffffff

/* Vendor regs access */
Expand Down Expand Up @@ -57,6 +58,34 @@
#define DW_C73_2500KX BIT(0)
#define DW_C73_5000KR BIT(1)

/* Clause 37 Defines */
/* VR MII MMD registers offsets */
#define DW_VR_MII_DIG_CTRL1 0x8000
#define DW_VR_MII_AN_CTRL 0x8001
#define DW_VR_MII_AN_INTR_STS 0x8002

/* VR_MII_DIG_CTRL1 */
#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)

/* VR_MII_AN_CTRL */
#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3
#define DW_VR_MII_TX_CONFIG_MASK BIT(3)
#define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1
#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0
#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1
#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1)
#define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0
#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2

/* VR_MII_AN_INTR_STS */
#define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1)
#define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2
#define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2)
#define DW_VR_MII_C37_ANSGM_SP_10 0x0
#define DW_VR_MII_C37_ANSGM_SP_100 0x1
#define DW_VR_MII_C37_ANSGM_SP_1000 0x2
#define DW_VR_MII_C37_ANSGM_SP_LNKSTS BIT(4)

static const int xpcs_usxgmii_features[] = {
ETHTOOL_LINK_MODE_Pause_BIT,
ETHTOOL_LINK_MODE_Asym_Pause_BIT,
Expand Down Expand Up @@ -105,6 +134,16 @@ static const int xpcs_xlgmii_features[] = {
__ETHTOOL_LINK_MODE_MASK_NBITS,
};

static const int xpcs_sgmii_features[] = {
ETHTOOL_LINK_MODE_10baseT_Half_BIT,
ETHTOOL_LINK_MODE_10baseT_Full_BIT,
ETHTOOL_LINK_MODE_100baseT_Half_BIT,
ETHTOOL_LINK_MODE_100baseT_Full_BIT,
ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
__ETHTOOL_LINK_MODE_MASK_NBITS,
};

static const phy_interface_t xpcs_usxgmii_interfaces[] = {
PHY_INTERFACE_MODE_USXGMII,
PHY_INTERFACE_MODE_MAX,
Expand All @@ -120,6 +159,11 @@ static const phy_interface_t xpcs_xlgmii_interfaces[] = {
PHY_INTERFACE_MODE_MAX,
};

static const phy_interface_t xpcs_sgmii_interfaces[] = {
PHY_INTERFACE_MODE_SGMII,
PHY_INTERFACE_MODE_MAX,
};

static struct xpcs_id {
u32 id;
u32 mask;
Expand All @@ -145,6 +189,12 @@ static struct xpcs_id {
.supported = xpcs_xlgmii_features,
.interface = xpcs_xlgmii_interfaces,
.an_mode = DW_AN_C73,
}, {
.id = SYNOPSYS_XPCS_SGMII_ID,
.mask = SYNOPSYS_XPCS_MASK,
.supported = xpcs_sgmii_features,
.interface = xpcs_sgmii_interfaces,
.an_mode = DW_AN_C37_SGMII,
},
};

Expand Down Expand Up @@ -207,6 +257,9 @@ static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs)
case DW_AN_C73:
dev = MDIO_MMD_PCS;
break;
case DW_AN_C37_SGMII:
dev = MDIO_MMD_VEND2;
break;
default:
return -1;
}
Expand Down Expand Up @@ -597,6 +650,47 @@ static int xpcs_validate(struct mdio_xpcs_args *xpcs,
return 0;
}

static int xpcs_config_aneg_c37_sgmii(struct mdio_xpcs_args *xpcs)
{
int ret;

/* For AN for C37 SGMII mode, the settings are :-
* 1) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN)
* 2) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII)
* DW xPCS used with DW EQoS MAC is always MAC side SGMII.
* 3) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic
* speed/duplex mode change by HW after SGMII AN complete)
*
* Note: Since it is MAC side SGMII, there is no need to set
* SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from
* PHY about the link state change after C28 AN is completed
* between PHY and Link Partner. There is also no need to
* trigger AN restart for MAC-side SGMII.
*/
ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
if (ret < 0)
return ret;

ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK);
ret |= (DW_VR_MII_PCS_MODE_C37_SGMII <<
DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT &
DW_VR_MII_PCS_MODE_MASK);
ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII <<
DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
DW_VR_MII_TX_CONFIG_MASK);
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
if (ret < 0)
return ret;

ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
if (ret < 0)
return ret;

ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;

return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
}

static int xpcs_config(struct mdio_xpcs_args *xpcs,
const struct phylink_link_state *state)
{
Expand All @@ -610,6 +704,11 @@ static int xpcs_config(struct mdio_xpcs_args *xpcs,
return ret;
}
break;
case DW_AN_C37_SGMII:
ret = xpcs_config_aneg_c37_sgmii(xpcs);
if (ret)
return ret;
break;
default:
return -1;
}
Expand Down Expand Up @@ -650,6 +749,47 @@ static int xpcs_get_state_c73(struct mdio_xpcs_args *xpcs,
return 0;
}

static int xpcs_get_state_c37_sgmii(struct mdio_xpcs_args *xpcs,
struct phylink_link_state *state)
{
int ret;

/* Reset link_state */
state->link = false;
state->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN;
state->pause = 0;

/* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link
* status, speed and duplex.
*/
ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS);
if (ret < 0)
return false;

if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) {
int speed_value;

state->link = true;

speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >>
DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT;
if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000)
state->speed = SPEED_1000;
else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100)
state->speed = SPEED_100;
else
state->speed = SPEED_10;

if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD)
state->duplex = DUPLEX_FULL;
else
state->duplex = DUPLEX_HALF;
}

return 0;
}

static int xpcs_get_state(struct mdio_xpcs_args *xpcs,
struct phylink_link_state *state)
{
Expand All @@ -661,6 +801,11 @@ static int xpcs_get_state(struct mdio_xpcs_args *xpcs,
if (ret)
return ret;
break;
case DW_AN_C37_SGMII:
ret = xpcs_get_state_c37_sgmii(xpcs, state);
if (ret)
return ret;
break;
default:
return -1;
}
Expand All @@ -682,6 +827,7 @@ static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs)
int ret;
u32 id;

/* First, search C73 PCS using PCS MMD */
ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1);
if (ret < 0)
return 0xffffffff;
Expand All @@ -692,7 +838,26 @@ static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs)
if (ret < 0)
return 0xffffffff;

return id | ret;
/* If Device IDs are not all zeros, we found C73 AN-type device */
if (id | ret)
return id | ret;

/* Next, search C37 PCS using Vendor-Specific MII MMD */
ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1);
if (ret < 0)
return 0xffffffff;

id = ret << 16;

ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2);
if (ret < 0)
return 0xffffffff;

/* If Device IDs are not all zeros, we found C37 AN-type device */
if (id | ret)
return id | ret;

return 0xffffffff;
}

static bool xpcs_check_features(struct mdio_xpcs_args *xpcs,
Expand Down
1 change: 1 addition & 0 deletions include/linux/pcs/pcs-xpcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

/* AN mode */
#define DW_AN_C73 1
#define DW_AN_C37_SGMII 2

struct mdio_xpcs_args {
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
Expand Down

0 comments on commit b97b533

Please sign in to comment.