generic: 6.1: backport patch for multi CPU port support on QCA8K
authorChristian Marangi <ansuelsmth@gmail.com>
Tue, 20 Jun 2023 06:41:32 +0000 (08:41 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Sat, 30 Sep 2023 11:51:19 +0000 (13:51 +0200)
Backport pending patch for multi CPU port support on QCA8K. 6.1 already
supports all the requiredt code to change a DSA master port so only this
patch fixing the driver is required.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch [new file with mode: 0644]

diff --git a/target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch b/target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch
new file mode 100644 (file)
index 0000000..04935d0
--- /dev/null
@@ -0,0 +1,86 @@
+From 3b4329230db8750bea7a56ef07f07cbbf5fc6c5a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 4 Jul 2023 22:50:12 +0200
+Subject: [PATCH 19/20] net: dsa: qca8k: implement lag_fdb_add/del ops
+
+Implement lag_fdb_add/del ops to correctly support using LAG interface.
+Qca8k switch supports declaring fdb entry for link aggregation by simply
+setting the DES_PORT bits to all the LAG member.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c   |  2 ++
+ drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h        |  6 ++++
+ 3 files changed, 56 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1993,6 +1993,8 @@ static const struct dsa_switch_ops qca8k
+       .port_fdb_add           = qca8k_port_fdb_add,
+       .port_fdb_del           = qca8k_port_fdb_del,
+       .port_fdb_dump          = qca8k_port_fdb_dump,
++      .lag_fdb_add            = qca8k_lag_fdb_add,
++      .lag_fdb_del            = qca8k_lag_fdb_del,
+       .port_mdb_add           = qca8k_port_mdb_add,
+       .port_mdb_del           = qca8k_port_mdb_del,
+       .port_mirror_add        = qca8k_port_mirror_add,
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -1215,6 +1215,42 @@ int qca8k_port_lag_leave(struct dsa_swit
+       return qca8k_lag_refresh_portmap(ds, port, lag, true);
+ }
++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db)
++{
++      struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++      struct dsa_port *dp;
++      u16 port_mask = 0;
++
++      /* Set the vid to the port vlan id if no vid is set */
++      if (!vid)
++              vid = QCA8K_PORT_VID_DEF;
++
++      dsa_lag_foreach_port(dp, ds->dst, &lag)
++              port_mask |= BIT(dp->index);
++
++      return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
++}
++
++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db)
++{
++      struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++      struct dsa_port *dp;
++      u16 port_mask = 0;
++
++      /* Set the vid to the port vlan id if no vid is set */
++      if (!vid)
++              vid = QCA8K_PORT_VID_DEF;
++
++      dsa_lag_foreach_port(dp, ds->dst, &lag)
++      port_mask |= BIT(dp->index);
++
++      return qca8k_fdb_del(priv, addr, port_mask, vid);
++}
++
+ int qca8k_read_switch_id(struct qca8k_priv *priv)
+ {
+       u32 val;
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -590,5 +590,11 @@ int qca8k_port_lag_join(struct dsa_switc
+                       struct netlink_ext_ack *extack);
+ int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+                        struct dsa_lag lag);
++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db);
++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db);
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch b/target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch
new file mode 100644 (file)
index 0000000..27539b9
--- /dev/null
@@ -0,0 +1,37 @@
+From b954d61d9ecfa64450fc178586719dc2a95b92a7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 20 Jun 2023 21:48:24 +0200
+Subject: [PATCH 3/4] net: dsa: qca8k: enable flooding to both CPU port
+
+To permit a multi-CPU setup, flood all unknown frames to all CPU ports.
+Each CPU port should have correct LOOKUP MEMBER configuration to
+prevent receiving duplicate packets from user ports.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++--------
+ 1 file changed, 5 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1882,15 +1882,12 @@ qca8k_setup(struct dsa_switch *ds)
+               }
+       }
+-      /* Forward all unknown frames to CPU port for Linux processing
+-       * Notice that in multi-cpu config only one port should be set
+-       * for igmp, unknown, multicast and broadcast packet
+-       */
++      /* Forward all unknown frames to CPU port for Linux processing */
+       ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) |
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) |
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) |
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds)));
+       if (ret)
+               return ret;
diff --git a/target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch b/target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch
new file mode 100644 (file)
index 0000000..53a29f5
--- /dev/null
@@ -0,0 +1,158 @@
+From b2d6ebf2f92f8695c83fa6979f4ab579c588df76 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 20 Jun 2023 07:57:38 +0200
+Subject: [PATCH 4/4] net: dsa: qca8k: add support for port_change_master
+
+Add support for port_change_master to permit assigning an alternative
+CPU port if the switch have both CPU port connected or create a LAG on
+both CPU port and assign the LAG as DSA master.
+
+On port change master request, we check if the master is a LAG.
+With LAG we compose the cpu_port_mask with the CPU port in the LAG, if
+master is a simple dsa_port, we derive the index.
+
+Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit
+the port to receive packet by the new CPU port setup for the port and we
+refresh the CPU ports LOOKUP MEMBER configuration to reflect the new
+user port state.
+
+port_lag_join/leave is updated to refresh the user ports if we detect
+that the LAG is a DSA master and we have user port using it as a master.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 116 ++++++++++++++++++++++++++++++-
+ 1 file changed, 114 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1719,6 +1719,117 @@ qca8k_get_tag_protocol(struct dsa_switch
+       return DSA_TAG_PROTO_QCA;
+ }
++static int qca8k_port_change_master(struct dsa_switch *ds, int port,
++                                  struct net_device *master,
++                                  struct netlink_ext_ack *extack)
++{
++      struct dsa_switch_tree *dst = ds->dst;
++      struct qca8k_priv *priv = ds->priv;
++      u8 cpu_port_mask = 0;
++      struct dsa_port *dp;
++      u32 val;
++      int ret;
++
++      /* With LAG of CPU port, compose the mask for port LOOKUP MEMBER */
++      if (netif_is_lag_master(master)) {
++              struct dsa_lag *lag;
++              int id;
++
++              id = dsa_lag_id(dst, master);
++              lag = dsa_lag_by_id(dst, id);
++
++              dsa_lag_foreach_port(dp, dst, lag)
++                      if (dsa_port_is_cpu(dp))
++                              cpu_port_mask |= BIT(dp->index);
++      } else {
++              dp = master->dsa_ptr;
++              cpu_port_mask |= BIT(dp->index);
++      }
++
++      /* Connect port to new cpu port */
++      ret = regmap_read(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), &val);
++      if (ret)
++              return ret;
++
++      /* Reset connected CPU port in port LOOKUP MEMBER */
++      val &=  ~dsa_cpu_ports(ds);
++      /* Assign the new CPU port in port LOOKUP MEMBER */
++      val |= cpu_port_mask;
++
++      ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
++                               QCA8K_PORT_LOOKUP_MEMBER,
++                               val);
++      if (ret)
++              return ret;
++
++      /* Refresh CPU port LOOKUP MEMBER with new port */
++      dsa_tree_for_each_cpu_port(dp, ds->dst) {
++              u32 reg = QCA8K_PORT_LOOKUP_CTRL(dp->index);
++
++              /* If CPU port in mask assign port, else remove port */
++              if (BIT(dp->index) & cpu_port_mask)
++                      ret = regmap_set_bits(priv->regmap, reg, BIT(port));
++              else
++                      ret = regmap_clear_bits(priv->regmap, reg, BIT(port));
++
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int qca8k_port_lag_refresh_user_ports(struct dsa_switch *ds,
++                                           struct dsa_lag lag)
++{
++      struct net_device *lag_dev = lag.dev;
++      struct dsa_port *dp;
++      int ret;
++
++      /* Ignore if LAG is not a DSA master */
++      if (!netif_is_lag_master(lag_dev))
++              return 0;
++
++      dsa_switch_for_each_user_port(dp, ds) {
++              /* Skip if assigned master is not the LAG */
++              if (dsa_port_to_master(dp) != lag_dev)
++                      continue;
++
++              ret = qca8k_port_change_master(ds, dp->index,
++                                             lag_dev, NULL);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int qca8xxx_port_lag_join(struct dsa_switch *ds, int port,
++                               struct dsa_lag lag,
++                               struct netdev_lag_upper_info *info,
++                               struct netlink_ext_ack *extack)
++{
++      int ret;
++
++      ret = qca8k_port_lag_join(ds, port, lag, info, extack);
++      if (ret)
++              return ret;
++
++      return qca8k_port_lag_refresh_user_ports(ds, lag);
++}
++
++static int qca8xxx_port_lag_leave(struct dsa_switch *ds, int port,
++                                struct dsa_lag lag)
++{
++      int ret;
++
++      ret = qca8k_port_lag_leave(ds, port, lag);
++      if (ret)
++              return ret;
++
++      return qca8k_port_lag_refresh_user_ports(ds, lag);
++}
++
+ static void
+ qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
+                   bool operational)
+@@ -2005,8 +2116,9 @@ static const struct dsa_switch_ops qca8k
+       .phylink_mac_link_down  = qca8k_phylink_mac_link_down,
+       .phylink_mac_link_up    = qca8k_phylink_mac_link_up,
+       .get_phy_flags          = qca8k_get_phy_flags,
+-      .port_lag_join          = qca8k_port_lag_join,
+-      .port_lag_leave         = qca8k_port_lag_leave,
++      .port_lag_join          = qca8xxx_port_lag_join,
++      .port_lag_leave         = qca8xxx_port_lag_leave,
++      .port_change_master     = qca8k_port_change_master,
+       .master_state_change    = qca8k_master_change,
+       .connect_tag_protocol   = qca8k_connect_tag_protocol,
+ };