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.c142
1 files changed, 88 insertions, 54 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 0a0abe8e4be0..3ad7397b8119 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -132,6 +132,17 @@ void phylink_set_port_modes(unsigned long *mask)
}
EXPORT_SYMBOL_GPL(phylink_set_port_modes);
+void phylink_set_10g_modes(unsigned long *mask)
+{
+ phylink_set(mask, 10000baseT_Full);
+ phylink_set(mask, 10000baseCR_Full);
+ phylink_set(mask, 10000baseSR_Full);
+ phylink_set(mask, 10000baseLR_Full);
+ phylink_set(mask, 10000baseLRM_Full);
+ phylink_set(mask, 10000baseER_Full);
+}
+EXPORT_SYMBOL_GPL(phylink_set_10g_modes);
+
static int phylink_is_empty_linkmode(const unsigned long *linkmode)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, };
@@ -155,9 +166,45 @@ static const char *phylink_an_mode_str(unsigned int mode)
return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
}
+static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
+ struct phylink_link_state t;
+ int intf;
+
+ for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
+ if (test_bit(intf, pl->config->supported_interfaces)) {
+ linkmode_copy(s, supported);
+
+ t = *state;
+ t.interface = intf;
+ pl->mac_ops->validate(pl->config, s, &t);
+ linkmode_or(all_s, all_s, s);
+ linkmode_or(all_adv, all_adv, t.advertising);
+ }
+ }
+
+ linkmode_copy(supported, all_s);
+ linkmode_copy(state->advertising, all_adv);
+
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+}
+
static int phylink_validate(struct phylink *pl, unsigned long *supported,
struct phylink_link_state *state)
{
+ if (!phy_interface_empty(pl->config->supported_interfaces)) {
+ if (state->interface == PHY_INTERFACE_MODE_NA)
+ return phylink_validate_any(pl, supported, state);
+
+ if (!test_bit(state->interface,
+ pl->config->supported_interfaces))
+ return -EINVAL;
+ }
+
pl->mac_ops->validate(pl->config, supported, state);
return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
@@ -540,9 +587,15 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
linkmode_zero(state->lp_advertising);
state->interface = pl->link_config.interface;
state->an_enabled = pl->link_config.an_enabled;
- state->speed = SPEED_UNKNOWN;
- state->duplex = DUPLEX_UNKNOWN;
- state->pause = MLO_PAUSE_NONE;
+ if (state->an_enabled) {
+ state->speed = SPEED_UNKNOWN;
+ state->duplex = DUPLEX_UNKNOWN;
+ state->pause = MLO_PAUSE_NONE;
+ } else {
+ state->speed = pl->link_config.speed;
+ state->duplex = pl->link_config.duplex;
+ state->pause = pl->link_config.pause;
+ }
state->an_complete = 0;
state->link = 1;
@@ -1333,7 +1386,10 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
* but one would hope all packets have been sent. This
* also means phylink_resolve() will do nothing.
*/
- netif_carrier_off(pl->netdev);
+ if (pl->netdev)
+ netif_carrier_off(pl->netdev);
+ else
+ pl->old_link_state = false;
/* We do not call mac_link_down() here as we want the
* link to remain up to receive the WoL packets.
@@ -1598,20 +1654,11 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
config.an_enabled);
- /* Validate without changing the current supported mask. */
- linkmode_copy(support, pl->supported);
- if (phylink_validate(pl, support, &config))
- return -EINVAL;
-
- /* If autonegotiation is enabled, we must have an advertisement */
- if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
- return -EINVAL;
-
/* If this link is with an SFP, ensure that changes to advertised modes
* also cause the associated interface to be selected such that the
* link can be configured correctly.
*/
- if (pl->sfp_port && pl->sfp_bus) {
+ if (pl->sfp_bus) {
config.interface = sfp_select_interface(pl->sfp_bus,
config.advertising);
if (config.interface == PHY_INTERFACE_MODE_NA) {
@@ -1631,8 +1678,17 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
return -EINVAL;
}
+ } else {
+ /* Validate without changing the current supported mask. */
+ linkmode_copy(support, pl->supported);
+ if (phylink_validate(pl, support, &config))
+ return -EINVAL;
}
+ /* If autonegotiation is enabled, we must have an advertisement */
+ if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
+ return -EINVAL;
+
mutex_lock(&pl->state_mutex);
pl->link_config.speed = config.speed;
pl->link_config.duplex = config.duplex;
@@ -1724,7 +1780,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
return -EOPNOTSUPP;
if (!phylink_test(pl->supported, Asym_Pause) &&
- !pause->autoneg && pause->rx_pause != pause->tx_pause)
+ pause->rx_pause != pause->tx_pause)
return -EINVAL;
pause_state = 0;
@@ -2522,12 +2578,10 @@ EXPORT_SYMBOL_GPL(phylink_decode_usxgmii_word);
void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
struct phylink_link_state *state)
{
- struct mii_bus *bus = pcs->bus;
- int addr = pcs->addr;
int bmsr, lpa;
- bmsr = mdiobus_read(bus, addr, MII_BMSR);
- lpa = mdiobus_read(bus, addr, MII_LPA);
+ bmsr = mdiodev_read(pcs, MII_BMSR);
+ lpa = mdiodev_read(pcs, MII_LPA);
if (bmsr < 0 || lpa < 0) {
state->link = false;
return;
@@ -2535,7 +2589,10 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
state->link = !!(bmsr & BMSR_LSTATUS);
state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
- if (!state->link)
+ /* 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 || !state->an_enabled)
return;
switch (state->interface) {
@@ -2580,9 +2637,6 @@ int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs,
phy_interface_t interface,
const unsigned long *advertising)
{
- struct mii_bus *bus = pcs->bus;
- int addr = pcs->addr;
- int val, ret;
u16 adv;
switch (interface) {
@@ -2596,32 +2650,10 @@ int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs,
advertising))
adv |= ADVERTISE_1000XPSE_ASYM;
- val = mdiobus_read(bus, addr, MII_ADVERTISE);
- if (val < 0)
- return val;
-
- if (val == adv)
- return 0;
-
- ret = mdiobus_write(bus, addr, MII_ADVERTISE, adv);
- if (ret < 0)
- return ret;
-
- return 1;
+ return mdiodev_modify_changed(pcs, MII_ADVERTISE, 0xffff, adv);
case PHY_INTERFACE_MODE_SGMII:
- val = mdiobus_read(bus, addr, MII_ADVERTISE);
- if (val < 0)
- return val;
-
- if (val == 0x0001)
- return 0;
-
- ret = mdiobus_write(bus, addr, MII_ADVERTISE, 0x0001);
- if (ret < 0)
- return ret;
-
- return 1;
+ return mdiodev_modify_changed(pcs, MII_ADVERTISE, 0xffff, 0x0001);
default:
/* Nothing to do for other modes */
@@ -2658,9 +2690,13 @@ int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
changed = ret > 0;
/* Ensure ISOLATE bit is disabled */
- bmcr = mode == MLO_AN_INBAND ? BMCR_ANENABLE : 0;
- ret = mdiobus_modify(pcs->bus, pcs->addr, MII_BMCR,
- BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
+ if (mode == MLO_AN_INBAND &&
+ linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising))
+ bmcr = BMCR_ANENABLE;
+ else
+ bmcr = 0;
+
+ ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
if (ret < 0)
return ret;
@@ -2681,14 +2717,12 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_config);
*/
void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs)
{
- struct mii_bus *bus = pcs->bus;
- int val, addr = pcs->addr;
+ int val = mdiodev_read(pcs, MII_BMCR);
- val = mdiobus_read(bus, addr, MII_BMCR);
if (val >= 0) {
val |= BMCR_ANRESTART;
- mdiobus_write(bus, addr, MII_BMCR, val);
+ mdiodev_write(pcs, MII_BMCR, val);
}
}
EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_an_restart);