summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2021-10-23 10:27:03 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-06-18 12:12:04 +0100
commit255ce71ef1e22b3b529b91a27dcd73628accead8 (patch)
tree011c75b5fd3b30519e123b776012bb581e874d81
parentd5c852253c0ccedf29264eff020897afb118cb48 (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>
-rw-r--r--drivers/net/phy/phylink.c51
-rw-r--r--include/linux/phylink.h16
2 files changed, 57 insertions, 10 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 8d2eeff56df6..49615f89128e 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,
};
/**
@@ -71,6 +75,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;
bool using_mac_select_pcs;
@@ -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);
}
@@ -831,11 +853,17 @@ static void phylink_major_config(struct phylink *pl, bool restart,
/* If we have a new PCS, switch to the new PCS after preparing the MAC
* for the change.
*/
- if (pcs_changed)
+ if (pcs_changed) {
+ phylink_pcs_disable(pl->pcs);
+
pl->pcs = pcs;
+ }
phylink_mac_config(pl, state);
+ if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed)
+ phylink_pcs_enable(pl->pcs);
+
if (pl->pcs) {
err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
state->interface,
@@ -1270,6 +1298,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);
@@ -1636,6 +1665,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.
@@ -1646,6 +1677,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) {
@@ -1664,15 +1697,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)
@@ -1709,6 +1736,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);
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index e7bc8d4b306e..2fb19b576bda 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -429,6 +429,8 @@ struct phylink_pcs {
/**
* struct phylink_pcs_ops - MAC PCS operations structure.
* @pcs_validate: validate the link configuration.
+ * @pcs_enable: enable the PCS.
+ * @pcs_disable: disable the PCS.
* @pcs_get_state: read the current MAC PCS link state from the hardware.
* @pcs_config: configure the MAC PCS for the selected mode and state.
* @pcs_an_restart: restart 802.3z BaseX autonegotiation.
@@ -438,6 +440,8 @@ struct phylink_pcs {
struct phylink_pcs_ops {
int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
const struct phylink_link_state *state);
+ int (*pcs_enable)(struct phylink_pcs *pcs);
+ void (*pcs_disable)(struct phylink_pcs *pcs);
void (*pcs_get_state)(struct phylink_pcs *pcs,
struct phylink_link_state *state);
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
@@ -468,6 +472,18 @@ int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
const struct phylink_link_state *state);
/**
+ * pcs_enable() - enable the PCS.
+ * @pcs: a pointer to a &struct phylink_pcs.
+ */
+int pcs_enable(struct phylink_pcs *pcs);
+
+/**
+ * pcs_disable() - disable the PCS.
+ * @pcs: a pointer to a &struct phylink_pcs.
+ */
+void pcs_disable(struct phylink_pcs *pcs);
+
+/**
* pcs_get_state() - Read the current inband link state from the hardware
* @pcs: a pointer to a &struct phylink_pcs.
* @state: a pointer to a &struct phylink_link_state.