--- /dev/null
+From 38c310eb46f5f80213a92093af11af270c209a76 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 26 Oct 2021 11:06:06 +0100
+Subject: [PATCH] net: phylink: add MAC phy_interface_t bitmap
+
+Add a phy_interface_t bitmap so the MAC driver can specifiy which PHY
+interface modes it supports.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/phylink.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -76,6 +76,7 @@ struct phylink_config {
+ bool ovr_an_inband;
+ void (*get_fixed_state)(struct phylink_config *config,
+ struct phylink_link_state *state);
++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
+ };
+
+ /**
--- /dev/null
+From d25f3a74f30aace819163dfa54f2a4b8ca1dc932 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 26 Oct 2021 11:06:11 +0100
+Subject: [PATCH] net: phylink: use supported_interfaces for phylink
+ validation
+
+If the network device supplies a supported interface bitmap, we can use
+that during phylink's validation to simplify MAC drivers in two ways by
+using the supported_interfaces bitmap to:
+
+1. reject unsupported interfaces before calling into the MAC driver.
+2. generate the set of all supported link modes across all supported
+ interfaces (used mainly for SFP, but also some 10G PHYs.)
+
+Suggested-by: Sean Anderson <sean.anderson@seco.com>
+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 | 36 ++++++++++++++++++++++++++++++++++++
+ include/linux/phylink.h | 12 ++++++++++--
+ 2 files changed, 46 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -155,9 +155,45 @@ static const char *phylink_an_mode_str(u
+ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+
++static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
++ struct phylink_link_state t;
++ int intf;
++
++ for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
++ if (test_bit(intf, pl->config->supported_interfaces)) {
++ linkmode_copy(s, supported);
++
++ 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);
++ }
++ }
++
++ linkmode_copy(supported, all_s);
++ linkmode_copy(state->advertising, all_adv);
++
++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
++}
++
+ static int phylink_validate(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state)
+ {
++ if (!phy_interface_empty(pl->config->supported_interfaces)) {
++ if (state->interface == PHY_INTERFACE_MODE_NA)
++ return phylink_validate_any(pl, supported, state);
++
++ if (!test_bit(state->interface,
++ pl->config->supported_interfaces))
++ return -EINVAL;
++ }
++
+ pl->mac_ops->validate(pl->config, supported, state);
+
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -67,6 +67,8 @@ enum phylink_op_type {
+ * @ovr_an_inband: if true, override PCS to MLO_AN_INBAND
+ * @get_fixed_state: callback to execute to determine the fixed link state,
+ * if MAC link is at %MLO_AN_FIXED mode.
++ * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
++ * are supported by the MAC/PCS.
+ */
+ struct phylink_config {
+ struct device *dev;
+@@ -134,8 +136,14 @@ struct phylink_mac_ops {
+ * based on @state->advertising and/or @state->speed and update
+ * @state->interface accordingly. See phylink_helper_basex_speed().
+ *
+- * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink expects the
+- * MAC driver to return all supported link modes.
++ * When @config->supported_interfaces has been set, phylink will iterate
++ * over the supported interfaces to determine the full capability of the
++ * MAC. The validation function must not print errors if @state->interface
++ * is set to an unexpected value.
++ *
++ * When @config->supported_interfaces is empty, phylink will call this
++ * function with @state->interface set to %PHY_INTERFACE_MODE_NA, and
++ * expects the MAC driver to return all supported link modes.
+ *
+ * If the @state->interface mode is not supported, then the @supported
+ * mask must be cleared.
--- /dev/null
+From c07c6e8eb4b38bae921f9e2f108d1e7f8e14226e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Thu, 28 Oct 2021 18:00:14 +0100
+Subject: [PATCH] net: dsa: populate supported_interfaces member
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add a new DSA switch operation, phylink_get_interfaces, which should
+fill in which PHY_INTERFACE_MODE_* are supported by given port.
+
+Use this before phylink_create() to fill phylinks supported_interfaces
+member, allowing phylink to determine which PHY_INTERFACE_MODEs are
+supported.
+
+Signed-off-by: Marek BehĂºn <kabel@kernel.org>
+[tweaked patch and description to add more complete support -- rmk]
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 2 ++
+ net/dsa/port.c | 4 ++++
+ net/dsa/slave.c | 4 ++++
+ 3 files changed, 10 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -626,6 +626,8 @@ struct dsa_switch_ops {
+ /*
+ * PHYLINK integration
+ */
++ void (*phylink_get_interfaces)(struct dsa_switch *ds, int port,
++ unsigned long *supported_interfaces);
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1172,6 +1172,10 @@ static int dsa_port_phylink_register(str
+ dp->pl_config.type = PHYLINK_DEV;
+ dp->pl_config.pcs_poll = ds->pcs_poll;
+
++ if (ds->ops->phylink_get_interfaces)
++ ds->ops->phylink_get_interfaces(ds, dp->index,
++ dp->pl_config.supported_interfaces);
++
+ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
+ mode, &dsa_port_phylink_mac_ops);
+ if (IS_ERR(dp->pl)) {
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1837,6 +1837,10 @@ static int dsa_slave_phy_setup(struct ne
+ dp->pl_config.poll_fixed_state = true;
+ }
+
++ if (ds->ops->phylink_get_interfaces)
++ ds->ops->phylink_get_interfaces(ds, dp->index,
++ dp->pl_config.supported_interfaces);
++
+ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
+ &dsa_port_phylink_mac_ops);
+ if (IS_ERR(dp->pl)) {
--- /dev/null
+From 21bd64bd717dedac96f53b668144cbe37d3c12d4 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:09:55 +0000
+Subject: [PATCH] net: dsa: consolidate phylink creation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The code in port.c and slave.c creating the phylink instance is very
+similar - let's consolidate this into a single function.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Marek BehĂºn <kabel@kernel.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/dsa_priv.h | 2 +-
+ net/dsa/port.c | 44 ++++++++++++++++++++++++++++----------------
+ net/dsa/slave.c | 19 +++----------------
+ 3 files changed, 32 insertions(+), 33 deletions(-)
+
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -260,13 +260,13 @@ int dsa_port_mrp_add_ring_role(const str
+ const struct switchdev_obj_ring_role_mrp *mrp);
+ int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,
+ const struct switchdev_obj_ring_role_mrp *mrp);
++int dsa_port_phylink_create(struct dsa_port *dp);
+ int dsa_port_link_register_of(struct dsa_port *dp);
+ void dsa_port_link_unregister_of(struct dsa_port *dp);
+ int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);
+ void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr);
+ int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast);
+ void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast);
+-extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
+
+ static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp,
+ const struct net_device *dev)
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1076,7 +1076,7 @@ static void dsa_port_phylink_mac_link_up
+ speed, duplex, tx_pause, rx_pause);
+ }
+
+-const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
++static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
+ .validate = dsa_port_phylink_validate,
+ .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
+ .mac_config = dsa_port_phylink_mac_config,
+@@ -1085,6 +1085,30 @@ const struct phylink_mac_ops dsa_port_ph
+ .mac_link_up = dsa_port_phylink_mac_link_up,
+ };
+
++int dsa_port_phylink_create(struct dsa_port *dp)
++{
++ struct dsa_switch *ds = dp->ds;
++ phy_interface_t mode;
++ int err;
++
++ err = of_get_phy_mode(dp->dn, &mode);
++ if (err)
++ mode = PHY_INTERFACE_MODE_NA;
++
++ if (ds->ops->phylink_get_interfaces)
++ ds->ops->phylink_get_interfaces(ds, dp->index,
++ dp->pl_config.supported_interfaces);
++
++ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
++ mode, &dsa_port_phylink_mac_ops);
++ if (IS_ERR(dp->pl)) {
++ pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
++ return PTR_ERR(dp->pl);
++ }
++
++ return 0;
++}
++
+ static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
+ {
+ struct dsa_switch *ds = dp->ds;
+@@ -1161,27 +1185,15 @@ static int dsa_port_phylink_register(str
+ {
+ struct dsa_switch *ds = dp->ds;
+ struct device_node *port_dn = dp->dn;
+- phy_interface_t mode;
+ int err;
+
+- err = of_get_phy_mode(port_dn, &mode);
+- if (err)
+- mode = PHY_INTERFACE_MODE_NA;
+-
+ dp->pl_config.dev = ds->dev;
+ dp->pl_config.type = PHYLINK_DEV;
+ dp->pl_config.pcs_poll = ds->pcs_poll;
+
+- if (ds->ops->phylink_get_interfaces)
+- ds->ops->phylink_get_interfaces(ds, dp->index,
+- dp->pl_config.supported_interfaces);
+-
+- dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
+- mode, &dsa_port_phylink_mac_ops);
+- if (IS_ERR(dp->pl)) {
+- pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
+- return PTR_ERR(dp->pl);
+- }
++ err = dsa_port_phylink_create(dp);
++ if (err)
++ return err;
+
+ err = phylink_of_phy_connect(dp->pl, port_dn, 0);
+ if (err && err != -ENODEV) {
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1817,14 +1817,9 @@ static int dsa_slave_phy_setup(struct ne
+ struct dsa_port *dp = dsa_slave_to_port(slave_dev);
+ struct device_node *port_dn = dp->dn;
+ struct dsa_switch *ds = dp->ds;
+- phy_interface_t mode;
+ u32 phy_flags = 0;
+ int ret;
+
+- ret = of_get_phy_mode(port_dn, &mode);
+- if (ret)
+- mode = PHY_INTERFACE_MODE_NA;
+-
+ dp->pl_config.dev = &slave_dev->dev;
+ dp->pl_config.type = PHYLINK_NETDEV;
+
+@@ -1837,17 +1832,9 @@ static int dsa_slave_phy_setup(struct ne
+ dp->pl_config.poll_fixed_state = true;
+ }
+
+- if (ds->ops->phylink_get_interfaces)
+- ds->ops->phylink_get_interfaces(ds, dp->index,
+- dp->pl_config.supported_interfaces);
+-
+- dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
+- &dsa_port_phylink_mac_ops);
+- if (IS_ERR(dp->pl)) {
+- netdev_err(slave_dev,
+- "error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
+- return PTR_ERR(dp->pl);
+- }
++ ret = dsa_port_phylink_create(dp);
++ if (ret)
++ return ret;
+
+ if (ds->ops->get_phy_flags)
+ phy_flags = ds->ops->get_phy_flags(ds, dp->index);
--- /dev/null
+From 072eea6c22b2af680c3949e64f9adde278c71e68 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:10:01 +0000
+Subject: [PATCH] net: dsa: replace phylink_get_interfaces() with
+ phylink_get_caps()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Phylink needs slightly more information than phylink_get_interfaces()
+allows us to get from the DSA drivers - we need the MAC capabilities.
+Replace the phylink_get_interfaces() method with phylink_get_caps() to
+allow DSA drivers to fill in the phylink_config MAC capabilities field
+as well.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Marek BehĂºn <kabel@kernel.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/net/dsa.h | 4 ++--
+ net/dsa/port.c | 5 ++---
+ 2 files changed, 4 insertions(+), 5 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -626,8 +626,8 @@ struct dsa_switch_ops {
+ /*
+ * PHYLINK integration
+ */
+- void (*phylink_get_interfaces)(struct dsa_switch *ds, int port,
+- unsigned long *supported_interfaces);
++ void (*phylink_get_caps)(struct dsa_switch *ds, int port,
++ struct phylink_config *config);
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1095,9 +1095,8 @@ int dsa_port_phylink_create(struct dsa_p
+ if (err)
+ mode = PHY_INTERFACE_MODE_NA;
+
+- if (ds->ops->phylink_get_interfaces)
+- ds->ops->phylink_get_interfaces(ds, dp->index,
+- dp->pl_config.supported_interfaces);
++ if (ds->ops->phylink_get_caps)
++ ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config);
+
+ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
+ mode, &dsa_port_phylink_mac_ops);
--- /dev/null
+From bde018222c6b084ac32933a9f933581dd83da18e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 17 Feb 2022 18:30:35 +0000
+Subject: [PATCH] net: dsa: add support for phylink mac_select_pcs()
+
+Add DSA support for the phylink mac_select_pcs() method so DSA drivers
+can return provide phylink with the appropriate PCS for the PHY
+interface mode.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 3 +++
+ net/dsa/port.c | 15 +++++++++++++++
+ 2 files changed, 18 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -631,6 +631,9 @@ struct dsa_switch_ops {
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
++ struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds,
++ int port,
++ phy_interface_t iface);
+ int (*phylink_mac_link_state)(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state);
+ void (*phylink_mac_config)(struct dsa_switch *ds, int port,
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1012,6 +1012,20 @@ static void dsa_port_phylink_mac_pcs_get
+ }
+ }
+
++static struct phylink_pcs *
++dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
++ phy_interface_t interface)
++{
++ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++ struct dsa_switch *ds = dp->ds;
++ struct phylink_pcs *pcs = NULL;
++
++ if (ds->ops->phylink_mac_select_pcs)
++ pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
++
++ return pcs;
++}
++
+ static void dsa_port_phylink_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+@@ -1078,6 +1092,7 @@ static void dsa_port_phylink_mac_link_up
+
+ static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
+ .validate = dsa_port_phylink_validate,
++ .mac_select_pcs = dsa_port_phylink_mac_select_pcs,
+ .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
+ .mac_config = dsa_port_phylink_mac_config,
+ .mac_an_restart = dsa_port_phylink_mac_an_restart,
--- /dev/null
+From 8e20f591f204f8db7f1182918f8e2285d3f589e0 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 26 Oct 2021 11:06:01 +0100
+Subject: [PATCH] net: phy: add phy_interface_t bitmap support
+
+Add support for a bitmap for phy interface modes, which includes:
+- a macro to declare the interface bitmap
+- an inline helper to zero the interface bitmap
+- an inline helper to detect an empty interface bitmap
+- inline helpers to do a bitwise AND and OR operations on two interface
+ bitmaps
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/phy.h | 34 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -155,6 +155,40 @@ typedef enum {
+ PHY_INTERFACE_MODE_MAX,
+ } phy_interface_t;
+
++/* PHY interface mode bitmap handling */
++#define DECLARE_PHY_INTERFACE_MASK(name) \
++ DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX)
++
++static inline void phy_interface_zero(unsigned long *intf)
++{
++ bitmap_zero(intf, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline bool phy_interface_empty(const unsigned long *intf)
++{
++ return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline void phy_interface_and(unsigned long *dst, const unsigned long *a,
++ const unsigned long *b)
++{
++ bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline void phy_interface_or(unsigned long *dst, const unsigned long *a,
++ const unsigned long *b)
++{
++ bitmap_or(dst, a, b, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline void phy_interface_set_rgmii(unsigned long *intf)
++{
++ __set_bit(PHY_INTERFACE_MODE_RGMII, intf);
++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, intf);
++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, intf);
++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, intf);
++}
++
+ /*
+ * phy_supported_speeds - return all speeds currently supported by a PHY device
+ */
--- /dev/null
+From d1e86325af377129adb7fc6f34eb044ca6068b47 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 15 Dec 2021 15:34:15 +0000
+Subject: [PATCH] net: phylink: add mac_select_pcs() method to phylink_mac_ops
+
+mac_select_pcs() allows us to have an explicit point to query which
+PCS the MAC wishes to use for a particular PHY interface mode, thereby
+allowing us to add support to validate the link settings with the PCS.
+
+Phylink will also use this to select the PCS to be used during a major
+configuration event without the MAC driver needing to call
+phylink_set_pcs().
+
+Note that if mac_select_pcs() is present, the supported_interfaces
+bitmap must be filled in; this avoids mac_select_pcs() being called
+with PHY_INTERFACE_MODE_NA when we want to get support for all
+interface types. Phylink will return an error in phylink_create()
+unless this condition is satisfied.
+
+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 | 68 +++++++++++++++++++++++++++++++++------
+ include/linux/phylink.h | 18 +++++++++++
+ 2 files changed, 77 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -155,6 +155,23 @@ static const char *phylink_an_mode_str(u
+ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+
++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)
+ {
+@@ -170,9 +187,10 @@ static int phylink_validate_any(struct p
+
+ 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);
++ }
+ }
+ }
+
+@@ -194,9 +212,7 @@ static int phylink_validate(struct phyli
+ 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,
+@@ -486,10 +502,21 @@ static void phylink_mac_pcs_an_restart(s
+ 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);
+@@ -500,6 +527,12 @@ static void phylink_major_config(struct
+ }
+ }
+
++ /* 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) {
+@@ -879,6 +912,14 @@ struct phylink *phylink_create(struct ph
+ 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);
+@@ -946,9 +987,10 @@ EXPORT_SYMBOL_GPL(phylink_create);
+ * @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
+@@ -959,6 +1001,14 @@ void phylink_set_pcs(struct phylink *pl,
+ {
+ 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);
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -84,6 +84,7 @@ struct phylink_config {
+ /**
+ * 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.
+@@ -98,6 +99,8 @@ struct phylink_mac_ops {
+ 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,
+@@ -150,6 +153,21 @@ struct phylink_mac_ops {
+ */
+ 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
--- /dev/null
+From 34ae2c09d46a2d0abd907e139b466f798e4095a8 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 15 Nov 2021 10:00:27 +0000
+Subject: [PATCH] net: phylink: add generic validate implementation
+
+Add a generic validate() implementation using the supported_interfaces
+and a bitmask of MAC pause/speed/duplex capabilities. This allows us
+to entirely eliminate many driver private validate() implementations.
+
+We expose the underlying phylink_get_linkmodes() function so that
+drivers which have special needs can still benefit from conversion.
+
+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 | 252 ++++++++++++++++++++++++++++++++++++++
+ include/linux/phylink.h | 31 +++++
+ 2 files changed, 283 insertions(+)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -172,6 +172,258 @@ static int phylink_validate_mac_and_pcs(
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
++static void phylink_caps_to_linkmodes(unsigned long *linkmodes,
++ unsigned long caps)
++{
++ if (caps & MAC_SYM_PAUSE)
++ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes);
++
++ if (caps & MAC_ASYM_PAUSE)
++ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes);
++
++ if (caps & MAC_10HD)
++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes);
++
++ if (caps & MAC_10FD)
++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes);
++
++ if (caps & MAC_100HD) {
++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, linkmodes);
++ }
++
++ if (caps & MAC_100FD) {
++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_1000HD)
++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, linkmodes);
++
++ if (caps & MAC_1000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_2500FD) {
++ __set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_5000FD)
++ __set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, linkmodes);
++
++ if (caps & MAC_10000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_25000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_40000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_50000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_56000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_100000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_200000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_400000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes);
++ }
++}
++
++/**
++ * phylink_get_linkmodes() - get acceptable link modes
++ * @linkmodes: ethtool linkmode mask (must be already initialised)
++ * @interface: phy interface mode defined by &typedef phy_interface_t
++ * @mac_capabilities: bitmask of MAC capabilities
++ *
++ * Set all possible pause, speed and duplex linkmodes in @linkmodes that
++ * are supported by the @interface mode and @mac_capabilities. @linkmodes
++ * must have been initialised previously.
++ */
++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
++ unsigned long mac_capabilities)
++{
++ unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_USXGMII:
++ caps |= MAC_10000FD | MAC_5000FD | MAC_2500FD;
++ fallthrough;
++
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_QSGMII:
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_GMII:
++ caps |= MAC_1000HD | MAC_1000FD;
++ fallthrough;
++
++ case PHY_INTERFACE_MODE_REVRMII:
++ case PHY_INTERFACE_MODE_RMII:
++ case PHY_INTERFACE_MODE_REVMII:
++ case PHY_INTERFACE_MODE_MII:
++ caps |= MAC_10HD | MAC_10FD;
++ fallthrough;
++
++ case PHY_INTERFACE_MODE_100BASEX:
++ caps |= MAC_100HD | MAC_100FD;
++ break;
++
++ case PHY_INTERFACE_MODE_TBI:
++ case PHY_INTERFACE_MODE_MOCA:
++ case PHY_INTERFACE_MODE_RTBI:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ caps |= MAC_1000HD;
++ fallthrough;
++ case PHY_INTERFACE_MODE_TRGMII:
++ caps |= MAC_1000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_2500BASEX:
++ caps |= MAC_2500FD;
++ break;
++
++ case PHY_INTERFACE_MODE_5GBASER:
++ caps |= MAC_5000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_XGMII:
++ case PHY_INTERFACE_MODE_RXAUI:
++ case PHY_INTERFACE_MODE_XAUI:
++ case PHY_INTERFACE_MODE_10GBASER:
++ case PHY_INTERFACE_MODE_10GKR:
++ caps |= MAC_10000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_25GBASER:
++ caps |= MAC_25000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_XLGMII:
++ caps |= MAC_40000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_INTERNAL:
++ caps |= ~0;
++ break;
++
++ case PHY_INTERFACE_MODE_NA:
++ case PHY_INTERFACE_MODE_MAX:
++ case PHY_INTERFACE_MODE_SMII:
++ break;
++ }
++
++ phylink_caps_to_linkmodes(linkmodes, caps & mac_capabilities);
++}
++EXPORT_SYMBOL_GPL(phylink_get_linkmodes);
++
++/**
++ * phylink_generic_validate() - generic validate() callback implementation
++ * @config: a pointer to a &struct phylink_config.
++ * @supported: ethtool bitmask for supported link modes.
++ * @state: a pointer to a &struct phylink_link_state.
++ *
++ * Generic implementation of the validate() callback that MAC drivers can
++ * use when they pass the range of supported interfaces and MAC capabilities.
++ * This makes use of phylink_get_linkmodes().
++ */
++void phylink_generic_validate(struct phylink_config *config,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
++
++ phylink_set_port_modes(mask);
++ phylink_set(mask, Autoneg);
++ phylink_get_linkmodes(mask, state->interface, config->mac_capabilities);
++
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
++}
++EXPORT_SYMBOL_GPL(phylink_generic_validate);
++
+ static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -20,6 +20,29 @@ enum {
+ MLO_AN_PHY = 0, /* Conventional PHY */
+ MLO_AN_FIXED, /* Fixed-link mode */
+ MLO_AN_INBAND, /* In-band protocol */
++
++ MAC_SYM_PAUSE = BIT(0),
++ MAC_ASYM_PAUSE = BIT(1),
++ MAC_10HD = BIT(2),
++ MAC_10FD = BIT(3),
++ MAC_10 = MAC_10HD | MAC_10FD,
++ MAC_100HD = BIT(4),
++ MAC_100FD = BIT(5),
++ MAC_100 = MAC_100HD | MAC_100FD,
++ MAC_1000HD = BIT(6),
++ MAC_1000FD = BIT(7),
++ MAC_1000 = MAC_1000HD | MAC_1000FD,
++ MAC_2500FD = BIT(8),
++ MAC_5000FD = BIT(9),
++ MAC_10000FD = BIT(10),
++ MAC_20000FD = BIT(11),
++ MAC_25000FD = BIT(12),
++ MAC_40000FD = BIT(13),
++ MAC_50000FD = BIT(14),
++ MAC_56000FD = BIT(15),
++ MAC_100000FD = BIT(16),
++ MAC_200000FD = BIT(17),
++ MAC_400000FD = BIT(18),
+ };
+
+ static inline bool phylink_autoneg_inband(unsigned int mode)
+@@ -69,6 +92,7 @@ enum phylink_op_type {
+ * if MAC link is at %MLO_AN_FIXED mode.
+ * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
+ * are supported by the MAC/PCS.
++ * @mac_capabilities: MAC pause/speed/duplex capabilities.
+ */
+ struct phylink_config {
+ struct device *dev;
+@@ -79,6 +103,7 @@ struct phylink_config {
+ void (*get_fixed_state)(struct phylink_config *config,
+ struct phylink_link_state *state);
+ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
++ unsigned long mac_capabilities;
+ };
+
+ /**
+@@ -460,6 +485,12 @@ void pcs_link_up(struct phylink_pcs *pcs
+ phy_interface_t interface, int speed, int duplex);
+ #endif
+
++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
++ unsigned long mac_capabilities);
++void phylink_generic_validate(struct phylink_config *config,
++ unsigned long *supported,
++ struct phylink_link_state *state);
++
+ struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
+ phy_interface_t iface,
+ const struct phylink_mac_ops *mac_ops);
--- /dev/null
+From 82b318983c515f29b8b3a0dad9f6a5fe8a68a7f4 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 20 Oct 2021 20:49:49 +0300
+Subject: [PATCH] net: dsa: introduce helpers for iterating through ports using
+ dp
+
+Since the DSA conversion from the ds->ports array into the dst->ports
+list, the DSA API has encouraged driver writers, as well as the core
+itself, to write inefficient code.
+
+Currently, code that wants to filter by a specific type of port when
+iterating, like {!unused, user, cpu, dsa}, uses the dsa_is_*_port helper.
+Under the hood, this uses dsa_to_port which iterates again through
+dst->ports. But the driver iterates through the port list already, so
+the complexity is quadratic for the typical case of a single-switch
+tree.
+
+This patch introduces some iteration helpers where the iterator is
+already a struct dsa_port *dp, so that the other variant of the
+filtering functions, dsa_port_is_{unused,user,cpu_dsa}, can be used
+directly on the iterator. This eliminates the second lookup.
+
+These functions can be used both by the core and by drivers.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -476,6 +476,34 @@ static inline bool dsa_is_user_port(stru
+ return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER;
+ }
+
++#define dsa_tree_for_each_user_port(_dp, _dst) \
++ list_for_each_entry((_dp), &(_dst)->ports, list) \
++ if (dsa_port_is_user((_dp)))
++
++#define dsa_switch_for_each_port(_dp, _ds) \
++ list_for_each_entry((_dp), &(_ds)->dst->ports, list) \
++ if ((_dp)->ds == (_ds))
++
++#define dsa_switch_for_each_port_safe(_dp, _next, _ds) \
++ list_for_each_entry_safe((_dp), (_next), &(_ds)->dst->ports, list) \
++ if ((_dp)->ds == (_ds))
++
++#define dsa_switch_for_each_port_continue_reverse(_dp, _ds) \
++ list_for_each_entry_continue_reverse((_dp), &(_ds)->dst->ports, list) \
++ if ((_dp)->ds == (_ds))
++
++#define dsa_switch_for_each_available_port(_dp, _ds) \
++ dsa_switch_for_each_port((_dp), (_ds)) \
++ if (!dsa_port_is_unused((_dp)))
++
++#define dsa_switch_for_each_user_port(_dp, _ds) \
++ dsa_switch_for_each_port((_dp), (_ds)) \
++ if (dsa_port_is_user((_dp)))
++
++#define dsa_switch_for_each_cpu_port(_dp, _ds) \
++ dsa_switch_for_each_port((_dp), (_ds)) \
++ if (dsa_port_is_cpu((_dp)))
++
+ static inline u32 dsa_user_ports(struct dsa_switch *ds)
+ {
+ u32 mask = 0;
--- /dev/null
+From 0d22d4b626a4eaa3196019092eb6c1919e9f8caa Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 15 Dec 2021 15:34:20 +0000
+Subject: [PATCH] net: phylink: add pcs_validate() method
+
+Add a hook for PCS to validate the link parameters. This avoids MAC
+drivers having to have knowledge of their PCS in their validate()
+method, thereby allowing several MAC drivers to be simplfied.
+
+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 | 31 +++++++++++++++++++++++++++++++
+ include/linux/phylink.h | 20 ++++++++++++++++++++
+ 2 files changed, 51 insertions(+)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -160,13 +160,44 @@ static int phylink_validate_mac_and_pcs(
+ struct phylink_link_state *state)
+ {
+ struct phylink_pcs *pcs;
++ int ret;
+
++ /* Get the PCS for this interface mode */
+ 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);
++ } else {
++ pcs = pl->pcs;
+ }
+
++ if (pcs) {
++ /* The PCS, if present, must be setup before phylink_create()
++ * has been called. If the ops is not initialised, print an
++ * error and backtrace rather than oopsing the kernel.
++ */
++ if (!pcs->ops) {
++ phylink_err(pl, "interface %s: uninitialised PCS\n",
++ phy_modes(state->interface));
++ dump_stack();
++ return -EINVAL;
++ }
++
++ /* Validate the link parameters with the PCS */
++ if (pcs->ops->pcs_validate) {
++ ret = pcs->ops->pcs_validate(pcs, supported, state);
++ if (ret < 0 || phylink_is_empty_linkmode(supported))
++ return -EINVAL;
++
++ /* Ensure the advertising mask is a subset of the
++ * supported mask.
++ */
++ linkmode_and(state->advertising, state->advertising,
++ supported);
++ }
++ }
++
++ /* Then validate the link parameters with the MAC */
+ pl->mac_ops->validate(pl->config, supported, state);
+
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -396,6 +396,7 @@ struct phylink_pcs {
+
+ /**
+ * struct phylink_pcs_ops - MAC PCS operations structure.
++ * @pcs_validate: validate the link configuration.
+ * @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.
+@@ -403,6 +404,8 @@ struct phylink_pcs {
+ * (where necessary).
+ */
+ struct phylink_pcs_ops {
++ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
++ const struct phylink_link_state *state);
+ void (*pcs_get_state)(struct phylink_pcs *pcs,
+ struct phylink_link_state *state);
+ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
+@@ -416,6 +419,23 @@ struct phylink_pcs_ops {
+
+ #if 0 /* For kernel-doc purposes only. */
+ /**
++ * pcs_validate() - validate the link configuration.
++ * @pcs: a pointer to a &struct phylink_pcs.
++ * @supported: ethtool bitmask for supported link modes.
++ * @state: a const pointer to a &struct phylink_link_state.
++ *
++ * Validate the interface mode, and advertising's autoneg bit, removing any
++ * media ethtool link modes that would not be supportable from the supported
++ * mask. Phylink will propagate the changes to the advertising mask. See the
++ * &struct phylink_mac_ops validate() method.
++ *
++ * Returns -EINVAL if the interface mode/autoneg mode is not supported.
++ * Returns non-zero positive if the link state can be supported.
++ */
++int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
++ const struct phylink_link_state *state);
++
++/**
+ * 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.
--- /dev/null
+From 3e5b1feccea7db576353ffc302f78d522e4116e6 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:32 +0000
+Subject: [PATCH] net: phylink: add legacy_pre_march2020 indicator
+
+Add a boolean to phylink_config to indicate whether a driver has not
+been updated for the changes in commit 7cceb599d15d ("net: phylink:
+avoid mac_config calls"), and thus are reliant on the old behaviour.
+
+We were currently keying the phylink behaviour on the presence of a
+PCS, but this is sub-optimal for modern drivers that may not have a
+PCS.
+
+This commit merely introduces the new flag, but does not add any use,
+since we need all legacy drivers to set this flag before it can be
+used. Once these legacy drivers have been updated, we can remove this
+flag.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/linux/phylink.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -84,6 +84,8 @@ enum phylink_op_type {
+ * struct phylink_config - PHYLINK configuration structure
+ * @dev: a pointer to a struct device associated with the MAC
+ * @type: operation type of PHYLINK instance
++ * @legacy_pre_march2020: driver has not been updated for March 2020 updates
++ * (See commit 7cceb599d15d ("net: phylink: avoid mac_config calls")
+ * @pcs_poll: MAC PCS cannot provide link change interrupt
+ * @poll_fixed_state: if true, starts link_poll,
+ * if MAC link is at %MLO_AN_FIXED mode.
+@@ -97,6 +99,7 @@ enum phylink_op_type {
+ struct phylink_config {
+ struct device *dev;
+ enum phylink_op_type type;
++ bool legacy_pre_march2020;
+ bool pcs_poll;
+ bool poll_fixed_state;
+ bool ovr_an_inband;
--- /dev/null
+From 0a9f0794d9bd67e590a9488afe87fbb0419d9539 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:38 +0000
+Subject: [PATCH] net: dsa: mark DSA phylink as legacy_pre_march2020
+
+The majority of DSA drivers do not make use of the PCS support, and
+thus operate in legacy mode. In order to preserve this behaviour in
+future, we need to set the legacy_pre_march2020 flag so phylink knows
+this may require the legacy calls.
+
+There are some DSA drivers that do make use of PCS support, and these
+will continue operating as before - legacy_pre_march2020 will not
+prevent split-PCS support enabling the newer phylink behaviour.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/port.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1110,6 +1110,13 @@ int dsa_port_phylink_create(struct dsa_p
+ if (err)
+ mode = PHY_INTERFACE_MODE_NA;
+
++ /* Presence of phylink_mac_link_state or phylink_mac_an_restart is
++ * an indicator of a legacy phylink driver.
++ */
++ if (ds->ops->phylink_mac_link_state ||
++ ds->ops->phylink_mac_an_restart)
++ dp->pl_config.legacy_pre_march2020 = true;
++
+ if (ds->ops->phylink_get_caps)
+ ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config);
+
--- /dev/null
+From 001f4261fe4d5ae710cf1f445b6cae6d9d3ae26e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:48 +0000
+Subject: [PATCH] net: phylink: use legacy_pre_march2020
+
+Use the legacy flag to indicate whether we should operate in legacy
+mode. This allows us to stop using the presence of a PCS as an
+indicator to the age of the phylink user, and make PCS presence
+optional.
+
+Legacy mode involves:
+1) calling mac_config() whenever the link comes up
+2) calling mac_config() whenever the inband advertisement changes,
+ possibly followed by a call to mac_an_restart()
+3) making use of mac_an_restart()
+4) making use of mac_pcs_get_state()
+
+All the above functionality was moved to a seperate "PCS" block of
+operations in March 2020.
+
+Update the documents to indicate that the differences that this flag
+makes.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 12 ++++++------
+ include/linux/phylink.h | 17 +++++++++++++++++
+ 2 files changed, 23 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -777,7 +777,7 @@ static void phylink_mac_pcs_an_restart(s
+ phylink_autoneg_inband(pl->cur_link_an_mode)) {
+ if (pl->pcs_ops)
+ pl->pcs_ops->pcs_an_restart(pl->pcs);
+- else
++ else if (pl->config->legacy_pre_march2020)
+ pl->mac_ops->mac_an_restart(pl->config);
+ }
+ }
+@@ -855,7 +855,7 @@ static int phylink_change_inband_advert(
+ if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
+ return 0;
+
+- if (!pl->pcs_ops) {
++ if (!pl->pcs_ops && pl->config->legacy_pre_march2020) {
+ /* Legacy method */
+ phylink_mac_config(pl, &pl->link_config);
+ phylink_mac_pcs_an_restart(pl);
+@@ -900,7 +900,8 @@ static void phylink_mac_pcs_get_state(st
+
+ if (pl->pcs_ops)
+ pl->pcs_ops->pcs_get_state(pl->pcs, state);
+- else if (pl->mac_ops->mac_pcs_get_state)
++ else if (pl->mac_ops->mac_pcs_get_state &&
++ pl->config->legacy_pre_march2020)
+ pl->mac_ops->mac_pcs_get_state(pl->config, state);
+ else
+ state->link = 0;
+@@ -1094,12 +1095,11 @@ static void phylink_resolve(struct work_
+ }
+ phylink_major_config(pl, false, &link_state);
+ pl->link_config.interface = link_state.interface;
+- } else if (!pl->pcs_ops) {
++ } else if (!pl->pcs_ops && pl->config->legacy_pre_march2020) {
+ /* The interface remains unchanged, only the speed,
+ * duplex or pause settings have changed. Call the
+ * old mac_config() method to configure the MAC/PCS
+- * only if we do not have a PCS installed (an
+- * unconverted user.)
++ * only if we do not have a legacy MAC driver.
+ */
+ phylink_mac_config(pl, &link_state);
+ }
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -208,6 +208,10 @@ struct phylink_pcs *mac_select_pcs(struc
+ * negotiation completion state in @state->an_complete, and link up state
+ * in @state->link. If possible, @state->lp_advertising should also be
+ * populated.
++ *
++ * Note: This is a legacy method. This function will not be called unless
++ * legacy_pre_march2020 is set in &struct phylink_config and there is no
++ * PCS attached.
+ */
+ void mac_pcs_get_state(struct phylink_config *config,
+ struct phylink_link_state *state);
+@@ -248,6 +252,15 @@ int mac_prepare(struct phylink_config *c
+ * guaranteed to be correct, and so any mac_config() implementation must
+ * never reference these fields.
+ *
++ * Note: For legacy March 2020 drivers (drivers with legacy_pre_march2020 set
++ * in their &phylnk_config and which don't have a PCS), this function will be
++ * called on each link up event, and to also change the in-band advert. For
++ * non-legacy drivers, it will only be called to reconfigure the MAC for a
++ * "major" change in e.g. interface mode. It will not be called for changes
++ * in speed, duplex or pause modes or to change the in-band advertisement.
++ * In any case, it is strongly preferred that speed, duplex and pause settings
++ * are handled in the mac_link_up() method and not in this method.
++ *
+ * (this requires a rewrite - please refer to mac_link_up() for situations
+ * where the PCS and MAC are not tightly integrated.)
+ *
+@@ -332,6 +345,10 @@ int mac_finish(struct phylink_config *co
+ /**
+ * mac_an_restart() - restart 802.3z BaseX autonegotiation
+ * @config: a pointer to a &struct phylink_config.
++ *
++ * Note: This is a legacy method. This function will not be called unless
++ * legacy_pre_march2020 is set in &struct phylink_config and there is no
++ * PCS attached.
+ */
+ void mac_an_restart(struct phylink_config *config);
+
--- /dev/null
+From 83800d29f0c578e82554e7d4c6bfdbdf9b6cf428 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:43 +0000
+Subject: [PATCH] net: mtk_eth_soc: populate supported_interfaces member
+
+Populate the phy interface mode bitmap for the Mediatek driver with
+interfaces modes supported by the MAC.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3352,6 +3352,26 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = ð->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
++ __set_bit(PHY_INTERFACE_MODE_MII,
++ mac->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ mac->phylink_config.supported_interfaces);
++
++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII))
++ phy_interface_set_rgmii(mac->phylink_config.supported_interfaces);
++
++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id)
++ __set_bit(PHY_INTERFACE_MODE_TRGMII,
++ mac->phylink_config.supported_interfaces);
++
++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ mac->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ mac->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ mac->phylink_config.supported_interfaces);
++ }
+
+ phylink = phylink_create(&mac->phylink_config,
+ of_fwnode_handle(mac->of_node),
--- /dev/null
+From db81ca153814475d7e07365d46a4d1134bd122e2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:48 +0000
+Subject: [PATCH] net: mtk_eth_soc: remove interface checks in mtk_validate()
+
+As phylink checks the interface mode against the supported_interfaces
+bitmap, we no longer need to validate the interface mode, nor handle
+PHY_INTERFACE_MODE_NA in the validation function. Remove these to
+simplify the implementation.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 ---------------------
+ 1 file changed, 34 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -567,24 +567,8 @@ static void mtk_validate(struct phylink_
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+- struct mtk_mac *mac = container_of(config, struct mtk_mac,
+- phylink_config);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_GMII &&
+- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII) &&
+- phy_interface_mode_is_rgmii(state->interface)) &&
+- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) &&
+- !mac->id && state->interface == PHY_INTERFACE_MODE_TRGMII) &&
+- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII) &&
+- (state->interface == PHY_INTERFACE_MODE_SGMII ||
+- phy_interface_mode_is_8023z(state->interface)))) {
+- linkmode_zero(supported);
+- return;
+- }
+-
+ phylink_set_port_modes(mask);
+ phylink_set(mask, Autoneg);
+
+@@ -611,7 +595,6 @@ static void mtk_validate(struct phylink_
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_RMII:
+ case PHY_INTERFACE_MODE_REVMII:
+- case PHY_INTERFACE_MODE_NA:
+ default:
+ phylink_set(mask, 10baseT_Half);
+ phylink_set(mask, 10baseT_Full);
+@@ -620,23 +603,6 @@ static void mtk_validate(struct phylink_
+ break;
+ }
+
+- if (state->interface == PHY_INTERFACE_MODE_NA) {
+- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- phylink_set(mask, 2500baseX_Full);
+- }
+- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseT_Half);
+- phylink_set(mask, 1000baseX_Full);
+- }
+- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GEPHY)) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseT_Half);
+- }
+- }
+-
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+
--- /dev/null
+From 71d927494463c4f016d828e1134da26b7e961af5 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:53 +0000
+Subject: [PATCH] net: mtk_eth_soc: drop use of phylink_helper_basex_speed()
+
+Now that we have a better method to select SFP interface modes, we
+no longer need to use phylink_helper_basex_speed() in a driver's
+validation function, and we can also get rid of our hack to indicate
+both 1000base-X and 2500base-X if the comphy is present to make that
+work. Remove this hack and use of phylink_helper_basex_speed().
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -577,8 +577,9 @@ static void mtk_validate(struct phylink_
+ phylink_set(mask, 1000baseT_Full);
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+- case PHY_INTERFACE_MODE_2500BASEX:
+ phylink_set(mask, 1000baseX_Full);
++ break;
++ case PHY_INTERFACE_MODE_2500BASEX:
+ phylink_set(mask, 2500baseX_Full);
+ break;
+ case PHY_INTERFACE_MODE_GMII:
+@@ -608,11 +609,6 @@ static void mtk_validate(struct phylink_
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+-
+- /* We can only operate at 2500BaseX or 1000BaseX. If requested
+- * to advertise both, only report advertising at 2500BaseX.
+- */
+- phylink_helper_basex_speed(state);
+ }
+
+ static const struct phylink_mac_ops mtk_phylink_ops = {
--- /dev/null
+From a4238f6ce151afa331375d74a5033b76da637644 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:58 +0000
+Subject: [PATCH] net: mtk_eth_soc: use phylink_generic_validate()
+
+mtk_eth_soc has no special behaviour in its validation implementation,
+so can be switched to phylink_generic_validate().
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 53 ++-------------------
+ 1 file changed, 4 insertions(+), 49 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -563,56 +563,8 @@ static void mtk_mac_link_up(struct phyli
+ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+ }
+
+-static void mtk_validate(struct phylink_config *config,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+- phylink_set_port_modes(mask);
+- phylink_set(mask, Autoneg);
+-
+- switch (state->interface) {
+- case PHY_INTERFACE_MODE_TRGMII:
+- phylink_set(mask, 1000baseT_Full);
+- break;
+- case PHY_INTERFACE_MODE_1000BASEX:
+- phylink_set(mask, 1000baseX_Full);
+- break;
+- case PHY_INTERFACE_MODE_2500BASEX:
+- phylink_set(mask, 2500baseX_Full);
+- break;
+- case PHY_INTERFACE_MODE_GMII:
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- phylink_set(mask, 1000baseT_Half);
+- fallthrough;
+- case PHY_INTERFACE_MODE_SGMII:
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- fallthrough;
+- case PHY_INTERFACE_MODE_MII:
+- case PHY_INTERFACE_MODE_RMII:
+- case PHY_INTERFACE_MODE_REVMII:
+- default:
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
+- break;
+- }
+-
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
+-}
+-
+ static const struct phylink_mac_ops mtk_phylink_ops = {
+- .validate = mtk_validate,
++ .validate = phylink_generic_validate,
+ .mac_pcs_get_state = mtk_mac_pcs_get_state,
+ .mac_an_restart = mtk_mac_an_restart,
+ .mac_config = mtk_mac_config,
+@@ -3314,6 +3266,9 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = ð->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
++ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
++
+ __set_bit(PHY_INTERFACE_MODE_MII,
+ mac->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_GMII,
--- /dev/null
+From b06515367facfadcf5e70cf6f39db749cf4eb5e3 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:43 +0000
+Subject: [PATCH] net: mtk_eth_soc: mark as a legacy_pre_march2020 driver
+
+mtk_eth_soc has not been updated for commit 7cceb599d15d ("net: phylink:
+avoid mac_config calls"), and makes use of state->speed and
+state->duplex in contravention of the phylink documentation. This makes
+reliant on the legacy behaviours, so mark it as a legacy driver.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3266,6 +3266,10 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = ð->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
++ /* This driver makes use of state->speed/state->duplex in
++ * mac_config
++ */
++ mac->phylink_config.legacy_pre_march2020 = true;
+ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
+
--- /dev/null
+From 889e3691b9d6573de133da1f5e78f590e52152cd Mon Sep 17 00:00:00 2001
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Thu, 28 Apr 2022 14:23:13 -0700
+Subject: [PATCH] eth: mtk_eth_soc: remove a copy of the NAPI_POLL_WEIGHT
+ define
+
+Defining local versions of NAPI_POLL_WEIGHT with the same
+values in the drivers just makes refactoring harder.
+
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 -
+ 2 files changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3565,9 +3565,9 @@ static int mtk_probe(struct platform_dev
+ */
+ init_dummy_netdev(ð->dummy_dev);
+ netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx,
+- MTK_NAPI_WEIGHT);
++ NAPI_POLL_WEIGHT);
+ netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx,
+- MTK_NAPI_WEIGHT);
++ NAPI_POLL_WEIGHT);
+
+ platform_set_drvdata(pdev, eth);
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -25,7 +25,6 @@
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
+ #define MTK_TX_DMA_BUF_LEN_V2 0xffff
+ #define MTK_DMA_SIZE 512
+-#define MTK_NAPI_WEIGHT 64
+ #define MTK_MAC_COUNT 2
+ #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN)
+ #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
--- /dev/null
+From 0600bdde1fae75fb9bad72033d28edddc72b44b2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:31 +0100
+Subject: [PATCH 01/12] net: mtk_eth_soc: remove unused mac->mode
+
+mac->mode is only ever written to in one location, and is thus
+superflous. Remove it.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 -
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 -
+ 2 files changed, 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3261,7 +3261,6 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ /* mac config is not set */
+ mac->interface = PHY_INTERFACE_MODE_NA;
+- mac->mode = MLO_AN_PHY;
+ mac->speed = SPEED_UNKNOWN;
+
+ mac->phylink_config.dev = ð->netdev[id]->dev;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1085,7 +1085,6 @@ struct mtk_eth {
+ struct mtk_mac {
+ int id;
+ phy_interface_t interface;
+- unsigned int mode;
+ int speed;
+ struct device_node *of_node;
+ struct phylink *phylink;
--- /dev/null
+From 5a7a2f4b29d7546244da7d8bbc1962fce5b230f2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:36 +0100
+Subject: [PATCH 02/12] net: mtk_eth_soc: remove unused sgmii flags
+
+The "flags" member of struct mtk_sgmii appears to be unused, as are
+the MTK_SGMII_PHYSPEED_* and MTK_HAS_FLAGS() macros. Remove them.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 --------
+ 1 file changed, 8 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -956,23 +956,15 @@ struct mtk_soc_data {
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
+-#define MTK_SGMII_PHYSPEED_AN BIT(31)
+-#define MTK_SGMII_PHYSPEED_MASK GENMASK(2, 0)
+-#define MTK_SGMII_PHYSPEED_1000 BIT(0)
+-#define MTK_SGMII_PHYSPEED_2500 BIT(1)
+-#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x))
+-
+ /* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+ * characteristics
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+- * @flags: The enum refers to which mode the sgmii wants to run on
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ */
+
+ struct mtk_sgmii {
+ struct regmap *regmap[MTK_MAX_DEVS];
+- u32 flags[MTK_MAX_DEVS];
+ u32 ana_rgc3;
+ };
+
--- /dev/null
+From bc5e93e0cd22e360eda23859b939280205567580 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:42 +0100
+Subject: [PATCH 03/12] net: mtk_eth_soc: add mask and update PCS speed
+ definitions
+
+The PCS speed setting is a two bit field, but it is defined as two
+separate bits. Add a bitfield mask for the speed definitions, an
+ use the FIELD_PREP() macro to define each PCS speed.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -17,6 +17,7 @@
+ #include <linux/phylink.h>
+ #include <linux/rhashtable.h>
+ #include <linux/dim.h>
++#include <linux/bitfield.h>
+ #include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+@@ -473,9 +474,10 @@
+ #define SGMSYS_SGMII_MODE 0x20
+ #define SGMII_IF_MODE_BIT0 BIT(0)
+ #define SGMII_SPEED_DUPLEX_AN BIT(1)
+-#define SGMII_SPEED_10 0x0
+-#define SGMII_SPEED_100 BIT(2)
+-#define SGMII_SPEED_1000 BIT(3)
++#define SGMII_SPEED_MASK GENMASK(3, 2)
++#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
++#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
++#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
+ #define SGMII_DUPLEX_FULL BIT(4)
+ #define SGMII_IF_MODE_BIT5 BIT(5)
+ #define SGMII_REMOTE_FAULT_DIS BIT(8)
--- /dev/null
+From 7da3f901f8ecb425105fad39a0f5de73306abe52 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:47 +0100
+Subject: [PATCH 04/12] net: mtk_eth_soc: correct 802.3z speed setting
+
+Phylink does not guarantee that state->speed will be set correctly in
+the mac_config() call, so it's a bug that the driver makes use of it.
+Moreover, it is making use of it in a function that is only ever called
+for 1000BASE-X and 2500BASE-X which operate at a fixed speed which
+happens to be the same setting irrespective of the interface mode. We
+can simply remove the switch statement and just set the SGMII interface
+speed.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 +++++-------------
+ 1 file changed, 5 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -34,6 +34,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ return 0;
+ }
+
++/* For SGMII interface mode */
+ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
+ {
+ unsigned int val;
+@@ -60,6 +61,9 @@ int mtk_sgmii_setup_mode_an(struct mtk_s
+ return 0;
+ }
+
++/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
++ * fixed speed.
++ */
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+ const struct phylink_link_state *state)
+ {
+@@ -82,19 +86,7 @@ int mtk_sgmii_setup_mode_force(struct mt
+ /* SGMII force mode setting */
+ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_IF_MODE_MASK;
+-
+- switch (state->speed) {
+- case SPEED_10:
+- val |= SGMII_SPEED_10;
+- break;
+- case SPEED_100:
+- val |= SGMII_SPEED_100;
+- break;
+- case SPEED_2500:
+- case SPEED_1000:
+- val |= SGMII_SPEED_1000;
+- break;
+- }
++ val |= SGMII_SPEED_1000;
+
+ if (state->duplex == DUPLEX_FULL)
+ val |= SGMII_DUPLEX_FULL;
--- /dev/null
+From a459187390bb221827f9c07866c3a5ffbdf9622b Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:52 +0100
+Subject: [PATCH 05/12] net: mtk_eth_soc: correct 802.3z duplex setting
+
+Phylink does not guarantee that state->duplex will be set correctly in
+the mac_config() call, so it's a bug that the driver makes use of it.
+
+Move the 802.3z PCS duplex configuration to mac_link_up().
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 +++++++++++----
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 22 +++++++++++++++------
+ 3 files changed, 29 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -532,8 +532,18 @@ static void mtk_mac_link_up(struct phyli
+ {
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
+- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
++ u32 mcr;
+
++ if (phy_interface_mode_is_8023z(interface)) {
++ struct mtk_eth *eth = mac->hw;
++
++ /* Decide how GMAC and SGMIISYS be mapped */
++ int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
++ 0 : mac->id;
++ mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex);
++ }
++
++ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
+ MAC_MCR_FORCE_RX_FC);
+@@ -3265,9 +3275,7 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = ð->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
+- /* This driver makes use of state->speed/state->duplex in
+- * mac_config
+- */
++ /* This driver makes use of state->speed in mac_config */
+ mac->phylink_config.legacy_pre_march2020 = true;
+ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1103,6 +1103,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+ const struct phylink_link_state *state);
++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -83,14 +83,10 @@ int mtk_sgmii_setup_mode_force(struct mt
+ val &= ~SGMII_AN_ENABLE;
+ regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
+
+- /* SGMII force mode setting */
++ /* Set the speed etc but leave the duplex unchanged */
+ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
+- val &= ~SGMII_IF_MODE_MASK;
++ val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
+ val |= SGMII_SPEED_1000;
+-
+- if (state->duplex == DUPLEX_FULL)
+- val |= SGMII_DUPLEX_FULL;
+-
+ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
+
+ /* Release PHYA power down state */
+@@ -101,6 +97,20 @@ int mtk_sgmii_setup_mode_force(struct mt
+ return 0;
+ }
+
++/* For 1000BASE-X and 2500BASE-X interface modes */
++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
++{
++ unsigned int val;
++
++ /* SGMII force duplex setting */
++ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ val &= ~SGMII_DUPLEX_FULL;
++ if (duplex == DUPLEX_FULL)
++ val |= SGMII_DUPLEX_FULL;
++
++ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++}
++
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
+ {
+ struct mtk_sgmii *ss = eth->sgmii;
--- /dev/null
+From 4ce5a0bd3958ed248f0325bfcb95339f7c74feb2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:57 +0100
+Subject: [PATCH 06/12] net: mtk_eth_soc: stop passing phylink state to sgmii
+ setup
+
+Now that mtk_sgmii_setup_mode_force() only uses the interface mode
+from the phylink state, pass just the interface mode into this
+function.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 4 ++--
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -437,7 +437,7 @@ static void mtk_mac_config(struct phylin
+ /* Setup SGMIISYS with the determined property */
+ if (state->interface != PHY_INTERFACE_MODE_SGMII)
+ err = mtk_sgmii_setup_mode_force(eth->sgmii, sid,
+- state);
++ state->interface);
+ else if (phylink_autoneg_inband(mode))
+ err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1102,7 +1102,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ u32 ana_rgc3);
+ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- const struct phylink_link_state *state);
++ phy_interface_t interface);
+ void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -65,7 +65,7 @@ int mtk_sgmii_setup_mode_an(struct mtk_s
+ * fixed speed.
+ */
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- const struct phylink_link_state *state)
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+@@ -74,7 +74,7 @@ int mtk_sgmii_setup_mode_force(struct mt
+
+ regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
+ val &= ~RG_PHY_SPEED_MASK;
+- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
++ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ val |= RG_PHY_SPEED_3_125G;
+ regmap_write(ss->regmap[id], ss->ana_rgc3, val);
+
--- /dev/null
+From 1ec619ee4a052fb9ac48b57554ac2722a0bfe73c Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:02 +0100
+Subject: [PATCH 07/12] net: mtk_eth_soc: provide mtk_sgmii_config()
+
+Provide mtk_sgmii_config() to wrap up the decisions about which SGMII
+configuration will be called.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 ++---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 20 +++++++++++++++++---
+ 3 files changed, 20 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -435,12 +435,7 @@ static void mtk_mac_config(struct phylin
+ 0 : mac->id;
+
+ /* Setup SGMIISYS with the determined property */
+- if (state->interface != PHY_INTERFACE_MODE_SGMII)
+- err = mtk_sgmii_setup_mode_force(eth->sgmii, sid,
+- state->interface);
+- else if (phylink_autoneg_inband(mode))
+- err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
+-
++ err = mtk_sgmii_config(eth->sgmii, sid, mode, state->interface);
+ if (err)
+ goto init_err;
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1100,9 +1100,8 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne
+
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
+ u32 ana_rgc3);
+-int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
+-int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- phy_interface_t interface);
++int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
++ phy_interface_t interface);
+ void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -35,7 +35,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ }
+
+ /* For SGMII interface mode */
+-int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
++static int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
+ {
+ unsigned int val;
+
+@@ -64,8 +64,8 @@ int mtk_sgmii_setup_mode_an(struct mtk_s
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+ * fixed speed.
+ */
+-int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- phy_interface_t interface)
++static int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+@@ -97,6 +97,20 @@ int mtk_sgmii_setup_mode_force(struct mt
+ return 0;
+ }
+
++int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
++ phy_interface_t interface)
++{
++ int err = 0;
++
++ /* Setup SGMIISYS with the determined property */
++ if (interface != PHY_INTERFACE_MODE_SGMII)
++ err = mtk_sgmii_setup_mode_force(ss, id, interface);
++ else if (phylink_autoneg_inband(mode))
++ err = mtk_sgmii_setup_mode_an(ss, id);
++
++ return err;
++}
++
+ /* For 1000BASE-X and 2500BASE-X interface modes */
+ void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
+ {
--- /dev/null
+From 650a49bc65df6b0e0051a8f62d7c22d95a8f350d Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:07 +0100
+Subject: [PATCH 08/12] net: mtk_eth_soc: add fixme comment for state->speed
+ use
+
+Add a fixme comment for the last remaining incorrect usage of
+state->speed in the mac_config() method, which is strangely in a code
+path which is only run when the PHY interface mode changes.
+
+This means if we are in RGMII mode, changes in state->speed will not
+cause the INTF_MODE, TRGMII_RCK_CTRL and TRGMII_TCK_CTRL registers to
+be set according to the speed, nor will the TRGPLL clock be set to the
+correct value.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -374,6 +374,14 @@ static void mtk_mac_config(struct phylin
+ state->interface))
+ goto err_phy;
+ } else {
++ /* FIXME: this is incorrect. Not only does it
++ * use state->speed (which is not guaranteed
++ * to be correct) but it also makes use of it
++ * in a code path that will only be reachable
++ * when the PHY interface mode changes, not
++ * when the speed changes. Consequently, RGMII
++ * is probably broken.
++ */
+ mtk_gmac0_rgmii_adjust(mac->hw,
+ state->interface,
+ state->speed);
--- /dev/null
+From 0e37ad71b2ff772009595002da2860999e98e14e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:12 +0100
+Subject: [PATCH 09/12] net: mtk_eth_soc: move MAC_MCR setting to mac_finish()
+
+Move the setting of the MTK_MAC_MCR register from the end of mac_config
+into the phylink mac_finish() method, to keep it as the very last write
+that is done during configuration.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 33 ++++++++++++++-------
+ 1 file changed, 22 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -316,8 +316,8 @@ static void mtk_mac_config(struct phylin
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
+ struct mtk_eth *eth = mac->hw;
+- u32 mcr_cur, mcr_new, sid, i;
+ int val, ge_mode, err = 0;
++ u32 sid, i;
+
+ /* MT76x8 has no hardware settings between for the MAC */
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
+@@ -455,16 +455,6 @@ static void mtk_mac_config(struct phylin
+ return;
+ }
+
+- /* Setup gmac */
+- mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+- mcr_new = mcr_cur;
+- mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
+- MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
+-
+- /* Only update control register when needed! */
+- if (mcr_new != mcr_cur)
+- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+-
+ return;
+
+ err_phy:
+@@ -477,6 +467,26 @@ init_err:
+ mac->id, phy_modes(state->interface), err);
+ }
+
++static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
++ phy_interface_t interface)
++{
++ struct mtk_mac *mac = container_of(config, struct mtk_mac,
++ phylink_config);
++ u32 mcr_cur, mcr_new;
++
++ /* Setup gmac */
++ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
++ mcr_new = mcr_cur;
++ mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
++ MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
++
++ /* Only update control register when needed! */
++ if (mcr_new != mcr_cur)
++ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
++
++ return 0;
++}
++
+ static void mtk_mac_pcs_get_state(struct phylink_config *config,
+ struct phylink_link_state *state)
+ {
+@@ -581,6 +591,7 @@ static const struct phylink_mac_ops mtk_
+ .mac_pcs_get_state = mtk_mac_pcs_get_state,
+ .mac_an_restart = mtk_mac_an_restart,
+ .mac_config = mtk_mac_config,
++ .mac_finish = mtk_mac_finish,
+ .mac_link_down = mtk_mac_link_down,
+ .mac_link_up = mtk_mac_link_up,
+ };
--- /dev/null
+From 21089867278deb2a110b685e3cd33f64f9ce41e2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:17 +0100
+Subject: [PATCH 10/12] net: mtk_eth_soc: move restoration of SYSCFG0 to
+ mac_finish()
+
+The SGMIISYS configuration is performed while ETHSYS_SYSCFG0 is in a
+disabled state. In order to preserve this when we switch to phylink_pcs
+we need to move the restoration of this register to the mac_finish()
+callback.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 +++++++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -447,8 +447,8 @@ static void mtk_mac_config(struct phylin
+ if (err)
+ goto init_err;
+
+- regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
+- SYSCFG0_SGMII_MASK, val);
++ /* Save the syscfg0 value for mac_finish */
++ mac->syscfg0 = val;
+ } else if (phylink_autoneg_inband(mode)) {
+ dev_err(eth->dev,
+ "In-band mode not supported in non SGMII mode!\n");
+@@ -472,8 +472,15 @@ static int mtk_mac_finish(struct phylink
+ {
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
++ struct mtk_eth *eth = mac->hw;
+ u32 mcr_cur, mcr_new;
+
++ /* Enable SGMII */
++ if (interface == PHY_INTERFACE_MODE_SGMII ||
++ phy_interface_mode_is_8023z(interface))
++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++ SYSCFG0_SGMII_MASK, mac->syscfg0);
++
+ /* Setup gmac */
+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr_new = mcr_cur;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1087,6 +1087,7 @@ struct mtk_mac {
+ struct mtk_hw_stats *hw_stats;
+ __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
+ int hwlro_ip_cnt;
++ unsigned int syscfg0;
+ };
+
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
--- /dev/null
+From 901f3fbe13c3e56f0742e02717ccbfabbc95c463 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:22 +0100
+Subject: [PATCH 11/12] net: mtk_eth_soc: convert code structure to suit split
+ PCS support
+
+Provide a mtk_pcs structure which encapsulates everything that the PCS
+functions need (the regmap and ana_rgc3 offset), and use this in the
+PCS functions. Provide shim functions to convert from the existing
+"mtk_sgmii_*" interface to the converted PCS functions.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 15 ++-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 123 +++++++++++---------
+ 2 files changed, 79 insertions(+), 59 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -958,16 +958,23 @@ struct mtk_soc_data {
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
+-/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+- * characteristics
++/* struct mtk_pcs - This structure holds each sgmii regmap and associated
++ * data
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ */
++struct mtk_pcs {
++ struct regmap *regmap;
++ u32 ana_rgc3;
++};
+
++/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
++ * characteristics
++ * @pcs Array of individual PCS structures
++ */
+ struct mtk_sgmii {
+- struct regmap *regmap[MTK_MAX_DEVS];
+- u32 ana_rgc3;
++ struct mtk_pcs pcs[MTK_MAX_DEVS];
+ };
+
+ /* struct mtk_eth - This is the main datasructure for holding the state
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -9,90 +9,71 @@
+
+ #include <linux/mfd/syscon.h>
+ #include <linux/of.h>
++#include <linux/phylink.h>
+ #include <linux/regmap.h>
+
+ #include "mtk_eth_soc.h"
+
+-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
+-{
+- struct device_node *np;
+- int i;
+-
+- ss->ana_rgc3 = ana_rgc3;
+-
+- for (i = 0; i < MTK_MAX_DEVS; i++) {
+- np = of_parse_phandle(r, "mediatek,sgmiisys", i);
+- if (!np)
+- break;
+-
+- ss->regmap[i] = syscon_node_to_regmap(np);
+- of_node_put(np);
+- if (IS_ERR(ss->regmap[i]))
+- return PTR_ERR(ss->regmap[i]);
+- }
+-
+- return 0;
+-}
+-
+ /* For SGMII interface mode */
+-static int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
++static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+ unsigned int val;
+
+- if (!ss->regmap[id])
++ if (!mpcs->regmap)
+ return -EINVAL;
+
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+- regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER,
++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+
+- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val |= SGMII_REMOTE_FAULT_DIS;
+- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+- regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ val |= SGMII_AN_RESTART;
+- regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+
+ return 0;
++
+ }
+
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+ * fixed speed.
+ */
+-static int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- phy_interface_t interface)
++static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+- if (!ss->regmap[id])
++ if (!mpcs->regmap)
+ return -EINVAL;
+
+- regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
++ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
+ val &= ~RG_PHY_SPEED_MASK;
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ val |= RG_PHY_SPEED_3_125G;
+- regmap_write(ss->regmap[id], ss->ana_rgc3, val);
++ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
+
+ /* Disable SGMII AN */
+- regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ val &= ~SGMII_AN_ENABLE;
+- regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+ /* Set the speed etc but leave the duplex unchanged */
+- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
+ val |= SGMII_SPEED_1000;
+- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+ /* Release PHYA power down state */
+- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+
+ return 0;
+ }
+@@ -100,44 +81,76 @@ static int mtk_sgmii_setup_mode_force(st
+ int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
+ phy_interface_t interface)
+ {
++ struct mtk_pcs *mpcs = &ss->pcs[id];
+ int err = 0;
+
+ /* Setup SGMIISYS with the determined property */
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+- err = mtk_sgmii_setup_mode_force(ss, id, interface);
++ err = mtk_pcs_setup_mode_force(mpcs, interface);
+ else if (phylink_autoneg_inband(mode))
+- err = mtk_sgmii_setup_mode_an(ss, id);
++ err = mtk_pcs_setup_mode_an(mpcs);
+
+ return err;
+ }
+
+-/* For 1000BASE-X and 2500BASE-X interface modes */
+-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
++static void mtk_pcs_restart_an(struct mtk_pcs *mpcs)
++{
++ unsigned int val;
++
++ if (!mpcs->regmap)
++ return;
++
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
++ val |= SGMII_AN_RESTART;
++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++}
++
++static void mtk_pcs_link_up(struct mtk_pcs *mpcs, int speed, int duplex)
+ {
+ unsigned int val;
+
+ /* SGMII force duplex setting */
+- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_DUPLEX_FULL;
+ if (duplex == DUPLEX_FULL)
+ val |= SGMII_DUPLEX_FULL;
+
+- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++}
++
++/* For 1000BASE-X and 2500BASE-X interface modes */
++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
++{
++ mtk_pcs_link_up(&ss->pcs[id], speed, duplex);
++}
++
++int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
++{
++ struct device_node *np;
++ int i;
++
++ for (i = 0; i < MTK_MAX_DEVS; i++) {
++ np = of_parse_phandle(r, "mediatek,sgmiisys", i);
++ if (!np)
++ break;
++
++ ss->pcs[i].ana_rgc3 = ana_rgc3;
++ ss->pcs[i].regmap = syscon_node_to_regmap(np);
++ of_node_put(np);
++ if (IS_ERR(ss->pcs[i].regmap))
++ return PTR_ERR(ss->pcs[i].regmap);
++ }
++
++ return 0;
+ }
+
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
+ {
+- struct mtk_sgmii *ss = eth->sgmii;
+- unsigned int val, sid;
++ unsigned int sid;
+
+ /* Decide how GMAC and SGMIISYS be mapped */
+ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+ 0 : mac_id;
+
+- if (!ss->regmap[sid])
+- return;
+-
+- regmap_read(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, &val);
+- val |= SGMII_AN_RESTART;
+- regmap_write(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, val);
++ mtk_pcs_restart_an(ð->sgmii->pcs[sid]);
+ }
--- /dev/null
+From 14a44ab0330d290fade1403a920e299cc56d7300 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:28 +0100
+Subject: [PATCH 12/12] net: mtk_eth_soc: partially convert to phylink_pcs
+
+Partially convert mtk_eth_soc to phylink_pcs, moving the configuration,
+link up and AN restart over. However, it seems mac_pcs_get_state()
+doesn't actually get the state from the PCS, so we can't convert that
+over without a better understanding of the hardware.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 49 ++++++++----------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 ++-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 55 +++++++++++----------
+ 3 files changed, 53 insertions(+), 58 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -310,6 +310,25 @@ static void mtk_gmac0_rgmii_adjust(struc
+ mtk_w32(eth, val, TRGMII_TCK_CTRL);
+ }
+
++static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
++ phy_interface_t interface)
++{
++ struct mtk_mac *mac = container_of(config, struct mtk_mac,
++ phylink_config);
++ struct mtk_eth *eth = mac->hw;
++ unsigned int sid;
++
++ if (interface == PHY_INTERFACE_MODE_SGMII ||
++ phy_interface_mode_is_8023z(interface)) {
++ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
++ 0 : mac->id;
++
++ return mtk_sgmii_select_pcs(eth->sgmii, sid);
++ }
++
++ return NULL;
++}
++
+ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+ {
+@@ -317,7 +336,7 @@ static void mtk_mac_config(struct phylin
+ phylink_config);
+ struct mtk_eth *eth = mac->hw;
+ int val, ge_mode, err = 0;
+- u32 sid, i;
++ u32 i;
+
+ /* MT76x8 has no hardware settings between for the MAC */
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
+@@ -438,15 +457,6 @@ static void mtk_mac_config(struct phylin
+ SYSCFG0_SGMII_MASK,
+ ~(u32)SYSCFG0_SGMII_MASK);
+
+- /* Decide how GMAC and SGMIISYS be mapped */
+- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+- 0 : mac->id;
+-
+- /* Setup SGMIISYS with the determined property */
+- err = mtk_sgmii_config(eth->sgmii, sid, mode, state->interface);
+- if (err)
+- goto init_err;
+-
+ /* Save the syscfg0 value for mac_finish */
+ mac->syscfg0 = val;
+ } else if (phylink_autoneg_inband(mode)) {
+@@ -526,14 +536,6 @@ static void mtk_mac_pcs_get_state(struct
+ state->pause |= MLO_PAUSE_TX;
+ }
+
+-static void mtk_mac_an_restart(struct phylink_config *config)
+-{
+- struct mtk_mac *mac = container_of(config, struct mtk_mac,
+- phylink_config);
+-
+- mtk_sgmii_restart_an(mac->hw, mac->id);
+-}
+-
+ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+ {
+@@ -554,15 +556,6 @@ static void mtk_mac_link_up(struct phyli
+ phylink_config);
+ u32 mcr;
+
+- if (phy_interface_mode_is_8023z(interface)) {
+- struct mtk_eth *eth = mac->hw;
+-
+- /* Decide how GMAC and SGMIISYS be mapped */
+- int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+- 0 : mac->id;
+- mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex);
+- }
+-
+ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
+@@ -595,8 +588,8 @@ static void mtk_mac_link_up(struct phyli
+
+ static const struct phylink_mac_ops mtk_phylink_ops = {
+ .validate = phylink_generic_validate,
++ .mac_select_pcs = mtk_mac_select_pcs,
+ .mac_pcs_get_state = mtk_mac_pcs_get_state,
+- .mac_an_restart = mtk_mac_an_restart,
+ .mac_config = mtk_mac_config,
+ .mac_finish = mtk_mac_finish,
+ .mac_link_down = mtk_mac_link_down,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -963,10 +963,12 @@ struct mtk_soc_data {
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
++ * @pcs: Phylink PCS structure
+ */
+ struct mtk_pcs {
+ struct regmap *regmap;
+ u32 ana_rgc3;
++ struct phylink_pcs pcs;
+ };
+
+ /* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+@@ -1106,12 +1108,9 @@ void mtk_stats_update_mac(struct mtk_mac
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+ u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
++struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
+ u32 ana_rgc3);
+-int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
+- phy_interface_t interface);
+-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -14,14 +14,16 @@
+
+ #include "mtk_eth_soc.h"
+
++static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
++{
++ return container_of(pcs, struct mtk_pcs, pcs);
++}
++
+ /* For SGMII interface mode */
+ static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+ unsigned int val;
+
+- if (!mpcs->regmap)
+- return -EINVAL;
+-
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+@@ -50,9 +52,6 @@ static int mtk_pcs_setup_mode_force(stru
+ {
+ unsigned int val;
+
+- if (!mpcs->regmap)
+- return -EINVAL;
+-
+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
+ val &= ~RG_PHY_SPEED_MASK;
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+@@ -78,10 +77,12 @@ static int mtk_pcs_setup_mode_force(stru
+ return 0;
+ }
+
+-int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
+- phy_interface_t interface)
++static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface,
++ const unsigned long *advertising,
++ bool permit_pause_to_mac)
+ {
+- struct mtk_pcs *mpcs = &ss->pcs[id];
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ int err = 0;
+
+ /* Setup SGMIISYS with the determined property */
+@@ -93,22 +94,25 @@ int mtk_sgmii_config(struct mtk_sgmii *s
+ return err;
+ }
+
+-static void mtk_pcs_restart_an(struct mtk_pcs *mpcs)
++static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+ {
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int val;
+
+- if (!mpcs->regmap)
+- return;
+-
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ val |= SGMII_AN_RESTART;
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+ }
+
+-static void mtk_pcs_link_up(struct mtk_pcs *mpcs, int speed, int duplex)
++static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed, int duplex)
+ {
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int val;
+
++ if (!phy_interface_mode_is_8023z(interface))
++ return;
++
+ /* SGMII force duplex setting */
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_DUPLEX_FULL;
+@@ -118,11 +122,11 @@ static void mtk_pcs_link_up(struct mtk_p
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ }
+
+-/* For 1000BASE-X and 2500BASE-X interface modes */
+-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
+-{
+- mtk_pcs_link_up(&ss->pcs[id], speed, duplex);
+-}
++static const struct phylink_pcs_ops mtk_pcs_ops = {
++ .pcs_config = mtk_pcs_config,
++ .pcs_an_restart = mtk_pcs_restart_an,
++ .pcs_link_up = mtk_pcs_link_up,
++};
+
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
+ {
+@@ -139,18 +143,17 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ of_node_put(np);
+ if (IS_ERR(ss->pcs[i].regmap))
+ return PTR_ERR(ss->pcs[i].regmap);
++
++ ss->pcs[i].pcs.ops = &mtk_pcs_ops;
+ }
+
+ return 0;
+ }
+
+-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
++struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
+ {
+- unsigned int sid;
+-
+- /* Decide how GMAC and SGMIISYS be mapped */
+- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+- 0 : mac_id;
++ if (!ss->pcs[id].regmap)
++ return NULL;
+
+- mtk_pcs_restart_an(ð->sgmii->pcs[sid]);
++ return &ss->pcs[id].pcs;
+ }
--- /dev/null
+From 505560028b6deb9b4385cf6100f05ca6f4aacaf8 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Mon, 6 Dec 2021 18:57:49 +0200
+Subject: [PATCH 01/13] net: dsa: mt7530: iterate using
+ dsa_switch_for_each_user_port in bridging ops
+
+Avoid repeated calls to dsa_to_port() (some hidden behind dsa_is_user_port
+and some in plain sight) by keeping two struct dsa_port references: one
+to the port passed as argument, and another to the other ports of the
+switch that we're iterating over.
+
+dsa_to_port(ds, i) gets replaced by other_dp, i gets replaced by
+other_port which is derived from other_dp->index, dsa_is_user_port is
+handled by the DSA iterator.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 52 +++++++++++++++++++++++-----------------
+ 1 file changed, 30 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1188,27 +1188,31 @@ static int
+ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+ u32 port_bitmap = BIT(MT7530_CPU_PORT);
+- int i;
++ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+
+- for (i = 0; i < MT7530_NUM_PORTS; i++) {
++ dsa_switch_for_each_user_port(other_dp, ds) {
++ int other_port = other_dp->index;
++
++ if (dp == other_dp)
++ continue;
++
+ /* Add this port to the port matrix of the other ports in the
+ * same bridge. If the port is disabled, port matrix is kept
+ * and not being setup until the port becomes enabled.
+ */
+- if (dsa_is_user_port(ds, i) && i != port) {
+- if (dsa_to_port(ds, i)->bridge_dev != bridge)
+- continue;
+- if (priv->ports[i].enable)
+- mt7530_set(priv, MT7530_PCR_P(i),
+- PCR_MATRIX(BIT(port)));
+- priv->ports[i].pm |= PCR_MATRIX(BIT(port));
++ if (other_dp->bridge_dev != bridge)
++ continue;
+
+- port_bitmap |= BIT(i);
+- }
++ if (priv->ports[other_port].enable)
++ mt7530_set(priv, MT7530_PCR_P(other_port),
++ PCR_MATRIX(BIT(port)));
++ priv->ports[other_port].pm |= PCR_MATRIX(BIT(port));
++
++ port_bitmap |= BIT(other_port);
+ }
+
+ /* Add the all other ports to this port matrix. */
+@@ -1301,24 +1305,28 @@ static void
+ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+ {
++ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+ struct mt7530_priv *priv = ds->priv;
+- int i;
+
+ mutex_lock(&priv->reg_mutex);
+
+- for (i = 0; i < MT7530_NUM_PORTS; i++) {
++ dsa_switch_for_each_user_port(other_dp, ds) {
++ int other_port = other_dp->index;
++
++ if (dp == other_dp)
++ continue;
++
+ /* Remove this port from the port matrix of the other ports
+ * in the same bridge. If the port is disabled, port matrix
+ * is kept and not being setup until the port becomes enabled.
+ */
+- if (dsa_is_user_port(ds, i) && i != port) {
+- if (dsa_to_port(ds, i)->bridge_dev != bridge)
+- continue;
+- if (priv->ports[i].enable)
+- mt7530_clear(priv, MT7530_PCR_P(i),
+- PCR_MATRIX(BIT(port)));
+- priv->ports[i].pm &= ~PCR_MATRIX(BIT(port));
+- }
++ if (other_dp->bridge_dev != bridge)
++ continue;
++
++ if (priv->ports[other_port].enable)
++ mt7530_clear(priv, MT7530_PCR_P(other_port),
++ PCR_MATRIX(BIT(port)));
++ priv->ports[other_port].pm &= ~PCR_MATRIX(BIT(port));
+ }
+
+ /* Set the cpu port to be the only one in the port matrix of
--- /dev/null
+From a1da54bcd664fc27169386db966575675ac3ccb0 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:01 +0100
+Subject: [PATCH 02/13] net: dsa: mt7530: populate supported_interfaces and
+ mac_capabilities
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Populate the supported interfaces and MAC capabilities for mt7530,
+mt7531 and mt7621 DSA switches. Filling this in will enable phylink
+to pre-check the PHY interface mode against the the supported
+interfaces bitmap prior to calling the validate function, and will
+eventually allow us to convert to using the generic validation.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 74 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/mt7530.h | 2 ++
+ 2 files changed, 76 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2385,6 +2385,32 @@ mt7531_setup(struct dsa_switch *ds)
+ return 0;
+ }
+
++static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ switch (port) {
++ case 0 ... 4: /* Internal phy */
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ break;
++
++ case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
++ phy_interface_set_rgmii(config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_MII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ break;
++
++ case 6: /* 1st cpu port */
++ __set_bit(PHY_INTERFACE_MODE_RGMII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_TRGMII,
++ config->supported_interfaces);
++ break;
++ }
++}
++
+ static bool
+ mt7530_phy_mode_supported(struct dsa_switch *ds, int port,
+ const struct phylink_link_state *state)
+@@ -2421,6 +2447,37 @@ static bool mt7531_is_rgmii_port(struct
+ return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII);
+ }
+
++static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ switch (port) {
++ case 0 ... 4: /* Internal phy */
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ break;
++
++ case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
++ if (mt7531_is_rgmii_port(priv, port)) {
++ phy_interface_set_rgmii(config->supported_interfaces);
++ break;
++ }
++ fallthrough;
++
++ case 6: /* 1st cpu port supports sgmii/8023z only */
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ config->supported_interfaces);
++
++ config->mac_capabilities |= MAC_2500FD;
++ break;
++ }
++}
++
+ static bool
+ mt7531_phy_mode_supported(struct dsa_switch *ds, int port,
+ const struct phylink_link_state *state)
+@@ -2899,6 +2956,18 @@ mt7531_cpu_port_config(struct dsa_switch
+ return 0;
+ }
+
++static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ /* This switch only supports full-duplex at 1Gbps */
++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
++ MAC_10 | MAC_100 | MAC_1000FD;
++
++ priv->info->mac_port_get_caps(ds, port, config);
++}
++
+ static void
+ mt7530_mac_port_validate(struct dsa_switch *ds, int port,
+ unsigned long *supported)
+@@ -3134,6 +3203,7 @@ static const struct dsa_switch_ops mt753
+ .port_vlan_del = mt7530_port_vlan_del,
+ .port_mirror_add = mt753x_port_mirror_add,
+ .port_mirror_del = mt753x_port_mirror_del,
++ .phylink_get_caps = mt753x_phylink_get_caps,
+ .phylink_validate = mt753x_phylink_validate,
+ .phylink_mac_link_state = mt753x_phylink_mac_link_state,
+ .phylink_mac_config = mt753x_phylink_mac_config,
+@@ -3151,6 +3221,7 @@ static const struct mt753x_info mt753x_t
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
++ .mac_port_get_caps = mt7530_mac_port_get_caps,
+ .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+@@ -3162,6 +3233,7 @@ static const struct mt753x_info mt753x_t
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
++ .mac_port_get_caps = mt7530_mac_port_get_caps,
+ .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+@@ -3174,6 +3246,7 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7531_ind_phy_write,
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
++ .mac_port_get_caps = mt7531_mac_port_get_caps,
+ .phy_mode_supported = mt7531_phy_mode_supported,
+ .mac_port_validate = mt7531_mac_port_validate,
+ .mac_port_get_state = mt7531_phylink_mac_link_state,
+@@ -3236,6 +3309,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ */
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
++ !priv->info->mac_port_get_caps ||
+ !priv->info->phy_mode_supported ||
+ !priv->info->mac_port_validate ||
+ !priv->info->mac_port_get_state || !priv->info->mac_port_config)
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -769,6 +769,8 @@ struct mt753x_info {
+ int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val);
+ int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface);
+ int (*cpu_port_config)(struct dsa_switch *ds, int port);
++ void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
++ struct phylink_config *config);
+ bool (*phy_mode_supported)(struct dsa_switch *ds, int port,
+ const struct phylink_link_state *state);
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
--- /dev/null
+From e3f6719e2269868ca129b05da50cd55786848954 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:06 +0100
+Subject: [PATCH 03/13] net: dsa: mt7530: remove interface checks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+As phylink checks the interface mode against the supported_interfaces
+bitmap, we no longer need to validate the interface mode, nor handle
+PHY_INTERFACE_MODE_NA in the validation function. Remove these to
+simplify the implementation.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 82 ----------------------------------------
+ drivers/net/dsa/mt7530.h | 2 -
+ 2 files changed, 84 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2411,37 +2411,6 @@ static void mt7530_mac_port_get_caps(str
+ }
+ }
+
+-static bool
+-mt7530_phy_mode_supported(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- switch (port) {
+- case 0 ... 4: /* Internal phy */
+- if (state->interface != PHY_INTERFACE_MODE_GMII)
+- return false;
+- break;
+- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
+- if (!phy_interface_mode_is_rgmii(state->interface) &&
+- state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_GMII)
+- return false;
+- break;
+- case 6: /* 1st cpu port */
+- if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_TRGMII)
+- return false;
+- break;
+- default:
+- dev_err(priv->dev, "%s: unsupported port: %i\n", __func__,
+- port);
+- return false;
+- }
+-
+- return true;
+-}
+-
+ static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port)
+ {
+ return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII);
+@@ -2478,44 +2447,6 @@ static void mt7531_mac_port_get_caps(str
+ }
+ }
+
+-static bool
+-mt7531_phy_mode_supported(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- switch (port) {
+- case 0 ... 4: /* Internal phy */
+- if (state->interface != PHY_INTERFACE_MODE_GMII)
+- return false;
+- break;
+- case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
+- if (mt7531_is_rgmii_port(priv, port))
+- return phy_interface_mode_is_rgmii(state->interface);
+- fallthrough;
+- case 6: /* 1st cpu port supports sgmii/8023z only */
+- if (state->interface != PHY_INTERFACE_MODE_SGMII &&
+- !phy_interface_mode_is_8023z(state->interface))
+- return false;
+- break;
+- default:
+- dev_err(priv->dev, "%s: unsupported port: %i\n", __func__,
+- port);
+- return false;
+- }
+-
+- return true;
+-}
+-
+-static bool
+-mt753x_phy_mode_supported(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- return priv->info->phy_mode_supported(ds, port, state);
+-}
+-
+ static int
+ mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)
+ {
+@@ -2770,9 +2701,6 @@ mt753x_phylink_mac_config(struct dsa_swi
+ struct mt7530_priv *priv = ds->priv;
+ u32 mcr_cur, mcr_new;
+
+- if (!mt753x_phy_mode_supported(ds, port, state))
+- goto unsupported;
+-
+ switch (port) {
+ case 0 ... 4: /* Internal phy */
+ if (state->interface != PHY_INTERFACE_MODE_GMII)
+@@ -2990,12 +2918,6 @@ mt753x_phylink_validate(struct dsa_switc
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+ struct mt7530_priv *priv = ds->priv;
+
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- !mt753x_phy_mode_supported(ds, port, state)) {
+- linkmode_zero(supported);
+- return;
+- }
+-
+ phylink_set_port_modes(mask);
+
+ if (state->interface != PHY_INTERFACE_MODE_TRGMII &&
+@@ -3222,7 +3144,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+@@ -3234,7 +3155,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+@@ -3247,7 +3167,6 @@ static const struct mt753x_info mt753x_t
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
+ .mac_port_get_caps = mt7531_mac_port_get_caps,
+- .phy_mode_supported = mt7531_phy_mode_supported,
+ .mac_port_validate = mt7531_mac_port_validate,
+ .mac_port_get_state = mt7531_phylink_mac_link_state,
+ .mac_port_config = mt7531_mac_config,
+@@ -3310,7 +3229,6 @@ mt7530_probe(struct mdio_device *mdiodev
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
+ !priv->info->mac_port_get_caps ||
+- !priv->info->phy_mode_supported ||
+ !priv->info->mac_port_validate ||
+ !priv->info->mac_port_get_state || !priv->info->mac_port_config)
+ return -EINVAL;
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -771,8 +771,6 @@ struct mt753x_info {
+ int (*cpu_port_config)(struct dsa_switch *ds, int port);
+ void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+ struct phylink_config *config);
+- bool (*phy_mode_supported)(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state);
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported);
+ int (*mac_port_get_state)(struct dsa_switch *ds, int port,
--- /dev/null
+From 58344a3b85f1bd5ffddfc2c11f6f2bf688b5f990 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:12 +0100
+Subject: [PATCH 04/13] net: dsa: mt7530: drop use of
+ phylink_helper_basex_speed()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now that we have a better method to select SFP interface modes, we
+no longer need to use phylink_helper_basex_speed() in a driver's
+validation function.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2942,11 +2942,6 @@ mt753x_phylink_validate(struct dsa_switc
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+-
+- /* We can only operate at 2500BaseX or 1000BaseX. If requested
+- * to advertise both, only report advertising at 2500BaseX.
+- */
+- phylink_helper_basex_speed(state);
+ }
+
+ static int
--- /dev/null
+From 3c1d788a62dc648d1846049b66119ebb69dedd52 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:17 +0100
+Subject: [PATCH 05/13] net: dsa: mt7530: only indicate linkmodes that can be
+ supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now that mt7530 is not using the basex helper, it becomes unnecessary to
+indicate support for both 1000baseX and 2500baseX when one of the 803.3z
+PHY interface modes is being selected. Ensure that the driver indicates
+only those linkmodes that can actually be supported by the PHY interface
+mode.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 12 ++++++++----
+ drivers/net/dsa/mt7530.h | 1 +
+ 2 files changed, 9 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2518,12 +2518,13 @@ static int mt7531_rgmii_setup(struct mt7
+ }
+
+ static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port,
++ phy_interface_t interface,
+ unsigned long *supported)
+ {
+ /* Port5 supports ethier RGMII or SGMII.
+ * Port6 supports SGMII only.
+ */
+- if (port == 6) {
++ if (port == 6 && interface == PHY_INTERFACE_MODE_2500BASEX) {
+ phylink_set(supported, 2500baseX_Full);
+ phylink_set(supported, 2500baseT_Full);
+ }
+@@ -2898,16 +2899,18 @@ static void mt753x_phylink_get_caps(stru
+
+ static void
+ mt7530_mac_port_validate(struct dsa_switch *ds, int port,
++ phy_interface_t interface,
+ unsigned long *supported)
+ {
+ }
+
+ static void mt7531_mac_port_validate(struct dsa_switch *ds, int port,
++ phy_interface_t interface,
+ unsigned long *supported)
+ {
+ struct mt7530_priv *priv = ds->priv;
+
+- mt7531_sgmii_validate(priv, port, supported);
++ mt7531_sgmii_validate(priv, port, interface, supported);
+ }
+
+ static void
+@@ -2930,12 +2933,13 @@ mt753x_phylink_validate(struct dsa_switc
+ }
+
+ /* This switch only supports 1G full-duplex. */
+- if (state->interface != PHY_INTERFACE_MODE_MII) {
++ if (state->interface != PHY_INTERFACE_MODE_MII &&
++ state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+ }
+
+- priv->info->mac_port_validate(ds, port, mask);
++ priv->info->mac_port_validate(ds, port, state->interface, mask);
+
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -772,6 +772,7 @@ struct mt753x_info {
+ void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+ struct phylink_config *config);
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
++ phy_interface_t interface,
+ unsigned long *supported);
+ int (*mac_port_get_state)(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state);
--- /dev/null
+From 1c2211cb15dd3957fb26c0e1615eceb5db851ad6 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:22 +0100
+Subject: [PATCH 06/13] net: dsa: mt7530: switch to use phylink_get_linkmodes()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Switch mt7530 to use phylink_get_linkmodes() to generate the ethtool
+linkmodes that can be supported. We are unable to use the generic
+helper for this as pause modes are dependent on the interface as
+the Autoneg bit depends on the interface mode.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 57 ++++------------------------------------
+ 1 file changed, 5 insertions(+), 52 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2517,19 +2517,6 @@ static int mt7531_rgmii_setup(struct mt7
+ return 0;
+ }
+
+-static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port,
+- phy_interface_t interface,
+- unsigned long *supported)
+-{
+- /* Port5 supports ethier RGMII or SGMII.
+- * Port6 supports SGMII only.
+- */
+- if (port == 6 && interface == PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(supported, 2500baseX_Full);
+- phylink_set(supported, 2500baseT_Full);
+- }
+-}
+-
+ static void
+ mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port,
+ unsigned int mode, phy_interface_t interface,
+@@ -2898,51 +2885,21 @@ static void mt753x_phylink_get_caps(stru
+ }
+
+ static void
+-mt7530_mac_port_validate(struct dsa_switch *ds, int port,
+- phy_interface_t interface,
+- unsigned long *supported)
+-{
+-}
+-
+-static void mt7531_mac_port_validate(struct dsa_switch *ds, int port,
+- phy_interface_t interface,
+- unsigned long *supported)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- mt7531_sgmii_validate(priv, port, interface, supported);
+-}
+-
+-static void
+ mt753x_phylink_validate(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+- struct mt7530_priv *priv = ds->priv;
++ u32 caps;
++
++ caps = dsa_to_port(ds, port)->pl_config.mac_capabilities;
+
+ phylink_set_port_modes(mask);
++ phylink_get_linkmodes(mask, state->interface, caps);
+
+ if (state->interface != PHY_INTERFACE_MODE_TRGMII &&
+- !phy_interface_mode_is_8023z(state->interface)) {
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
++ !phy_interface_mode_is_8023z(state->interface))
+ phylink_set(mask, Autoneg);
+- }
+-
+- /* This switch only supports 1G full-duplex. */
+- if (state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- }
+-
+- priv->info->mac_port_validate(ds, port, state->interface, mask);
+-
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+@@ -3143,7 +3100,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+@@ -3154,7 +3110,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+@@ -3166,7 +3121,6 @@ static const struct mt753x_info mt753x_t
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
+ .mac_port_get_caps = mt7531_mac_port_get_caps,
+- .mac_port_validate = mt7531_mac_port_validate,
+ .mac_port_get_state = mt7531_phylink_mac_link_state,
+ .mac_port_config = mt7531_mac_config,
+ .mac_pcs_an_restart = mt7531_sgmii_restart_an,
+@@ -3228,7 +3182,6 @@ mt7530_probe(struct mdio_device *mdiodev
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
+ !priv->info->mac_port_get_caps ||
+- !priv->info->mac_port_validate ||
+ !priv->info->mac_port_get_state || !priv->info->mac_port_config)
+ return -EINVAL;
+
--- /dev/null
+From fd993fd59d96d5e2d5972ec4ca1f9651025c987b Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:27 +0100
+Subject: [PATCH 07/13] net: dsa: mt7530: partially convert to phylink_pcs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Partially convert the mt7530 driver to use phylink's PCS support. This
+is a partial implementation as we don't move anything into the
+pcs_config method yet - this driver supports SGMII or 1000BASE-X
+without in-band.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 144 +++++++++++++++++++++++----------------
+ drivers/net/dsa/mt7530.h | 21 +++---
+ 2 files changed, 95 insertions(+), 70 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -24,6 +24,11 @@
+
+ #include "mt7530.h"
+
++static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs)
++{
++ return container_of(pcs, struct mt753x_pcs, pcs);
++}
++
+ /* String, offset, and register size in bytes if different from 4 bytes */
+ static const struct mt7530_mib_desc mt7530_mib[] = {
+ MIB_DESC(1, 0x00, "TxDrop"),
+@@ -2517,12 +2522,11 @@ static int mt7531_rgmii_setup(struct mt7
+ return 0;
+ }
+
+-static void
+-mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port,
+- unsigned int mode, phy_interface_t interface,
+- int speed, int duplex)
++static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed, int duplex)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+ unsigned int val;
+
+ /* For adjusting speed and duplex of SGMII force mode. */
+@@ -2548,6 +2552,9 @@ mt7531_sgmii_link_up_force(struct dsa_sw
+
+ /* MT7531 SGMII 1G force mode can only work in full duplex mode,
+ * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
++ *
++ * The speed check is unnecessary as the MAC capabilities apply
++ * this restriction. --rmk
+ */
+ if ((speed == SPEED_10 || speed == SPEED_100) &&
+ duplex != DUPLEX_FULL)
+@@ -2623,9 +2630,10 @@ static int mt7531_sgmii_setup_mode_an(st
+ return 0;
+ }
+
+-static void mt7531_sgmii_restart_an(struct dsa_switch *ds, int port)
++static void mt7531_pcs_an_restart(struct phylink_pcs *pcs)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+ u32 val;
+
+ /* Only restart AN when AN is enabled */
+@@ -2682,6 +2690,24 @@ mt753x_mac_config(struct dsa_switch *ds,
+ return priv->info->mac_port_config(ds, port, mode, state->interface);
+ }
+
++static struct phylink_pcs *
++mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
++ phy_interface_t interface)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_TRGMII:
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ case PHY_INTERFACE_MODE_2500BASEX:
++ return &priv->pcs[port].pcs;
++
++ default:
++ return NULL;
++ }
++}
++
+ static void
+ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+ const struct phylink_link_state *state)
+@@ -2743,17 +2769,6 @@ unsupported:
+ mt7530_write(priv, MT7530_PMCR_P(port), mcr_new);
+ }
+
+-static void
+-mt753x_phylink_mac_an_restart(struct dsa_switch *ds, int port)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- if (!priv->info->mac_pcs_an_restart)
+- return;
+-
+- priv->info->mac_pcs_an_restart(ds, port);
+-}
+-
+ static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface)
+@@ -2763,16 +2778,13 @@ static void mt753x_phylink_mac_link_down
+ mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+ }
+
+-static void mt753x_mac_pcs_link_up(struct dsa_switch *ds, int port,
+- unsigned int mode, phy_interface_t interface,
+- int speed, int duplex)
++static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs,
++ unsigned int mode,
++ phy_interface_t interface,
++ int speed, int duplex)
+ {
+- struct mt7530_priv *priv = ds->priv;
+-
+- if (!priv->info->mac_pcs_link_up)
+- return;
+-
+- priv->info->mac_pcs_link_up(ds, port, mode, interface, speed, duplex);
++ if (pcs->ops->pcs_link_up)
++ pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
+ }
+
+ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
+@@ -2785,8 +2797,6 @@ static void mt753x_phylink_mac_link_up(s
+ struct mt7530_priv *priv = ds->priv;
+ u32 mcr;
+
+- mt753x_mac_pcs_link_up(ds, port, mode, interface, speed, duplex);
+-
+ mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
+
+ /* MT753x MAC works in 1G full duplex mode for all up-clocked
+@@ -2866,6 +2876,8 @@ mt7531_cpu_port_config(struct dsa_switch
+ return ret;
+ mt7530_write(priv, MT7530_PMCR_P(port),
+ PMCR_CPU_PORT_SETTING(priv->id));
++ mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED,
++ interface, speed, DUPLEX_FULL);
+ mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
+ speed, DUPLEX_FULL, true, true);
+
+@@ -2905,16 +2917,13 @@ mt753x_phylink_validate(struct dsa_switc
+ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+-static int
+-mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
++static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
++ struct phylink_link_state *state)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+ u32 pmsr;
+
+- if (port < 0 || port >= MT7530_NUM_PORTS)
+- return -EINVAL;
+-
+ pmsr = mt7530_read(priv, MT7530_PMSR_P(port));
+
+ state->link = (pmsr & PMSR_LINK);
+@@ -2941,8 +2950,6 @@ mt7530_phylink_mac_link_state(struct dsa
+ state->pause |= MLO_PAUSE_RX;
+ if (pmsr & PMSR_TX_FC)
+ state->pause |= MLO_PAUSE_TX;
+-
+- return 1;
+ }
+
+ static int
+@@ -2984,32 +2991,49 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
+ return 0;
+ }
+
+-static int
+-mt7531_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
++static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
++ struct phylink_link_state *state)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+
+ if (state->interface == PHY_INTERFACE_MODE_SGMII)
+- return mt7531_sgmii_pcs_get_state_an(priv, port, state);
+-
+- return -EOPNOTSUPP;
++ mt7531_sgmii_pcs_get_state_an(priv, port, state);
++ else
++ state->link = false;
+ }
+
+-static int
+-mt753x_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
++static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface,
++ const unsigned long *advertising,
++ bool permit_pause_to_mac)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ return 0;
++}
+
+- return priv->info->mac_port_get_state(ds, port, state);
++static void mt7530_pcs_an_restart(struct phylink_pcs *pcs)
++{
+ }
+
++static const struct phylink_pcs_ops mt7530_pcs_ops = {
++ .pcs_get_state = mt7530_pcs_get_state,
++ .pcs_config = mt753x_pcs_config,
++ .pcs_an_restart = mt7530_pcs_an_restart,
++};
++
++static const struct phylink_pcs_ops mt7531_pcs_ops = {
++ .pcs_get_state = mt7531_pcs_get_state,
++ .pcs_config = mt753x_pcs_config,
++ .pcs_an_restart = mt7531_pcs_an_restart,
++ .pcs_link_up = mt7531_pcs_link_up,
++};
++
+ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
+ int ret = priv->info->sw_setup(ds);
++ int i;
+
+ if (ret)
+ return ret;
+@@ -3022,6 +3046,13 @@ mt753x_setup(struct dsa_switch *ds)
+ if (ret && priv->irq)
+ mt7530_free_irq_common(priv);
+
++ /* Initialise the PCS devices */
++ for (i = 0; i < priv->ds->num_ports; i++) {
++ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
++ priv->pcs[i].priv = priv;
++ priv->pcs[i].port = i;
++ }
++
+ return ret;
+ }
+
+@@ -3083,9 +3114,8 @@ static const struct dsa_switch_ops mt753
+ .port_mirror_del = mt753x_port_mirror_del,
+ .phylink_get_caps = mt753x_phylink_get_caps,
+ .phylink_validate = mt753x_phylink_validate,
+- .phylink_mac_link_state = mt753x_phylink_mac_link_state,
++ .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs,
+ .phylink_mac_config = mt753x_phylink_mac_config,
+- .phylink_mac_an_restart = mt753x_phylink_mac_an_restart,
+ .phylink_mac_link_down = mt753x_phylink_mac_link_down,
+ .phylink_mac_link_up = mt753x_phylink_mac_link_up,
+ .get_mac_eee = mt753x_get_mac_eee,
+@@ -3095,36 +3125,34 @@ static const struct dsa_switch_ops mt753
+ static const struct mt753x_info mt753x_table[] = {
+ [ID_MT7621] = {
+ .id = ID_MT7621,
++ .pcs_ops = &mt7530_pcs_ops,
+ .sw_setup = mt7530_setup,
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+ [ID_MT7530] = {
+ .id = ID_MT7530,
++ .pcs_ops = &mt7530_pcs_ops,
+ .sw_setup = mt7530_setup,
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+ [ID_MT7531] = {
+ .id = ID_MT7531,
++ .pcs_ops = &mt7531_pcs_ops,
+ .sw_setup = mt7531_setup,
+ .phy_read = mt7531_ind_phy_read,
+ .phy_write = mt7531_ind_phy_write,
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
+ .mac_port_get_caps = mt7531_mac_port_get_caps,
+- .mac_port_get_state = mt7531_phylink_mac_link_state,
+ .mac_port_config = mt7531_mac_config,
+- .mac_pcs_an_restart = mt7531_sgmii_restart_an,
+- .mac_pcs_link_up = mt7531_sgmii_link_up_force,
+ },
+ };
+
+@@ -3182,7 +3210,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
+ !priv->info->mac_port_get_caps ||
+- !priv->info->mac_port_get_state || !priv->info->mac_port_config)
++ !priv->info->mac_port_config)
+ return -EINVAL;
+
+ priv->id = priv->info->id;
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -741,6 +741,12 @@ static const char *p5_intf_modes(unsigne
+
+ struct mt7530_priv;
+
++struct mt753x_pcs {
++ struct phylink_pcs pcs;
++ struct mt7530_priv *priv;
++ int port;
++};
++
+ /* struct mt753x_info - This is the main data structure for holding the specific
+ * part for each supported device
+ * @sw_setup: Holding the handler to a device initialization
+@@ -752,18 +758,14 @@ struct mt7530_priv;
+ * port
+ * @mac_port_validate: Holding the way to set addition validate type for a
+ * certan MAC port
+- * @mac_port_get_state: Holding the way getting the MAC/PCS state for a certain
+- * MAC port
+ * @mac_port_config: Holding the way setting up the PHY attribute to a
+ * certain MAC port
+- * @mac_pcs_an_restart Holding the way restarting PCS autonegotiation for a
+- * certain MAC port
+- * @mac_pcs_link_up: Holding the way setting up the PHY attribute to the pcs
+- * of the certain MAC port
+ */
+ struct mt753x_info {
+ enum mt753x_id id;
+
++ const struct phylink_pcs_ops *pcs_ops;
++
+ int (*sw_setup)(struct dsa_switch *ds);
+ int (*phy_read)(struct mt7530_priv *priv, int port, int regnum);
+ int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val);
+@@ -774,15 +776,9 @@ struct mt753x_info {
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
+ phy_interface_t interface,
+ unsigned long *supported);
+- int (*mac_port_get_state)(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state);
+ int (*mac_port_config)(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface);
+- void (*mac_pcs_an_restart)(struct dsa_switch *ds, int port);
+- void (*mac_pcs_link_up)(struct dsa_switch *ds, int port,
+- unsigned int mode, phy_interface_t interface,
+- int speed, int duplex);
+ };
+
+ /* struct mt7530_priv - This is the main data structure for holding the state
+@@ -824,6 +820,7 @@ struct mt7530_priv {
+ u8 mirror_tx;
+
+ struct mt7530_port ports[MT7530_NUM_PORTS];
++ struct mt753x_pcs pcs[MT7530_NUM_PORTS];
+ /* protect among processes for registers access*/
+ struct mutex reg_mutex;
+ int irq;
--- /dev/null
+From 2b0ee6768f3ac09072e5fd60b36580924e1cfa1c Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:32 +0100
+Subject: [PATCH 08/13] net: dsa: mt7530: move autoneg handling to PCS
+ validation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Move the autoneg bit handling to the PCS validation, which allows us to
+get rid of mt753x_phylink_validate() and rely on the default
+phylink_generic_validate() implementation for the MAC side.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 28 ++++++++++------------------
+ 1 file changed, 10 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2896,25 +2896,16 @@ static void mt753x_phylink_get_caps(stru
+ priv->info->mac_port_get_caps(ds, port, config);
+ }
+
+-static void
+-mt753x_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+- u32 caps;
+-
+- caps = dsa_to_port(ds, port)->pl_config.mac_capabilities;
+-
+- phylink_set_port_modes(mask);
+- phylink_get_linkmodes(mask, state->interface, caps);
++static int mt753x_pcs_validate(struct phylink_pcs *pcs,
++ unsigned long *supported,
++ const struct phylink_link_state *state)
++{
++ /* Autonegotiation is not supported in TRGMII nor 802.3z modes */
++ if (state->interface == PHY_INTERFACE_MODE_TRGMII ||
++ phy_interface_mode_is_8023z(state->interface))
++ phylink_clear(supported, Autoneg);
+
+- if (state->interface != PHY_INTERFACE_MODE_TRGMII &&
+- !phy_interface_mode_is_8023z(state->interface))
+- phylink_set(mask, Autoneg);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
++ return 0;
+ }
+
+ static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
+@@ -3016,12 +3007,14 @@ static void mt7530_pcs_an_restart(struct
+ }
+
+ static const struct phylink_pcs_ops mt7530_pcs_ops = {
++ .pcs_validate = mt753x_pcs_validate,
+ .pcs_get_state = mt7530_pcs_get_state,
+ .pcs_config = mt753x_pcs_config,
+ .pcs_an_restart = mt7530_pcs_an_restart,
+ };
+
+ static const struct phylink_pcs_ops mt7531_pcs_ops = {
++ .pcs_validate = mt753x_pcs_validate,
+ .pcs_get_state = mt7531_pcs_get_state,
+ .pcs_config = mt753x_pcs_config,
+ .pcs_an_restart = mt7531_pcs_an_restart,
+@@ -3113,7 +3106,6 @@ static const struct dsa_switch_ops mt753
+ .port_mirror_add = mt753x_port_mirror_add,
+ .port_mirror_del = mt753x_port_mirror_del,
+ .phylink_get_caps = mt753x_phylink_get_caps,
+- .phylink_validate = mt753x_phylink_validate,
+ .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs,
+ .phylink_mac_config = mt753x_phylink_mac_config,
+ .phylink_mac_link_down = mt753x_phylink_mac_link_down,
--- /dev/null
+From 5bc26de9bfaa6bb5539c09d4435dced98f429cfc Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:37 +0100
+Subject: [PATCH 09/13] net: dsa: mt7530: mark as non-legacy
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The mt7530 driver does not make use of the speed, duplex, pause or
+advertisement in its phylink_mac_config() implementation, so it can be
+marked as a non-legacy driver.
+
+Tested-by: Marek BehĂºn <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2893,6 +2893,12 @@ static void mt753x_phylink_get_caps(stru
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000FD;
+
++ /* This driver does not make use of the speed, duplex, pause or the
++ * advertisement in its mac_config, so it is safe to mark this driver
++ * as non-legacy.
++ */
++ config->legacy_pre_march2020 = false;
++
+ priv->info->mac_port_get_caps(ds, port, config);
+ }
+
--- /dev/null
+From 1f15b5e8733115cee65342bcaafeaf0368809fae Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 25 Apr 2022 22:28:02 +0100
+Subject: [PATCH 10/13] net: dsa: mt753x: fix pcs conversion regression
+
+Daniel Golle reports that the conversion of mt753x to phylink PCS caused
+an oops as below.
+
+The problem is with the placement of the PCS initialisation, which
+occurs after mt7531_setup() has been called. However, burited in this
+function is a call to setup the CPU port, which requires the PCS
+structure to be already setup.
+
+Fix this by changing the initialisation order.
+
+Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020
+Mem abort info:
+ ESR = 0x96000005
+ EC = 0x25: DABT (current EL), IL = 32 bits
+ SET = 0, FnV = 0
+ EA = 0, S1PTW = 0
+ FSC = 0x05: level 1 translation fault
+Data abort info:
+ ISV = 0, ISS = 0x00000005
+ CM = 0, WnR = 0
+user pgtable: 4k pages, 39-bit VAs, pgdp=0000000046057000
+[0000000000000020] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000
+Internal error: Oops: 96000005 [#1] SMP
+Modules linked in:
+CPU: 0 PID: 32 Comm: kworker/u4:1 Tainted: G S 5.18.0-rc3-next-20220422+ #0
+Hardware name: Bananapi BPI-R64 (DT)
+Workqueue: events_unbound deferred_probe_work_func
+pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+pc : mt7531_cpu_port_config+0xcc/0x1b0
+lr : mt7531_cpu_port_config+0xc0/0x1b0
+sp : ffffffc008d5b980
+x29: ffffffc008d5b990 x28: ffffff80060562c8 x27: 00000000f805633b
+x26: ffffff80001a8880 x25: 00000000000009c4 x24: 0000000000000016
+x23: ffffff8005eb6470 x22: 0000000000003600 x21: ffffff8006948080
+x20: 0000000000000000 x19: 0000000000000006 x18: 0000000000000000
+x17: 0000000000000001 x16: 0000000000000001 x15: 02963607fcee069e
+x14: 0000000000000000 x13: 0000000000000030 x12: 0101010101010101
+x11: ffffffc037302000 x10: 0000000000000870 x9 : ffffffc008d5b800
+x8 : ffffff800028f950 x7 : 0000000000000001 x6 : 00000000662b3000
+x5 : 00000000000002f0 x4 : 0000000000000000 x3 : ffffff800028f080
+x2 : 0000000000000000 x1 : ffffff800028f080 x0 : 0000000000000000
+Call trace:
+ mt7531_cpu_port_config+0xcc/0x1b0
+ mt753x_cpu_port_enable+0x24/0x1f0
+ mt7531_setup+0x49c/0x5c0
+ mt753x_setup+0x20/0x31c
+ dsa_register_switch+0x8bc/0x1020
+ mt7530_probe+0x118/0x200
+ mdio_probe+0x30/0x64
+ really_probe.part.0+0x98/0x280
+ __driver_probe_device+0x94/0x140
+ driver_probe_device+0x40/0x114
+ __device_attach_driver+0xb0/0x10c
+ bus_for_each_drv+0x64/0xa0
+ __device_attach+0xa8/0x16c
+ device_initial_probe+0x10/0x20
+ bus_probe_device+0x94/0x9c
+ deferred_probe_work_func+0x80/0xb4
+ process_one_work+0x200/0x3a0
+ worker_thread+0x260/0x4c0
+ kthread+0xd4/0xe0
+ ret_from_fork+0x10/0x20
+Code: 9409e911 937b7e60 8b0002a0 f9405800 (f9401005)
+---[ end trace 0000000000000000 ]---
+
+Reported-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Fixes: cbd1f243bc41 ("net: dsa: mt7530: partially convert to phylink_pcs")
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Link: https://lore.kernel.org/r/E1nj6FW-007WZB-5Y@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -3031,9 +3031,16 @@ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
+- int ret = priv->info->sw_setup(ds);
+- int i;
++ int i, ret;
+
++ /* Initialise the PCS devices */
++ for (i = 0; i < priv->ds->num_ports; i++) {
++ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
++ priv->pcs[i].priv = priv;
++ priv->pcs[i].port = i;
++ }
++
++ ret = priv->info->sw_setup(ds);
+ if (ret)
+ return ret;
+
+@@ -3045,13 +3052,6 @@ mt753x_setup(struct dsa_switch *ds)
+ if (ret && priv->irq)
+ mt7530_free_irq_common(priv);
+
+- /* Initialise the PCS devices */
+- for (i = 0; i < priv->ds->num_ports; i++) {
+- priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+- priv->pcs[i].priv = priv;
+- priv->pcs[i].port = i;
+- }
+-
+ return ret;
+ }
+
--- /dev/null
+From e26be16262e1fc1e9f1798c12762663bd9c265c6 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Fri, 10 Jun 2022 19:05:37 +0200
+Subject: [PATCH 11/13] net: dsa: mt7530: rework mt7530_hw_vlan_{add,del}
+
+Rework vlan_add/vlan_del functions in preparation for dynamic cpu port.
+
+Currently BIT(MT7530_CPU_PORT) is added to new_members, even though
+mt7530_port_vlan_add() will be called on the CPU port too.
+
+Let DSA core decide when to call port_vlan_add for the CPU port, rather
+than doing it implicitly.
+
+We can do autonomous forwarding in a certain VLAN, but not add br0 to that
+VLAN and avoid flooding the CPU with those packets, if software knows it
+doesn't need to process them.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 30 ++++++++++++------------------
+ 1 file changed, 12 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1522,11 +1522,11 @@ static void
+ mt7530_hw_vlan_add(struct mt7530_priv *priv,
+ struct mt7530_hw_vlan_entry *entry)
+ {
++ struct dsa_port *dp = dsa_to_port(priv->ds, entry->port);
+ u8 new_members;
+ u32 val;
+
+- new_members = entry->old_members | BIT(entry->port) |
+- BIT(MT7530_CPU_PORT);
++ new_members = entry->old_members | BIT(entry->port);
+
+ /* Validate the entry with independent learning, create egress tag per
+ * VLAN and joining the port as one of the port members.
+@@ -1537,22 +1537,20 @@ mt7530_hw_vlan_add(struct mt7530_priv *p
+
+ /* Decide whether adding tag or not for those outgoing packets from the
+ * port inside the VLAN.
+- */
+- val = entry->untagged ? MT7530_VLAN_EGRESS_UNTAG :
+- MT7530_VLAN_EGRESS_TAG;
+- mt7530_rmw(priv, MT7530_VAWD2,
+- ETAG_CTRL_P_MASK(entry->port),
+- ETAG_CTRL_P(entry->port, val));
+-
+- /* CPU port is always taken as a tagged port for serving more than one
++ * CPU port is always taken as a tagged port for serving more than one
+ * VLANs across and also being applied with egress type stack mode for
+ * that VLAN tags would be appended after hardware special tag used as
+ * DSA tag.
+ */
++ if (dsa_port_is_cpu(dp))
++ val = MT7530_VLAN_EGRESS_STACK;
++ else if (entry->untagged)
++ val = MT7530_VLAN_EGRESS_UNTAG;
++ else
++ val = MT7530_VLAN_EGRESS_TAG;
+ mt7530_rmw(priv, MT7530_VAWD2,
+- ETAG_CTRL_P_MASK(MT7530_CPU_PORT),
+- ETAG_CTRL_P(MT7530_CPU_PORT,
+- MT7530_VLAN_EGRESS_STACK));
++ ETAG_CTRL_P_MASK(entry->port),
++ ETAG_CTRL_P(entry->port, val));
+ }
+
+ static void
+@@ -1571,11 +1569,7 @@ mt7530_hw_vlan_del(struct mt7530_priv *p
+ return;
+ }
+
+- /* If certain member apart from CPU port is still alive in the VLAN,
+- * the entry would be kept valid. Otherwise, the entry is got to be
+- * disabled.
+- */
+- if (new_members && new_members != BIT(MT7530_CPU_PORT)) {
++ if (new_members) {
+ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) |
+ VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
--- /dev/null
+From 1f0dfd443eea7fc3e818e96f7c8264913ba41859 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Fri, 10 Jun 2022 19:05:38 +0200
+Subject: [PATCH 12/13] net: dsa: mt7530: rework mt753[01]_setup
+
+Enumerate available cpu-ports instead of using hardcoded constant.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2087,11 +2087,12 @@ static int
+ mt7530_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
++ struct device_node *dn = NULL;
+ struct device_node *phy_node;
+ struct device_node *mac_np;
+ struct mt7530_dummy_poll p;
+ phy_interface_t interface;
+- struct device_node *dn;
++ struct dsa_port *cpu_dp;
+ u32 id, val;
+ int ret, i;
+
+@@ -2099,7 +2100,19 @@ mt7530_setup(struct dsa_switch *ds)
+ * controller also is the container for two GMACs nodes representing
+ * as two netdev instances.
+ */
+- dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;
++ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
++ dn = cpu_dp->master->dev.of_node->parent;
++ /* It doesn't matter which CPU port is found first,
++ * their masters should share the same parent OF node
++ */
++ break;
++ }
++
++ if (!dn) {
++ dev_err(ds->dev, "parent OF node of DSA master not found");
++ return -EINVAL;
++ }
++
+ ds->assisted_learning_on_cpu_port = true;
+ ds->mtu_enforcement_ingress = true;
+
+@@ -2261,6 +2274,7 @@ mt7531_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
+ struct mt7530_dummy_poll p;
++ struct dsa_port *cpu_dp;
+ u32 val, id;
+ int ret, i;
+
+@@ -2333,8 +2347,11 @@ mt7531_setup(struct dsa_switch *ds)
+ CORE_PLL_GROUP4, val);
+
+ /* BPDU to CPU port */
+- mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
+- BIT(MT7530_CPU_PORT));
++ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
++ mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
++ BIT(cpu_dp->index));
++ break;
++ }
+ mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK,
+ MT753X_BPDU_CPU_ONLY);
+
--- /dev/null
+From ad2606f6fafae3a7d41c4f2af5554c8f6adecbc7 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Fri, 10 Jun 2022 19:05:39 +0200
+Subject: [PATCH 13/13] net: dsa: mt7530: get cpu-port via dp->cpu_dp instead
+ of constant
+
+Replace last occurences of hardcoded cpu-port by cpu_dp member of
+dsa_port struct.
+
+Now the constant can be dropped.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 27 ++++++++++++++++++++-------
+ drivers/net/dsa/mt7530.h | 1 -
+ 2 files changed, 20 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1038,6 +1038,7 @@ static int
+ mt7530_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+ {
++ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+@@ -1046,7 +1047,11 @@ mt7530_port_enable(struct dsa_switch *ds
+ * restore the port matrix if the port is the member of a certain
+ * bridge.
+ */
+- priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT));
++ if (dsa_port_is_user(dp)) {
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
++ priv->ports[port].pm |= PCR_MATRIX(BIT(cpu_dp->index));
++ }
+ priv->ports[port].enable = true;
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ priv->ports[port].pm);
+@@ -1194,7 +1199,8 @@ mt7530_port_bridge_join(struct dsa_switc
+ struct net_device *bridge)
+ {
+ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+- u32 port_bitmap = BIT(MT7530_CPU_PORT);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++ u32 port_bitmap = BIT(cpu_dp->index);
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+@@ -1271,9 +1277,12 @@ mt7530_port_set_vlan_unaware(struct dsa_
+ * the CPU port get out of VLAN filtering mode.
+ */
+ if (all_user_ports_removed) {
+- mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT),
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
++ mt7530_write(priv, MT7530_PCR_P(cpu_dp->index),
+ PCR_MATRIX(dsa_user_ports(priv->ds)));
+- mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), PORT_SPEC_TAG
++ mt7530_write(priv, MT7530_PVC_P(cpu_dp->index), PORT_SPEC_TAG
+ | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
+ }
+ }
+@@ -1311,6 +1320,7 @@ mt7530_port_bridge_leave(struct dsa_swit
+ struct net_device *bridge)
+ {
+ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
++ struct dsa_port *cpu_dp = dp->cpu_dp;
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+@@ -1339,8 +1349,8 @@ mt7530_port_bridge_leave(struct dsa_swit
+ */
+ if (priv->ports[port].enable)
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+- PCR_MATRIX(BIT(MT7530_CPU_PORT)));
+- priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
++ PCR_MATRIX(BIT(cpu_dp->index)));
++ priv->ports[port].pm = PCR_MATRIX(BIT(cpu_dp->index));
+
+ /* When a port is removed from the bridge, the port would be set up
+ * back to the default as is at initial boot which is a VLAN-unaware
+@@ -1503,6 +1513,9 @@ static int
+ mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+ {
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
+ if (vlan_filtering) {
+ /* The port is being kept as VLAN-unaware port when bridge is
+ * set up with vlan_filtering not being set, Otherwise, the
+@@ -1510,7 +1523,7 @@ mt7530_port_vlan_filtering(struct dsa_sw
+ * for becoming a VLAN-aware port.
+ */
+ mt7530_port_set_vlan_aware(ds, port);
+- mt7530_port_set_vlan_aware(ds, MT7530_CPU_PORT);
++ mt7530_port_set_vlan_aware(ds, cpu_dp->index);
+ } else {
+ mt7530_port_set_vlan_unaware(ds, port);
+ }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -8,7 +8,6 @@
+
+ #define MT7530_NUM_PORTS 7
+ #define MT7530_NUM_PHYS 5
+-#define MT7530_CPU_PORT 6
+ #define MT7530_NUM_FDB_RECORDS 2048
+ #define MT7530_ALL_MEMBERS 0xff
+
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1986,13 +1986,6 @@ int dsa_slave_create(struct dsa_port *po
+@@ -1977,13 +1977,6 @@ int dsa_slave_create(struct dsa_port *po
port->slave = slave_dev;
dsa_slave_setup_tagger(slave_dev);
netif_carrier_off(slave_dev);
ret = dsa_slave_phy_setup(slave_dev);
-@@ -2004,6 +1997,13 @@ int dsa_slave_create(struct dsa_port *po
+@@ -1995,6 +1988,13 @@ int dsa_slave_create(struct dsa_port *po
}
rtnl_lock();
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1997,14 +1997,12 @@ int dsa_slave_create(struct dsa_port *po
+@@ -1988,14 +1988,12 @@ int dsa_slave_create(struct dsa_port *po
}
rtnl_lock();
static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p)
{
return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
-@@ -916,6 +926,13 @@ struct dsa_switch_ops {
+@@ -949,6 +959,13 @@ struct dsa_switch_ops {
int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
u16 flags);
int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -2320,6 +2320,36 @@ static int dsa_slave_netdevice_event(str
+@@ -2311,6 +2311,36 @@ static int dsa_slave_netdevice_event(str
err = dsa_port_lag_change(dp, info->lower_state_info);
return notifier_from_errno(err);
}
case NETDEV_GOING_DOWN: {
struct dsa_port *dp, *cpu_dp;
struct dsa_switch_tree *dst;
-@@ -2331,6 +2361,8 @@ static int dsa_slave_netdevice_event(str
+@@ -2322,6 +2352,8 @@ static int dsa_slave_netdevice_event(str
cpu_dp = dev->dsa_ptr;
dst = cpu_dp->ds->dst;
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2443,8 +2443,8 @@ static irqreturn_t mtk_handle_irq_rx(int
+@@ -2381,8 +2381,8 @@ static irqreturn_t mtk_handle_irq_rx(int
eth->rx_events++;
if (likely(napi_schedule_prep(ð->rx_napi))) {
}
return IRQ_HANDLED;
-@@ -2456,8 +2456,8 @@ static irqreturn_t mtk_handle_irq_tx(int
+@@ -2394,8 +2394,8 @@ static irqreturn_t mtk_handle_irq_tx(int
eth->tx_events++;
if (likely(napi_schedule_prep(ð->tx_napi))) {
}
return IRQ_HANDLED;
-@@ -3623,6 +3623,8 @@ static int mtk_probe(struct platform_dev
+@@ -3585,6 +3585,8 @@ static int mtk_probe(struct platform_dev
* for NAPI to work
*/
init_dummy_netdev(ð->dummy_dev);
+ eth->dummy_dev.threaded = 1;
+ strcpy(eth->dummy_dev.name, "mtk_eth");
netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx,
- MTK_NAPI_WEIGHT);
+ NAPI_POLL_WEIGHT);
netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx,
sysfs_remove_link(&dev->dev.kobj, "phydev");
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -789,6 +789,12 @@ struct phy_driver {
+@@ -823,6 +823,12 @@ struct phy_driver {
/** @handle_interrupt: Override default interrupt handling */
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3675,6 +3675,7 @@ static const struct mtk_soc_data mt2701_
+@@ -3637,6 +3637,7 @@ static const struct mtk_soc_data mt2701_
.hw_features = MTK_HW_FEATURES,
.required_clks = MT7623_CLKS_BITMAP,
.required_pctl = true,
ret = mtk_mdio_busy_wait(eth);
if (ret < 0)
-@@ -683,6 +726,7 @@ static int mtk_mdio_init(struct mtk_eth
+@@ -621,6 +664,7 @@ static int mtk_mdio_init(struct mtk_eth
eth->mii_bus->name = "mdio";
eth->mii_bus->read = mtk_mdio_read;
eth->mii_bus->write = mtk_mdio_write;
+++ /dev/null
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -667,6 +667,7 @@ static void mtk_validate(struct phylink_
- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
- phylink_set(mask, 1000baseT_Full);
- phylink_set(mask, 1000baseX_Full);
-+ phylink_set(mask, 2500baseT_Full);
- phylink_set(mask, 2500baseX_Full);
- }
- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) {
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3290,6 +3290,7 @@ static const struct net_device_ops mtk_n
+@@ -3228,6 +3228,7 @@ static const struct net_device_ops mtk_n
static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
{
const __be32 *_id = of_get_property(np, "reg", NULL);
phy_interface_t phy_mode;
struct phylink *phylink;
-@@ -3385,6 +3386,9 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -3347,6 +3348,9 @@ static int mtk_add_mac(struct mtk_eth *e
else
eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
phydev->mii_ts->link_state(phydev->mii_ts, phydev);
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
-@@ -946,7 +946,8 @@ void phylink_destroy(struct phylink *pl)
+@@ -1315,7 +1315,8 @@ void phylink_destroy(struct phylink *pl)
}
EXPORT_SYMBOL_GPL(phylink_destroy);
bool tx_pause, rx_pause;
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -669,7 +669,7 @@ struct phy_device {
+@@ -703,7 +703,7 @@ struct phy_device {
u8 mdix;
u8 mdix_ctrl;
break;
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -586,6 +586,7 @@ struct phy_device {
+@@ -620,6 +620,7 @@ struct phy_device {
unsigned downshifted_rate:1;
unsigned is_on_sfp_module:1;
unsigned mac_managed_pm:1;