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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-03-15 12:53:12 -07:00
commit 91de5ac99a
7 changed files with 271 additions and 44 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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,8 +277,8 @@ 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,
struct phylink_link_state *state)
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,8 +483,8 @@ 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,
struct phylink_link_state *state)
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,8 +509,8 @@ static int xpcs_aneg_done(struct mdio_xpcs_args *xpcs,
return 0;
}
static int xpcs_read_lpa(struct mdio_xpcs_args *xpcs,
struct phylink_link_state *state)
static int xpcs_read_lpa_c73(struct mdio_xpcs_args *xpcs,
struct phylink_link_state *state)
{
int ret;
@ -493,8 +558,8 @@ static int xpcs_read_lpa(struct mdio_xpcs_args *xpcs,
return 0;
}
static void xpcs_resolve_lpa(struct mdio_xpcs_args *xpcs,
struct phylink_link_state *state)
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,15 +650,141 @@ 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;
if (state->an_enabled) {
ret = xpcs_config_aneg(xpcs);
switch (xpcs->an_mode) {
case DW_AN_C73:
if (state->an_enabled) {
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_c73(struct mdio_xpcs_args *xpcs,
struct phylink_link_state *state)
{
int ret;
/* Link needs to be read first ... */
state->link = xpcs_read_link_c73(xpcs, state->an_enabled) > 0 ? 1 : 0;
/* ... and then we check the faults. */
ret = xpcs_read_fault_c73(xpcs, state);
if (ret) {
ret = xpcs_soft_reset(xpcs);
if (ret)
return ret;
state->link = 0;
return xpcs_config(xpcs, state);
}
if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state)) {
state->an_complete = true;
xpcs_read_lpa_c73(xpcs, state);
xpcs_resolve_lpa_c73(xpcs, state);
} else if (state->an_enabled) {
state->link = 0;
} else if (state->link) {
xpcs_resolve_pma(xpcs, state);
}
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;
@ -604,29 +795,19 @@ static int xpcs_get_state(struct mdio_xpcs_args *xpcs,
{
int ret;
/* Link needs to be read first ... */
state->link = xpcs_read_link(xpcs, state->an_enabled) > 0 ? 1 : 0;
/* ... and then we check the faults. */
ret = xpcs_read_fault(xpcs, state);
if (ret) {
ret = xpcs_soft_reset(xpcs, MDIO_MMD_PCS);
switch (xpcs->an_mode) {
case DW_AN_C73:
ret = xpcs_get_state_c73(xpcs, state);
if (ret)
return ret;
state->link = 0;
return xpcs_config(xpcs, state);
}
if (state->an_enabled && xpcs_aneg_done(xpcs, state)) {
state->an_complete = true;
xpcs_read_lpa(xpcs, state);
xpcs_resolve_lpa(xpcs, state);
} else if (state->an_enabled) {
state->link = 0;
} else if (state->link) {
xpcs_resolve_pma(xpcs, state);
break;
case DW_AN_C37_SGMII:
ret = xpcs_get_state_c37_sgmii(xpcs, state);
if (ret)
return ret;
break;
default:
return -1;
}
return 0;
@ -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;
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,
@ -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);
}
}

View File

@ -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");

View File

@ -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 {

View File

@ -64,6 +64,7 @@ enum phylink_op_type {
* @pcs_poll: MAC PCS cannot provide link change interrupt
* @poll_fixed_state: if true, starts link_poll,
* if MAC link is at %MLO_AN_FIXED mode.
* @ovr_an_inband: if true, override PCS to MLO_AN_INBAND
* @get_fixed_state: callback to execute to determine the fixed link state,
* if MAC link is at %MLO_AN_FIXED mode.
*/
@ -72,6 +73,7 @@ struct phylink_config {
enum phylink_op_type type;
bool pcs_poll;
bool poll_fixed_state;
bool ovr_an_inband;
void (*get_fixed_state)(struct phylink_config *config,
struct phylink_link_state *state);
};

View File

@ -81,6 +81,7 @@
struct stmmac_mdio_bus_data {
unsigned int phy_mask;
unsigned int has_xpcs;
unsigned int xpcs_an_inband;
int *irqs;
int probed_phy_irq;
bool needs_reset;