diff options
author | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2021-10-23 10:27:03 +0100 |
---|---|---|
committer | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2022-01-18 10:17:06 +0000 |
commit | 804f0d381ce1096fa648821a15a5a0e2e43fc29b (patch) | |
tree | daee537764fd18339740fb73d5d0e06206b4c073 /drivers | |
parent | 0d4ad731785a852965a011786192062d782ca01e (diff) |
net: phylink: add pcs_enable()/pcs_disable() methods
Add phylink PCS enable/disable callbacks that will allow us to place
IEEE 802.3 register compliant PCS in power-down mode while not being
used.
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/phy/phylink.c | 59 |
1 files changed, 49 insertions, 10 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index b975684049b5..5eda247c8303 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -34,6 +34,10 @@ enum { PHYLINK_DISABLE_STOPPED, PHYLINK_DISABLE_LINK, PHYLINK_DISABLE_MAC_WOL, + + PCS_STATE_DOWN = 0, + PCS_STATE_STARTING, + PCS_STATE_STARTED, }; /** @@ -72,6 +76,7 @@ struct phylink { struct mutex state_mutex; struct phylink_link_state phy_state; struct work_struct resolve; + unsigned int pcs_state; bool mac_link_dropped; @@ -756,6 +761,22 @@ static void phylink_resolve_flow(struct phylink_link_state *state) } } +static void phylink_pcs_disable(struct phylink_pcs *pcs) +{ + if (pcs && pcs->ops->pcs_disable) + pcs->ops->pcs_disable(pcs); +} + +static int phylink_pcs_enable(struct phylink_pcs *pcs) +{ + int err = 0; + + if (pcs && pcs->ops->pcs_enable) + err = pcs->ops->pcs_enable(pcs); + + return err; +} + static void phylink_pcs_poll_stop(struct phylink *pl) { if (pl->cfg_link_an_mode == MLO_AN_INBAND) @@ -764,7 +785,8 @@ static void phylink_pcs_poll_stop(struct phylink *pl) static void phylink_pcs_poll_start(struct phylink *pl) { - if (pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND) + if (pl->pcs && pl->pcs->poll && + pl->cfg_link_an_mode == MLO_AN_INBAND) mod_timer(&pl->link_poll, jiffies + HZ); } @@ -838,7 +860,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, phylink_mac_config(pl, state); - if (pcs_changed) { + if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) { phylink_pcs_enable(pl->pcs); phylink_pcs_poll_start(pl); } @@ -1268,6 +1290,7 @@ struct phylink *phylink_create(struct phylink_config *config, pl->link_config.speed = SPEED_UNKNOWN; pl->link_config.duplex = DUPLEX_UNKNOWN; pl->link_config.an_enabled = true; + pl->pcs_state = PCS_STATE_DOWN; pl->mac_ops = mac_ops; __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); timer_setup(&pl->link_poll, phylink_fixed_poll, 0); @@ -1322,6 +1345,20 @@ void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs) if (!pl->phylink_disable_state) phylink_pcs_poll_stop(pl); + if (pl->pcs != pcs) { + /* The PCS has changed */ + if (!pl->phylink_disable_state) { + /* Phylink is currently running. Disable the old PCS + * and enable the new PCS. + */ + phylink_pcs_disable(pl->pcs); + WARN_ON(phylink_pcs_enable(pcs)); + } else { + /* Phylink is currently stopped, disable the new PCS */ + phylink_pcs_disable(pcs); + } + } + pl->pcs = pcs; pl->pcs_ops = pcs->ops; @@ -1662,6 +1699,8 @@ void phylink_start(struct phylink *pl) if (pl->netdev) netif_carrier_off(pl->netdev); + pl->pcs_state = PCS_STATE_STARTING; + /* Apply the link configuration to the MAC when starting. This allows * a fixed-link to start with the correct parameters, and also * ensures that we set the appropriate advertisement for Serdes links. @@ -1672,6 +1711,8 @@ void phylink_start(struct phylink *pl) */ phylink_mac_initial_config(pl, true); + pl->pcs_state = PCS_STATE_STARTED; + phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { @@ -1690,15 +1731,9 @@ void phylink_start(struct phylink *pl) poll = true; } - switch (pl->cfg_link_an_mode) { - case MLO_AN_FIXED: + if (pl->cfg_link_an_mode == MLO_AN_FIXED) poll |= pl->config->poll_fixed_state; - break; - case MLO_AN_INBAND: - if (pl->pcs) - poll |= pl->pcs->poll; - break; - } + if (poll) mod_timer(&pl->link_poll, jiffies + HZ); if (pl->phydev) @@ -1735,6 +1770,10 @@ void phylink_stop(struct phylink *pl) } phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); + + pl->pcs_state = PCS_STATE_DOWN; + + phylink_pcs_disable(pl->pcs); } EXPORT_SYMBOL_GPL(phylink_stop); |