diff options
author | Christian Marangi <ansuelsmth@gmail.com> | 2023-09-26 13:47:44 +0200 |
---|---|---|
committer | Christian Marangi <ansuelsmth@gmail.com> | 2023-09-30 13:51:19 +0200 |
commit | a62b1544d15d5cfd9622efcd22b7b835297a1a41 (patch) | |
tree | e8b048150f929395e9f1096e80d24b71fa76f6c4 /target | |
parent | f166c9e6177123d8ef67150bb6376c217a261414 (diff) | |
download | openwrt-a62b1544d15d5cfd9622efcd22b7b835297a1a41.tar.gz openwrt-a62b1544d15d5cfd9622efcd22b7b835297a1a41.tar.bz2 openwrt-a62b1544d15d5cfd9622efcd22b7b835297a1a41.zip |
generic: 6.1: backport various qca8k fixes patch
Backport various QCA8K fixes patch merged upstream. Refresh any changed
patches due to backports.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Diffstat (limited to 'target')
9 files changed, 875 insertions, 5 deletions
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 index 0000000000..9331fd536d --- /dev/null +++ b/target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch @@ -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 index 0000000000..f66149ae37 --- /dev/null +++ b/target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch @@ -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 index 0000000000..3619440f07 --- /dev/null +++ b/target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch @@ -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 index 0000000000..b1c0a32e1b --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch @@ -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 index 0000000000..b430d2f792 --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch @@ -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 index 0000000000..9423c4b604 --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch @@ -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 index 0000000000..60de710965 --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch @@ -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) | diff --git a/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch b/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch index ba8216f783..3c89819ac0 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch @@ -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]; }; diff --git a/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch b/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch index e5cd03f1f3..58478f56cc 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch @@ -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; |