summaryrefslogtreecommitdiff
path: root/drivers/net/phy/phylink.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r--drivers/net/phy/phylink.c841
1 files changed, 498 insertions, 343 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index b905a6b93d80..aff3313d4de7 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -60,6 +60,7 @@ struct phylink {
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);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_lpi);
/* The link configuration settings */
struct phylink_link_state link_config;
@@ -75,12 +76,18 @@ 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;
- bool mac_link_dropped;
- bool using_mac_select_pcs;
+ bool link_failed;
+ bool mac_supports_eee_ops;
+ bool mac_supports_eee;
+ bool phy_enable_tx_lpi;
+ bool mac_enable_tx_lpi;
+ bool mac_tx_clk_stop;
+ u32 mac_tx_lpi_timer;
struct sfp_bus *sfp_bus;
bool sfp_may_have_phy;
@@ -89,7 +96,6 @@ struct phylink {
u8 sfp_port;
struct eee_config eee_cfg;
- bool eee_active;
};
#define phylink_printk(level, pl, fmt, ...) \
@@ -189,9 +195,9 @@ static const char *phylink_pcs_mode_str(unsigned int mode)
if (mode & PHYLINK_PCS_NEG_INBAND) {
if (mode & PHYLINK_PCS_NEG_ENABLED)
- return "inband/an-enabled";
+ return "inband,an-enabled";
else
- return "inband/an-disabled";
+ return "inband,an-disabled";
}
return "unknown";
@@ -253,6 +259,7 @@ static int phylink_interface_max_speed(phy_interface_t interface)
return SPEED_1000;
case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
return SPEED_2500;
case PHY_INTERFACE_MODE_5GBASER:
@@ -522,7 +529,11 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface,
switch (interface) {
case PHY_INTERFACE_MODE_USXGMII:
- caps |= MAC_10000FD | MAC_5000FD | MAC_2500FD;
+ caps |= MAC_10000FD | MAC_5000FD;
+ fallthrough;
+
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ caps |= MAC_2500FD;
fallthrough;
case PHY_INTERFACE_MODE_RGMII_TXID:
@@ -616,15 +627,8 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface,
* max speed at full duplex.
*/
if (mac_capabilities &
- phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL)) {
- /* Although a duplex-matching phy might exist, we
- * conservatively remove these modes because the MAC
- * will not be aware of the half-duplex nature of the
- * link.
- */
+ phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL))
matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD));
- matched_caps &= ~(MAC_1000HD | MAC_100HD | MAC_10HD);
- }
break;
}
case RATE_MATCH_CRS:
@@ -673,17 +677,15 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
unsigned long *supported,
struct phylink_link_state *state)
{
+ struct phylink_pcs *pcs = NULL;
unsigned long capabilities;
- struct phylink_pcs *pcs;
int ret;
/* Get the PCS for this interface mode */
- if (pl->using_mac_select_pcs) {
+ if (pl->mac_ops->mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
if (IS_ERR(pcs))
return PTR_ERR(pcs);
- } else {
- pcs = pl->pcs;
}
if (pcs) {
@@ -698,6 +700,17 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
return -EINVAL;
}
+ /* Ensure that this PCS supports the interface which the MAC
+ * returned it for. It is an error for the MAC to return a PCS
+ * that does not support the interface mode.
+ */
+ if (!phy_interface_empty(pcs->supported_interfaces) &&
+ !test_bit(state->interface, pcs->supported_interfaces)) {
+ phylink_err(pl, "MAC returned PCS which does not support %s\n",
+ phy_modes(state->interface));
+ return -EINVAL;
+ }
+
/* Validate the link parameters with the PCS */
if (pcs->ops->pcs_validate) {
ret = pcs->ops->pcs_validate(pcs, supported, state);
@@ -791,8 +804,8 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported,
static int phylink_parse_fixedlink(struct phylink *pl,
const struct fwnode_handle *fwnode)
{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
struct fwnode_handle *fixed_node;
- bool pause, asym_pause, autoneg;
const struct phy_setting *s;
struct gpio_desc *desc;
u32 speed;
@@ -865,22 +878,15 @@ static int phylink_parse_fixedlink(struct phylink *pl,
linkmode_copy(pl->link_config.advertising, pl->supported);
phylink_validate(pl, pl->supported, &pl->link_config);
- pause = phylink_test(pl->supported, Pause);
- asym_pause = phylink_test(pl->supported, Asym_Pause);
- autoneg = phylink_test(pl->supported, Autoneg);
s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
pl->supported, true);
- linkmode_zero(pl->supported);
- phylink_set(pl->supported, MII);
-
- if (pause)
- phylink_set(pl->supported, Pause);
- if (asym_pause)
- phylink_set(pl->supported, Asym_Pause);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+ linkmode_and(pl->supported, pl->supported, mask);
- if (autoneg)
- phylink_set(pl->supported, Autoneg);
+ phylink_set(pl->supported, MII);
if (s) {
__set_bit(s->bit, pl->supported);
@@ -907,26 +913,31 @@ static int phylink_parse_mode(struct phylink *pl,
const char *managed;
unsigned long caps;
+ if (pl->config->default_an_inband)
+ pl->cfg_link_an_mode = MLO_AN_INBAND;
+
dn = fwnode_get_named_child_node(fwnode, "fixed-link");
if (dn || fwnode_property_present(fwnode, "fixed-link"))
pl->cfg_link_an_mode = MLO_AN_FIXED;
fwnode_handle_put(dn);
if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
- strcmp(managed, "in-band-status") == 0) ||
- pl->config->ovr_an_inband) {
- if (phylink_mode_fixed(pl->cfg_link_an_mode)) {
+ strcmp(managed, "in-band-status") == 0)) {
+ if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
phylink_err(pl,
"can't use both fixed-link and in-band-status\n");
return -EINVAL;
}
+ pl->cfg_link_an_mode = MLO_AN_INBAND;
+ }
+
+ if (pl->cfg_link_an_mode == MLO_AN_INBAND) {
linkmode_zero(pl->supported);
phylink_set(pl->supported, MII);
phylink_set(pl->supported, Autoneg);
phylink_set(pl->supported, Asym_Pause);
phylink_set(pl->supported, Pause);
- pl->cfg_link_an_mode = MLO_AN_INBAND;
switch (pl->link_config.interface) {
case PHY_INTERFACE_MODE_SGMII:
@@ -943,6 +954,7 @@ static int phylink_parse_mode(struct phylink *pl,
case PHY_INTERFACE_MODE_5GBASER:
case PHY_INTERFACE_MODE_25GBASER:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
case PHY_INTERFACE_MODE_10GKR:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_XLGMII:
@@ -999,11 +1011,11 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
}
}
-static unsigned int phylink_pcs_query_inband(struct phylink_pcs *pcs,
+static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs,
phy_interface_t interface)
{
- if (pcs && pcs->ops->pcs_query_inband)
- return pcs->ops->pcs_query_inband(pcs, interface);
+ if (pcs && pcs->ops->pcs_inband_caps)
+ return pcs->ops->pcs_inband_caps(pcs, interface);
return 0;
}
@@ -1061,37 +1073,63 @@ 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);
}
+static void phylink_pcs_disable_eee(struct phylink_pcs *pcs)
+{
+ if (pcs && pcs->ops->pcs_disable_eee)
+ pcs->ops->pcs_disable_eee(pcs);
+}
+
+static void phylink_pcs_enable_eee(struct phylink_pcs *pcs)
+{
+ if (pcs && pcs->ops->pcs_enable_eee)
+ pcs->ops->pcs_enable_eee(pcs);
+}
+
/* 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_query_inband(struct phylink *pl,
+static unsigned int phylink_inband_caps(struct phylink *pl,
phy_interface_t interface)
{
struct phylink_pcs *pcs;
- if (!pl->using_mac_select_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_query_inband(pcs, interface);
+ return phylink_pcs_inband_caps(pcs, interface);
}
static void phylink_pcs_poll_stop(struct phylink *pl)
{
- if (phylink_mode_inband(pl->cfg_link_an_mode))
+ if (pl->cfg_link_an_mode == MLO_AN_INBAND)
del_timer(&pl->link_poll);
}
static void phylink_pcs_poll_start(struct phylink *pl)
{
- if (pl->pcs && pl->pcs->poll &&
- phylink_mode_inband(pl->cfg_link_an_mode))
+ if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND)
mod_timer(&pl->link_poll, jiffies + HZ);
}
+int phylink_pcs_pre_init(struct phylink *pl, struct phylink_pcs *pcs)
+{
+ int ret = 0;
+
+ /* Signal to PCS driver that MAC requires RX clock for init */
+ if (pl->config->mac_requires_rxc)
+ pcs->rxc_always_on = true;
+
+ if (pcs->ops->pcs_pre_init)
+ ret = pcs->ops->pcs_pre_init(pcs);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_pcs_pre_init);
+
static void phylink_mac_config(struct phylink *pl,
const struct phylink_link_state *state)
{
@@ -1128,7 +1166,6 @@ static void phylink_pcs_an_restart(struct phylink *pl)
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
* @pl: a pointer to a &struct phylink returned from phylink_create()
* @pcs: a pointer to &struct phylink_pcs
- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
* @interface: interface mode to be used
* @advertising: adertisement ethtool link mode mask
*
@@ -1145,25 +1182,28 @@ 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(struct phylink *pl,
- struct phylink_pcs *pcs,
- 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 phy_link_mode = 0;
- unsigned int pcs_link_mode;
- 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_8023Z,
+ INBAND_BASEX,
} type;
+ mode = pl->req_link_an_mode;
+
+ pl->phy_ib_mode = 0;
+
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_QUSGMII:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
/* 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
@@ -1180,52 +1220,73 @@ static unsigned int phylink_pcs_neg_mode(struct phylink *pl,
* as well, but drivers may not support this, so may
* need to override this.
*/
- type = INBAND_8023Z;
+ type = INBAND_BASEX;
break;
default:
- return PHYLINK_PCS_NEG_NONE;
+ pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
+ pl->act_link_an_mode = mode;
+ return;
}
- pcs_link_mode = phylink_pcs_query_inband(pcs, interface);
- pcs_link_mode &= LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
- if (pl->phydev) {
- phy_link_mode = phy_query_inband(pl->phydev, interface);
- phy_link_mode &= LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
- }
+ if (pcs)
+ pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface);
- if (!phylink_autoneg_inband(mode)) {
- const char *s1, *s2, *s3, *empty = "";
+ if (pl->phydev)
+ phy_ib_caps = phy_inband_caps(pl->phydev, interface);
- s1 = s2 = s3 = empty;
+ phylink_dbg(pl, "interface %s inband modes: pcs=%02x phy=%02x\n",
+ phy_modes(interface), pcs_ib_caps, phy_ib_caps);
- if (pcs_link_mode == LINK_INBAND_ENABLE)
- s1 = "PCS";
+ if (!phylink_autoneg_inband(mode)) {
+ bool pcs_ib_only = false;
+ bool phy_ib_only = false;
- if (phy_link_mode == LINK_INBAND_ENABLE)
- s3 = "PHY";
+ 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 (s1 != empty || s3 != empty) {
- if (s1 != empty && s3 != empty)
- s2 = " and ";
-
+ 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),
- s1, s2, s3);
- }
- return PHYLINK_PCS_NEG_OUTBAND;
- }
-
- /* For SGMII modes, which are designed to be used with PHYs, we
- * try to use inband mode where-ever possible. However, there are
- * some PHYs e.g. BCM84881 which do not support in-band.
- */
- if (type == INBAND_CISCO_SGMII) {
+ 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
@@ -1245,61 +1306,57 @@ static unsigned int phylink_pcs_neg_mode(struct phylink *pl,
* 0 1 1 1 pcs required, phy optional inband enabled
* 1 1 1 1 pcs+phy optional inband enabled
*/
- if (!pcs_link_mode)
- pcs_link_mode = LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
-
- if (!phy_link_mode)
- phy_link_mode = LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
-
- if (pcs_link_mode & phy_link_mode & LINK_INBAND_ENABLE) {
- /* inband supported at both ends */
- return PHYLINK_PCS_NEG_INBAND_ENABLED;
- } else if (pcs_link_mode & phy_link_mode & LINK_INBAND_DISABLE) {
- /* inband not supported at both ends */
- return PHYLINK_PCS_NEG_OUTBAND;
+ 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;
}
- }
-
- /* For 802.3z, handle the PHY present vs absent cases separately */
- if (!pl->phydev) {
- /* PHY absent: always use inband but inband may be disabled */
- /* If the PCS doesn't support inband, then inband must be
- * disabled.
- */
- if (pcs_link_mode == LINK_INBAND_DISABLE)
- return PHYLINK_PCS_NEG_INBAND_DISABLED;
-
- /* If the PCS requires inband, then inband must always be
- * enabled.
- */
- if (pcs_link_mode == LINK_INBAND_ENABLE)
- return PHYLINK_PCS_NEG_INBAND_ENABLED;
-
- /* For the possible case, fall through to the "legacy" code. */
} else {
- /* PHY present, inband mode depends on the capabilities
- * of both.
- */
+ /* 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;
}
- /* Legacy, so determine inband depending on the advertising bit */
- 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;
-
- return neg_mode;
-}
-
-static u8 phylink_choose_act_link_an_mode(struct phylink *pl)
-{
- if (pl->req_link_an_mode == MLO_AN_INBAND && pl->phydev &&
- pl->pcs_neg_mode == PHYLINK_PCS_NEG_OUTBAND)
- return MLO_AN_PHY;
-
- return pl->req_link_an_mode;
+ pl->pcs_neg_mode = neg_mode;
+ pl->act_link_an_mode = mode;
}
static void phylink_major_config(struct phylink *pl, bool restart,
@@ -1315,7 +1372,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
phylink_an_mode_str(pl->req_link_an_mode),
phy_modes(state->interface));
- if (pl->using_mac_select_pcs) {
+ if (pl->mac_ops->mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
if (IS_ERR(pcs)) {
phylink_err(pl,
@@ -1324,18 +1381,10 @@ static void phylink_major_config(struct phylink *pl, bool restart,
return;
}
- pcs_changed = pcs && pl->pcs != pcs;
+ pcs_changed = pl->pcs != pcs;
}
- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pcs,
- pl->req_link_an_mode,
- state->interface,
- state->advertising);
-
- /* set the active link AN mode, which may end up different from the
- * current link AN mode depending on the PCS and PHY capabilities.
- */
- pl->act_link_an_mode = phylink_choose_act_link_an_mode(pl);
+ 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),
@@ -1402,6 +1451,13 @@ static void phylink_major_config(struct phylink *pl, bool restart,
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)
@@ -1432,15 +1488,8 @@ static int phylink_change_inband_advert(struct phylink *pl)
pl->link_config.pause);
/* Recompute the PCS neg mode */
- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pl->pcs,
- pl->req_link_an_mode,
- pl->link_config.interface,
- pl->link_config.advertising);
-
- /* set the active link AN mode, which may end up different from the
- * current link AN mode depending on the PCS and PHY capabilities.
- */
- pl->act_link_an_mode = phylink_choose_act_link_an_mode(pl);
+ phylink_pcs_neg_mode(pl, pl->pcs, pl->link_config.interface,
+ pl->link_config.advertising);
neg_mode = pl->act_link_an_mode;
if (pl->pcs->neg_mode)
@@ -1464,12 +1513,24 @@ static int phylink_change_inband_advert(struct phylink *pl)
static void phylink_mac_pcs_get_state(struct phylink *pl,
struct phylink_link_state *state)
{
+ struct phylink_pcs *pcs;
+ bool autoneg;
+
linkmode_copy(state->advertising, pl->link_config.advertising);
linkmode_zero(state->lp_advertising);
state->interface = pl->link_config.interface;
state->rate_matching = pl->link_config.rate_matching;
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
- state->advertising)) {
+ state->an_complete = 0;
+ state->link = 1;
+
+ pcs = pl->pcs;
+ if (!pcs || pcs->neg_mode)
+ autoneg = pl->pcs_neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
+ else
+ autoneg = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ state->advertising);
+
+ if (autoneg) {
state->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN;
state->pause = MLO_PAUSE_NONE;
@@ -1478,11 +1539,9 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
state->duplex = pl->link_config.duplex;
state->pause = pl->link_config.pause;
}
- state->an_complete = 0;
- state->link = 1;
- if (pl->pcs)
- pl->pcs->ops->pcs_get_state(pl->pcs, state);
+ if (pcs)
+ pcs->ops->pcs_get_state(pcs, pl->pcs_neg_mode, state);
else
state->link = 0;
}
@@ -1546,73 +1605,49 @@ static const char *phylink_pause_to_str(int pause)
}
}
-static void phylink_disable_tx_lpi(struct phylink *pl)
+static void phylink_deactivate_lpi(struct phylink *pl)
{
- phylink_dbg(pl, "disabling tx_lpi\n");
+ if (pl->mac_enable_tx_lpi) {
+ pl->mac_enable_tx_lpi = false;
- if (pl->mac_ops->mac_disable_tx_lpi)
- pl->mac_ops->mac_disable_tx_lpi(pl->config);
-}
-
-static void phylink_enable_tx_lpi(struct phylink *pl)
-{
- phylink_dbg(pl, "enabling tx_lpi, timer %uus\n",
- pl->eee_cfg.tx_lpi_timer);
+ phylink_dbg(pl, "disabling LPI\n");
- if (pl->mac_ops->mac_enable_tx_lpi)
- pl->mac_ops->mac_enable_tx_lpi(pl->config,
- pl->eee_cfg.tx_lpi_timer);
-}
-
-static bool phylink_eee_is_active(struct phylink *pl)
-{
- return phylink_init_eee(pl, pl->config->eee_clk_stop_enable) >= 0;
-}
+ pl->mac_ops->mac_disable_tx_lpi(pl->config, pl->cur_interface);
-static void phylink_deactivate_eee(struct phylink *pl)
-{
- phylink_dbg(pl, "deactivating EEE, was %sactive\n",
- pl->eee_active ? "" : "in");
-
- if (pl->eee_active) {
- pl->eee_active = false;
- phylink_disable_tx_lpi(pl);
+ phylink_pcs_disable_eee(pl->pcs);
}
}
-static void phylink_activate_eee(struct phylink *pl)
+static void phylink_activate_lpi(struct phylink *pl)
{
- pl->eee_active = phylink_eee_is_active(pl);
-
- phylink_dbg(pl, "can LPI, EEE enabled, %sactive\n",
- pl->eee_active ? "" : "in");
-
- if (pl->eee_active)
- phylink_enable_tx_lpi(pl);
-}
+ int err;
-/* Determine whether the MAC has new EEE support. We detect this by checking
- * for the two new methods being present, but for DSA it will populate these
- * anyway, so also check that lpi_capabilities is non-zero.
- */
-static bool phylink_mac_supports_eee(struct phylink *pl)
-{
- return pl->mac_ops->mac_disable_tx_lpi &&
- pl->mac_ops->mac_enable_tx_lpi &&
- pl->config->lpi_capabilities;
-}
+ if (!test_bit(pl->cur_interface, pl->config->lpi_interfaces)) {
+ phylink_dbg(pl, "MAC does not support LPI with %s\n",
+ phy_modes(pl->cur_interface));
+ return;
+ }
-static void phylink_phy_restrict_eee(struct phylink *pl, struct phy_device *phy)
-{
- __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_supported);
+ phylink_dbg(pl, "LPI timer %uus, tx clock stop %u\n",
+ pl->mac_tx_lpi_timer, pl->mac_tx_clk_stop);
- /* Convert the MAC's LPI capabilities to linkmodes */
- linkmode_zero(eee_supported);
- phylink_caps_to_linkmodes(eee_supported, pl->config->lpi_capabilities);
+ phylink_pcs_enable_eee(pl->pcs);
- /* Mask out EEE modes that are not supported */
- linkmode_and(phy->supported_eee, phy->supported_eee, eee_supported);
- linkmode_and(phy->advertising_eee, phy->advertising_eee, eee_supported);
+ err = pl->mac_ops->mac_enable_tx_lpi(pl->config, pl->cur_interface,
+ pl->mac_tx_lpi_timer,
+ pl->mac_tx_clk_stop);
+ if (!err) {
+ pl->mac_enable_tx_lpi = true;
+ } else {
+ phylink_pcs_disable_eee(pl->pcs);
+ if (err == -EOPNOTSUPP)
+ phylink_dbg(pl, "%ps doesn't support LPI",
+ pl->mac_ops->mac_enable_tx_lpi);
+ else
+ phylink_err(pl, "%ps() failed: %pe\n",
+ pl->mac_ops->mac_enable_tx_lpi,
+ ERR_PTR(err));
+ }
}
static void phylink_link_up(struct phylink *pl,
@@ -1661,8 +1696,8 @@ static void phylink_link_up(struct phylink *pl,
pl->cur_interface, speed, duplex,
!!(link_state.pause & MLO_PAUSE_TX), rx_pause);
- if (eeecfg_mac_can_tx_lpi(&pl->eee_cfg))
- phylink_activate_eee(pl);
+ if (pl->mac_supports_eee && pl->phy_enable_tx_lpi)
+ phylink_activate_lpi(pl);
if (ndev)
netif_carrier_on(ndev);
@@ -1681,7 +1716,7 @@ static void phylink_link_down(struct phylink *pl)
if (ndev)
netif_carrier_off(ndev);
- phylink_deactivate_eee(pl);
+ phylink_deactivate_lpi(pl);
pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode,
pl->cur_interface);
@@ -1705,31 +1740,24 @@ static void phylink_resolve(struct work_struct *w)
cur_link_state = phylink_link_is_up(pl);
if (pl->phylink_disable_state) {
- pl->mac_link_dropped = false;
+ pl->link_failed = false;
link_state.link = false;
- } else if (pl->mac_link_dropped) {
+ } else if (pl->link_failed) {
link_state.link = false;
retrigger = true;
} 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->act_link_an_mode == MLO_AN_PHY) {
- /* PHY mode, or PCS configured for outband mode */
- if (pl->phydev) {
- link_state = pl->phy_state;
- } else {
- /* No PHY - assume link is down */
- link_state.link = false;
- }
+ link_state = pl->phy_state;
mac_config = link_state.link;
} else {
- /* Inband mode */
phylink_mac_pcs_get_state(pl, &link_state);
- /* The PCS may have a latching link-fail indicator.
- * If the link was up, bring the link down and
- * re-trigger the resolve. Otherwise, re-read the
- * PCS state to get the current status of the link.
+ /* The PCS may have a latching link-fail indicator. If the link
+ * was up, bring the link down and re-trigger the resolve.
+ * Otherwise, re-read the PCS state to get the current status
+ * of the link.
*/
if (!link_state.link) {
if (cur_link_state)
@@ -1738,26 +1766,26 @@ static void phylink_resolve(struct work_struct *w)
phylink_mac_pcs_get_state(pl, &link_state);
}
- /* If we have a phy, the "up" state is the union of
- * both the PHY and the MAC
+ /* If we have a phy, the "up" state is the union of both the
+ * PHY and the MAC
*/
if (pl->phydev)
link_state.link &= pl->phy_state.link;
/* Only update if the PHY link is up */
if (pl->phydev && pl->phy_state.link) {
- /* If the interface has changed, force a
- * link down event if the link isn't already
- * down, and re-resolve.
+ /* If the interface has changed, force a link down
+ * event if the link isn't already down, and re-resolve.
*/
if (link_state.interface != pl->phy_state.interface) {
retrigger = true;
link_state.link = false;
}
+
link_state.interface = pl->phy_state.interface;
- /* If we are doing rate matching, then the
- * link speed/duplex comes from the PHY
+ /* If we are doing rate matching, then the link
+ * speed/duplex comes from the PHY
*/
if (pl->phy_state.rate_matching) {
link_state.rate_matching =
@@ -1766,8 +1794,8 @@ static void phylink_resolve(struct work_struct *w)
link_state.duplex = pl->phy_state.duplex;
}
- /* If we have a PHY, we need to update with
- * the PHY flow control bits.
+ /* If we have a PHY, we need to update with the PHY
+ * flow control bits.
*/
link_state.pause = pl->phy_state.pause;
mac_config = true;
@@ -1799,7 +1827,7 @@ static void phylink_resolve(struct work_struct *w)
phylink_link_up(pl, link_state);
}
if (!link_state.link && retrigger) {
- pl->mac_link_dropped = false;
+ pl->link_failed = false;
queue_work(system_power_efficient_wq, &pl->resolve);
}
mutex_unlock(&pl->state_mutex);
@@ -1863,6 +1891,48 @@ static int phylink_register_sfp(struct phylink *pl,
}
/**
+ * phylink_set_fixed_link() - set the fixed link
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @state: a pointer to a struct phylink_link_state.
+ *
+ * This function is used when the link parameters are known and do not change,
+ * making it suitable for certain types of network connections.
+ *
+ * Returns: zero on success or negative error code.
+ */
+int phylink_set_fixed_link(struct phylink *pl,
+ const struct phylink_link_state *state)
+{
+ const struct phy_setting *s;
+ unsigned long *adv;
+
+ if (pl->cfg_link_an_mode != MLO_AN_PHY || !state ||
+ !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
+ return -EINVAL;
+
+ s = phy_lookup_setting(state->speed, state->duplex,
+ pl->supported, true);
+ if (!s)
+ return -EINVAL;
+
+ adv = pl->link_config.advertising;
+ linkmode_zero(adv);
+ linkmode_set_bit(s->bit, adv);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv);
+
+ pl->link_config.speed = state->speed;
+ pl->link_config.duplex = state->duplex;
+ pl->link_config.link = 1;
+ pl->link_config.an_complete = 1;
+
+ pl->cfg_link_an_mode = MLO_AN_FIXED;
+ pl->req_link_an_mode = pl->cfg_link_an_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+
+/**
* phylink_create() - create a phylink instance
* @config: a pointer to the target &struct phylink_config
* @fwnode: a pointer to a &struct fwnode_handle describing the network
@@ -1883,7 +1953,6 @@ struct phylink *phylink_create(struct phylink_config *config,
phy_interface_t iface,
const struct phylink_mac_ops *mac_ops)
{
- bool using_mac_select_pcs = false;
struct phylink *pl;
int ret;
@@ -1894,11 +1963,6 @@ struct phylink *phylink_create(struct phylink_config *config,
return ERR_PTR(-EINVAL);
}
- if (mac_ops->mac_select_pcs &&
- mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
- ERR_PTR(-EOPNOTSUPP))
- using_mac_select_pcs = true;
-
pl = kzalloc(sizeof(*pl), GFP_KERNEL);
if (!pl)
return ERR_PTR(-ENOMEM);
@@ -1917,7 +1981,16 @@ struct phylink *phylink_create(struct phylink_config *config,
return ERR_PTR(-EINVAL);
}
- pl->using_mac_select_pcs = using_mac_select_pcs;
+ pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops);
+ pl->mac_supports_eee = pl->mac_supports_eee_ops &&
+ pl->config->lpi_capabilities &&
+ !phy_interface_empty(pl->config->lpi_interfaces);
+
+ /* Set the default EEE configuration */
+ pl->eee_cfg.eee_enabled = pl->config->eee_enabled_default;
+ pl->eee_cfg.tx_lpi_enabled = pl->eee_cfg.eee_enabled;
+ pl->eee_cfg.tx_lpi_timer = pl->config->lpi_timer_default;
+
pl->phy_state.interface = iface;
pl->link_interface = iface;
if (iface == PHY_INTERFACE_MODE_MOCA)
@@ -1937,16 +2010,13 @@ struct phylink *phylink_create(struct phylink_config *config,
linkmode_copy(pl->link_config.advertising, pl->supported);
phylink_validate(pl, pl->supported, &pl->link_config);
- /* Set the default EEE configuration */
- pl->eee_cfg = pl->config->eee;
-
ret = phylink_parse_mode(pl, fwnode);
if (ret < 0) {
kfree(pl);
return ERR_PTR(ret);
}
- if (phylink_mode_fixed(pl->cfg_link_an_mode)) {
+ if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
ret = phylink_parse_fixedlink(pl, fwnode);
if (ret < 0) {
kfree(pl);
@@ -1955,7 +2025,6 @@ struct phylink *phylink_create(struct phylink_config *config,
}
pl->req_link_an_mode = pl->cfg_link_an_mode;
- pl->act_link_an_mode = pl->req_link_an_mode;
ret = phylink_register_sfp(pl, fwnode);
if (ret < 0) {
@@ -2024,16 +2093,24 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)
pl->phy_state.pause |= MLO_PAUSE_RX;
pl->phy_state.interface = phydev->interface;
pl->phy_state.link = up;
+ if (!up)
+ pl->link_failed = true;
+
+ /* Get the LPI state from phylib */
+ pl->phy_enable_tx_lpi = phydev->enable_tx_lpi;
+ pl->mac_tx_lpi_timer = phydev->eee_cfg.tx_lpi_timer;
mutex_unlock(&pl->state_mutex);
phylink_run_resolve(pl);
- phylink_dbg(pl, "phy link %s %s/%s/%s/%s/%s\n", up ? "up" : "down",
+ phylink_dbg(pl, "phy link %s %s/%s/%s/%s/%s/%slpi\n",
+ up ? "up" : "down",
phy_modes(phydev->interface),
phy_speed_to_str(phydev->speed),
phy_duplex_to_str(phydev->duplex),
phy_rate_matching_to_str(phydev->rate_matching),
- phylink_pause_to_str(pl->phy_state.pause));
+ phylink_pause_to_str(pl->phy_state.pause),
+ phydev->enable_tx_lpi ? "" : "no");
}
static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
@@ -2167,8 +2244,31 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
/* If the MAC supports phylink managed EEE, restrict the EEE
* advertisement according to the MAC's LPI capabilities.
*/
- if (phylink_mac_supports_eee(pl))
- phylink_phy_restrict_eee(pl, phy);
+ if (pl->mac_supports_eee) {
+ /* If EEE is enabled, then we need to call phy_support_eee()
+ * to ensure that the advertising mask is appropriately set.
+ * This also enables EEE at the PHY.
+ */
+ if (pl->eee_cfg.eee_enabled)
+ phy_support_eee(phy);
+
+ phy->eee_cfg.tx_lpi_enabled = pl->eee_cfg.tx_lpi_enabled;
+ phy->eee_cfg.tx_lpi_timer = pl->eee_cfg.tx_lpi_timer;
+
+ /* Convert the MAC's LPI capabilities to linkmodes */
+ linkmode_zero(pl->supported_lpi);
+ phylink_caps_to_linkmodes(pl->supported_lpi,
+ pl->config->lpi_capabilities);
+
+ /* Restrict the PHYs EEE support/advertisement to the modes
+ * that the MAC supports.
+ */
+ linkmode_and(phy->advertising_eee, phy->advertising_eee,
+ pl->supported_lpi);
+ } else if (pl->mac_supports_eee_ops) {
+ /* MAC supports phylink EEE, but wants EEE always disabled. */
+ phy_disable_eee(phy);
+ }
mutex_unlock(&pl->state_mutex);
mutex_unlock(&phy->lock);
@@ -2185,21 +2285,39 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
if (pl->config->mac_managed_pm)
phy->mac_managed_pm = true;
- return 0;
+ /* Allow the MAC to stop its clock if the PHY has the capability */
+ pl->mac_tx_clk_stop = phy_eee_tx_clock_stop_capable(phy) > 0;
+
+ if (pl->mac_supports_eee_ops) {
+ /* Explicitly configure whether the PHY is allowed to stop its
+ * receive clock.
+ */
+ ret = phy_eee_rx_clock_stop(phy,
+ pl->config->eee_rx_clk_stop_enable);
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+ }
+
+ return ret;
}
static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
phy_interface_t interface)
{
- if (WARN_ON(phylink_mode_fixed(pl->cfg_link_an_mode) ||
- (phylink_mode_inband(pl->cfg_link_an_mode) &&
+ u32 flags = 0;
+
+ if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED ||
+ (pl->cfg_link_an_mode == MLO_AN_INBAND &&
phy_interface_mode_is_8023z(interface) && !pl->sfp_bus)))
return -EINVAL;
if (pl->phydev)
return -EBUSY;
- return phy_attach_direct(pl->netdev, phy, 0, interface);
+ if (pl->config->mac_requires_rxc)
+ flags |= PHY_F_RXC_ALWAYS_ON;
+
+ return phy_attach_direct(pl->netdev, phy, flags, interface);
}
/**
@@ -2278,14 +2396,14 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
int ret;
/* Fixed links and 802.3z are handled without needing a PHY */
- if (phylink_mode_fixed(pl->cfg_link_an_mode) ||
- (phylink_mode_inband(pl->cfg_link_an_mode) &&
+ if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
+ (pl->cfg_link_an_mode == MLO_AN_INBAND &&
phy_interface_mode_is_8023z(pl->link_interface)))
return 0;
phy_fwnode = fwnode_get_phy_node(fwnode);
if (IS_ERR(phy_fwnode)) {
- if (phylink_mode_phy(pl->cfg_link_an_mode))
+ if (pl->cfg_link_an_mode == MLO_AN_PHY)
return -ENODEV;
return 0;
}
@@ -2302,6 +2420,9 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
pl->link_config.interface = pl->link_interface;
}
+ if (pl->config->mac_requires_rxc)
+ flags |= PHY_F_RXC_ALWAYS_ON;
+
ret = phy_attach_direct(pl->netdev, phy_dev, flags,
pl->link_interface);
phy_device_free(phy_dev);
@@ -2334,6 +2455,8 @@ void phylink_disconnect_phy(struct phylink *pl)
mutex_lock(&phy->lock);
mutex_lock(&pl->state_mutex);
pl->phydev = NULL;
+ pl->phy_enable_tx_lpi = false;
+ pl->mac_tx_clk_stop = false;
mutex_unlock(&pl->state_mutex);
mutex_unlock(&phy->lock);
flush_work(&pl->resolve);
@@ -2346,7 +2469,7 @@ EXPORT_SYMBOL_GPL(phylink_disconnect_phy);
static void phylink_link_changed(struct phylink *pl, bool up, const char *what)
{
if (!up)
- pl->mac_link_dropped = true;
+ pl->link_failed = true;
phylink_run_resolve(pl);
phylink_dbg(pl, "%s link %s\n", what, up ? "up" : "down");
}
@@ -2432,7 +2555,7 @@ void phylink_start(struct phylink *pl)
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
- if (phylink_mode_fixed(pl->cfg_link_an_mode) && pl->link_gpio) {
+ if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
int irq = gpiod_to_irq(pl->link_gpio);
if (irq > 0) {
@@ -2512,7 +2635,7 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
{
ASSERT_RTNL();
- if (mac_wol && (!pl->netdev || pl->netdev->wol_enabled)) {
+ if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) {
/* Wake-on-Lan enabled, MAC handling */
mutex_lock(&pl->state_mutex);
@@ -2539,6 +2662,30 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
EXPORT_SYMBOL_GPL(phylink_suspend);
/**
+ * phylink_prepare_resume() - prepare to resume a network device
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ *
+ * Optional, thus must be called prior to phylink_resume().
+ *
+ * Prepare to resume a network device, preparing the PHY as necessary.
+ */
+void phylink_prepare_resume(struct phylink *pl)
+{
+ ASSERT_RTNL();
+
+ /* If the MAC requires the receive clock, but receive clock
+ * stop was enabled at the PHY, we need to ensure that the
+ * receive clock is running. Disable receive clock stop.
+ * phylink_resume() will re-enable it if necessary.
+ */
+ if (pl->mac_supports_eee_ops && pl->phydev &&
+ pl->config->mac_requires_rxc &&
+ pl->config->eee_rx_clk_stop_enable)
+ phy_eee_rx_clock_stop(pl->phydev, false);
+}
+EXPORT_SYMBOL_GPL(phylink_prepare_resume);
+
+/**
* phylink_resume() - handle a network device resume event
* @pl: a pointer to a &struct phylink returned from phylink_create()
*
@@ -2547,8 +2694,22 @@ EXPORT_SYMBOL_GPL(phylink_suspend);
*/
void phylink_resume(struct phylink *pl)
{
+ int ret;
+
ASSERT_RTNL();
+ if (pl->mac_supports_eee_ops && pl->phydev) {
+ /* Explicitly configure whether the PHY is allowed to stop its
+ * receive clock on resume to ensure that it is correctly
+ * configured.
+ */
+ ret = phy_eee_rx_clock_stop(pl->phydev,
+ pl->config->eee_rx_clk_stop_enable);
+ if (ret == -EOPNOTSUPP)
+ phylink_warn(pl, "failed to set rx clock stop: %pe\n",
+ ERR_PTR(ret));
+ }
+
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Wake-on-Lan enabled, MAC handling */
@@ -2636,7 +2797,7 @@ static phy_interface_t phylink_sfp_select_interface(struct phylink *pl,
if (!test_bit(interface, pl->config->supported_interfaces)) {
phylink_err(pl,
- "selection of interface failed - SFP selected %s (%u) but MAC supports %*pbl\n",
+ "selection of interface failed, SFP selected %s (%u) but MAC supports %*pbl\n",
phy_modes(interface), interface,
(int)PHY_INTERFACE_MODE_MAX,
pl->config->supported_interfaces);
@@ -2729,7 +2890,7 @@ static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl,
phy_interface_t interface,
unsigned long *adv)
{
- unsigned int inband = phylink_query_inband(pl, interface);
+ unsigned int inband = phylink_inband_caps(pl, interface);
unsigned int mask;
/* If the PCS doesn't implement inband support, be permissive. */
@@ -2806,7 +2967,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 (phylink_mode_fixed(pl->req_link_an_mode)) {
+ if (pl->req_link_an_mode == MLO_AN_FIXED) {
if (s->speed != pl->link_config.speed ||
s->duplex != pl->link_config.duplex)
return -EINVAL;
@@ -2822,7 +2983,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 (phylink_mode_fixed(pl->req_link_an_mode)) {
+ if (pl->req_link_an_mode == MLO_AN_FIXED) {
if (!linkmode_equal(config.advertising,
pl->link_config.advertising))
return -EINVAL;
@@ -2964,7 +3125,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
ASSERT_RTNL();
- if (phylink_mode_fixed(pl->req_link_an_mode))
+ if (pl->req_link_an_mode == MLO_AN_FIXED)
return -EOPNOTSUPP;
if (!phylink_test(pl->supported, Pause) &&
@@ -3028,7 +3189,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
* link will cycle.
*/
if (manual_changed) {
- pl->mac_link_dropped = true;
+ pl->link_failed = true;
phylink_run_resolve(pl);
}
@@ -3072,8 +3233,6 @@ int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
if (pl->phydev)
ret = phy_init_eee(pl->phydev, clk_stop_enable);
- else if (pl->sfp_bus && phylink_mac_supports_eee(pl))
- ret = 0;
return ret;
}
@@ -3082,26 +3241,23 @@ EXPORT_SYMBOL_GPL(phylink_init_eee);
/**
* phylink_ethtool_get_eee() - read the energy efficient ethernet parameters
* @pl: a pointer to a &struct phylink returned from phylink_create()
- * @eee: a pointer to a &struct ethtool_eee for the read parameters
+ * @eee: a pointer to a &struct ethtool_keee for the read parameters
*/
-int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee)
+int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee)
{
int ret = -EOPNOTSUPP;
ASSERT_RTNL();
+ if (pl->mac_supports_eee_ops && !pl->mac_supports_eee)
+ return ret;
+
if (pl->phydev) {
ret = phy_ethtool_get_eee(pl->phydev, eee);
- } else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) {
- /* For optical SFPs, ensure that eee->supported is nonzero. */
- eee->supported = SUPPORTED_FIBRE;
- ret = 0;
- }
-
- if (!ret && phylink_mac_supports_eee(pl)) {
- /* Overwrite phylib's interpretation of configuration */
- eeecfg_to_eee(&pl->eee_cfg, eee);
- eee->eee_active = pl->eee_active;
+ /* Restrict supported linkmode mask */
+ if (ret == 0 && pl->mac_supports_eee_ops)
+ linkmode_and(eee->supported, eee->supported,
+ pl->supported_lpi);
}
return ret;
@@ -3111,56 +3267,32 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);
/**
* phylink_ethtool_set_eee() - set the energy efficient ethernet parameters
* @pl: a pointer to a &struct phylink returned from phylink_create()
- * @eee: a pointer to a &struct ethtool_eee for the desired parameters
+ * @eee: a pointer to a &struct ethtool_keee for the desired parameters
*/
-int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee)
+int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee)
{
+ bool mac_eee = pl->mac_supports_eee;
int ret = -EOPNOTSUPP;
- bool mac_eee;
ASSERT_RTNL();
- mac_eee = phylink_mac_supports_eee(pl);
-
- phylink_dbg(pl, "mac %s phylink EEE%s, adv 0x%08x, LPI%s timer %uus\n",
+ phylink_dbg(pl, "mac %s phylink EEE%s, adv %*pbl, LPI%s timer %uus\n",
mac_eee ? "supports" : "does not support",
- eee->eee_enabled ? ", enabled" : "", eee->advertised,
+ eee->eee_enabled ? ", enabled" : "",
+ __ETHTOOL_LINK_MODE_MASK_NBITS, eee->advertised,
eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer);
- /* Clamp the LPI timer maximum value */
- if (mac_eee && eee->tx_lpi_timer > pl->config->lpi_timer_limit_us) {
- eee->tx_lpi_timer = pl->config->lpi_timer_limit_us;
- phylink_dbg(pl, "LPI timer limited to %uus\n",
- eee->tx_lpi_timer);
- }
+ if (pl->mac_supports_eee_ops && !mac_eee)
+ return ret;
- if (pl->phydev)
+ if (pl->phydev) {
+ /* Restrict advertisement mask */
+ if (pl->mac_supports_eee_ops)
+ linkmode_and(eee->advertised, eee->advertised,
+ pl->supported_lpi);
ret = phy_ethtool_set_eee(pl->phydev, eee);
- else if (pl->sfp_bus && phylink_mac_supports_eee(pl))
- ret = 0;
-
- if (!ret && mac_eee) {
- bool can_lpi, old_can_lpi;
-
- mutex_lock(&pl->state_mutex);
- old_can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg);
- eee_to_eeecfg(eee, &pl->eee_cfg);
- can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg);
-
- phylink_dbg(pl, "can_lpi %u -> %u\n", old_can_lpi, can_lpi);
-
- /* If the link is up, and the configuration changes the
- * LPI permissive state, deal with the change at the MAC.
- */
- if (phylink_link_is_up(pl) && old_can_lpi != can_lpi) {
- phylink_dbg(pl, "link is up, lpi changed\n");
- if (can_lpi)
- phylink_activate_eee(pl);
- else
- phylink_deactivate_eee(pl);
- }
-
- mutex_unlock(&pl->state_mutex);
+ if (ret == 0)
+ eee_to_eeecfg(&pl->eee_cfg, eee);
}
return ret;
@@ -3477,12 +3609,11 @@ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
return interface;
}
-static void phylink_sfp_set_config(struct phylink *pl,
- unsigned long *supported,
- struct phylink_link_state *state)
+static void phylink_sfp_set_config(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state,
+ bool changed)
{
u8 mode = MLO_AN_INBAND;
- bool changed = false;
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
phylink_an_mode_str(mode), phy_modes(state->interface),
@@ -3559,7 +3690,7 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)
pl->link_port = pl->sfp_port;
- phylink_sfp_set_config(pl, support, &config);
+ phylink_sfp_set_config(pl, support, &config, true);
return 0;
}
@@ -3634,7 +3765,7 @@ static int phylink_sfp_config_optical(struct phylink *pl)
pl->link_port = pl->sfp_port;
- phylink_sfp_set_config(pl, pl->sfp_support, &config);
+ phylink_sfp_set_config(pl, pl->sfp_support, &config, false);
return 0;
}
@@ -3712,6 +3843,13 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
phy_interface_t interface;
int ret;
+ if (!phy->drv) {
+ phylink_err(pl, "PHY %s (id 0x%.8lx) has no driver loaded\n",
+ phydev_name(phy), (unsigned long)phy->phy_id);
+ phylink_err(pl, "Drivers which handle known common cases: CONFIG_BCM84881_PHY, CONFIG_MARVELL_PHY\n");
+ return -EINVAL;
+ }
+
/*
* This is the new way of dealing with flow control for PHYs,
* as described by Timur Tabi in commit 529ed1275263 ("net: phy:
@@ -3776,7 +3914,8 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
return 0;
}
-static void phylink_sfp_disconnect_phy(void *upstream)
+static void phylink_sfp_disconnect_phy(void *upstream,
+ struct phy_device *phydev)
{
phylink_disconnect_phy(upstream);
}
@@ -3973,6 +4112,7 @@ static void phylink_decode_usgmii_word(struct phylink_link_state *state,
/**
* phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers
* @state: a pointer to a &struct phylink_link_state.
+ * @neg_mode: link negotiation mode (PHYLINK_PCS_NEG_xxx)
* @bmsr: The value of the %MII_BMSR register
* @lpa: The value of the %MII_LPA register
*
@@ -3985,32 +4125,45 @@ static void phylink_decode_usgmii_word(struct phylink_link_state *state,
* accessing @bmsr and @lpa cannot be done with MDIO directly.
*/
void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
- u16 bmsr, u16 lpa)
+ unsigned int neg_mode, u16 bmsr, u16 lpa)
{
state->link = !!(bmsr & BMSR_LSTATUS);
state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
- /* If there is no link or autonegotiation is disabled, the LP advertisement
- * data is not meaningful, so don't go any further.
- */
- if (!state->link || !linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
- state->advertising))
+
+ /* If the link is down, the advertisement data is undefined. */
+ if (!state->link)
return;
switch (state->interface) {
case PHY_INTERFACE_MODE_1000BASEX:
- phylink_decode_c37_word(state, lpa, SPEED_1000);
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
+ phylink_decode_c37_word(state, lpa, SPEED_1000);
+ } else {
+ state->speed = SPEED_1000;
+ state->duplex = DUPLEX_FULL;
+ state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
+ }
break;
case PHY_INTERFACE_MODE_2500BASEX:
- phylink_decode_c37_word(state, lpa, SPEED_2500);
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
+ phylink_decode_c37_word(state, lpa, SPEED_2500);
+ } else {
+ state->speed = SPEED_2500;
+ state->duplex = DUPLEX_FULL;
+ state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
+ }
break;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
- phylink_decode_sgmii_word(state, lpa);
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ phylink_decode_sgmii_word(state, lpa);
break;
+
case PHY_INTERFACE_MODE_QUSGMII:
- phylink_decode_usgmii_word(state, lpa);
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ phylink_decode_usgmii_word(state, lpa);
break;
default:
@@ -4023,6 +4176,7 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state);
/**
* phylink_mii_c22_pcs_get_state() - read the MAC PCS state
* @pcs: a pointer to a &struct mdio_device.
+ * @neg_mode: link negotiation mode (PHYLINK_PCS_NEG_xxx)
* @state: a pointer to a &struct phylink_link_state.
*
* Helper for MAC PCS supporting the 802.3 clause 22 register set for
@@ -4035,6 +4189,7 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state);
* structure.
*/
void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
+ unsigned int neg_mode,
struct phylink_link_state *state)
{
int bmsr, lpa;
@@ -4046,7 +4201,7 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
return;
}
- phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
}
EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state);