generic: 6.1: backport various qca8k fixes patch
authorChristian Marangi <ansuelsmth@gmail.com>
Tue, 26 Sep 2023 11:47:44 +0000 (13:47 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Sat, 30 Sep 2023 11:51:19 +0000 (13:51 +0200)
Backport various QCA8K fixes patch merged upstream. Refresh any changed
patches due to backports.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch
target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch

diff --git a/target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch b/target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch
new file mode 100644 (file)
index 0000000..9331fd5
--- /dev/null
@@ -0,0 +1,63 @@
+From e03cea60c3db8c6b011cc36ecef9281dff8377f3 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 25 Jan 2023 21:35:16 +0100
+Subject: [PATCH] net: dsa: qca8k: add QCA8K_ATU_TABLE_SIZE define for fdb
+ access
+
+Add and use QCA8K_ATU_TABLE_SIZE instead of hardcoding the ATU size with
+a pure number and using sizeof on the array.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-common.c | 10 ++++++----
+ drivers/net/dsa/qca/qca8k.h        |  2 ++
+ 2 files changed, 8 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -150,11 +150,12 @@ static int qca8k_busy_wait(struct qca8k_
+ static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+ {
+-      u32 reg[3];
++      u32 reg[QCA8K_ATU_TABLE_SIZE];
+       int ret;
+       /* load the ARL table into an array */
+-      ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++      ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg,
++                            QCA8K_ATU_TABLE_SIZE * sizeof(u32));
+       if (ret)
+               return ret;
+@@ -178,7 +179,7 @@ static int qca8k_fdb_read(struct qca8k_p
+ static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask,
+                           const u8 *mac, u8 aging)
+ {
+-      u32 reg[3] = { 0 };
++      u32 reg[QCA8K_ATU_TABLE_SIZE] = { 0 };
+       /* vid - 83:72 */
+       reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+@@ -195,7 +196,8 @@ static void qca8k_fdb_write(struct qca8k
+       reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+       /* load the array into the ARL table */
+-      qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++      qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg,
++                       QCA8K_ATU_TABLE_SIZE * sizeof(u32));
+ }
+ static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd,
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -148,6 +148,8 @@
+ #define QCA8K_REG_IPV4_PRI_ADDR_MASK                  0x474
+ /* Lookup registers */
++#define QCA8K_ATU_TABLE_SIZE                          3 /* 12 bytes wide table / sizeof(u32) */
++
+ #define QCA8K_REG_ATU_DATA0                           0x600
+ #define   QCA8K_ATU_ADDR2_MASK                                GENMASK(31, 24)
+ #define   QCA8K_ATU_ADDR3_MASK                                GENMASK(23, 16)
diff --git a/target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch b/target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch
new file mode 100644 (file)
index 0000000..f66149a
--- /dev/null
@@ -0,0 +1,261 @@
+From c766e077d927e1775902c18827205ea2ade3a35d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 25 Jan 2023 21:35:17 +0100
+Subject: [PATCH] net: dsa: qca8k: convert to regmap read/write API
+
+Convert qca8k to regmap read/write bulk API. The mgmt eth can write up
+to 32 bytes of data at times. Currently we use a custom function to do
+it but regmap now supports declaration of read/write bulk even without a
+bus.
+
+Drop the custom function and rework the regmap function to this new
+implementation.
+
+Rework the qca8k_fdb_read/write function to use the new
+regmap_bulk_read/write as the old qca8k_bulk_read/write are now dropped.
+
+Cc: Mark Brown <broonie@kernel.org>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c   | 92 ++++++++++++++++++++++++------
+ drivers/net/dsa/qca/qca8k-common.c | 47 ++-------------
+ drivers/net/dsa/qca/qca8k.h        |  3 -
+ 3 files changed, 77 insertions(+), 65 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -425,16 +425,12 @@ qca8k_regmap_update_bits_eth(struct qca8
+ }
+ static int
+-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
++qca8k_read_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t *val)
+ {
+-      struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+       struct mii_bus *bus = priv->bus;
+       u16 r1, r2, page;
+       int ret;
+-      if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
+-              return 0;
+-
+       qca8k_split_addr(reg, &r1, &r2, &page);
+       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+@@ -451,16 +447,12 @@ exit:
+ }
+ static int
+-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
++qca8k_write_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t val)
+ {
+-      struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+       struct mii_bus *bus = priv->bus;
+       u16 r1, r2, page;
+       int ret;
+-      if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
+-              return 0;
+-
+       qca8k_split_addr(reg, &r1, &r2, &page);
+       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+@@ -477,17 +469,14 @@ exit:
+ }
+ static int
+-qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
++qca8k_regmap_update_bits_mii(struct qca8k_priv *priv, uint32_t reg,
++                           uint32_t mask, uint32_t write_val)
+ {
+-      struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+       struct mii_bus *bus = priv->bus;
+       u16 r1, r2, page;
+       u32 val;
+       int ret;
+-      if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
+-              return 0;
+-
+       qca8k_split_addr(reg, &r1, &r2, &page);
+       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+@@ -510,17 +499,84 @@ exit:
+       return ret;
+ }
++static int
++qca8k_bulk_read(void *ctx, const void *reg_buf, size_t reg_len,
++              void *val_buf, size_t val_len)
++{
++      int i, count = val_len / sizeof(u32), ret;
++      u32 reg = *(u32 *)reg_buf & U16_MAX;
++      struct qca8k_priv *priv = ctx;
++
++      if (priv->mgmt_master &&
++          !qca8k_read_eth(priv, reg, val_buf, val_len))
++              return 0;
++
++      /* loop count times and increment reg of 4 */
++      for (i = 0; i < count; i++, reg += sizeof(u32)) {
++              ret = qca8k_read_mii(priv, reg, val_buf + i);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int
++qca8k_bulk_gather_write(void *ctx, const void *reg_buf, size_t reg_len,
++                      const void *val_buf, size_t val_len)
++{
++      int i, count = val_len / sizeof(u32), ret;
++      u32 reg = *(u32 *)reg_buf & U16_MAX;
++      struct qca8k_priv *priv = ctx;
++      u32 *val = (u32 *)val_buf;
++
++      if (priv->mgmt_master &&
++          !qca8k_write_eth(priv, reg, val, val_len))
++              return 0;
++
++      /* loop count times, increment reg of 4 and increment val ptr to
++       * the next value
++       */
++      for (i = 0; i < count; i++, reg += sizeof(u32), val++) {
++              ret = qca8k_write_mii(priv, reg, *val);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int
++qca8k_bulk_write(void *ctx, const void *data, size_t bytes)
++{
++      return qca8k_bulk_gather_write(ctx, data, sizeof(u16), data + sizeof(u16),
++                                     bytes - sizeof(u16));
++}
++
++static int
++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
++{
++      struct qca8k_priv *priv = ctx;
++
++      if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
++              return 0;
++
++      return qca8k_regmap_update_bits_mii(priv, reg, mask, write_val);
++}
++
+ static struct regmap_config qca8k_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = 0x16ac, /* end MIB - Port6 range */
+-      .reg_read = qca8k_regmap_read,
+-      .reg_write = qca8k_regmap_write,
++      .read = qca8k_bulk_read,
++      .write = qca8k_bulk_write,
+       .reg_update_bits = qca8k_regmap_update_bits,
+       .rd_table = &qca8k_readable_table,
+       .disable_locking = true, /* Locking is handled by qca8k read/write */
+       .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
++      .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */
++      .max_raw_write = 32,
+ };
+ static int
+@@ -2091,8 +2147,6 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ static const struct qca8k_info_ops qca8xxx_ops = {
+       .autocast_mib = qca8k_get_ethtool_stats_eth,
+-      .read_eth = qca8k_read_eth,
+-      .write_eth = qca8k_write_eth,
+ };
+ static const struct qca8k_match_data qca8327 = {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -101,45 +101,6 @@ const struct regmap_access_table qca8k_r
+       .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+ };
+-/* TODO: remove these extra ops when we can support regmap bulk read/write */
+-static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+-      int i, count = len / sizeof(u32), ret;
+-
+-      if (priv->mgmt_master && priv->info->ops->read_eth &&
+-          !priv->info->ops->read_eth(priv, reg, val, len))
+-              return 0;
+-
+-      for (i = 0; i < count; i++) {
+-              ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+-/* TODO: remove these extra ops when we can support regmap bulk read/write */
+-static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+-      int i, count = len / sizeof(u32), ret;
+-      u32 tmp;
+-
+-      if (priv->mgmt_master && priv->info->ops->write_eth &&
+-          !priv->info->ops->write_eth(priv, reg, val, len))
+-              return 0;
+-
+-      for (i = 0; i < count; i++) {
+-              tmp = val[i];
+-
+-              ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+ static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+       u32 val;
+@@ -154,8 +115,8 @@ static int qca8k_fdb_read(struct qca8k_p
+       int ret;
+       /* load the ARL table into an array */
+-      ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg,
+-                            QCA8K_ATU_TABLE_SIZE * sizeof(u32));
++      ret = regmap_bulk_read(priv->regmap, QCA8K_REG_ATU_DATA0, reg,
++                             QCA8K_ATU_TABLE_SIZE);
+       if (ret)
+               return ret;
+@@ -196,8 +157,8 @@ static void qca8k_fdb_write(struct qca8k
+       reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+       /* load the array into the ARL table */
+-      qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg,
+-                       QCA8K_ATU_TABLE_SIZE * sizeof(u32));
++      regmap_bulk_write(priv->regmap, QCA8K_REG_ATU_DATA0, reg,
++                        QCA8K_ATU_TABLE_SIZE);
+ }
+ static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd,
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -330,9 +330,6 @@ struct qca8k_priv;
+ struct qca8k_info_ops {
+       int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data);
+-      /* TODO: remove these extra ops when we can support regmap bulk read/write */
+-      int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+-      int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+ };
+ struct qca8k_match_data {
diff --git a/target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch b/target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch
new file mode 100644 (file)
index 0000000..3619440
--- /dev/null
@@ -0,0 +1,78 @@
+From 2c39dd025da489cf87d26469d9f5ff19715324a0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 24 Jul 2023 05:25:28 +0200
+Subject: [PATCH 1/4] net: dsa: qca8k: enable use_single_write for qca8xxx
+
+The qca8xxx switch supports 2 way to write reg values, a slow way using
+mdio and a fast way by sending specially crafted mgmt packet to
+read/write reg.
+
+The fast way can support up to 32 bytes of data as eth packet are used
+to send/receive.
+
+This correctly works for almost the entire regmap of the switch but with
+the use of some kernel selftests for dsa drivers it was found a funny
+and interesting hw defect/limitation.
+
+For some specific reg, bulk write won't work and will result in writing
+only part of the requested regs resulting in half data written. This was
+especially hard to track and discover due to the total strangeness of
+the problem and also by the specific regs where this occurs.
+
+This occurs in the specific regs of the ATU table, where multiple entry
+needs to be written to compose the entire entry.
+It was discovered that with a bulk write of 12 bytes on
+QCA8K_REG_ATU_DATA0 only QCA8K_REG_ATU_DATA0 and QCA8K_REG_ATU_DATA2
+were written, but QCA8K_REG_ATU_DATA1 was always zero.
+Tcpdump was used to make sure the specially crafted packet was correct
+and this was confirmed.
+
+The problem was hard to track as the lack of QCA8K_REG_ATU_DATA1
+resulted in an entry somehow possible as the first bytes of the mac
+address are set in QCA8K_REG_ATU_DATA0 and the entry type is set in
+QCA8K_REG_ATU_DATA2.
+
+Funlly enough writing QCA8K_REG_ATU_DATA1 results in the same problem
+with QCA8K_REG_ATU_DATA2 empty and QCA8K_REG_ATU_DATA1 and
+QCA8K_REG_ATU_FUNC correctly written.
+A speculation on the problem might be that there are some kind of
+indirection internally when accessing these regs and they can't be
+accessed all together, due to the fact that it's really a table mapped
+somewhere in the switch SRAM.
+
+Even more funny is the fact that every other reg was tested with all
+kind of combination and they are not affected by this problem. Read
+operation was also tested and always worked so it's not affected by this
+problem.
+
+The problem is not present if we limit writing a single reg at times.
+
+To handle this hardware defect, enable use_single_write so that bulk
+api can correctly split the write in multiple different operation
+effectively reverting to a non-bulk write.
+
+Cc: Mark Brown <broonie@kernel.org>
+Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -575,8 +575,11 @@ static struct regmap_config qca8k_regmap
+       .rd_table = &qca8k_readable_table,
+       .disable_locking = true, /* Locking is handled by qca8k read/write */
+       .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
+-      .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */
+-      .max_raw_write = 32,
++      .max_raw_read = 32, /* mgmt eth can read up to 8 registers at time */
++      /* ATU regs suffer from a bug where some data are not correctly
++       * written. Disable bulk write to correctly write ATU entry.
++       */
++      .use_single_write = true,
+ };
+ static int
diff --git a/target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch b/target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch
new file mode 100644 (file)
index 0000000..b1c0a32
--- /dev/null
@@ -0,0 +1,146 @@
+From 23cfc7172e5297d0bee49ac6f6f8248d1cf0820d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sun, 30 Jul 2023 09:41:10 +0200
+Subject: [PATCH 1/4] net: dsa: qca8k: make learning configurable and keep off
+ if standalone
+
+Address learning should initially be turned off by the driver for port
+operation in standalone mode, then the DSA core handles changes to it
+via ds->ops->port_bridge_flags().
+
+Currently this is not the case for qca8k where learning is enabled
+unconditionally in qca8k_setup for every user port.
+
+Handle ports configured in standalone mode by making the learning
+configurable and not enabling it by default.
+
+Implement .port_pre_bridge_flags and .port_bridge_flags dsa ops to
+enable learning for bridge that request it and tweak
+.port_stp_state_set to correctly disable learning when port is
+configured in standalone mode.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Link: https://lore.kernel.org/r/20230730074113.21889-2-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c   |  7 +++--
+ drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h        |  6 ++++
+ 3 files changed, 58 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1883,9 +1883,8 @@ qca8k_setup(struct dsa_switch *ds)
+                       if (ret)
+                               return ret;
+-                      /* Enable ARP Auto-learning by default */
+-                      ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+-                                            QCA8K_PORT_LOOKUP_LEARN);
++                      ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
++                                              QCA8K_PORT_LOOKUP_LEARN);
+                       if (ret)
+                               return ret;
+@@ -1991,6 +1990,8 @@ static const struct dsa_switch_ops qca8k
+       .port_change_mtu        = qca8k_port_change_mtu,
+       .port_max_mtu           = qca8k_port_max_mtu,
+       .port_stp_state_set     = qca8k_port_stp_state_set,
++      .port_pre_bridge_flags  = qca8k_port_pre_bridge_flags,
++      .port_bridge_flags      = qca8k_port_bridge_flags,
+       .port_bridge_join       = qca8k_port_bridge_join,
+       .port_bridge_leave      = qca8k_port_bridge_leave,
+       .port_fast_age          = qca8k_port_fast_age,
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -565,9 +565,26 @@ int qca8k_get_mac_eee(struct dsa_switch
+       return 0;
+ }
++static int qca8k_port_configure_learning(struct dsa_switch *ds, int port,
++                                       bool learning)
++{
++      struct qca8k_priv *priv = ds->priv;
++
++      if (learning)
++              return regmap_set_bits(priv->regmap,
++                                     QCA8K_PORT_LOOKUP_CTRL(port),
++                                     QCA8K_PORT_LOOKUP_LEARN);
++      else
++              return regmap_clear_bits(priv->regmap,
++                                       QCA8K_PORT_LOOKUP_CTRL(port),
++                                       QCA8K_PORT_LOOKUP_LEARN);
++}
++
+ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+ {
++      struct dsa_port *dp = dsa_to_port(ds, port);
+       struct qca8k_priv *priv = ds->priv;
++      bool learning = false;
+       u32 stp_state;
+       switch (state) {
+@@ -582,8 +599,11 @@ void qca8k_port_stp_state_set(struct dsa
+               break;
+       case BR_STATE_LEARNING:
+               stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
++              learning = dp->learning;
+               break;
+       case BR_STATE_FORWARDING:
++              learning = dp->learning;
++              fallthrough;
+       default:
+               stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
+               break;
+@@ -591,6 +611,34 @@ void qca8k_port_stp_state_set(struct dsa
+       qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+                 QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
++
++      qca8k_port_configure_learning(ds, port, learning);
++}
++
++int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
++                              struct switchdev_brport_flags flags,
++                              struct netlink_ext_ack *extack)
++{
++      if (flags.mask & ~BR_LEARNING)
++              return -EINVAL;
++
++      return 0;
++}
++
++int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,
++                          struct switchdev_brport_flags flags,
++                          struct netlink_ext_ack *extack)
++{
++      int ret;
++
++      if (flags.mask & BR_LEARNING) {
++              ret = qca8k_port_configure_learning(ds, port,
++                                                  flags.val & BR_LEARNING);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
+ }
+ int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -448,6 +448,12 @@ int qca8k_get_mac_eee(struct dsa_switch
+ /* Common bridge function */
+ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
++int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
++                              struct switchdev_brport_flags flags,
++                              struct netlink_ext_ack *extack);
++int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,
++                          struct switchdev_brport_flags flags,
++                          struct netlink_ext_ack *extack);
+ int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
+                          struct dsa_bridge bridge,
+                          bool *tx_fwd_offload,
diff --git a/target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch b/target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch
new file mode 100644 (file)
index 0000000..b430d2f
--- /dev/null
@@ -0,0 +1,53 @@
+From 18e8feae4a807994e4906d659116d249bfecd4c5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sun, 30 Jul 2023 09:41:11 +0200
+Subject: [PATCH 2/4] net: dsa: qca8k: limit user ports access to the first CPU
+ port on setup
+
+In preparation for multi-CPU support, set CPU port LOOKUP MEMBER outside
+the port loop and setup the LOOKUP MEMBER mask for user ports only to
+the first CPU port.
+
+This is to handle flooding condition where every CPU port is set as
+target and prevent packet duplication for unknown frames from user ports.
+
+Secondary CPU port LOOKUP MEMBER mask will be setup later when
+port_change_master will be implemented.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20230730074113.21889-3-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 14 ++++++--------
+ 1 file changed, 6 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1863,18 +1863,16 @@ qca8k_setup(struct dsa_switch *ds)
+       if (ret)
+               return ret;
++      /* CPU port gets connected to all user ports of the switch */
++      ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
++                      QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
++      if (ret)
++              return ret;
++
+       /* Setup connection between CPU port & user ports
+        * Configure specific switch configuration for ports
+        */
+       for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+-              /* CPU port gets connected to all user ports of the switch */
+-              if (dsa_is_cpu_port(ds, i)) {
+-                      ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+-                                      QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+-                      if (ret)
+-                              return ret;
+-              }
+-
+               /* Individual user ports get connected to CPU port only */
+               if (dsa_is_user_port(ds, i)) {
+                       ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
diff --git a/target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch b/target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch
new file mode 100644 (file)
index 0000000..9423c4b
--- /dev/null
@@ -0,0 +1,111 @@
+From a9108b0712bf018dc69020864b21485b71b17dfc Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sun, 30 Jul 2023 09:41:12 +0200
+Subject: [PATCH 3/4] net: dsa: qca8k: move qca8xxx hol fixup to separate
+ function
+
+Move qca8xxx hol fixup to separate function to tidy things up and to
+permit using a more efficent loop in future patch.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Link: https://lore.kernel.org/r/20230730074113.21889-4-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 78 +++++++++++++++++---------------
+ 1 file changed, 42 insertions(+), 36 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1773,6 +1773,46 @@ static int qca8k_connect_tag_protocol(st
+       return 0;
+ }
++static void qca8k_setup_hol_fixup(struct qca8k_priv *priv, int port)
++{
++      u32 mask;
++
++      switch (port) {
++      /* The 2 CPU port and port 5 requires some different
++       * priority than any other ports.
++       */
++      case 0:
++      case 5:
++      case 6:
++              mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
++              break;
++      default:
++              mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
++                      QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
++      }
++      regmap_write(priv->regmap, QCA8K_REG_PORT_HOL_CTRL0(port), mask);
++
++      mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
++             QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++             QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++             QCA8K_PORT_HOL_CTRL1_WRED_EN;
++      regmap_update_bits(priv->regmap, QCA8K_REG_PORT_HOL_CTRL1(port),
++                         QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
++                         QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++                         QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++                         QCA8K_PORT_HOL_CTRL1_WRED_EN,
++                         mask);
++}
++
+ static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+@@ -1908,42 +1948,8 @@ qca8k_setup(struct dsa_switch *ds)
+                * missing settings to improve switch stability under load condition.
+                * This problem is limited to qca8337 and other qca8k switch are not affected.
+                */
+-              if (priv->switch_id == QCA8K_ID_QCA8337) {
+-                      switch (i) {
+-                      /* The 2 CPU port and port 5 requires some different
+-                       * priority than any other ports.
+-                       */
+-                      case 0:
+-                      case 5:
+-                      case 6:
+-                              mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
+-                              break;
+-                      default:
+-                              mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
+-                                      QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
+-                      }
+-                      qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
+-
+-                      mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
+-                      QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+-                      QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+-                      QCA8K_PORT_HOL_CTRL1_WRED_EN;
+-                      qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
+-                                QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
+-                                QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+-                                QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+-                                QCA8K_PORT_HOL_CTRL1_WRED_EN,
+-                                mask);
+-              }
++              if (priv->switch_id == QCA8K_ID_QCA8337)
++                      qca8k_setup_hol_fixup(priv, i);
+       }
+       /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
diff --git a/target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch b/target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch
new file mode 100644 (file)
index 0000000..60de710
--- /dev/null
@@ -0,0 +1,158 @@
+From 01e6f8ad8d26ced14b0cf288c42e55d03a7c5070 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sun, 30 Jul 2023 09:41:13 +0200
+Subject: [PATCH 4/4] net: dsa: qca8k: use dsa_for_each macro instead of for
+ loop
+
+Convert for loop to dsa_for_each macro to save some redundant write on
+unconnected/unused port and tidy things up.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Link: https://lore.kernel.org/r/20230730074113.21889-5-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 107 ++++++++++++++++---------------
+ 1 file changed, 54 insertions(+), 53 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1817,7 +1817,8 @@ static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+       struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-      int cpu_port, ret, i;
++      struct dsa_port *dp;
++      int cpu_port, ret;
+       u32 mask;
+       cpu_port = qca8k_find_cpu_port(ds);
+@@ -1868,27 +1869,27 @@ qca8k_setup(struct dsa_switch *ds)
+               dev_warn(priv->dev, "mib init failed");
+       /* Initial setup of all ports */
+-      for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++      dsa_switch_for_each_port(dp, ds) {
+               /* Disable forwarding by default on all ports */
+-              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(dp->index),
+                               QCA8K_PORT_LOOKUP_MEMBER, 0);
+               if (ret)
+                       return ret;
++      }
+-              /* Enable QCA header mode on all cpu ports */
+-              if (dsa_is_cpu_port(ds, i)) {
+-                      ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
+-                                        FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
+-                                        FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
+-                      if (ret) {
+-                              dev_err(priv->dev, "failed enabling QCA header mode");
+-                              return ret;
+-                      }
++      /* Disable MAC by default on all user ports */
++      dsa_switch_for_each_user_port(dp, ds)
++              qca8k_port_set_status(priv, dp->index, 0);
++
++      /* Enable QCA header mode on all cpu ports */
++      dsa_switch_for_each_cpu_port(dp, ds) {
++              ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(dp->index),
++                                FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
++                                FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
++              if (ret) {
++                      dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index);
++                      return ret;
+               }
+-
+-              /* Disable MAC by default on all user ports */
+-              if (dsa_is_user_port(ds, i))
+-                      qca8k_port_set_status(priv, i, 0);
+       }
+       /* Forward all unknown frames to CPU port for Linux processing
+@@ -1910,48 +1911,48 @@ qca8k_setup(struct dsa_switch *ds)
+               return ret;
+       /* Setup connection between CPU port & user ports
+-       * Configure specific switch configuration for ports
++       * Individual user ports get connected to CPU port only
+        */
+-      for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+-              /* Individual user ports get connected to CPU port only */
+-              if (dsa_is_user_port(ds, i)) {
+-                      ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+-                                      QCA8K_PORT_LOOKUP_MEMBER,
+-                                      BIT(cpu_port));
+-                      if (ret)
+-                              return ret;
+-
+-                      ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+-                                              QCA8K_PORT_LOOKUP_LEARN);
+-                      if (ret)
+-                              return ret;
+-
+-                      /* For port based vlans to work we need to set the
+-                       * default egress vid
+-                       */
+-                      ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+-                                      QCA8K_EGREES_VLAN_PORT_MASK(i),
+-                                      QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
+-                      if (ret)
+-                              return ret;
+-
+-                      ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
+-                                        QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
+-                                        QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
+-                      if (ret)
+-                              return ret;
+-              }
++      dsa_switch_for_each_user_port(dp, ds) {
++              u8 port = dp->index;
+-              /* The port 5 of the qca8337 have some problem in flood condition. The
+-               * original legacy driver had some specific buffer and priority settings
+-               * for the different port suggested by the QCA switch team. Add this
+-               * missing settings to improve switch stability under load condition.
+-               * This problem is limited to qca8337 and other qca8k switch are not affected.
++              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++                              QCA8K_PORT_LOOKUP_MEMBER,
++                              BIT(cpu_port));
++              if (ret)
++                      return ret;
++
++              ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
++                                      QCA8K_PORT_LOOKUP_LEARN);
++              if (ret)
++                      return ret;
++
++              /* For port based vlans to work we need to set the
++               * default egress vid
+                */
+-              if (priv->switch_id == QCA8K_ID_QCA8337)
+-                      qca8k_setup_hol_fixup(priv, i);
++              ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
++                              QCA8K_EGREES_VLAN_PORT_MASK(port),
++                              QCA8K_EGREES_VLAN_PORT(port, QCA8K_PORT_VID_DEF));
++              if (ret)
++                      return ret;
++
++              ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
++                                QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
++                                QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
++              if (ret)
++                      return ret;
+       }
++      /* The port 5 of the qca8337 have some problem in flood condition. The
++       * original legacy driver had some specific buffer and priority settings
++       * for the different port suggested by the QCA switch team. Add this
++       * missing settings to improve switch stability under load condition.
++       * This problem is limited to qca8337 and other qca8k switch are not affected.
++       */
++      if (priv->switch_id == QCA8K_ID_QCA8337)
++              dsa_switch_for_each_available_port(dp, ds)
++                      qca8k_setup_hol_fixup(priv, dp->index);
++
+       /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+       if (priv->switch_id == QCA8K_ID_QCA8327) {
+               mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
index ba8216f78305ac3777fcd8df69bebed81a65b341..3c89819ac08aa4df19a02e8319d10ba81add2eea 100644 (file)
@@ -20,7 +20,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
 
 --- a/drivers/net/dsa/qca/qca8k-8xxx.c
 +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -719,21 +719,6 @@ err_clear_skb:
+@@ -778,21 +778,6 @@ err_clear_skb:
        return ret;
  }
  
@@ -44,7 +44,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
  {
 --- a/drivers/net/dsa/qca/qca8k.h
 +++ b/drivers/net/dsa/qca/qca8k.h
-@@ -422,6 +422,20 @@ struct qca8k_fdb {
+@@ -421,6 +421,20 @@ struct qca8k_fdb {
        u8 mac[6];
  };
  
index e5cd03f1f301439318cc5e098709145f496061a8..58478f56ccc40aece9537d23dce949c3f425dd29 100644 (file)
@@ -71,7 +71,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
  
  static void
  qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
-@@ -1729,6 +1730,10 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -1829,6 +1830,10 @@ qca8k_setup(struct dsa_switch *ds)
        if (ret)
                return ret;
  
@@ -386,7 +386,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
  #define QCA8K_GOL_MAC_ADDR0                           0x60
  #define QCA8K_GOL_MAC_ADDR1                           0x64
  #define QCA8K_MAX_FRAME_SIZE                          0x78
-@@ -383,6 +429,19 @@ struct qca8k_pcs {
+@@ -382,6 +428,19 @@ struct qca8k_pcs {
        int port;
  };
  
@@ -406,7 +406,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
  struct qca8k_priv {
        u8 switch_id;
        u8 switch_revision;
-@@ -407,6 +466,7 @@ struct qca8k_priv {
+@@ -406,6 +465,7 @@ struct qca8k_priv {
        struct qca8k_pcs pcs_port_0;
        struct qca8k_pcs pcs_port_6;
        const struct qca8k_match_data *info;