Commit 91de5ac9 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-pcs-stmmac=add-C37-AN-SGMII-support'



Ong Boon Leong says:

====================
net: pcs, stmmac: add C37 AN SGMII support

This patch series adds MAC-side SGMII support to stmmac driver and it is
changed as follow:-

1/6: Refactor the current C73 implementation in pcs-xpcs to prepare for
     adding C37 AN later.
2/6: Add MAC-side SGMII C37 AN support to pcs-xpcs
3,4/6: make phylink_parse_mode() to work for non-DT platform so that
       we can use stmmac platform_data to set it.
5/6: Make stmmac_open() to only skip PHY init if C73 is used, otherwise
     C37 AN will need phydev to be connected to phylink.
6/6: Finally, add pcs-xpcs SGMII interface support to Intel mGbE
     controller.

The patch series have been tested on EHL CRB PCH TSN (eth2) controller
that has Marvell 88E1512 PHY attached over SGMII interface and the
iterative tests of speed change (AN) + ping test have been successful.

[63446.009295] intel-eth-pci 0000:00:1e.4 eth2: Link is Down
[63449.986365] intel-eth-pci 0000:00:1e.4 eth2: Link is Up - 1Gbps/Full - flow control off
[63449.987625] IPv6: ADDRCONF(NETDEV_CHANGE): eth2: link becomes ready
[63451.248064] intel-eth-pci 0000:00:1e.4 eth2: Link is Down
[63454.082366] intel-eth-pci 0000:00:1e.4 eth2: Link is Up - 100Mbps/Full - flow control off
[63454.083650] IPv6: ADDRCONF(NETDEV_CHANGE): eth2: link becomes ready
[63456.465179] intel-eth-pci 0000:00:1e.4 eth2: Link is Down
[63459.202367] intel-eth-pci 0000:00:1e.4 eth2: Link is Up - 10Mbps/Full - flow control off
[63459.203639] IPv6: ADDRCONF(NETDEV_CHANGE): eth2: link becomes ready
[63460.882832] intel-eth-pci 0000:00:1e.4 eth2: Link is Down
[63464.322366] intel-eth-pci 0000:00:1e.4 eth2: Link is Up - 1Gbps/Full - flow control off
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 29c35da1 7310fe53
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -9,6 +9,9 @@
#include "dwmac4.h"
#include "stmmac.h"

#define INTEL_MGBE_ADHOC_ADDR	0x15
#define INTEL_MGBE_XPCS_ADDR	0x16

struct intel_priv_data {
	int mdio_adhoc_addr;	/* mdio address for serdes & etc */
};
@@ -333,6 +336,16 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
	/* Use the last Rx queue */
	plat->vlan_fail_q = plat->rx_queues_to_use - 1;

	/* Intel mgbe SGMII interface uses pcs-xcps */
	if (plat->phy_interface == PHY_INTERFACE_MODE_SGMII) {
		plat->mdio_bus_data->has_xpcs = true;
		plat->mdio_bus_data->xpcs_an_inband = true;
	}

	/* Ensure mdio bus scan skips intel serdes and pcs-xpcs */
	plat->mdio_bus_data->phy_mask = 1 << INTEL_MGBE_ADHOC_ADDR;
	plat->mdio_bus_data->phy_mask |= 1 << INTEL_MGBE_XPCS_ADDR;

	return 0;
}

@@ -664,7 +677,7 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
	pci_set_master(pdev);

	plat->bsp_priv = intel_priv;
	intel_priv->mdio_adhoc_addr = 0x15;
	intel_priv->mdio_adhoc_addr = INTEL_MGBE_ADHOC_ADDR;

	ret = info->setup(pdev, plat);
	if (ret)
+3 −1
Original line number Diff line number Diff line
@@ -1117,6 +1117,8 @@ static int stmmac_phy_setup(struct stmmac_priv *priv)
	priv->phylink_config.dev = &priv->dev->dev;
	priv->phylink_config.type = PHYLINK_NETDEV;
	priv->phylink_config.pcs_poll = true;
	priv->phylink_config.ovr_an_inband =
		priv->plat->mdio_bus_data->xpcs_an_inband;

	if (!fwnode)
		fwnode = dev_fwnode(priv->device);
@@ -2896,7 +2898,7 @@ static int stmmac_open(struct net_device *dev)

	if (priv->hw->pcs != STMMAC_PCS_TBI &&
	    priv->hw->pcs != STMMAC_PCS_RTBI &&
	    priv->hw->xpcs == NULL) {
	    priv->hw->xpcs_args.an_mode != DW_AN_C73) {
		ret = stmmac_init_phy(dev);
		if (ret) {
			netdev_err(priv->dev,
+230 −27
Original line number Diff line number Diff line
@@ -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 */
@@ -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,
@@ -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,
@@ -120,27 +159,42 @@ 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;
	const int *supported;
	const phy_interface_t *interface;
	int an_mode;
} xpcs_id_list[] = {
	{
		.id = SYNOPSYS_XPCS_USXGMII_ID,
		.mask = SYNOPSYS_XPCS_MASK,
		.supported = xpcs_usxgmii_features,
		.interface = xpcs_usxgmii_interfaces,
		.an_mode = DW_AN_C73,
	}, {
		.id = SYNOPSYS_XPCS_10GKR_ID,
		.mask = SYNOPSYS_XPCS_MASK,
		.supported = xpcs_10gkr_features,
		.interface = xpcs_10gkr_interfaces,
		.an_mode = DW_AN_C73,
	}, {
		.id = SYNOPSYS_XPCS_XLGMII_ID,
		.mask = SYNOPSYS_XPCS_MASK,
		.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,
	},
};

@@ -195,9 +249,20 @@ static int xpcs_poll_reset(struct mdio_xpcs_args *xpcs, int dev)
	return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0;
}

static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs, int dev)
static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs)
{
	int ret;
	int ret, dev;

	switch (xpcs->an_mode) {
	case DW_AN_C73:
		dev = MDIO_MMD_PCS;
		break;
	case DW_AN_C37_SGMII:
		dev = MDIO_MMD_VEND2;
		break;
	default:
		return -1;
	}

	ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET);
	if (ret < 0)
@@ -212,7 +277,7 @@ static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs, int dev)
		dev_warn(&(__xpcs)->bus->dev, ##__args); \
})

static int xpcs_read_fault(struct mdio_xpcs_args *xpcs,
static int xpcs_read_fault_c73(struct mdio_xpcs_args *xpcs,
			       struct phylink_link_state *state)
{
	int ret;
@@ -263,7 +328,7 @@ static int xpcs_read_fault(struct mdio_xpcs_args *xpcs,
	return 0;
}

static int xpcs_read_link(struct mdio_xpcs_args *xpcs, bool an)
static int xpcs_read_link_c73(struct mdio_xpcs_args *xpcs, bool an)
{
	bool link = true;
	int ret;
@@ -357,7 +422,7 @@ static int xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed)
	return xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST);
}

static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs)
static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs)
{
	int ret, adv;

@@ -401,11 +466,11 @@ static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs)
	return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv);
}

static int xpcs_config_aneg(struct mdio_xpcs_args *xpcs)
static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs)
{
	int ret;

	ret = xpcs_config_aneg_c73(xpcs);
	ret = _xpcs_config_aneg_c73(xpcs);
	if (ret < 0)
		return ret;

@@ -418,7 +483,7 @@ static int xpcs_config_aneg(struct mdio_xpcs_args *xpcs)
	return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret);
}

static int xpcs_aneg_done(struct mdio_xpcs_args *xpcs,
static int xpcs_aneg_done_c73(struct mdio_xpcs_args *xpcs,
			      struct phylink_link_state *state)
{
	int ret;
@@ -434,7 +499,7 @@ static int xpcs_aneg_done(struct mdio_xpcs_args *xpcs,

		/* Check if Aneg outcome is valid */
		if (!(ret & DW_C73_AN_ADV_SF)) {
			xpcs_config_aneg(xpcs);
			xpcs_config_aneg_c73(xpcs);
			return 0;
		}

@@ -444,7 +509,7 @@ static int xpcs_aneg_done(struct mdio_xpcs_args *xpcs,
	return 0;
}

static int xpcs_read_lpa(struct mdio_xpcs_args *xpcs,
static int xpcs_read_lpa_c73(struct mdio_xpcs_args *xpcs,
			     struct phylink_link_state *state)
{
	int ret;
@@ -493,7 +558,7 @@ static int xpcs_read_lpa(struct mdio_xpcs_args *xpcs,
	return 0;
}

static void xpcs_resolve_lpa(struct mdio_xpcs_args *xpcs,
static void xpcs_resolve_lpa_c73(struct mdio_xpcs_args *xpcs,
				 struct phylink_link_state *state)
{
	int max_speed = xpcs_get_max_usxgmii_speed(state->lp_advertising);
@@ -585,32 +650,84 @@ 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)
{
	int ret;

	switch (xpcs->an_mode) {
	case DW_AN_C73:
		if (state->an_enabled) {
		ret = xpcs_config_aneg(xpcs);
			ret = xpcs_config_aneg_c73(xpcs);
			if (ret)
				return ret;
		}
		break;
	case DW_AN_C37_SGMII:
		ret = xpcs_config_aneg_c37_sgmii(xpcs);
		if (ret)
			return ret;
		break;
	default:
		return -1;
	}

	return 0;
}

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

	/* Link needs to be read first ... */
	state->link = xpcs_read_link(xpcs, state->an_enabled) > 0 ? 1 : 0;
	state->link = xpcs_read_link_c73(xpcs, state->an_enabled) > 0 ? 1 : 0;

	/* ... and then we check the faults. */
	ret = xpcs_read_fault(xpcs, state);
	ret = xpcs_read_fault_c73(xpcs, state);
	if (ret) {
		ret = xpcs_soft_reset(xpcs, MDIO_MMD_PCS);
		ret = xpcs_soft_reset(xpcs);
		if (ret)
			return ret;

@@ -619,10 +736,10 @@ static int xpcs_get_state(struct mdio_xpcs_args *xpcs,
		return xpcs_config(xpcs, state);
	}

	if (state->an_enabled && xpcs_aneg_done(xpcs, state)) {
	if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state)) {
		state->an_complete = true;
		xpcs_read_lpa(xpcs, state);
		xpcs_resolve_lpa(xpcs, state);
		xpcs_read_lpa_c73(xpcs, state);
		xpcs_resolve_lpa_c73(xpcs, state);
	} else if (state->an_enabled) {
		state->link = 0;
	} else if (state->link) {
@@ -632,6 +749,70 @@ static int xpcs_get_state(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)
{
	int ret;

	switch (xpcs->an_mode) {
	case DW_AN_C73:
		ret = xpcs_get_state_c73(xpcs, state);
		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;
	}

	return 0;
}

static int xpcs_link_up(struct mdio_xpcs_args *xpcs, int speed,
			phy_interface_t interface)
{
@@ -646,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;
@@ -656,7 +838,26 @@ static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs)
	if (ret < 0)
		return 0xffffffff;

	/* 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,
@@ -676,6 +877,8 @@ static bool xpcs_check_features(struct mdio_xpcs_args *xpcs,
	for (i = 0; match->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
		set_bit(match->supported[i], xpcs->supported);

	xpcs->an_mode = match->an_mode;

	return true;
}

@@ -692,7 +895,7 @@ static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface)
			match = entry;

			if (xpcs_check_features(xpcs, match, interface))
				return xpcs_soft_reset(xpcs, MDIO_MMD_PCS);
				return xpcs_soft_reset(xpcs);
		}
	}

+3 −2
Original line number Diff line number Diff line
@@ -271,8 +271,9 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
		pl->cfg_link_an_mode = MLO_AN_FIXED;
	fwnode_handle_put(dn);

	if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
	    strcmp(managed, "in-band-status") == 0) {
	if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
	     strcmp(managed, "in-band-status") == 0) ||
	    pl->config->ovr_an_inband) {
		if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
			phylink_err(pl,
				    "can't use both fixed-link and in-band-status\n");
+5 −0
Original line number Diff line number Diff line
@@ -10,10 +10,15 @@
#include <linux/phy.h>
#include <linux/phylink.h>

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

struct mdio_xpcs_args {
	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
	struct mii_bus *bus;
	int addr;
	int an_mode;
};

struct mdio_xpcs_ops {
Loading