net: phylink: add pcs_enable()/pcs_disable() methods
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Thu, 13 Jul 2023 08:42:07 +0000 (09:42 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 14 Jul 2023 07:51:47 +0000 (08:51 +0100)
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>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phylink.c
include/linux/phylink.h

index d0aaa5c..748c62e 100644 (file)
@@ -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 phylink_link_state phy_state;
        struct work_struct resolve;
        unsigned int pcs_neg_mode;
+       unsigned int pcs_state;
 
        bool mac_link_dropped;
        bool using_mac_select_pcs;
@@ -993,6 +998,22 @@ static void phylink_resolve_an_pause(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 int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                              const struct phylink_link_state *state,
                              bool permit_pause_to_mac)
@@ -1095,11 +1116,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);
+
        neg_mode = pl->cur_link_an_mode;
        if (pl->pcs && pl->pcs->neg_mode)
                neg_mode = pl->pcs_neg_mode;
@@ -1586,6 +1613,7 @@ struct phylink *phylink_create(struct phylink_config *config,
        pl->link_config.pause = MLO_PAUSE_AN;
        pl->link_config.speed = SPEED_UNKNOWN;
        pl->link_config.duplex = DUPLEX_UNKNOWN;
+       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);
@@ -1987,6 +2015,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.
@@ -1997,6 +2027,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) {
@@ -2015,15 +2047,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)
@@ -2060,6 +2086,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);
 
index 1817940..8e2fdd4 100644 (file)
@@ -535,6 +535,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.
@@ -544,6 +546,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 neg_mode,
@@ -574,6 +578,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.