}
 EXPORT_SYMBOL_GPL(phylink_generic_validate);
 
+static int phylink_validate_mac_and_pcs(struct phylink *pl,
+                                       unsigned long *supported,
+                                       struct phylink_link_state *state)
+{
+       struct phylink_pcs *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);
+       }
+
+       pl->mac_ops->validate(pl->config, supported, state);
+
+       return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+}
+
 static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
                                struct phylink_link_state *state)
 {
 
                        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);
+                       if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+                               linkmode_or(all_s, all_s, s);
+                               linkmode_or(all_adv, all_adv, t.advertising);
+                       }
                }
        }
 
                        return -EINVAL;
        }
 
-       pl->mac_ops->validate(pl->config, supported, state);
-
-       return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+       return phylink_validate_mac_and_pcs(pl, supported, state);
 }
 
 static int phylink_parse_fixedlink(struct phylink *pl,
 static void phylink_major_config(struct phylink *pl, bool restart,
                                  const struct phylink_link_state *state)
 {
+       struct phylink_pcs *pcs = NULL;
        int err;
 
        phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
 
+       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,
+                                   "mac_select_pcs unexpectedly failed: %pe\n",
+                                   pcs);
+                       return;
+               }
+       }
+
        if (pl->mac_ops->mac_prepare) {
                err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
                                               state->interface);
                }
        }
 
+       /* If we have a new PCS, switch to the new PCS after preparing the MAC
+        * for the change.
+        */
+       if (pcs)
+               phylink_set_pcs(pl, pcs);
+
        phylink_mac_config(pl, state);
 
        if (pl->pcs_ops) {
        struct phylink *pl;
        int ret;
 
+       /* Validate the supplied configuration */
+       if (mac_ops->mac_select_pcs &&
+           phy_interface_empty(config->supported_interfaces)) {
+               dev_err(config->dev,
+                       "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
+               return ERR_PTR(-EINVAL);
+       }
+
        pl = kzalloc(sizeof(*pl), GFP_KERNEL);
        if (!pl)
                return ERR_PTR(-ENOMEM);
  * @pl: a pointer to a &struct phylink returned from phylink_create()
  * @pcs: a pointer to the &struct phylink_pcs
  *
- * Bind the MAC PCS to phylink.  This may be called after phylink_create(),
- * in mac_prepare() or mac_config() methods if it is desired to dynamically
- * change the PCS.
+ * Bind the MAC PCS to phylink.  This may be called after phylink_create().
+ * If it is desired to dynamically change the PCS, then the preferred method
+ * is to use mac_select_pcs(), but it may also be called in mac_prepare()
+ * or mac_config().
  *
  * Please note that there are behavioural changes with the mac_config()
  * callback if a PCS is present (denoting a newer setup) so removing a PCS
 {
        pl->pcs = pcs;
        pl->pcs_ops = pcs->ops;
+
+       if (!pl->phylink_disable_state &&
+           pl->cfg_link_an_mode == MLO_AN_INBAND) {
+               if (pl->config->pcs_poll || pcs->poll)
+                       mod_timer(&pl->link_poll, jiffies + HZ);
+               else
+                       del_timer(&pl->link_poll);
+       }
 }
 EXPORT_SYMBOL_GPL(phylink_set_pcs);
 
 
 /**
  * struct phylink_mac_ops - MAC operations structure.
  * @validate: Validate and update the link configuration.
+ * @mac_select_pcs: Select a PCS for the interface mode.
  * @mac_pcs_get_state: Read the current link state from the hardware.
  * @mac_prepare: prepare for a major reconfiguration of the interface.
  * @mac_config: configure the MAC for the selected mode and state.
        void (*validate)(struct phylink_config *config,
                         unsigned long *supported,
                         struct phylink_link_state *state);
+       struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config,
+                                             phy_interface_t interface);
        void (*mac_pcs_get_state)(struct phylink_config *config,
                                  struct phylink_link_state *state);
        int (*mac_prepare)(struct phylink_config *config, unsigned int mode,
  */
 void validate(struct phylink_config *config, unsigned long *supported,
              struct phylink_link_state *state);
+/**
+ * mac_select_pcs: Select a PCS for the interface mode.
+ * @config: a pointer to a &struct phylink_config.
+ * @interface: PHY interface mode for PCS
+ *
+ * Return the &struct phylink_pcs for the specified interface mode, or
+ * NULL if none is required, or an error pointer on error.
+ *
+ * This must not modify any state. It is used to query which PCS should
+ * be used. Phylink will use this during validation to ensure that the
+ * configuration is valid, and when setting a configuration to internally
+ * set the PCS that will be used.
+ */
+struct phylink_pcs *mac_select_pcs(struct phylink_config *config,
+                                  phy_interface_t interface);
 
 /**
  * mac_pcs_get_state() - Read the current inband link state from the hardware