In order to support split PCS using PHYLINK properly, we need to add a
phylink_pcs_ops structure.

Note that a DSA driver that wants to use these needs to implement all 4
of them: the DSA core checks the presence of these 4 function pointers
in dsa_switch_ops and only then does it add a PCS to PHYLINK. This is
done in order to preserve compatibility with drivers that have not yet
been converted, or don't need, a split PCS setup.

Also, when pcs_get_state() and pcs_an_restart() are present, their mac
counterparts (mac_pcs_get_state(), mac_an_restart()) will no longer get
called, as can be seen in phylink.c.

Signed-off-by: Ioana Ciornei <ioana.cior...@nxp.com>
---
Changes in v3:
 * patch added

 include/net/dsa.h  | 12 ++++++++++++
 net/dsa/dsa_priv.h |  1 +
 net/dsa/port.c     | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 net/dsa/slave.c    |  6 ++++++
 4 files changed, 65 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 50389772c597..09aa36198f4b 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -446,6 +446,18 @@ struct dsa_switch_ops {
                                       bool tx_pause, bool rx_pause);
        void    (*phylink_fixed_state)(struct dsa_switch *ds, int port,
                                       struct phylink_link_state *state);
+       void    (*phylink_pcs_get_state)(struct dsa_switch *ds, int port,
+                                        struct phylink_link_state *state);
+       int     (*phylink_pcs_config)(struct dsa_switch *ds, int port,
+                                     unsigned int mode,
+                                     phy_interface_t interface,
+                                     const unsigned long *advertising);
+       void    (*phylink_pcs_an_restart)(struct dsa_switch *ds, int port);
+       void    (*phylink_pcs_link_up)(struct dsa_switch *ds, int port,
+                                      unsigned int mode,
+                                      phy_interface_t interface, int speed,
+                                      int duplex);
+
        /*
         * ethtool hardware statistics.
         */
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index adecf73bd608..de8e11796178 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -169,6 +169,7 @@ int dsa_port_vid_del(struct dsa_port *dp, u16 vid);
 int dsa_port_link_register_of(struct dsa_port *dp);
 void dsa_port_link_unregister_of(struct dsa_port *dp);
 extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
+extern const struct phylink_pcs_ops dsa_port_phylink_pcs_ops;
 
 /* slave.c */
 extern const struct dsa_device_ops notag_netdev_ops;
diff --git a/net/dsa/port.c b/net/dsa/port.c
index e23ece229c7e..a2b0460d2593 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -592,6 +592,52 @@ const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
        .mac_link_up = dsa_port_phylink_mac_link_up,
 };
 
+static void dsa_port_pcs_get_state(struct phylink_config *config,
+                                  struct phylink_link_state *state)
+{
+       struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+       struct dsa_switch *ds = dp->ds;
+
+       ds->ops->phylink_pcs_get_state(ds, dp->index, state);
+}
+
+static void dsa_port_pcs_an_restart(struct phylink_config *config)
+{
+       struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+       struct dsa_switch *ds = dp->ds;
+
+       ds->ops->phylink_pcs_an_restart(ds, dp->index);
+}
+
+static int dsa_port_pcs_config(struct phylink_config *config,
+                              unsigned int mode, phy_interface_t interface,
+                              const unsigned long *advertising)
+{
+       struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+       struct dsa_switch *ds = dp->ds;
+
+       return ds->ops->phylink_pcs_config(ds, dp->index, mode, interface,
+                                          advertising);
+}
+
+static void dsa_port_pcs_link_up(struct phylink_config *config,
+                                unsigned int mode, phy_interface_t interface,
+                                int speed, int duplex)
+{
+       struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+       struct dsa_switch *ds = dp->ds;
+
+       ds->ops->phylink_pcs_link_up(ds, dp->index, mode, interface, speed,
+                                    duplex);
+}
+
+const struct phylink_pcs_ops dsa_port_phylink_pcs_ops = {
+       .pcs_get_state = dsa_port_pcs_get_state,
+       .pcs_config = dsa_port_pcs_config,
+       .pcs_an_restart = dsa_port_pcs_an_restart,
+       .pcs_link_up = dsa_port_pcs_link_up,
+};
+
 static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
 {
        struct dsa_switch *ds = dp->ds;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 4c7f086a047b..8856e70f6a06 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1650,6 +1650,12 @@ static int dsa_slave_phy_setup(struct net_device 
*slave_dev)
        if (ds->ops->get_phy_flags)
                phy_flags = ds->ops->get_phy_flags(ds, dp->index);
 
+       if (ds->ops->phylink_pcs_get_state &&
+           ds->ops->phylink_pcs_an_restart &&
+           ds->ops->phylink_pcs_config &&
+           ds->ops->phylink_pcs_link_up)
+               phylink_add_pcs(dp->pl, &dsa_port_phylink_pcs_ops);
+
        ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags);
        if (ret == -ENODEV && ds->slave_mii_bus) {
                /* We could not connect to a designated PHY or SFP, so try to
-- 
2.25.1

Reply via email to