mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-03 22:57:21 -04:00
Merge branch 'net-add-negotiation-of-in-band-capabilities'
Russell King says: ==================== net: add negotiation of in-band capabilities This is a repost without RFC for this series, shrunk down to 13 patches by removing the non-Marvell PCS. Phylink's handling of in-band has been deficient for a long time, and people keep hitting problems with it. Notably, situations with the way- to-late standardized 2500Base-X and whether that should or should not have in-band enabled. We have also been carrying a hack in the form of phylink_phy_no_inband() for a PHY that has been used on a SFP module, but has no in-band capabilities, not even for SGMII. When phylink is trying to operate in in-band mode, this series will look at the capabilities of the MAC-side PCS and PHY, and work out whether in-band can or should be used, programming the PHY as appropriate. This includes in-band bypass mode at the PHY. We don't... yet... support bypass on the MAC side PCS, because that requires yet more complexity. Patch 1 passes struct phylink and struct phylink_pcs into phylink_pcs_neg_mode() so we can look at more state in this function in a future patch. Patch 2 splits "cur_link_an_mode" (the MLO_AN_* mode) into two separate purposes - a requested and an active mode. The active mode is the one we will be using for the MAC, which becomes dependent on the result of in-band negotiation. Patch 3 adds debug to phylink_major_config() so we can see what is going on with the requested and active AN modes. Patch 4 adds to phylib a method to get the in-band capabilities of the PHY from phylib. Patches 5 and 6 add implementations for BCM84881 and some Marvell PHYs found on SFPs. Patch 7 adds to phylib a method to configure the PHY in-band signalling, and patch 8 implements it for those Marvell PHYs that support the method in patch 4. Patch 9 does the same as patch 4 but for the MAC-side PCS, with patches 10 and 11 adding support to Marvell NETA and PP2. Patch 12 adds the code to phylink_pcs_neg_mode() which looks at the capabilities, and works out whether to use in-band or out-band mode for driving the link between the MAC PCS and PHY. Patch 13 removes the phylink_phy_no_inband() hack now that we are publishing the in-band capabilities from the BCM84881 PHY driver. Three more PCS, omitted from this series due to the limit of 15 patches, will be sent once this has been merged. ==================== Link: https://patch.msgid.link/Z08kCwxdkU4n2V6x@shell.armlinux.org.uk Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -3960,20 +3960,27 @@ static struct mvneta_port *mvneta_pcs_to_port(struct phylink_pcs *pcs)
|
||||
return container_of(pcs, struct mvneta_port, phylink_pcs);
|
||||
}
|
||||
|
||||
static int mvneta_pcs_validate(struct phylink_pcs *pcs,
|
||||
unsigned long *supported,
|
||||
const struct phylink_link_state *state)
|
||||
static unsigned int mvneta_pcs_inband_caps(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
/* We only support QSGMII, SGMII, 802.3z and RGMII modes.
|
||||
* When in 802.3z mode, we must have AN enabled:
|
||||
/* When operating in an 802.3z mode, we must have AN enabled:
|
||||
* "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
|
||||
* When <PortType> = 1 (1000BASE-X) this field must be set to 1."
|
||||
* Therefore, inband is "required".
|
||||
*/
|
||||
if (phy_interface_mode_is_8023z(state->interface) &&
|
||||
!phylink_test(state->advertising, Autoneg))
|
||||
return -EINVAL;
|
||||
if (phy_interface_mode_is_8023z(interface))
|
||||
return LINK_INBAND_ENABLE;
|
||||
|
||||
return 0;
|
||||
/* QSGMII, SGMII and RGMII can be configured to use inband
|
||||
* signalling of the AN result. Indicate these as "possible".
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII ||
|
||||
interface == PHY_INTERFACE_MODE_QSGMII ||
|
||||
phy_interface_mode_is_rgmii(interface))
|
||||
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
||||
|
||||
/* For any other modes, indicate that inband is not supported. */
|
||||
return LINK_INBAND_DISABLE;
|
||||
}
|
||||
|
||||
static void mvneta_pcs_get_state(struct phylink_pcs *pcs,
|
||||
@@ -4071,7 +4078,7 @@ static void mvneta_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
}
|
||||
|
||||
static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = {
|
||||
.pcs_validate = mvneta_pcs_validate,
|
||||
.pcs_inband_caps = mvneta_pcs_inband_caps,
|
||||
.pcs_get_state = mvneta_pcs_get_state,
|
||||
.pcs_config = mvneta_pcs_config,
|
||||
.pcs_an_restart = mvneta_pcs_an_restart,
|
||||
|
||||
@@ -6224,19 +6224,26 @@ static const struct phylink_pcs_ops mvpp2_phylink_xlg_pcs_ops = {
|
||||
.pcs_config = mvpp2_xlg_pcs_config,
|
||||
};
|
||||
|
||||
static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs,
|
||||
unsigned long *supported,
|
||||
const struct phylink_link_state *state)
|
||||
static unsigned int mvpp2_gmac_pcs_inband_caps(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
/* When in 802.3z mode, we must have AN enabled:
|
||||
/* When operating in an 802.3z mode, we must have AN enabled:
|
||||
* Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
|
||||
* When <PortType> = 1 (1000BASE-X) this field must be set to 1.
|
||||
* Therefore, inband is "required".
|
||||
*/
|
||||
if (phy_interface_mode_is_8023z(state->interface) &&
|
||||
!phylink_test(state->advertising, Autoneg))
|
||||
return -EINVAL;
|
||||
if (phy_interface_mode_is_8023z(interface))
|
||||
return LINK_INBAND_ENABLE;
|
||||
|
||||
return 0;
|
||||
/* SGMII and RGMII can be configured to use inband signalling of the
|
||||
* AN result. Indicate these as "possible".
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII ||
|
||||
phy_interface_mode_is_rgmii(interface))
|
||||
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
||||
|
||||
/* For any other modes, indicate that inband is not supported. */
|
||||
return LINK_INBAND_DISABLE;
|
||||
}
|
||||
|
||||
static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
|
||||
@@ -6343,7 +6350,7 @@ static void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
}
|
||||
|
||||
static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = {
|
||||
.pcs_validate = mvpp2_gmac_pcs_validate,
|
||||
.pcs_inband_caps = mvpp2_gmac_pcs_inband_caps,
|
||||
.pcs_get_state = mvpp2_gmac_pcs_get_state,
|
||||
.pcs_config = mvpp2_gmac_pcs_config,
|
||||
.pcs_an_restart = mvpp2_gmac_pcs_an_restart,
|
||||
|
||||
@@ -235,11 +235,21 @@ static int bcm84881_read_status(struct phy_device *phydev)
|
||||
return genphy_c45_read_mdix(phydev);
|
||||
}
|
||||
|
||||
/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
|
||||
* or 802.3z control word, so inband will not work.
|
||||
*/
|
||||
static unsigned int bcm84881_inband_caps(struct phy_device *phydev,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
return LINK_INBAND_DISABLE;
|
||||
}
|
||||
|
||||
static struct phy_driver bcm84881_drivers[] = {
|
||||
{
|
||||
.phy_id = 0xae025150,
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "Broadcom BCM84881",
|
||||
.inband_caps = bcm84881_inband_caps,
|
||||
.config_init = bcm84881_config_init,
|
||||
.probe = bcm84881_probe,
|
||||
.get_features = bcm84881_get_features,
|
||||
|
||||
@@ -717,6 +717,48 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev)
|
||||
return genphy_check_and_restart_aneg(phydev, changed);
|
||||
}
|
||||
|
||||
static unsigned int m88e1111_inband_caps(struct phy_device *phydev,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
/* In 1000base-X and SGMII modes, the inband mode can be changed
|
||||
* through the Fibre page BMCR ANENABLE bit.
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_1000BASEX ||
|
||||
interface == PHY_INTERFACE_MODE_SGMII)
|
||||
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE |
|
||||
LINK_INBAND_BYPASS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes)
|
||||
{
|
||||
u16 extsr, bmcr;
|
||||
int err;
|
||||
|
||||
if (phydev->interface != PHY_INTERFACE_MODE_1000BASEX &&
|
||||
phydev->interface != PHY_INTERFACE_MODE_SGMII)
|
||||
return -EINVAL;
|
||||
|
||||
if (modes == LINK_INBAND_BYPASS)
|
||||
extsr = MII_M1111_HWCFG_SERIAL_AN_BYPASS;
|
||||
else
|
||||
extsr = 0;
|
||||
|
||||
if (modes == LINK_INBAND_DISABLE)
|
||||
bmcr = 0;
|
||||
else
|
||||
bmcr = BMCR_ANENABLE;
|
||||
|
||||
err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
|
||||
MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr);
|
||||
if (err < 0)
|
||||
return extsr;
|
||||
|
||||
return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR,
|
||||
BMCR_ANENABLE, bmcr);
|
||||
}
|
||||
|
||||
static int m88e1111_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
|
||||
@@ -3677,6 +3719,8 @@ static struct phy_driver marvell_drivers[] = {
|
||||
.name = "Marvell 88E1112",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
.probe = marvell_probe,
|
||||
.inband_caps = m88e1111_inband_caps,
|
||||
.config_inband = m88e1111_config_inband,
|
||||
.config_init = m88e1112_config_init,
|
||||
.config_aneg = marvell_config_aneg,
|
||||
.config_intr = marvell_config_intr,
|
||||
@@ -3698,6 +3742,8 @@ static struct phy_driver marvell_drivers[] = {
|
||||
/* PHY_GBIT_FEATURES */
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
.probe = marvell_probe,
|
||||
.inband_caps = m88e1111_inband_caps,
|
||||
.config_inband = m88e1111_config_inband,
|
||||
.config_init = m88e1111gbe_config_init,
|
||||
.config_aneg = m88e1111_config_aneg,
|
||||
.read_status = marvell_read_status,
|
||||
@@ -3721,6 +3767,8 @@ static struct phy_driver marvell_drivers[] = {
|
||||
.name = "Marvell 88E1111 (Finisar)",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
.probe = marvell_probe,
|
||||
.inband_caps = m88e1111_inband_caps,
|
||||
.config_inband = m88e1111_config_inband,
|
||||
.config_init = m88e1111gbe_config_init,
|
||||
.config_aneg = m88e1111_config_aneg,
|
||||
.read_status = marvell_read_status,
|
||||
|
||||
@@ -1005,6 +1005,59 @@ static int phy_check_link_status(struct phy_device *phydev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* phy_inband_caps - query which in-band signalling modes are supported
|
||||
* @phydev: a pointer to a &struct phy_device
|
||||
* @interface: the interface mode for the PHY
|
||||
*
|
||||
* Returns zero if it is unknown what in-band signalling is supported by the
|
||||
* PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
|
||||
* returns a bit mask of the LINK_INBAND_* values from
|
||||
* &enum link_inband_signalling to describe which inband modes are supported
|
||||
* by the PHY for this interface mode.
|
||||
*/
|
||||
unsigned int phy_inband_caps(struct phy_device *phydev,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
if (phydev->drv && phydev->drv->inband_caps)
|
||||
return phydev->drv->inband_caps(phydev, interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_inband_caps);
|
||||
|
||||
/**
|
||||
* phy_config_inband - configure the desired PHY in-band mode
|
||||
* @phydev: the phy_device struct
|
||||
* @modes: in-band modes to configure
|
||||
*
|
||||
* Description: disables, enables or enables-with-bypass in-band signalling
|
||||
* between the PHY and host system.
|
||||
*
|
||||
* Returns: zero on success, or negative errno value.
|
||||
*/
|
||||
int phy_config_inband(struct phy_device *phydev, unsigned int modes)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!!(modes & LINK_INBAND_DISABLE) +
|
||||
!!(modes & LINK_INBAND_ENABLE) +
|
||||
!!(modes & LINK_INBAND_BYPASS) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&phydev->lock);
|
||||
if (!phydev->drv)
|
||||
err = -EIO;
|
||||
else if (!phydev->drv->config_inband)
|
||||
err = -EOPNOTSUPP;
|
||||
else
|
||||
err = phydev->drv->config_inband(phydev, modes);
|
||||
mutex_unlock(&phydev->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_config_inband);
|
||||
|
||||
/**
|
||||
* _phy_start_aneg - start auto-negotiation for this PHY device
|
||||
* @phydev: the phy_device struct
|
||||
|
||||
@@ -56,7 +56,8 @@ struct phylink {
|
||||
struct phy_device *phydev;
|
||||
phy_interface_t link_interface; /* PHY_INTERFACE_xxx */
|
||||
u8 cfg_link_an_mode; /* MLO_AN_xxx */
|
||||
u8 cur_link_an_mode;
|
||||
u8 req_link_an_mode; /* Requested MLO_AN_xxx mode */
|
||||
u8 act_link_an_mode; /* Active MLO_AN_xxx mode */
|
||||
u8 link_port; /* The current non-phy ethtool port */
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
|
||||
|
||||
@@ -74,6 +75,7 @@ struct phylink {
|
||||
|
||||
struct mutex state_mutex;
|
||||
struct phylink_link_state phy_state;
|
||||
unsigned int phy_ib_mode;
|
||||
struct work_struct resolve;
|
||||
unsigned int pcs_neg_mode;
|
||||
unsigned int pcs_state;
|
||||
@@ -174,6 +176,24 @@ static const char *phylink_an_mode_str(unsigned int mode)
|
||||
return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
|
||||
}
|
||||
|
||||
static const char *phylink_pcs_mode_str(unsigned int mode)
|
||||
{
|
||||
if (!mode)
|
||||
return "none";
|
||||
|
||||
if (mode & PHYLINK_PCS_NEG_OUTBAND)
|
||||
return "outband";
|
||||
|
||||
if (mode & PHYLINK_PCS_NEG_INBAND) {
|
||||
if (mode & PHYLINK_PCS_NEG_ENABLED)
|
||||
return "inband,an-enabled";
|
||||
else
|
||||
return "inband,an-disabled";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static unsigned int phylink_interface_signal_rate(phy_interface_t interface)
|
||||
{
|
||||
switch (interface) {
|
||||
@@ -971,6 +991,15 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
if (pcs && pcs->ops->pcs_inband_caps)
|
||||
return pcs->ops->pcs_inband_caps(pcs, interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void phylink_pcs_pre_config(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
@@ -1024,6 +1053,24 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
|
||||
}
|
||||
|
||||
/* Query inband for a specific interface mode, asking the MAC for the
|
||||
* PCS which will be used to handle the interface mode.
|
||||
*/
|
||||
static unsigned int phylink_inband_caps(struct phylink *pl,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
struct phylink_pcs *pcs;
|
||||
|
||||
if (!pl->mac_ops->mac_select_pcs)
|
||||
return 0;
|
||||
|
||||
pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
|
||||
if (!pcs)
|
||||
return 0;
|
||||
|
||||
return phylink_pcs_inband_caps(pcs, interface);
|
||||
}
|
||||
|
||||
static void phylink_pcs_poll_stop(struct phylink *pl)
|
||||
{
|
||||
if (pl->cfg_link_an_mode == MLO_AN_INBAND)
|
||||
@@ -1065,13 +1112,13 @@ static void phylink_mac_config(struct phylink *pl,
|
||||
|
||||
phylink_dbg(pl,
|
||||
"%s: mode=%s/%s/%s adv=%*pb pause=%02x\n",
|
||||
__func__, phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
__func__, phylink_an_mode_str(pl->act_link_an_mode),
|
||||
phy_modes(st.interface),
|
||||
phy_rate_matching_to_str(st.rate_matching),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, st.advertising,
|
||||
st.pause);
|
||||
|
||||
pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, &st);
|
||||
pl->mac_ops->mac_config(pl->config, pl->act_link_an_mode, &st);
|
||||
}
|
||||
|
||||
static void phylink_pcs_an_restart(struct phylink *pl)
|
||||
@@ -1079,13 +1126,14 @@ static void phylink_pcs_an_restart(struct phylink *pl)
|
||||
if (pl->pcs && linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
pl->link_config.advertising) &&
|
||||
phy_interface_mode_is_8023z(pl->link_config.interface) &&
|
||||
phylink_autoneg_inband(pl->cur_link_an_mode))
|
||||
phylink_autoneg_inband(pl->act_link_an_mode))
|
||||
pl->pcs->ops->pcs_an_restart(pl->pcs);
|
||||
}
|
||||
|
||||
/**
|
||||
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
|
||||
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
* @pl: a pointer to a &struct phylink returned from phylink_create()
|
||||
* @pcs: a pointer to &struct phylink_pcs
|
||||
* @interface: interface mode to be used
|
||||
* @advertising: adertisement ethtool link mode mask
|
||||
*
|
||||
@@ -1102,11 +1150,21 @@ static void phylink_pcs_an_restart(struct phylink *pl)
|
||||
* Note: this is for cases where the PCS itself is involved in negotiation
|
||||
* (e.g. Clause 37, SGMII and similar) not Clause 73.
|
||||
*/
|
||||
static unsigned int phylink_pcs_neg_mode(unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
static void phylink_pcs_neg_mode(struct phylink *pl, struct phylink_pcs *pcs,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
{
|
||||
unsigned int neg_mode;
|
||||
unsigned int pcs_ib_caps = 0;
|
||||
unsigned int phy_ib_caps = 0;
|
||||
unsigned int neg_mode, mode;
|
||||
enum {
|
||||
INBAND_CISCO_SGMII,
|
||||
INBAND_BASEX,
|
||||
} type;
|
||||
|
||||
mode = pl->req_link_an_mode;
|
||||
|
||||
pl->phy_ib_mode = 0;
|
||||
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
@@ -1119,10 +1177,7 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
|
||||
* 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;
|
||||
type = INBAND_CISCO_SGMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
@@ -1133,21 +1188,143 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
|
||||
* as well, but drivers may not support this, so may
|
||||
* need to override this.
|
||||
*/
|
||||
if (!phylink_autoneg_inband(mode))
|
||||
type = INBAND_BASEX;
|
||||
break;
|
||||
|
||||
default:
|
||||
pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
|
||||
pl->act_link_an_mode = mode;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pcs)
|
||||
pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface);
|
||||
|
||||
if (pl->phydev)
|
||||
phy_ib_caps = phy_inband_caps(pl->phydev, interface);
|
||||
|
||||
phylink_dbg(pl, "interface %s inband modes: pcs=%02x phy=%02x\n",
|
||||
phy_modes(interface), pcs_ib_caps, phy_ib_caps);
|
||||
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
bool pcs_ib_only = false;
|
||||
bool phy_ib_only = false;
|
||||
|
||||
if (pcs_ib_caps && pcs_ib_caps != LINK_INBAND_DISABLE) {
|
||||
/* PCS supports reporting in-band capabilities, and
|
||||
* supports more than disable mode.
|
||||
*/
|
||||
if (pcs_ib_caps & LINK_INBAND_DISABLE)
|
||||
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
else if (pcs_ib_caps & LINK_INBAND_ENABLE)
|
||||
pcs_ib_only = true;
|
||||
}
|
||||
|
||||
if (phy_ib_caps && phy_ib_caps != LINK_INBAND_DISABLE) {
|
||||
/* PHY supports in-band capabilities, and supports
|
||||
* more than disable mode.
|
||||
*/
|
||||
if (phy_ib_caps & LINK_INBAND_DISABLE)
|
||||
pl->phy_ib_mode = LINK_INBAND_DISABLE;
|
||||
else if (phy_ib_caps & LINK_INBAND_BYPASS)
|
||||
pl->phy_ib_mode = LINK_INBAND_BYPASS;
|
||||
else if (phy_ib_caps & LINK_INBAND_ENABLE)
|
||||
phy_ib_only = true;
|
||||
}
|
||||
|
||||
/* If either the PCS or PHY requires inband to be enabled,
|
||||
* this is an invalid configuration. Provide a diagnostic
|
||||
* message for this case, but don't try to force the issue.
|
||||
*/
|
||||
if (pcs_ib_only || phy_ib_only)
|
||||
phylink_warn(pl,
|
||||
"firmware wants %s mode, but %s%s%s requires inband\n",
|
||||
phylink_an_mode_str(mode),
|
||||
pcs_ib_only ? "PCS" : "",
|
||||
pcs_ib_only && phy_ib_only ? " and " : "",
|
||||
phy_ib_only ? "PHY" : "");
|
||||
|
||||
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
} else if (type == INBAND_CISCO_SGMII || pl->phydev) {
|
||||
/* For SGMII modes which are designed to be used with PHYs, or
|
||||
* Base-X with a PHY, we try to use in-band mode where-ever
|
||||
* possible. However, there are some PHYs e.g. BCM84881 which
|
||||
* do not support in-band.
|
||||
*/
|
||||
const unsigned int inband_ok = LINK_INBAND_ENABLE |
|
||||
LINK_INBAND_BYPASS;
|
||||
const unsigned int outband_ok = LINK_INBAND_DISABLE |
|
||||
LINK_INBAND_BYPASS;
|
||||
/* PCS PHY
|
||||
* D E D E
|
||||
* 0 0 0 0 no information inband enabled
|
||||
* 1 0 0 0 pcs doesn't support outband
|
||||
* 0 1 0 0 pcs required inband enabled
|
||||
* 1 1 0 0 pcs optional inband enabled
|
||||
* 0 0 1 0 phy doesn't support outband
|
||||
* 1 0 1 0 pcs+phy doesn't support outband
|
||||
* 0 1 1 0 pcs required, phy doesn't support, invalid
|
||||
* 1 1 1 0 pcs optional, phy doesn't support, outband
|
||||
* 0 0 0 1 phy required inband enabled
|
||||
* 1 0 0 1 pcs doesn't support, phy required, invalid
|
||||
* 0 1 0 1 pcs+phy required inband enabled
|
||||
* 1 1 0 1 pcs optional, phy required inband enabled
|
||||
* 0 0 1 1 phy optional inband enabled
|
||||
* 1 0 1 1 pcs doesn't support, phy optional, outband
|
||||
* 0 1 1 1 pcs required, phy optional inband enabled
|
||||
* 1 1 1 1 pcs+phy optional inband enabled
|
||||
*/
|
||||
if ((!pcs_ib_caps || pcs_ib_caps & inband_ok) &&
|
||||
(!phy_ib_caps || phy_ib_caps & inband_ok)) {
|
||||
/* In-band supported or unknown at both ends. Enable
|
||||
* in-band mode with or without bypass at the PHY.
|
||||
*/
|
||||
if (phy_ib_caps & LINK_INBAND_ENABLE)
|
||||
pl->phy_ib_mode = LINK_INBAND_ENABLE;
|
||||
else if (phy_ib_caps & LINK_INBAND_BYPASS)
|
||||
pl->phy_ib_mode = LINK_INBAND_BYPASS;
|
||||
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
} else if ((!pcs_ib_caps || pcs_ib_caps & outband_ok) &&
|
||||
(!phy_ib_caps || phy_ib_caps & outband_ok)) {
|
||||
/* Either in-band not supported at at least one end.
|
||||
* In-band bypass at the other end is possible.
|
||||
*/
|
||||
if (phy_ib_caps & LINK_INBAND_DISABLE)
|
||||
pl->phy_ib_mode = LINK_INBAND_DISABLE;
|
||||
else if (phy_ib_caps & LINK_INBAND_BYPASS)
|
||||
pl->phy_ib_mode = LINK_INBAND_BYPASS;
|
||||
|
||||
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
if (pl->phydev)
|
||||
mode = MLO_AN_PHY;
|
||||
} else {
|
||||
/* invalid */
|
||||
phylink_warn(pl, "%s: incompatible in-band capabilities, trying in-band",
|
||||
phy_modes(interface));
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
}
|
||||
} else {
|
||||
/* For Base-X without a PHY */
|
||||
if (pcs_ib_caps == LINK_INBAND_DISABLE)
|
||||
/* If the PCS doesn't support inband, then inband must
|
||||
* be disabled.
|
||||
*/
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
|
||||
else if (pcs_ib_caps == LINK_INBAND_ENABLE)
|
||||
/* If the PCS requires inband, then inband must always
|
||||
* be enabled.
|
||||
*/
|
||||
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
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;
|
||||
pl->pcs_neg_mode = neg_mode;
|
||||
pl->act_link_an_mode = mode;
|
||||
}
|
||||
|
||||
static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
@@ -1159,11 +1336,9 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
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);
|
||||
phylink_dbg(pl, "major config, requested %s/%s\n",
|
||||
phylink_an_mode_str(pl->req_link_an_mode),
|
||||
phy_modes(state->interface));
|
||||
|
||||
if (pl->mac_ops->mac_select_pcs) {
|
||||
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
|
||||
@@ -1177,10 +1352,17 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
pcs_changed = pl->pcs != pcs;
|
||||
}
|
||||
|
||||
phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising);
|
||||
|
||||
phylink_dbg(pl, "major config, active %s/%s/%s\n",
|
||||
phylink_an_mode_str(pl->act_link_an_mode),
|
||||
phylink_pcs_mode_str(pl->pcs_neg_mode),
|
||||
phy_modes(state->interface));
|
||||
|
||||
phylink_pcs_poll_stop(pl);
|
||||
|
||||
if (pl->mac_ops->mac_prepare) {
|
||||
err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
|
||||
err = pl->mac_ops->mac_prepare(pl->config, pl->act_link_an_mode,
|
||||
state->interface);
|
||||
if (err < 0) {
|
||||
phylink_err(pl, "mac_prepare failed: %pe\n",
|
||||
@@ -1214,7 +1396,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed)
|
||||
phylink_pcs_enable(pl->pcs);
|
||||
|
||||
neg_mode = pl->cur_link_an_mode;
|
||||
neg_mode = pl->act_link_an_mode;
|
||||
if (pl->pcs && pl->pcs->neg_mode)
|
||||
neg_mode = pl->pcs_neg_mode;
|
||||
|
||||
@@ -1230,13 +1412,20 @@ static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
phylink_pcs_an_restart(pl);
|
||||
|
||||
if (pl->mac_ops->mac_finish) {
|
||||
err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode,
|
||||
err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode,
|
||||
state->interface);
|
||||
if (err < 0)
|
||||
phylink_err(pl, "mac_finish failed: %pe\n",
|
||||
ERR_PTR(err));
|
||||
}
|
||||
|
||||
if (pl->phydev && pl->phy_ib_mode) {
|
||||
err = phy_config_inband(pl->phydev, pl->phy_ib_mode);
|
||||
if (err < 0)
|
||||
phylink_err(pl, "phy_config_inband: %pe\n",
|
||||
ERR_PTR(err));
|
||||
}
|
||||
|
||||
if (pl->sfp_bus) {
|
||||
rate_kbd = phylink_interface_signal_rate(state->interface);
|
||||
if (rate_kbd)
|
||||
@@ -1261,17 +1450,16 @@ static int phylink_change_inband_advert(struct phylink *pl)
|
||||
return 0;
|
||||
|
||||
phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__,
|
||||
phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phylink_an_mode_str(pl->req_link_an_mode),
|
||||
phy_modes(pl->link_config.interface),
|
||||
__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);
|
||||
phylink_pcs_neg_mode(pl, pl->pcs, pl->link_config.interface,
|
||||
pl->link_config.advertising);
|
||||
|
||||
neg_mode = pl->cur_link_an_mode;
|
||||
neg_mode = pl->act_link_an_mode;
|
||||
if (pl->pcs->neg_mode)
|
||||
neg_mode = pl->pcs_neg_mode;
|
||||
|
||||
@@ -1336,7 +1524,7 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
|
||||
{
|
||||
struct phylink_link_state link_state;
|
||||
|
||||
switch (pl->cur_link_an_mode) {
|
||||
switch (pl->req_link_an_mode) {
|
||||
case MLO_AN_PHY:
|
||||
link_state = pl->phy_state;
|
||||
break;
|
||||
@@ -1410,14 +1598,14 @@ static void phylink_link_up(struct phylink *pl,
|
||||
|
||||
pl->cur_interface = link_state.interface;
|
||||
|
||||
neg_mode = pl->cur_link_an_mode;
|
||||
neg_mode = pl->act_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->mac_ops->mac_link_up(pl->config, pl->phydev, pl->act_link_an_mode,
|
||||
pl->cur_interface, speed, duplex,
|
||||
!!(link_state.pause & MLO_PAUSE_TX), rx_pause);
|
||||
|
||||
@@ -1437,7 +1625,7 @@ static void phylink_link_down(struct phylink *pl)
|
||||
|
||||
if (ndev)
|
||||
netif_carrier_off(ndev);
|
||||
pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode,
|
||||
pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode,
|
||||
pl->cur_interface);
|
||||
phylink_info(pl, "Link is Down\n");
|
||||
}
|
||||
@@ -1463,10 +1651,10 @@ static void phylink_resolve(struct work_struct *w)
|
||||
} else if (pl->link_failed) {
|
||||
link_state.link = false;
|
||||
retrigger = true;
|
||||
} else if (pl->cur_link_an_mode == MLO_AN_FIXED) {
|
||||
} else if (pl->act_link_an_mode == MLO_AN_FIXED) {
|
||||
phylink_get_fixed_state(pl, &link_state);
|
||||
mac_config = link_state.link;
|
||||
} else if (pl->cur_link_an_mode == MLO_AN_PHY) {
|
||||
} else if (pl->act_link_an_mode == MLO_AN_PHY) {
|
||||
link_state = pl->phy_state;
|
||||
mac_config = link_state.link;
|
||||
} else {
|
||||
@@ -1520,7 +1708,7 @@ static void phylink_resolve(struct work_struct *w)
|
||||
}
|
||||
}
|
||||
|
||||
if (pl->cur_link_an_mode != MLO_AN_FIXED)
|
||||
if (pl->act_link_an_mode != MLO_AN_FIXED)
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
|
||||
if (mac_config) {
|
||||
@@ -1644,7 +1832,7 @@ int phylink_set_fixed_link(struct phylink *pl,
|
||||
pl->link_config.an_complete = 1;
|
||||
|
||||
pl->cfg_link_an_mode = MLO_AN_FIXED;
|
||||
pl->cur_link_an_mode = pl->cfg_link_an_mode;
|
||||
pl->req_link_an_mode = pl->cfg_link_an_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1732,7 +1920,7 @@ struct phylink *phylink_create(struct phylink_config *config,
|
||||
}
|
||||
}
|
||||
|
||||
pl->cur_link_an_mode = pl->cfg_link_an_mode;
|
||||
pl->req_link_an_mode = pl->cfg_link_an_mode;
|
||||
|
||||
ret = phylink_register_sfp(pl, fwnode);
|
||||
if (ret < 0) {
|
||||
@@ -2189,7 +2377,7 @@ void phylink_start(struct phylink *pl)
|
||||
ASSERT_RTNL();
|
||||
|
||||
phylink_info(pl, "configuring for %s/%s link mode\n",
|
||||
phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phylink_an_mode_str(pl->req_link_an_mode),
|
||||
phy_modes(pl->link_config.interface));
|
||||
|
||||
/* Always set the carrier off */
|
||||
@@ -2474,7 +2662,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
|
||||
|
||||
linkmode_copy(kset->link_modes.supported, pl->supported);
|
||||
|
||||
switch (pl->cur_link_an_mode) {
|
||||
switch (pl->act_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
/* We are using fixed settings. Report these as the
|
||||
* current link settings - and note that these also
|
||||
@@ -2505,6 +2693,26 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
|
||||
|
||||
static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl,
|
||||
phy_interface_t interface,
|
||||
unsigned long *adv)
|
||||
{
|
||||
unsigned int inband = phylink_inband_caps(pl, interface);
|
||||
unsigned int mask;
|
||||
|
||||
/* If the PCS doesn't implement inband support, be permissive. */
|
||||
if (!inband)
|
||||
return true;
|
||||
|
||||
if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv))
|
||||
mask = LINK_INBAND_ENABLE;
|
||||
else
|
||||
mask = LINK_INBAND_DISABLE;
|
||||
|
||||
/* Check whether the PCS implements the required mode */
|
||||
return !!(inband & mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* phylink_ethtool_ksettings_set() - set the link settings
|
||||
* @pl: a pointer to a &struct phylink returned from phylink_create()
|
||||
@@ -2566,7 +2774,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
/* If we have a fixed link, refuse to change link parameters.
|
||||
* If the link parameters match, accept them but do nothing.
|
||||
*/
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED) {
|
||||
if (pl->req_link_an_mode == MLO_AN_FIXED) {
|
||||
if (s->speed != pl->link_config.speed ||
|
||||
s->duplex != pl->link_config.duplex)
|
||||
return -EINVAL;
|
||||
@@ -2582,7 +2790,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
* is our default case) but do not allow the advertisement to
|
||||
* be changed. If the advertisement matches, simply return.
|
||||
*/
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED) {
|
||||
if (pl->req_link_an_mode == MLO_AN_FIXED) {
|
||||
if (!linkmode_equal(config.advertising,
|
||||
pl->link_config.advertising))
|
||||
return -EINVAL;
|
||||
@@ -2617,7 +2825,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
linkmode_copy(support, pl->supported);
|
||||
if (phylink_validate(pl, support, &config)) {
|
||||
phylink_err(pl, "validation of %s/%s with support %*pb failed\n",
|
||||
phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phylink_an_mode_str(pl->req_link_an_mode),
|
||||
phy_modes(config.interface),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
|
||||
return -EINVAL;
|
||||
@@ -2635,6 +2843,13 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
phylink_is_empty_linkmode(config.advertising))
|
||||
return -EINVAL;
|
||||
|
||||
/* Validate the autonegotiation state. We don't have a PHY in this
|
||||
* situation, so the PCS is the media-facing entity.
|
||||
*/
|
||||
if (!phylink_validate_pcs_inband_autoneg(pl, config.interface,
|
||||
config.advertising))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pl->state_mutex);
|
||||
pl->link_config.speed = config.speed;
|
||||
pl->link_config.duplex = config.duplex;
|
||||
@@ -2717,7 +2932,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED)
|
||||
if (pl->req_link_an_mode == MLO_AN_FIXED)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!phylink_test(pl->supported, Pause) &&
|
||||
@@ -2981,7 +3196,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
|
||||
struct phylink_link_state state;
|
||||
int val = 0xffff;
|
||||
|
||||
switch (pl->cur_link_an_mode) {
|
||||
switch (pl->act_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
if (phy_id == 0) {
|
||||
phylink_get_fixed_state(pl, &state);
|
||||
@@ -3006,7 +3221,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
|
||||
static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
switch (pl->cur_link_an_mode) {
|
||||
switch (pl->act_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
break;
|
||||
|
||||
@@ -3176,10 +3391,11 @@ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
|
||||
return interface;
|
||||
}
|
||||
|
||||
static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
|
||||
static void phylink_sfp_set_config(struct phylink *pl,
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
u8 mode = MLO_AN_INBAND;
|
||||
bool changed = false;
|
||||
|
||||
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
|
||||
@@ -3196,9 +3412,9 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (pl->cur_link_an_mode != mode ||
|
||||
if (pl->req_link_an_mode != mode ||
|
||||
pl->link_config.interface != state->interface) {
|
||||
pl->cur_link_an_mode = mode;
|
||||
pl->req_link_an_mode = mode;
|
||||
pl->link_config.interface = state->interface;
|
||||
|
||||
changed = true;
|
||||
@@ -3213,8 +3429,7 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
|
||||
phylink_mac_initial_config(pl, false);
|
||||
}
|
||||
|
||||
static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
|
||||
struct phy_device *phy)
|
||||
static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)
|
||||
{
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
|
||||
struct phylink_link_state config;
|
||||
@@ -3258,7 +3473,7 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
|
||||
|
||||
pl->link_port = pl->sfp_port;
|
||||
|
||||
phylink_sfp_set_config(pl, mode, support, &config);
|
||||
phylink_sfp_set_config(pl, support, &config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3314,6 +3529,12 @@ static int phylink_sfp_config_optical(struct phylink *pl)
|
||||
phylink_dbg(pl, "optical SFP: chosen %s interface\n",
|
||||
phy_modes(interface));
|
||||
|
||||
if (!phylink_validate_pcs_inband_autoneg(pl, interface,
|
||||
config.advertising)) {
|
||||
phylink_err(pl, "autoneg setting not compatible with PCS");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config.interface = interface;
|
||||
|
||||
/* Ignore errors if we're expecting a PHY to attach later */
|
||||
@@ -3327,7 +3548,7 @@ static int phylink_sfp_config_optical(struct phylink *pl)
|
||||
|
||||
pl->link_port = pl->sfp_port;
|
||||
|
||||
phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config);
|
||||
phylink_sfp_set_config(pl, pl->sfp_support, &config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3398,19 +3619,9 @@ static void phylink_sfp_link_up(void *upstream)
|
||||
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK);
|
||||
}
|
||||
|
||||
/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
|
||||
* or 802.3z control word, so inband will not work.
|
||||
*/
|
||||
static bool phylink_phy_no_inband(struct phy_device *phy)
|
||||
{
|
||||
return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
|
||||
0xae025150, 0xfffffff0);
|
||||
}
|
||||
|
||||
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
|
||||
{
|
||||
struct phylink *pl = upstream;
|
||||
u8 mode;
|
||||
|
||||
/*
|
||||
* This is the new way of dealing with flow control for PHYs,
|
||||
@@ -3421,17 +3632,12 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
|
||||
*/
|
||||
phy_support_asym_pause(phy);
|
||||
|
||||
if (phylink_phy_no_inband(phy))
|
||||
mode = MLO_AN_PHY;
|
||||
else
|
||||
mode = MLO_AN_INBAND;
|
||||
|
||||
/* Set the PHY's host supported interfaces */
|
||||
phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
|
||||
pl->config->supported_interfaces);
|
||||
|
||||
/* Do the initial configuration */
|
||||
return phylink_sfp_config_phy(pl, mode, phy);
|
||||
return phylink_sfp_config_phy(pl, phy);
|
||||
}
|
||||
|
||||
static void phylink_sfp_disconnect_phy(void *upstream,
|
||||
|
||||
@@ -817,6 +817,24 @@ struct phy_tdr_config {
|
||||
};
|
||||
#define PHY_PAIR_ALL -1
|
||||
|
||||
/**
|
||||
* enum link_inband_signalling - in-band signalling modes that are supported
|
||||
*
|
||||
* @LINK_INBAND_DISABLE: in-band signalling can be disabled
|
||||
* @LINK_INBAND_ENABLE: in-band signalling can be enabled without bypass
|
||||
* @LINK_INBAND_BYPASS: in-band signalling can be enabled with bypass
|
||||
*
|
||||
* The possible and required bits can only be used if the valid bit is set.
|
||||
* If possible is clear, that means inband signalling can not be used.
|
||||
* Required is only valid when possible is set, and means that inband
|
||||
* signalling must be used.
|
||||
*/
|
||||
enum link_inband_signalling {
|
||||
LINK_INBAND_DISABLE = BIT(0),
|
||||
LINK_INBAND_ENABLE = BIT(1),
|
||||
LINK_INBAND_BYPASS = BIT(2),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision
|
||||
* Avoidance) Reconciliation Sublayer.
|
||||
@@ -956,6 +974,19 @@ struct phy_driver {
|
||||
*/
|
||||
int (*get_features)(struct phy_device *phydev);
|
||||
|
||||
/**
|
||||
* @inband_caps: query whether in-band is supported for the given PHY
|
||||
* interface mode. Returns a bitmask of bits defined by enum
|
||||
* link_inband_signalling.
|
||||
*/
|
||||
unsigned int (*inband_caps)(struct phy_device *phydev,
|
||||
phy_interface_t interface);
|
||||
|
||||
/**
|
||||
* @config_inband: configure in-band mode for the PHY
|
||||
*/
|
||||
int (*config_inband)(struct phy_device *phydev, unsigned int modes);
|
||||
|
||||
/**
|
||||
* @get_rate_matching: Get the supported type of rate matching for a
|
||||
* particular phy interface. This is used by phy consumers to determine
|
||||
@@ -1818,6 +1849,9 @@ int phy_config_aneg(struct phy_device *phydev);
|
||||
int _phy_start_aneg(struct phy_device *phydev);
|
||||
int phy_start_aneg(struct phy_device *phydev);
|
||||
int phy_aneg_done(struct phy_device *phydev);
|
||||
unsigned int phy_inband_caps(struct phy_device *phydev,
|
||||
phy_interface_t interface);
|
||||
int phy_config_inband(struct phy_device *phydev, unsigned int modes);
|
||||
int phy_speed_down(struct phy_device *phydev, bool sync);
|
||||
int phy_speed_up(struct phy_device *phydev);
|
||||
bool phy_check_valid(int speed, int duplex, unsigned long *features);
|
||||
|
||||
@@ -419,6 +419,7 @@ struct phylink_pcs {
|
||||
/**
|
||||
* struct phylink_pcs_ops - MAC PCS operations structure.
|
||||
* @pcs_validate: validate the link configuration.
|
||||
* @pcs_inband_caps: query inband support for interface mode.
|
||||
* @pcs_enable: enable the PCS.
|
||||
* @pcs_disable: disable the PCS.
|
||||
* @pcs_pre_config: pre-mac_config method (for errata)
|
||||
@@ -434,6 +435,8 @@ struct phylink_pcs {
|
||||
struct phylink_pcs_ops {
|
||||
int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
|
||||
const struct phylink_link_state *state);
|
||||
unsigned int (*pcs_inband_caps)(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface);
|
||||
int (*pcs_enable)(struct phylink_pcs *pcs);
|
||||
void (*pcs_disable)(struct phylink_pcs *pcs);
|
||||
void (*pcs_pre_config)(struct phylink_pcs *pcs,
|
||||
@@ -470,6 +473,20 @@ struct phylink_pcs_ops {
|
||||
int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
|
||||
const struct phylink_link_state *state);
|
||||
|
||||
/**
|
||||
* pcs_inband_caps - query PCS in-band capabilities for interface mode.
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
* @interface: interface mode to be queried
|
||||
*
|
||||
* Returns zero if it is unknown what in-band signalling is supported by the
|
||||
* PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
|
||||
* returns a bit mask of the LINK_INBAND_* values from
|
||||
* &enum link_inband_signalling to describe which inband modes are supported
|
||||
* for this interface mode.
|
||||
*/
|
||||
unsigned int pcs_inband_caps(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface);
|
||||
|
||||
/**
|
||||
* pcs_enable() - enable the PCS.
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
|
||||
Reference in New Issue
Block a user