Commit 018c00dd authored by Jakub Kicinski's avatar Jakub Kicinski

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.ukSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 84ef94d9 f40df95d
......@@ -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 */
else
sgm_mode = 0;
use_an = false;
}
if (use_an)
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
} 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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment