Merge branch 'add-and-use-helper-for-pcs-negotiation-modes'
Russell King says: ==================== Add and use helper for PCS negotiation modes Earlier this month, I proposed a helper for deciding whether a PCS should use inband negotiation modes or not. There was some discussion around this topic, and I believe there was no disagreement about providing the helper. The initial discussion can be found at: https://lore.kernel.org/r/ZGIkGmyL8yL1q1zp@shell.armlinux.org.uk Subsequently, I posted a RFC series back in May: https://lore.kernel.org/r/ZGzhvePzPjJ0v2En@shell.armlinux.org.uk that added a helper, phylink_pcs_neg_mode() which PCS drivers could use to parse the state, and updated a bunch of drivers to use it. I got a couple of bits of feedback to it, including some ACKs. However, I've decided to take this slightly further and change the "mode" parameter to both the pcs_config() and pcs_link_up() methods when a PCS driver opts in to this (by setting "neg_mode" in the phylink_pcs structure.) If this is not set, we default to the old behaviour. That said, this series converts all the PCS implementations I can find currently in net-next. Doing this has the added benefit that the negotiation mode parameter is also available to the pcs_link_up() function, which can now know whether inband negotiation was in fact enabled or not at pcs_config() time. It has been posted as RFC at: https://lore.kernel.org/r/ZIh/CLQ3z89g0Ua0@shell.armlinux.org.uk and received one reply, thanks Elad, which is a similar amount of interest to previous postings. Let's post it as non-RFC and see whether we get more reaction. ==================== Link: https://lore.kernel.org/r/ZIxQIBfO9dH5xFlg@shell.armlinux.org.uk Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
018c00dd4e
@ -65,7 +65,7 @@ static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
|
||||
return b53_serdes_read_blk(dev, offset, block);
|
||||
}
|
||||
|
||||
static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -239,6 +239,7 @@ int b53_serdes_init(struct b53_device *dev, int port)
|
||||
pcs->dev = dev;
|
||||
pcs->lane = lane;
|
||||
pcs->pcs.ops = &b53_pcs_ops;
|
||||
pcs->pcs.neg_mode = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3005,7 +3005,7 @@ static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->pause |= MLO_PAUSE_TX;
|
||||
}
|
||||
|
||||
static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -3033,6 +3033,7 @@ mt753x_setup(struct dsa_switch *ds)
|
||||
/* Initialise the PCS devices */
|
||||
for (i = 0; i < priv->ds->num_ports; i++) {
|
||||
priv->pcs[i].pcs.ops = priv->info->pcs_ops;
|
||||
priv->pcs[i].pcs.neg_mode = true;
|
||||
priv->pcs[i].priv = priv;
|
||||
priv->pcs[i].port = i;
|
||||
}
|
||||
|
@ -1493,7 +1493,7 @@ static void qca8k_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->pause |= MLO_PAUSE_TX;
|
||||
}
|
||||
|
||||
static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -1520,14 +1520,12 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
}
|
||||
|
||||
/* Enable/disable SerDes auto-negotiation as necessary */
|
||||
ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
|
||||
val = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ?
|
||||
0 : QCA8K_PWS_SERDES_AEN_DIS;
|
||||
|
||||
ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (phylink_autoneg_inband(mode))
|
||||
val &= ~QCA8K_PWS_SERDES_AEN_DIS;
|
||||
else
|
||||
val |= QCA8K_PWS_SERDES_AEN_DIS;
|
||||
qca8k_write(priv, QCA8K_REG_PWS, val);
|
||||
|
||||
/* Configure the SGMII parameters */
|
||||
ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
|
||||
@ -1598,6 +1596,7 @@ static void qca8k_setup_pcs(struct qca8k_priv *priv, struct qca8k_pcs *qpcs,
|
||||
int port)
|
||||
{
|
||||
qpcs->pcs.ops = &qca8k_pcs_ops;
|
||||
qpcs->pcs.neg_mode = true;
|
||||
|
||||
/* We don't have interrupts for link changes, so we need to poll */
|
||||
qpcs->pcs.poll = true;
|
||||
|
@ -2314,7 +2314,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
|
||||
|
||||
for (i = 0; i < ds->num_ports; i++) {
|
||||
struct dw_xpcs *xpcs = priv->xpcs[i];
|
||||
unsigned int mode;
|
||||
unsigned int neg_mode;
|
||||
|
||||
rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]);
|
||||
if (rc < 0)
|
||||
@ -2324,17 +2324,15 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
|
||||
continue;
|
||||
|
||||
if (bmcr[i] & BMCR_ANENABLE)
|
||||
mode = MLO_AN_INBAND;
|
||||
else if (priv->fixed_link[i])
|
||||
mode = MLO_AN_FIXED;
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
else
|
||||
mode = MLO_AN_PHY;
|
||||
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
|
||||
rc = xpcs_do_config(xpcs, priv->phy_mode[i], mode, NULL);
|
||||
rc = xpcs_do_config(xpcs, priv->phy_mode[i], NULL, neg_mode);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_OUTBAND) {
|
||||
int speed = SPEED_UNKNOWN;
|
||||
|
||||
if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX)
|
||||
@ -2346,7 +2344,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
|
||||
else
|
||||
speed = SPEED_10;
|
||||
|
||||
xpcs_link_up(&xpcs->pcs, mode, priv->phy_mode[i],
|
||||
xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i],
|
||||
speed, DUPLEX_FULL);
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ static void macb_set_tx_clk(struct macb *bp, int speed)
|
||||
netdev_err(bp->dev, "adjusting tx_clk failed.\n");
|
||||
}
|
||||
|
||||
static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed,
|
||||
int duplex)
|
||||
{
|
||||
@ -596,7 +596,7 @@ static void macb_usx_pcs_get_state(struct phylink_pcs *pcs,
|
||||
}
|
||||
|
||||
static int macb_usx_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -621,7 +621,7 @@ static void macb_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
}
|
||||
|
||||
static int macb_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -862,7 +862,9 @@ static int macb_mii_probe(struct net_device *dev)
|
||||
struct macb *bp = netdev_priv(dev);
|
||||
|
||||
bp->phylink_sgmii_pcs.ops = &macb_phylink_pcs_ops;
|
||||
bp->phylink_sgmii_pcs.neg_mode = true;
|
||||
bp->phylink_usx_pcs.ops = &macb_phylink_usx_pcs_ops;
|
||||
bp->phylink_usx_pcs.neg_mode = true;
|
||||
|
||||
bp->phylink_config.dev = &dev->dev;
|
||||
bp->phylink_config.type = PHYLINK_NETDEV;
|
||||
|
@ -763,15 +763,15 @@ static void dtsec_pcs_get_state(struct phylink_pcs *pcs,
|
||||
phylink_mii_c22_pcs_get_state(dtsec->tbidev, state);
|
||||
}
|
||||
|
||||
static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct fman_mac *dtsec = pcs_to_dtsec(pcs);
|
||||
|
||||
return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface,
|
||||
advertising);
|
||||
return phylink_mii_c22_pcs_config(dtsec->tbidev, interface,
|
||||
advertising, neg_mode);
|
||||
}
|
||||
|
||||
static void dtsec_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
@ -1447,6 +1447,7 @@ int dtsec_initialization(struct mac_device *mac_dev,
|
||||
goto _return_fm_mac_free;
|
||||
}
|
||||
dtsec->pcs.ops = &dtsec_pcs_ops;
|
||||
dtsec->pcs.neg_mode = true;
|
||||
dtsec->pcs.poll = true;
|
||||
|
||||
supported = mac_dev->phylink_config.supported_interfaces;
|
||||
|
@ -4002,8 +4002,8 @@ static void mvneta_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->pause |= MLO_PAUSE_TX;
|
||||
}
|
||||
|
||||
static int mvneta_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode, phy_interface_t interface,
|
||||
static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
@ -4016,7 +4016,7 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs,
|
||||
MVNETA_GMAC_AN_FLOW_CTRL_EN |
|
||||
MVNETA_GMAC_AN_DUPLEX_EN;
|
||||
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
mask |= MVNETA_GMAC_CONFIG_MII_SPEED |
|
||||
MVNETA_GMAC_CONFIG_GMII_SPEED |
|
||||
MVNETA_GMAC_CONFIG_FULL_DUPLEX;
|
||||
@ -5518,6 +5518,7 @@ static int mvneta_probe(struct platform_device *pdev)
|
||||
clk_prepare_enable(pp->clk_bus);
|
||||
|
||||
pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops;
|
||||
pp->phylink_pcs.neg_mode = true;
|
||||
|
||||
pp->phylink_config.dev = &dev->dev;
|
||||
pp->phylink_config.type = PHYLINK_NETDEV;
|
||||
|
@ -6168,8 +6168,7 @@ static void mvpp2_xlg_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->pause |= MLO_PAUSE_RX;
|
||||
}
|
||||
|
||||
static int mvpp2_xlg_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
static int mvpp2_xlg_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -6232,7 +6231,7 @@ static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->pause |= MLO_PAUSE_TX;
|
||||
}
|
||||
|
||||
static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -6246,7 +6245,7 @@ static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
MVPP2_GMAC_FLOW_CTRL_AUTONEG |
|
||||
MVPP2_GMAC_AN_DUPLEX_EN;
|
||||
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
mask |= MVPP2_GMAC_CONFIG_MII_SPEED |
|
||||
MVPP2_GMAC_CONFIG_GMII_SPEED |
|
||||
MVPP2_GMAC_CONFIG_FULL_DUPLEX;
|
||||
@ -6649,8 +6648,9 @@ static void mvpp2_acpi_start(struct mvpp2_port *port)
|
||||
mvpp2_mac_prepare(&port->phylink_config, MLO_AN_INBAND,
|
||||
port->phy_interface);
|
||||
mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state);
|
||||
pcs->ops->pcs_config(pcs, MLO_AN_INBAND, port->phy_interface,
|
||||
state.advertising, false);
|
||||
pcs->ops->pcs_config(pcs, PHYLINK_PCS_NEG_INBAND_ENABLED,
|
||||
port->phy_interface, state.advertising,
|
||||
false);
|
||||
mvpp2_mac_finish(&port->phylink_config, MLO_AN_INBAND,
|
||||
port->phy_interface);
|
||||
mvpp2_mac_link_up(&port->phylink_config, NULL,
|
||||
@ -6896,7 +6896,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
||||
dev->dev.of_node = port_node;
|
||||
|
||||
port->pcs_gmac.ops = &mvpp2_phylink_gmac_pcs_ops;
|
||||
port->pcs_gmac.neg_mode = true;
|
||||
port->pcs_xlg.ops = &mvpp2_phylink_xlg_pcs_ops;
|
||||
port->pcs_xlg.neg_mode = true;
|
||||
|
||||
if (!mvpp2_use_acpi_compat_mode(port_fwnode)) {
|
||||
port->phylink_config.dev = &dev->dev;
|
||||
|
@ -300,8 +300,7 @@ static void prestera_pcs_get_state(struct phylink_pcs *pcs,
|
||||
}
|
||||
}
|
||||
|
||||
static int prestera_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
static int prestera_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -316,30 +315,25 @@ static int prestera_pcs_config(struct phylink_pcs *pcs,
|
||||
|
||||
cfg_mac.admin = true;
|
||||
cfg_mac.fec = PRESTERA_PORT_FEC_OFF;
|
||||
cfg_mac.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
cfg_mac.speed = SPEED_10000;
|
||||
cfg_mac.inband = 0;
|
||||
cfg_mac.mode = PRESTERA_MAC_MODE_SR_LR;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
cfg_mac.speed = SPEED_2500;
|
||||
cfg_mac.duplex = DUPLEX_FULL;
|
||||
cfg_mac.inband = test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising);
|
||||
cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
cfg_mac.inband = 1;
|
||||
cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
default:
|
||||
cfg_mac.speed = SPEED_1000;
|
||||
cfg_mac.duplex = DUPLEX_FULL;
|
||||
cfg_mac.inband = test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising);
|
||||
cfg_mac.mode = PRESTERA_MAC_MODE_1000BASE_X;
|
||||
break;
|
||||
}
|
||||
@ -401,6 +395,7 @@ static int prestera_port_sfp_bind(struct prestera_port *port)
|
||||
continue;
|
||||
|
||||
port->phylink_pcs.ops = &prestera_pcs_ops;
|
||||
port->phylink_pcs.neg_mode = true;
|
||||
|
||||
port->phy_config.dev = &port->dev->dev;
|
||||
port->phy_config.type = PHYLINK_NETDEV;
|
||||
|
@ -818,6 +818,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
|
||||
port->phylink_config.type = PHYLINK_NETDEV;
|
||||
port->phylink_pcs.poll = true;
|
||||
port->phylink_pcs.ops = &lan966x_phylink_pcs_ops;
|
||||
port->phylink_pcs.neg_mode = true;
|
||||
|
||||
port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
|
||||
MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD;
|
||||
|
@ -95,8 +95,7 @@ static void lan966x_pcs_get_state(struct phylink_pcs *pcs,
|
||||
lan966x_port_status_get(port, state);
|
||||
}
|
||||
|
||||
static int lan966x_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
static int lan966x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -107,8 +106,8 @@ static int lan966x_pcs_config(struct phylink_pcs *pcs,
|
||||
|
||||
config = port->config;
|
||||
config.portmode = interface;
|
||||
config.inband = phylink_autoneg_inband(mode);
|
||||
config.autoneg = phylink_test(advertising, Autoneg);
|
||||
config.inband = neg_mode & PHYLINK_PCS_NEG_INBAND;
|
||||
config.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
config.advertising = advertising;
|
||||
|
||||
ret = lan966x_port_pcs_set(port, &config);
|
||||
|
@ -281,6 +281,7 @@ static int sparx5_create_port(struct sparx5 *sparx5,
|
||||
spx5_port->custom_etype = 0x8880; /* Vitesse */
|
||||
spx5_port->phylink_pcs.poll = true;
|
||||
spx5_port->phylink_pcs.ops = &sparx5_phylink_pcs_ops;
|
||||
spx5_port->phylink_pcs.neg_mode = true;
|
||||
spx5_port->is_mrouter = false;
|
||||
INIT_LIST_HEAD(&spx5_port->tc_templates);
|
||||
sparx5->ports[config->portno] = spx5_port;
|
||||
|
@ -91,8 +91,7 @@ static void sparx5_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->pause = status.pause;
|
||||
}
|
||||
|
||||
static int sparx5_pcs_config(struct phylink_pcs *pcs,
|
||||
unsigned int mode,
|
||||
static int sparx5_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -104,8 +103,9 @@ static int sparx5_pcs_config(struct phylink_pcs *pcs,
|
||||
conf = port->conf;
|
||||
conf.power_down = false;
|
||||
conf.portmode = interface;
|
||||
conf.inband = phylink_autoneg_inband(mode);
|
||||
conf.autoneg = phylink_test(advertising, Autoneg);
|
||||
conf.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_DISABLED ||
|
||||
neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
conf.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
conf.pause_adv = 0;
|
||||
if (phylink_test(advertising, Pause))
|
||||
conf.pause_adv |= ADVERTISE_1000XPAUSE;
|
||||
|
@ -1631,7 +1631,7 @@ static void axienet_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
phylink_mii_c22_pcs_an_restart(pcs_phy);
|
||||
}
|
||||
|
||||
static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@ -1653,7 +1653,8 @@ static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
}
|
||||
}
|
||||
|
||||
ret = phylink_mii_c22_pcs_config(pcs_phy, mode, interface, advertising);
|
||||
ret = phylink_mii_c22_pcs_config(pcs_phy, interface, advertising,
|
||||
neg_mode);
|
||||
if (ret < 0)
|
||||
netdev_warn(ndev, "Failed to configure PCS: %d\n", ret);
|
||||
|
||||
@ -2129,6 +2130,7 @@ static int axienet_probe(struct platform_device *pdev)
|
||||
}
|
||||
of_node_put(np);
|
||||
lp->pcs.ops = &axienet_pcs_ops;
|
||||
lp->pcs.neg_mode = true;
|
||||
lp->pcs.poll = true;
|
||||
}
|
||||
|
||||
|
@ -112,9 +112,10 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs,
|
||||
state->link, state->an_complete);
|
||||
}
|
||||
|
||||
static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
|
||||
static int lynx_pcs_config_giga(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
const unsigned long *advertising,
|
||||
unsigned int neg_mode)
|
||||
{
|
||||
int link_timer_ns;
|
||||
u32 link_timer;
|
||||
@ -132,8 +133,9 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
|
||||
if (interface == PHY_INTERFACE_MODE_1000BASEX) {
|
||||
if_mode = 0;
|
||||
} else {
|
||||
/* SGMII and QSGMII */
|
||||
if_mode = IF_MODE_SGMII_EN;
|
||||
if (mode == MLO_AN_INBAND)
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
if_mode |= IF_MODE_USE_SGMII_AN;
|
||||
}
|
||||
|
||||
@ -143,16 +145,18 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising);
|
||||
return phylink_mii_c22_pcs_config(pcs, interface, advertising,
|
||||
neg_mode);
|
||||
}
|
||||
|
||||
static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
|
||||
const unsigned long *advertising)
|
||||
static int lynx_pcs_config_usxgmii(struct mdio_device *pcs,
|
||||
const unsigned long *advertising,
|
||||
unsigned int neg_mode)
|
||||
{
|
||||
struct mii_bus *bus = pcs->bus;
|
||||
int addr = pcs->addr;
|
||||
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
dev_err(&pcs->dev, "USXGMII only supports in-band AN for now\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -164,10 +168,9 @@ static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
|
||||
ADVERTISE_SGMII | ADVERTISE_LPACK);
|
||||
}
|
||||
|
||||
static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t ifmode,
|
||||
const unsigned long *advertising,
|
||||
bool permit)
|
||||
const unsigned long *advertising, bool permit)
|
||||
{
|
||||
struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
|
||||
|
||||
@ -175,17 +178,18 @@ static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
return lynx_pcs_config_giga(lynx->mdio, mode, ifmode,
|
||||
advertising);
|
||||
return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
|
||||
neg_mode);
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
dev_err(&lynx->mdio->dev,
|
||||
"AN not supported on 3.125GHz SerDes lane\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
return lynx_pcs_config_usxgmii(lynx->mdio, mode, advertising);
|
||||
return lynx_pcs_config_usxgmii(lynx->mdio, advertising,
|
||||
neg_mode);
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
/* Nothing to do here for 10GBASER */
|
||||
break;
|
||||
@ -203,7 +207,8 @@ static void lynx_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
phylink_mii_c22_pcs_an_restart(lynx->mdio);
|
||||
}
|
||||
|
||||
static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
|
||||
static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs,
|
||||
unsigned int neg_mode,
|
||||
int speed, int duplex)
|
||||
{
|
||||
u16 if_mode = 0, sgmii_speed;
|
||||
@ -211,7 +216,7 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
|
||||
/* The PCS needs to be configured manually only
|
||||
* when not operating on in-band mode
|
||||
*/
|
||||
if (mode == MLO_AN_INBAND)
|
||||
if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
return;
|
||||
|
||||
if (duplex == DUPLEX_HALF)
|
||||
@ -258,12 +263,12 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
|
||||
* 2500 Mbps and we do rate adaptation through pause frames.
|
||||
*/
|
||||
static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
|
||||
unsigned int mode,
|
||||
unsigned int neg_mode,
|
||||
int speed, int duplex)
|
||||
{
|
||||
u16 if_mode = 0;
|
||||
|
||||
if (mode == MLO_AN_INBAND) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
dev_err(&pcs->dev, "AN not supported for 2500BaseX\n");
|
||||
return;
|
||||
}
|
||||
@ -277,7 +282,7 @@ static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
|
||||
if_mode);
|
||||
}
|
||||
|
||||
static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
int speed, int duplex)
|
||||
{
|
||||
@ -286,10 +291,10 @@ static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
lynx_pcs_link_up_sgmii(lynx->mdio, mode, speed, duplex);
|
||||
lynx_pcs_link_up_sgmii(lynx->mdio, neg_mode, speed, duplex);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
lynx_pcs_link_up_2500basex(lynx->mdio, mode, speed, duplex);
|
||||
lynx_pcs_link_up_2500basex(lynx->mdio, neg_mode, speed, duplex);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
/* At the moment, only in-band AN is supported for USXGMII
|
||||
@ -319,6 +324,7 @@ static struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
|
||||
mdio_device_get(mdio);
|
||||
lynx->mdio = mdio;
|
||||
lynx->pcs.ops = &lynx_pcs_phylink_ops;
|
||||
lynx->pcs.neg_mode = true;
|
||||
lynx->pcs.poll = true;
|
||||
|
||||
return lynx_to_phylink_pcs(lynx);
|
||||
|
@ -102,13 +102,13 @@ static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
|
||||
FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
bool mode_changed = false, changed, use_an;
|
||||
bool mode_changed = false, changed;
|
||||
unsigned int rgc3, sgm_mode, bmcr;
|
||||
int advertise, link_timer;
|
||||
|
||||
@ -121,30 +121,21 @@ static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
* we assume that fixes it's speed at bitrate = line rate (in
|
||||
* other words, 1000Mbps or 2500Mbps).
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII) {
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII)
|
||||
sgm_mode = SGMII_IF_MODE_SGMII;
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
sgm_mode |= SGMII_REMOTE_FAULT_DIS |
|
||||
SGMII_SPEED_DUPLEX_AN;
|
||||
use_an = true;
|
||||
} else {
|
||||
use_an = false;
|
||||
}
|
||||
} else if (phylink_autoneg_inband(mode)) {
|
||||
/* 1000base-X or 2500base-X autoneg */
|
||||
sgm_mode = SGMII_REMOTE_FAULT_DIS;
|
||||
use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising);
|
||||
} else {
|
||||
/* 1000base-X or 2500base-X without autoneg */
|
||||
sgm_mode = 0;
|
||||
use_an = false;
|
||||
}
|
||||
|
||||
if (use_an)
|
||||
bmcr = BMCR_ANENABLE;
|
||||
else
|
||||
sgm_mode = 0;
|
||||
|
||||
if (neg_mode & PHYLINK_PCS_NEG_INBAND)
|
||||
sgm_mode |= SGMII_REMOTE_FAULT_DIS;
|
||||
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII)
|
||||
sgm_mode |= SGMII_SPEED_DUPLEX_AN;
|
||||
bmcr = BMCR_ANENABLE;
|
||||
} else {
|
||||
bmcr = 0;
|
||||
}
|
||||
|
||||
if (mpcs->interface != interface) {
|
||||
link_timer = phylink_get_link_timer_ns(interface);
|
||||
@ -216,14 +207,15 @@ static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
|
||||
}
|
||||
|
||||
static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs,
|
||||
unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed,
|
||||
int duplex)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
unsigned int sgm_mode;
|
||||
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
/* Force the speed and duplex setting */
|
||||
if (speed == SPEED_10)
|
||||
sgm_mode = SGMII_SPEED_10;
|
||||
@ -286,6 +278,7 @@ struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
|
||||
mpcs->regmap = regmap;
|
||||
mpcs->flags = flags;
|
||||
mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
|
||||
mpcs->pcs.neg_mode = true;
|
||||
mpcs->pcs.poll = true;
|
||||
mpcs->interface = PHY_INTERFACE_MODE_NA;
|
||||
|
||||
|
@ -657,7 +657,8 @@ int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xpcs_config_eee);
|
||||
|
||||
static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
|
||||
static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
|
||||
unsigned int neg_mode)
|
||||
{
|
||||
int ret, mdio_ctrl;
|
||||
|
||||
@ -707,7 +708,7 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (phylink_autoneg_inband(mode))
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
|
||||
else
|
||||
ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
|
||||
@ -716,14 +717,15 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (phylink_autoneg_inband(mode))
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
|
||||
mdio_ctrl | AN_CL37_EN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, unsigned int mode,
|
||||
static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
|
||||
unsigned int neg_mode,
|
||||
const unsigned long *advertising)
|
||||
{
|
||||
phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX;
|
||||
@ -774,8 +776,7 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, unsigned int mod
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (phylink_autoneg_inband(mode) &&
|
||||
linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
|
||||
mdio_ctrl | AN_CL37_EN);
|
||||
if (ret < 0)
|
||||
@ -808,7 +809,7 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
|
||||
}
|
||||
|
||||
int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||
unsigned int mode, const unsigned long *advertising)
|
||||
const unsigned long *advertising, unsigned int neg_mode)
|
||||
{
|
||||
const struct xpcs_compat *compat;
|
||||
int ret;
|
||||
@ -821,19 +822,19 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||
case DW_10GBASER:
|
||||
break;
|
||||
case DW_AN_C73:
|
||||
if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) {
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
ret = xpcs_config_aneg_c73(xpcs, compat);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case DW_AN_C37_SGMII:
|
||||
ret = xpcs_config_aneg_c37_sgmii(xpcs, mode);
|
||||
ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case DW_AN_C37_1000BASEX:
|
||||
ret = xpcs_config_aneg_c37_1000basex(xpcs, mode,
|
||||
ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode,
|
||||
advertising);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -857,14 +858,14 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xpcs_do_config);
|
||||
|
||||
static int xpcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
|
||||
|
||||
return xpcs_do_config(xpcs, interface, mode, advertising);
|
||||
return xpcs_do_config(xpcs, interface, advertising, neg_mode);
|
||||
}
|
||||
|
||||
static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
|
||||
@ -898,7 +899,8 @@ static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
|
||||
|
||||
state->link = 0;
|
||||
|
||||
return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND, NULL);
|
||||
return xpcs_do_config(xpcs, state->interface, NULL,
|
||||
PHYLINK_PCS_NEG_INBAND_ENABLED);
|
||||
}
|
||||
|
||||
/* There is no point doing anything else if the link is down. */
|
||||
@ -1046,12 +1048,12 @@ static void xpcs_get_state(struct phylink_pcs *pcs,
|
||||
}
|
||||
}
|
||||
|
||||
static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
|
||||
static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode,
|
||||
int speed, int duplex)
|
||||
{
|
||||
int val, ret;
|
||||
|
||||
if (phylink_autoneg_inband(mode))
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
return;
|
||||
|
||||
val = mii_bmcr_encode_fixed(speed, duplex);
|
||||
@ -1060,12 +1062,12 @@ static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
|
||||
pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
|
||||
}
|
||||
|
||||
static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int mode,
|
||||
static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode,
|
||||
int speed, int duplex)
|
||||
{
|
||||
int val, ret;
|
||||
|
||||
if (phylink_autoneg_inband(mode))
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
return;
|
||||
|
||||
switch (speed) {
|
||||
@ -1089,7 +1091,7 @@ static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int mode,
|
||||
pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
|
||||
}
|
||||
|
||||
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed, int duplex)
|
||||
{
|
||||
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
|
||||
@ -1097,9 +1099,9 @@ void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
if (interface == PHY_INTERFACE_MODE_USXGMII)
|
||||
return xpcs_config_usxgmii(xpcs, speed);
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII)
|
||||
return xpcs_link_up_sgmii(xpcs, mode, speed, duplex);
|
||||
return xpcs_link_up_sgmii(xpcs, neg_mode, speed, duplex);
|
||||
if (interface == PHY_INTERFACE_MODE_1000BASEX)
|
||||
return xpcs_link_up_1000basex(xpcs, mode, speed, duplex);
|
||||
return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xpcs_link_up);
|
||||
|
||||
@ -1283,6 +1285,7 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
|
||||
}
|
||||
|
||||
xpcs->pcs.ops = &xpcs_phylink_ops;
|
||||
xpcs->pcs.neg_mode = true;
|
||||
if (compat->an_mode == DW_10GBASER)
|
||||
return xpcs;
|
||||
|
||||
|
@ -71,6 +71,7 @@ struct phylink {
|
||||
struct mutex state_mutex;
|
||||
struct phylink_link_state phy_state;
|
||||
struct work_struct resolve;
|
||||
unsigned int pcs_neg_mode;
|
||||
|
||||
bool mac_link_dropped;
|
||||
bool using_mac_select_pcs;
|
||||
@ -992,23 +993,23 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
|
||||
}
|
||||
}
|
||||
|
||||
static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
const struct phylink_link_state *state,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
if (!pcs)
|
||||
return 0;
|
||||
|
||||
return pcs->ops->pcs_config(pcs, mode, state->interface,
|
||||
return pcs->ops->pcs_config(pcs, neg_mode, state->interface,
|
||||
state->advertising, permit_pause_to_mac);
|
||||
}
|
||||
|
||||
static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed,
|
||||
int duplex)
|
||||
{
|
||||
if (pcs && pcs->ops->pcs_link_up)
|
||||
pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
|
||||
pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
|
||||
}
|
||||
|
||||
static void phylink_pcs_poll_stop(struct phylink *pl)
|
||||
@ -1058,10 +1059,15 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
struct phylink_pcs *pcs = NULL;
|
||||
bool pcs_changed = false;
|
||||
unsigned int rate_kbd;
|
||||
unsigned int neg_mode;
|
||||
int err;
|
||||
|
||||
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
|
||||
|
||||
pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
|
||||
state->interface,
|
||||
state->advertising);
|
||||
|
||||
if (pl->using_mac_select_pcs) {
|
||||
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
|
||||
if (IS_ERR(pcs)) {
|
||||
@ -1094,9 +1100,12 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
|
||||
phylink_mac_config(pl, state);
|
||||
|
||||
err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
|
||||
!!(pl->link_config.pause &
|
||||
MLO_PAUSE_AN));
|
||||
neg_mode = pl->cur_link_an_mode;
|
||||
if (pl->pcs && pl->pcs->neg_mode)
|
||||
neg_mode = pl->pcs_neg_mode;
|
||||
|
||||
err = phylink_pcs_config(pl->pcs, neg_mode, state,
|
||||
!!(pl->link_config.pause & MLO_PAUSE_AN));
|
||||
if (err < 0)
|
||||
phylink_err(pl, "pcs_config failed: %pe\n",
|
||||
ERR_PTR(err));
|
||||
@ -1131,6 +1140,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
*/
|
||||
static int phylink_change_inband_advert(struct phylink *pl)
|
||||
{
|
||||
unsigned int neg_mode;
|
||||
int ret;
|
||||
|
||||
if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
|
||||
@ -1149,12 +1159,20 @@ static int phylink_change_inband_advert(struct phylink *pl)
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
|
||||
pl->link_config.pause);
|
||||
|
||||
/* Recompute the PCS neg mode */
|
||||
pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
|
||||
pl->link_config.interface,
|
||||
pl->link_config.advertising);
|
||||
|
||||
neg_mode = pl->cur_link_an_mode;
|
||||
if (pl->pcs->neg_mode)
|
||||
neg_mode = pl->pcs_neg_mode;
|
||||
|
||||
/* Modern PCS-based method; update the advert at the PCS, and
|
||||
* restart negotiation if the pcs_config() helper indicates that
|
||||
* the programmed advertisement has changed.
|
||||
*/
|
||||
ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
&pl->link_config,
|
||||
ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config,
|
||||
!!(pl->link_config.pause & MLO_PAUSE_AN));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -1257,6 +1275,7 @@ static void phylink_link_up(struct phylink *pl,
|
||||
struct phylink_link_state link_state)
|
||||
{
|
||||
struct net_device *ndev = pl->netdev;
|
||||
unsigned int neg_mode;
|
||||
int speed, duplex;
|
||||
bool rx_pause;
|
||||
|
||||
@ -1287,8 +1306,12 @@ static void phylink_link_up(struct phylink *pl,
|
||||
|
||||
pl->cur_interface = link_state.interface;
|
||||
|
||||
phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
|
||||
speed, duplex);
|
||||
neg_mode = pl->cur_link_an_mode;
|
||||
if (pl->pcs && pl->pcs->neg_mode)
|
||||
neg_mode = pl->pcs_neg_mode;
|
||||
|
||||
phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
|
||||
duplex);
|
||||
|
||||
pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
|
||||
pl->cur_interface, speed, duplex,
|
||||
@ -3522,18 +3545,19 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement);
|
||||
/**
|
||||
* phylink_mii_c22_pcs_config() - configure clause 22 PCS
|
||||
* @pcs: a pointer to a &struct mdio_device.
|
||||
* @mode: link autonegotiation mode
|
||||
* @interface: the PHY interface mode being configured
|
||||
* @advertising: the ethtool advertisement mask
|
||||
* @neg_mode: PCS negotiation mode
|
||||
*
|
||||
* Configure a Clause 22 PCS PHY with the appropriate negotiation
|
||||
* parameters for the @mode, @interface and @advertising parameters.
|
||||
* Returns negative error number on failure, zero if the advertisement
|
||||
* has not changed, or positive if there is a change.
|
||||
*/
|
||||
int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
const unsigned long *advertising,
|
||||
unsigned int neg_mode)
|
||||
{
|
||||
bool changed = 0;
|
||||
u16 bmcr;
|
||||
@ -3548,15 +3572,12 @@ int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
changed = ret;
|
||||
}
|
||||
|
||||
/* Ensure ISOLATE bit is disabled */
|
||||
if (mode == MLO_AN_INBAND &&
|
||||
(interface == PHY_INTERFACE_MODE_SGMII ||
|
||||
interface == PHY_INTERFACE_MODE_QSGMII ||
|
||||
linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)))
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
bmcr = BMCR_ANENABLE;
|
||||
else
|
||||
bmcr = 0;
|
||||
|
||||
/* Configure the inband state. Ensure ISOLATE bit is disabled */
|
||||
ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -29,10 +29,10 @@ struct dw_xpcs {
|
||||
};
|
||||
|
||||
int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
|
||||
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||
unsigned int mode, const unsigned long *advertising);
|
||||
const unsigned long *advertising, unsigned int neg_mode);
|
||||
void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces);
|
||||
int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
|
||||
int enable);
|
||||
|
@ -21,6 +21,24 @@ enum {
|
||||
MLO_AN_FIXED, /* Fixed-link mode */
|
||||
MLO_AN_INBAND, /* In-band protocol */
|
||||
|
||||
/* PCS "negotiation" mode.
|
||||
* PHYLINK_PCS_NEG_NONE - protocol has no inband capability
|
||||
* PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting
|
||||
* PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g.
|
||||
* 1000base-X with autoneg off
|
||||
* PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled
|
||||
* Additionally, this can be tested using bitmasks:
|
||||
* PHYLINK_PCS_NEG_INBAND - inband mode selected
|
||||
* PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled
|
||||
*/
|
||||
PHYLINK_PCS_NEG_NONE = 0,
|
||||
PHYLINK_PCS_NEG_ENABLED = BIT(4),
|
||||
PHYLINK_PCS_NEG_OUTBAND = BIT(5),
|
||||
PHYLINK_PCS_NEG_INBAND = BIT(6),
|
||||
PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND,
|
||||
PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND |
|
||||
PHYLINK_PCS_NEG_ENABLED,
|
||||
|
||||
/* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our
|
||||
* autonegotiation advertisement. They correspond to the PAUSE and
|
||||
* ASM_DIR bits defined by 802.3, respectively.
|
||||
@ -79,6 +97,70 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
|
||||
return mode == MLO_AN_INBAND;
|
||||
}
|
||||
|
||||
/**
|
||||
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
|
||||
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
* @interface: interface mode to be used
|
||||
* @advertising: adertisement ethtool link mode mask
|
||||
*
|
||||
* Determines the negotiation mode to be used by the PCS, and returns
|
||||
* one of:
|
||||
* %PHYLINK_PCS_NEG_NONE: interface mode does not support inband
|
||||
* %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY)
|
||||
* will be used.
|
||||
* %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled
|
||||
* %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled
|
||||
*
|
||||
* Note: this is for cases where the PCS itself is involved in negotiation
|
||||
* (e.g. Clause 37, SGMII and similar) not Clause 73.
|
||||
*/
|
||||
static inline unsigned int phylink_pcs_neg_mode(unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
{
|
||||
unsigned int neg_mode;
|
||||
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
/* These protocols are designed for use with a PHY which
|
||||
* communicates its negotiation result back to the MAC via
|
||||
* inband communication. Note: there exist PHYs that run
|
||||
* with SGMII but do not send the inband data.
|
||||
*/
|
||||
if (!phylink_autoneg_inband(mode))
|
||||
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
else
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
/* 1000base-X is designed for use media-side for Fibre
|
||||
* connections, and thus the Autoneg bit needs to be
|
||||
* taken into account. We also do this for 2500base-X
|
||||
* as well, but drivers may not support this, so may
|
||||
* need to override this.
|
||||
*/
|
||||
if (!phylink_autoneg_inband(mode))
|
||||
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising))
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
else
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
|
||||
break;
|
||||
|
||||
default:
|
||||
neg_mode = PHYLINK_PCS_NEG_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return neg_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct phylink_link_state - link state structure
|
||||
* @advertising: ethtool bitmask containing advertised link modes
|
||||
@ -436,6 +518,7 @@ struct phylink_pcs_ops;
|
||||
/**
|
||||
* struct phylink_pcs - PHYLINK PCS instance
|
||||
* @ops: a pointer to the &struct phylink_pcs_ops structure
|
||||
* @neg_mode: provide PCS neg mode via "mode" argument
|
||||
* @poll: poll the PCS for link changes
|
||||
*
|
||||
* This structure is designed to be embedded within the PCS private data,
|
||||
@ -443,6 +526,7 @@ struct phylink_pcs_ops;
|
||||
*/
|
||||
struct phylink_pcs {
|
||||
const struct phylink_pcs_ops *ops;
|
||||
bool neg_mode;
|
||||
bool poll;
|
||||
};
|
||||
|
||||
@ -460,12 +544,12 @@ struct phylink_pcs_ops {
|
||||
const struct phylink_link_state *state);
|
||||
void (*pcs_get_state)(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state);
|
||||
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
|
||||
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac);
|
||||
void (*pcs_an_restart)(struct phylink_pcs *pcs);
|
||||
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
|
||||
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
};
|
||||
|
||||
@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *pcs,
|
||||
/**
|
||||
* pcs_config() - Configure the PCS mode and advertisement
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
* @neg_mode: link negotiation mode (see below)
|
||||
* @interface: interface mode to be used
|
||||
* @advertising: adertisement ethtool link mode mask
|
||||
* @permit_pause_to_mac: permit forwarding pause resolution to MAC
|
||||
@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *pcs,
|
||||
* For 1000BASE-X, the advertisement should be programmed into the PCS.
|
||||
*
|
||||
* For most 10GBASE-R, there is no advertisement.
|
||||
*
|
||||
* The %neg_mode argument should be tested via the phylink_mode_*() family of
|
||||
* functions, or for PCS that set pcs->neg_mode true, should be tested
|
||||
* against the %PHYLINK_PCS_NEG_* definitions.
|
||||
*/
|
||||
int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, const unsigned long *advertising,
|
||||
bool permit_pause_to_mac);
|
||||
|
||||
@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs *pcs);
|
||||
/**
|
||||
* pcs_link_up() - program the PCS for the resolved link configuration
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
* @mode: link autonegotiation mode
|
||||
* @neg_mode: link negotiation mode (see below)
|
||||
* @interface: link &typedef phy_interface_t mode
|
||||
* @speed: link speed
|
||||
* @duplex: link duplex
|
||||
@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs *pcs);
|
||||
* the resolved link parameters. For example, a PCS operating in SGMII
|
||||
* mode without in-band AN needs to be manually configured for the link
|
||||
* and duplex setting. Otherwise, this should be a no-op.
|
||||
*
|
||||
* The %mode argument should be tested via the phylink_mode_*() family of
|
||||
* functions, or for PCS that set pcs->neg_mode true, should be tested
|
||||
* against the %PHYLINK_PCS_NEG_* definitions.
|
||||
*/
|
||||
void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
#endif
|
||||
|
||||
@ -651,9 +743,10 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
|
||||
struct phylink_link_state *state);
|
||||
int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
|
||||
const unsigned long *advertising);
|
||||
int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising);
|
||||
const unsigned long *advertising,
|
||||
unsigned int neg_mode);
|
||||
void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
|
||||
|
||||
void phylink_resolve_c73(struct phylink_link_state *state);
|
||||
|
Loading…
x
Reference in New Issue
Block a user