All of lore.kernel.org
 help / color / mirror / Atom feed
* [net-next PATCH 00/19] Multiple cleanup and feature for qca8k
@ 2021-11-17 21:04 Ansuel Smith
  2021-11-17 21:04 ` regmap: allow to define reg_update_bits for no bus configuration Ansuel Smith
                   ` (18 more replies)
  0 siblings, 19 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

This series contains 3 main patch groups:
- Cleanup with conversion of the driver to bitfield macro and regmap.
- Add multiple feature mdb add/del, lag support, ageing and fast age.
- Code split of common code from specific code.

The first patch is just a reference from linux-next needed for the
regmap conversion.

As said in the commits, the code split is required as ipq40xx internal
switch is based on the same qca8k reg but use a different way to
read/write to the switch regs. We convert the driver to the generic
regmap and we split the driver to common and specific code.

This also contains a patch to fix a corner case when and if multi cpu
will be supported to DSA.
We add mdb add/del using the ARL table.
We add ageing support and fast age.
We add support for mirror mode.
We add 2 additional MIB present on qca8337.

The regmap conversion patch and the lag patch contains checkpatch
warning for too long line and these error are not fixed to not make the
definition of these regs pratically not readable.

Ansuel Smith (19):
  regmap: allow to define reg_update_bits for no bus configuration
  net: dsa: qca8k: remove redundant check in parse_port_config
  net: dsa: qca8k: skip sgmii delay on double cpu conf
  net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET
  net: dsa: qca8k: move read switch id function in qca8k_setup
  net: dsa: qca8k: remove extra mutex_init in qca8k_setup
  net: dsa: qca8k: set regmap init as mandatory for regmap conversion
  net: dsa: qca8k: convert qca8k to regmap helper
  net: dsa: qca8k: add additional MIB counter and make it dynamic
  net: dsa: qca8k: add support for port fast aging
  net: dsa: qca8k: add support for mirror mode
  net: dsa: qca8k: add set_ageing_time support
  net: dsa: qca8k: add min/max ageing time
  net: dsa: qca8k: add support for mdb_add/del
  net: dsa: qca8k: add LAG support
  net: dsa: qca8k: enable mtu_enforcement_ingress
  net: dsa: qca8k: move qca8k to qca dir
  net: dsa: qca8k: use device_get_match_data instead of the OF variant
  net: dsa: qca8k: split qca8k in common and 8xxx specific code

 drivers/base/regmap/regmap.c                  |    1 +
 drivers/net/dsa/Kconfig                       |    8 -
 drivers/net/dsa/Makefile                      |    1 -
 drivers/net/dsa/qca/Kconfig                   |    9 +
 drivers/net/dsa/qca/Makefile                  |    2 +
 drivers/net/dsa/{qca8k.c => qca/qca8k-8xxx.c} | 1144 +++-------------
 drivers/net/dsa/qca/qca8k-common.c            | 1157 +++++++++++++++++
 drivers/net/dsa/qca/qca8k.h                   |  413 ++++++
 drivers/net/dsa/qca8k.h                       |  311 -----
 include/linux/regmap.h                        |    7 +
 10 files changed, 1750 insertions(+), 1303 deletions(-)
 rename drivers/net/dsa/{qca8k.c => qca/qca8k-8xxx.c} (56%)
 create mode 100644 drivers/net/dsa/qca/qca8k-common.c
 create mode 100644 drivers/net/dsa/qca/qca8k.h
 delete mode 100644 drivers/net/dsa/qca8k.h

-- 
2.32.0


^ permalink raw reply	[flat|nested] 50+ messages in thread

* regmap: allow to define reg_update_bits for no bus configuration
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-17 22:15   ` Mark Brown
  2021-11-19  2:00   ` patchwork-bot+netdevbpf
  2021-11-17 21:04 ` [net-next PATCH 02/19] net: dsa: qca8k: remove redundant check in parse_port_config Ansuel Smith
                   ` (17 subsequent siblings)
  18 siblings, 2 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev
  Cc: Mark Brown

Some device requires a special handling for reg_update_bits and can't use
the normal regmap read write logic. An example is when locking is
handled by the device and rmw operations requires to do atomic operations.
Allow to declare a dedicated function in regmap_config for
reg_update_bits in no bus configuration.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 drivers/base/regmap/regmap.c | 1 +
 include/linux/regmap.h       | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 21a0c2562ec06..2d74f9f82aa92 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -876,6 +876,7 @@ struct regmap *__regmap_init(struct device *dev,
 	if (!bus) {
 		map->reg_read  = config->reg_read;
 		map->reg_write = config->reg_write;
+		map->reg_update_bits = config->reg_update_bits;
 
 		map->defer_caching = false;
 		goto skip_format_initialization;
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index e3c9a25a853a8..22652e5fbc380 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -290,6 +290,11 @@ typedef void (*regmap_unlock)(void *);
  *		  read operation on a bus such as SPI, I2C, etc. Most of the
  *		  devices do not need this.
  * @reg_write:	  Same as above for writing.
+ * @reg_update_bits: Optional callback that if filled will be used to perform
+ *		     all the update_bits(rmw) operation. Should only be provided
+ *		     if the function require special handling with lock and reg
+ *		     handling and the operation cannot be represented as a simple
+ *		     update_bits operation on a bus such as SPI, I2C, etc.
  * @fast_io:	  Register IO is fast. Use a spinlock instead of a mutex
  *	     	  to perform locking. This field is ignored if custom lock/unlock
  *	     	  functions are used (see fields lock/unlock of struct regmap_config).
@@ -372,6 +377,8 @@ struct regmap_config {
 
 	int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
 	int (*reg_write)(void *context, unsigned int reg, unsigned int val);
+	int (*reg_update_bits)(void *context, unsigned int reg,
+			       unsigned int mask, unsigned int val);
 
 	bool fast_io;
 
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 02/19] net: dsa: qca8k: remove redundant check in parse_port_config
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
  2021-11-17 21:04 ` regmap: allow to define reg_update_bits for no bus configuration Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-18 23:59   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 03/19] net: dsa: qca8k: skip sgmii delay on double cpu conf Ansuel Smith
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev
  Cc: kernel test robot, Dan Carpenter

The very next check for port 0 and 6 already make sure we don't go out
of bounds with the ports_config delay table.
Remove the redundant check.

Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index a429c9750add..bfffc1fb7016 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -983,7 +983,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
 	u32 delay;
 
 	/* We have 2 CPU port. Check them */
-	for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
+	for (port = 0; port < QCA8K_NUM_PORTS; port++) {
 		/* Skip every other port */
 		if (port != 0 && port != 6)
 			continue;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 03/19] net: dsa: qca8k: skip sgmii delay on double cpu conf
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
  2021-11-17 21:04 ` regmap: allow to define reg_update_bits for no bus configuration Ansuel Smith
  2021-11-17 21:04 ` [net-next PATCH 02/19] net: dsa: qca8k: remove redundant check in parse_port_config Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  0:58   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 04/19] net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET Ansuel Smith
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

With a dual cpu configuration rgmii+sgmii, skip configuring sgmii delay
as it does cause no traffic. Configure only rgmii delay in this
specific configuration.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 12 ++++++++++--
 drivers/net/dsa/qca8k.h |  1 +
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index bfffc1fb7016..ace465c878f8 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1041,8 +1041,13 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
 			if (mode == PHY_INTERFACE_MODE_RGMII ||
 			    mode == PHY_INTERFACE_MODE_RGMII_ID ||
 			    mode == PHY_INTERFACE_MODE_RGMII_TXID ||
-			    mode == PHY_INTERFACE_MODE_RGMII_RXID)
+			    mode == PHY_INTERFACE_MODE_RGMII_RXID) {
+				if (priv->ports_config.rgmii_tx_delay[cpu_port_index] ||
+				    priv->ports_config.rgmii_rx_delay[cpu_port_index])
+					priv->ports_config.skip_sgmii_delay = true;
+
 				break;
+			}
 
 			if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
 				priv->ports_config.sgmii_tx_clk_falling_edge = true;
@@ -1457,8 +1462,11 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 
 		/* From original code is reported port instability as SGMII also
 		 * require delay set. Apply advised values here or take them from DT.
+		 * In dual CPU configuration, apply only delay to rgmii as applying
+		 * it also to the SGMII line cause no traffic to the entire switch.
 		 */
-		if (state->interface == PHY_INTERFACE_MODE_SGMII)
+		if (state->interface == PHY_INTERFACE_MODE_SGMII &&
+		    !priv->ports_config.skip_sgmii_delay)
 			qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
 
 		break;
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index 128b8cf85e08..57c4c0d93558 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -275,6 +275,7 @@ struct qca8k_ports_config {
 	bool sgmii_rx_clk_falling_edge;
 	bool sgmii_tx_clk_falling_edge;
 	bool sgmii_enable_pll;
+	bool skip_sgmii_delay;
 	u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
 	u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
 };
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 04/19] net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (2 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 03/19] net: dsa: qca8k: skip sgmii delay on double cpu conf Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-17 21:04 ` [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup Ansuel Smith
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Convert and try to standardize bit fields using
GENMASK/FIELD_PREP/FIELD_GET macros. Rework some logic to support the
standard macro and tidy things up. No functional change intended.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c |  98 ++++++++++++-------------
 drivers/net/dsa/qca8k.h | 153 ++++++++++++++++++++++------------------
 2 files changed, 130 insertions(+), 121 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index ace465c878f8..19331edf1fd4 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/phy.h>
 #include <linux/netdevice.h>
+#include <linux/bitfield.h>
 #include <net/dsa.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
@@ -319,18 +320,18 @@ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
 	}
 
 	/* vid - 83:72 */
-	fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
+	fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
 	/* aging - 67:64 */
-	fdb->aging = reg[2] & QCA8K_ATU_STATUS_M;
+	fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
 	/* portmask - 54:48 */
-	fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M;
+	fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
 	/* mac - 47:0 */
-	fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff;
-	fdb->mac[1] = reg[1] & 0xff;
-	fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff;
-	fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff;
-	fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff;
-	fdb->mac[5] = reg[0] & 0xff;
+	fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
+	fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
+	fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
+	fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
+	fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
+	fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
 
 	return 0;
 }
@@ -343,18 +344,18 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
 	int i;
 
 	/* vid - 83:72 */
-	reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S;
+	reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
 	/* aging - 67:64 */
-	reg[2] |= aging & QCA8K_ATU_STATUS_M;
+	reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
 	/* portmask - 54:48 */
-	reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S;
+	reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
 	/* mac - 47:0 */
-	reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S;
-	reg[1] |= mac[1];
-	reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S;
-	reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S;
-	reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S;
-	reg[0] |= mac[5];
+	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
+	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
 
 	/* load the array into the ARL table */
 	for (i = 0; i < 3; i++)
@@ -372,7 +373,7 @@ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
 	reg |= cmd;
 	if (port >= 0) {
 		reg |= QCA8K_ATU_FUNC_PORT_EN;
-		reg |= (port & QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S;
+		reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
 	}
 
 	/* Write the function register triggering the table access */
@@ -454,7 +455,7 @@ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
 	/* Set the command and VLAN index */
 	reg = QCA8K_VTU_FUNC1_BUSY;
 	reg |= cmd;
-	reg |= vid << QCA8K_VTU_FUNC1_VID_S;
+	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
 
 	/* Write the function register triggering the table access */
 	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
@@ -500,13 +501,11 @@ qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
 	if (ret < 0)
 		goto out;
 	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
-	reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
 	if (untagged)
-		reg |= QCA8K_VTU_FUNC0_EG_MODE_UNTAG <<
-				QCA8K_VTU_FUNC0_EG_MODE_S(port);
+		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
 	else
-		reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG <<
-				QCA8K_VTU_FUNC0_EG_MODE_S(port);
+		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
 
 	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
 	if (ret)
@@ -534,15 +533,13 @@ qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
 	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
 	if (ret < 0)
 		goto out;
-	reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
-	reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
-			QCA8K_VTU_FUNC0_EG_MODE_S(port);
+	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+	reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
 
 	/* Check if we're the last member to be removed */
 	del = true;
 	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-		mask = QCA8K_VTU_FUNC0_EG_MODE_NOT;
-		mask <<= QCA8K_VTU_FUNC0_EG_MODE_S(i);
+		mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
 
 		if ((reg & mask) != mask) {
 			del = false;
@@ -1014,7 +1011,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
 				 mode == PHY_INTERFACE_MODE_RGMII_TXID)
 				delay = 1;
 
-			if (delay > QCA8K_MAX_DELAY) {
+			if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
 				dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
 				delay = 3;
 			}
@@ -1030,7 +1027,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
 				 mode == PHY_INTERFACE_MODE_RGMII_RXID)
 				delay = 2;
 
-			if (delay > QCA8K_MAX_DELAY) {
+			if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
 				dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
 				delay = 3;
 			}
@@ -1146,8 +1143,8 @@ qca8k_setup(struct dsa_switch *ds)
 		/* 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),
-					  QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
-					  QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+					  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;
@@ -1164,10 +1161,10 @@ qca8k_setup(struct dsa_switch *ds)
 	 * for igmp, unknown, multicast and broadcast packet
 	 */
 	ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
-			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
-			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
-			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
-			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
+			  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)));
 	if (ret)
 		return ret;
 
@@ -1185,8 +1182,6 @@ qca8k_setup(struct dsa_switch *ds)
 
 		/* Individual user ports get connected to CPU port only */
 		if (dsa_is_user_port(ds, i)) {
-			int shift = 16 * (i % 2);
-
 			ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
 					QCA8K_PORT_LOOKUP_MEMBER,
 					BIT(cpu_port));
@@ -1203,8 +1198,8 @@ qca8k_setup(struct dsa_switch *ds)
 			 * default egress vid
 			 */
 			ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
-					0xfff << shift,
-					QCA8K_PORT_VID_DEF << shift);
+					QCA8K_EGREES_VLAN_PORT_MASK(i),
+					QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
 			if (ret)
 				return ret;
 
@@ -1251,7 +1246,7 @@ qca8k_setup(struct dsa_switch *ds)
 			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 |
+				  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,
@@ -1270,8 +1265,8 @@ qca8k_setup(struct dsa_switch *ds)
 		mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
 		       QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
 		qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
-			  QCA8K_GLOBAL_FC_GOL_XON_THRES_S |
-			  QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S,
+			  QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
+			  QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
 			  mask);
 	}
 
@@ -1920,11 +1915,11 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 
 	if (vlan_filtering) {
 		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-				QCA8K_PORT_LOOKUP_VLAN_MODE,
+				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
 				QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
 	} else {
 		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-				QCA8K_PORT_LOOKUP_VLAN_MODE,
+				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
 				QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
 	}
 
@@ -1948,10 +1943,9 @@ qca8k_port_vlan_add(struct dsa_switch *ds, int port,
 	}
 
 	if (pvid) {
-		int shift = 16 * (port % 2);
-
 		ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
-				0xfff << shift, vlan->vid << shift);
+				QCA8K_EGREES_VLAN_PORT_MASK(port),
+				QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
 		if (ret)
 			return ret;
 
@@ -2045,7 +2039,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *priv)
 	if (ret < 0)
 		return -ENODEV;
 
-	id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
+	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
 	if (id != data->id) {
 		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
 		return -ENODEV;
@@ -2054,7 +2048,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *priv)
 	priv->switch_id = id;
 
 	/* Save revision to communicate to the internal PHY driver */
-	priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK);
+	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
 
 	return 0;
 }
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index 57c4c0d93558..3722fbb6b461 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -30,9 +30,9 @@
 /* Global control registers */
 #define QCA8K_REG_MASK_CTRL				0x000
 #define   QCA8K_MASK_CTRL_REV_ID_MASK			GENMASK(7, 0)
-#define   QCA8K_MASK_CTRL_REV_ID(x)			((x) >> 0)
+#define   QCA8K_MASK_CTRL_REV_ID(x)			FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x)
 #define   QCA8K_MASK_CTRL_DEVICE_ID_MASK		GENMASK(15, 8)
-#define   QCA8K_MASK_CTRL_DEVICE_ID(x)			((x) >> 8)
+#define   QCA8K_MASK_CTRL_DEVICE_ID(x)			FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x)
 #define QCA8K_REG_PORT0_PAD_CTRL			0x004
 #define   QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN		BIT(31)
 #define   QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE	BIT(19)
@@ -41,12 +41,11 @@
 #define QCA8K_REG_PORT6_PAD_CTRL			0x00c
 #define   QCA8K_PORT_PAD_RGMII_EN			BIT(26)
 #define   QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK		GENMASK(23, 22)
-#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)		((x) << 22)
+#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)		FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x)
 #define   QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK		GENMASK(21, 20)
-#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)		((x) << 20)
+#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)		FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x)
 #define	  QCA8K_PORT_PAD_RGMII_TX_DELAY_EN		BIT(25)
 #define   QCA8K_PORT_PAD_RGMII_RX_DELAY_EN		BIT(24)
-#define   QCA8K_MAX_DELAY				3
 #define   QCA8K_PORT_PAD_SGMII_EN			BIT(7)
 #define QCA8K_REG_PWS					0x010
 #define   QCA8K_PWS_POWER_ON_SEL			BIT(31)
@@ -68,10 +67,12 @@
 #define   QCA8K_MDIO_MASTER_READ			BIT(27)
 #define   QCA8K_MDIO_MASTER_WRITE			0
 #define   QCA8K_MDIO_MASTER_SUP_PRE			BIT(26)
-#define   QCA8K_MDIO_MASTER_PHY_ADDR(x)			((x) << 21)
-#define   QCA8K_MDIO_MASTER_REG_ADDR(x)			((x) << 16)
-#define   QCA8K_MDIO_MASTER_DATA(x)			(x)
+#define   QCA8K_MDIO_MASTER_PHY_ADDR_MASK		GENMASK(25, 21)
+#define   QCA8K_MDIO_MASTER_PHY_ADDR(x)			FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x)
+#define   QCA8K_MDIO_MASTER_REG_ADDR_MASK		GENMASK(20, 16)
+#define   QCA8K_MDIO_MASTER_REG_ADDR(x)			FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x)
 #define   QCA8K_MDIO_MASTER_DATA_MASK			GENMASK(15, 0)
+#define   QCA8K_MDIO_MASTER_DATA(x)			FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
 #define   QCA8K_MDIO_MASTER_MAX_PORTS			5
 #define   QCA8K_MDIO_MASTER_MAX_REG			32
 #define QCA8K_GOL_MAC_ADDR0				0x60
@@ -93,9 +94,7 @@
 #define   QCA8K_PORT_STATUS_FLOW_AUTO			BIT(12)
 #define QCA8K_REG_PORT_HDR_CTRL(_i)			(0x9c + (_i * 4))
 #define   QCA8K_PORT_HDR_CTRL_RX_MASK			GENMASK(3, 2)
-#define   QCA8K_PORT_HDR_CTRL_RX_S			2
 #define   QCA8K_PORT_HDR_CTRL_TX_MASK			GENMASK(1, 0)
-#define   QCA8K_PORT_HDR_CTRL_TX_S			0
 #define   QCA8K_PORT_HDR_CTRL_ALL			2
 #define   QCA8K_PORT_HDR_CTRL_MGMT			1
 #define   QCA8K_PORT_HDR_CTRL_NONE			0
@@ -105,10 +104,11 @@
 #define   QCA8K_SGMII_EN_TX				BIT(3)
 #define   QCA8K_SGMII_EN_SD				BIT(4)
 #define   QCA8K_SGMII_CLK125M_DELAY			BIT(7)
-#define   QCA8K_SGMII_MODE_CTRL_MASK			(BIT(22) | BIT(23))
-#define   QCA8K_SGMII_MODE_CTRL_BASEX			(0 << 22)
-#define   QCA8K_SGMII_MODE_CTRL_PHY			(1 << 22)
-#define   QCA8K_SGMII_MODE_CTRL_MAC			(2 << 22)
+#define   QCA8K_SGMII_MODE_CTRL_MASK			GENMASK(23, 22)
+#define   QCA8K_SGMII_MODE_CTRL(x)			FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x)
+#define   QCA8K_SGMII_MODE_CTRL_BASEX			QCA8K_SGMII_MODE_CTRL(0x0)
+#define   QCA8K_SGMII_MODE_CTRL_PHY			QCA8K_SGMII_MODE_CTRL(0x1)
+#define   QCA8K_SGMII_MODE_CTRL_MAC			QCA8K_SGMII_MODE_CTRL(0x2)
 
 /* MAC_PWR_SEL registers */
 #define QCA8K_REG_MAC_PWR_SEL				0x0e4
@@ -121,100 +121,115 @@
 
 /* ACL registers */
 #define QCA8K_REG_PORT_VLAN_CTRL0(_i)			(0x420 + (_i * 8))
-#define   QCA8K_PORT_VLAN_CVID(x)			(x << 16)
-#define   QCA8K_PORT_VLAN_SVID(x)			x
+#define   QCA8K_PORT_VLAN_CVID_MASK			GENMASK(27, 16)
+#define   QCA8K_PORT_VLAN_CVID(x)			FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x)
+#define   QCA8K_PORT_VLAN_SVID_MASK			GENMASK(11, 0)
+#define   QCA8K_PORT_VLAN_SVID(x)			FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x)
 #define QCA8K_REG_PORT_VLAN_CTRL1(_i)			(0x424 + (_i * 8))
 #define QCA8K_REG_IPV4_PRI_BASE_ADDR			0x470
 #define QCA8K_REG_IPV4_PRI_ADDR_MASK			0x474
 
 /* Lookup registers */
 #define QCA8K_REG_ATU_DATA0				0x600
-#define   QCA8K_ATU_ADDR2_S				24
-#define   QCA8K_ATU_ADDR3_S				16
-#define   QCA8K_ATU_ADDR4_S				8
+#define   QCA8K_ATU_ADDR2_MASK				GENMASK(31, 24)
+#define   QCA8K_ATU_ADDR3_MASK				GENMASK(23, 16)
+#define   QCA8K_ATU_ADDR4_MASK				GENMASK(15, 8)
+#define   QCA8K_ATU_ADDR5_MASK				GENMASK(7, 0)
 #define QCA8K_REG_ATU_DATA1				0x604
-#define   QCA8K_ATU_PORT_M				0x7f
-#define   QCA8K_ATU_PORT_S				16
-#define   QCA8K_ATU_ADDR0_S				8
+#define   QCA8K_ATU_PORT_MASK				GENMASK(22, 16)
+#define   QCA8K_ATU_ADDR0_MASK				GENMASK(15, 8)
+#define   QCA8K_ATU_ADDR1_MASK				GENMASK(7, 0)
 #define QCA8K_REG_ATU_DATA2				0x608
-#define   QCA8K_ATU_VID_M				0xfff
-#define   QCA8K_ATU_VID_S				8
-#define   QCA8K_ATU_STATUS_M				0xf
+#define   QCA8K_ATU_VID_MASK				GENMASK(19, 8)
+#define   QCA8K_ATU_STATUS_MASK				GENMASK(3, 0)
 #define   QCA8K_ATU_STATUS_STATIC			0xf
 #define QCA8K_REG_ATU_FUNC				0x60c
 #define   QCA8K_ATU_FUNC_BUSY				BIT(31)
 #define   QCA8K_ATU_FUNC_PORT_EN			BIT(14)
 #define   QCA8K_ATU_FUNC_MULTI_EN			BIT(13)
 #define   QCA8K_ATU_FUNC_FULL				BIT(12)
-#define   QCA8K_ATU_FUNC_PORT_M				0xf
-#define   QCA8K_ATU_FUNC_PORT_S				8
+#define   QCA8K_ATU_FUNC_PORT_MASK			GENMASK(11, 8)
 #define QCA8K_REG_VTU_FUNC0				0x610
 #define   QCA8K_VTU_FUNC0_VALID				BIT(20)
 #define   QCA8K_VTU_FUNC0_IVL_EN			BIT(19)
-#define   QCA8K_VTU_FUNC0_EG_MODE_S(_i)			(4 + (_i) * 2)
-#define   QCA8K_VTU_FUNC0_EG_MODE_MASK			3
-#define   QCA8K_VTU_FUNC0_EG_MODE_UNMOD			0
-#define   QCA8K_VTU_FUNC0_EG_MODE_UNTAG			1
-#define   QCA8K_VTU_FUNC0_EG_MODE_TAG			2
-#define   QCA8K_VTU_FUNC0_EG_MODE_NOT			3
+/*        QCA8K_VTU_FUNC0_EG_MODE_MASK			GENMASK(17, 4)
+ *          It does contain VLAN_MODE for each port [5:4] for port0,
+ *          [7:6] for port1 ... [17:16] for port6. Use virtual port
+ *          define to handle this.
+ */
+#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)	(4 + (_i) * 2)
+#define   QCA8K_VTU_FUNC0_EG_MODE_MASK			GENMASK(1, 0)
+#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i)		(GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+#define   QCA8K_VTU_FUNC0_EG_MODE_UNMOD			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0)
+#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i)	(QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+#define   QCA8K_VTU_FUNC0_EG_MODE_UNTAG			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1)
+#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i)	(QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+#define   QCA8K_VTU_FUNC0_EG_MODE_TAG			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2)
+#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i)		(QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+#define   QCA8K_VTU_FUNC0_EG_MODE_NOT			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3)
+#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i)		(QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
 #define QCA8K_REG_VTU_FUNC1				0x614
 #define   QCA8K_VTU_FUNC1_BUSY				BIT(31)
-#define   QCA8K_VTU_FUNC1_VID_S				16
+#define   QCA8K_VTU_FUNC1_VID_MASK			GENMASK(27, 16)
 #define   QCA8K_VTU_FUNC1_FULL				BIT(4)
 #define QCA8K_REG_GLOBAL_FW_CTRL0			0x620
 #define   QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN		BIT(10)
 #define QCA8K_REG_GLOBAL_FW_CTRL1			0x624
-#define   QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S		24
-#define   QCA8K_GLOBAL_FW_CTRL1_BC_DP_S			16
-#define   QCA8K_GLOBAL_FW_CTRL1_MC_DP_S			8
-#define   QCA8K_GLOBAL_FW_CTRL1_UC_DP_S			0
+#define   QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK		GENMASK(30, 24)
+#define   QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK		GENMASK(22, 16)
+#define   QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK		GENMASK(14, 8)
+#define   QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK		GENMASK(6, 0)
 #define QCA8K_PORT_LOOKUP_CTRL(_i)			(0x660 + (_i) * 0xc)
 #define   QCA8K_PORT_LOOKUP_MEMBER			GENMASK(6, 0)
-#define   QCA8K_PORT_LOOKUP_VLAN_MODE			GENMASK(9, 8)
-#define   QCA8K_PORT_LOOKUP_VLAN_MODE_NONE		(0 << 8)
-#define   QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK		(1 << 8)
-#define   QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK		(2 << 8)
-#define   QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE		(3 << 8)
+#define   QCA8K_PORT_LOOKUP_VLAN_MODE_MASK		GENMASK(9, 8)
+#define   QCA8K_PORT_LOOKUP_VLAN_MODE(x)		FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x)
+#define   QCA8K_PORT_LOOKUP_VLAN_MODE_NONE		QCA8K_PORT_LOOKUP_VLAN_MODE(0x0)
+#define   QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK		QCA8K_PORT_LOOKUP_VLAN_MODE(0x1)
+#define   QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK		QCA8K_PORT_LOOKUP_VLAN_MODE(0x2)
+#define   QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE		QCA8K_PORT_LOOKUP_VLAN_MODE(0x3)
 #define   QCA8K_PORT_LOOKUP_STATE_MASK			GENMASK(18, 16)
-#define   QCA8K_PORT_LOOKUP_STATE_DISABLED		(0 << 16)
-#define   QCA8K_PORT_LOOKUP_STATE_BLOCKING		(1 << 16)
-#define   QCA8K_PORT_LOOKUP_STATE_LISTENING		(2 << 16)
-#define   QCA8K_PORT_LOOKUP_STATE_LEARNING		(3 << 16)
-#define   QCA8K_PORT_LOOKUP_STATE_FORWARD		(4 << 16)
-#define   QCA8K_PORT_LOOKUP_STATE			GENMASK(18, 16)
+#define   QCA8K_PORT_LOOKUP_STATE(x)			FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x)
+#define   QCA8K_PORT_LOOKUP_STATE_DISABLED		QCA8K_PORT_LOOKUP_STATE(0x0)
+#define   QCA8K_PORT_LOOKUP_STATE_BLOCKING		QCA8K_PORT_LOOKUP_STATE(0x1)
+#define   QCA8K_PORT_LOOKUP_STATE_LISTENING		QCA8K_PORT_LOOKUP_STATE(0x2)
+#define   QCA8K_PORT_LOOKUP_STATE_LEARNING		QCA8K_PORT_LOOKUP_STATE(0x3)
+#define   QCA8K_PORT_LOOKUP_STATE_FORWARD		QCA8K_PORT_LOOKUP_STATE(0x4)
 #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20)
 
 #define QCA8K_REG_GLOBAL_FC_THRESH			0x800
-#define   QCA8K_GLOBAL_FC_GOL_XON_THRES(x)		((x) << 16)
-#define   QCA8K_GLOBAL_FC_GOL_XON_THRES_S		GENMASK(24, 16)
-#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x)		((x) << 0)
-#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S		GENMASK(8, 0)
+#define   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK		GENMASK(24, 16)
+#define   QCA8K_GLOBAL_FC_GOL_XON_THRES(x)		FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
+#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK		GENMASK(8, 0)
+#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x)		FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x)
 
 #define QCA8K_REG_PORT_HOL_CTRL0(_i)			(0x970 + (_i) * 0x8)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF		GENMASK(3, 0)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0(x)		((x) << 0)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF		GENMASK(7, 4)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1(x)		((x) << 4)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF		GENMASK(11, 8)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2(x)		((x) << 8)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF		GENMASK(15, 12)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3(x)		((x) << 12)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF		GENMASK(19, 16)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4(x)		((x) << 16)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF		GENMASK(23, 20)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5(x)		((x) << 20)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF		GENMASK(29, 24)
-#define   QCA8K_PORT_HOL_CTRL0_EG_PORT(x)		((x) << 24)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK		GENMASK(3, 0)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK		GENMASK(7, 4)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK		GENMASK(11, 8)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK		GENMASK(15, 12)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK		GENMASK(19, 16)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK		GENMASK(23, 20)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK		GENMASK(29, 24)
+#define   QCA8K_PORT_HOL_CTRL0_EG_PORT(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x)
 
 #define QCA8K_REG_PORT_HOL_CTRL1(_i)			(0x974 + (_i) * 0x8)
-#define   QCA8K_PORT_HOL_CTRL1_ING_BUF			GENMASK(3, 0)
-#define   QCA8K_PORT_HOL_CTRL1_ING(x)			((x) << 0)
+#define   QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK		GENMASK(3, 0)
+#define   QCA8K_PORT_HOL_CTRL1_ING(x)			FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x)
 #define   QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN		BIT(6)
 #define   QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN		BIT(7)
 #define   QCA8K_PORT_HOL_CTRL1_WRED_EN			BIT(8)
 #define   QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN		BIT(16)
 
 /* Pkt edit registers */
+#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i)		(16 * ((_i) % 2))
+#define QCA8K_EGREES_VLAN_PORT_MASK(_i)			(GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
+#define QCA8K_EGREES_VLAN_PORT(_i, x)			((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
 #define QCA8K_EGRESS_VLAN(x)				(0x0c70 + (4 * (x / 2)))
 
 /* L3 registers */
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (3 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 04/19] net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:03   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 06/19] net: dsa: qca8k: remove extra mutex_init " Ansuel Smith
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Move read_switch_id function in qca8k_setup in preparation for regmap
conversion. Sw probe should NOT contain function that depends on reading
from switch regs.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 71 ++++++++++++++++++++---------------------
 1 file changed, 35 insertions(+), 36 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 19331edf1fd4..be98d11b17ec 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1073,6 +1073,36 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
 	return 0;
 }
 
+static int qca8k_read_switch_id(struct qca8k_priv *priv)
+{
+	const struct qca8k_match_data *data;
+	u32 val;
+	u8 id;
+	int ret;
+
+	/* get the switches ID from the compatible */
+	data = of_device_get_match_data(priv->dev);
+	if (!data)
+		return -ENODEV;
+
+	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
+	if (ret < 0)
+		return -ENODEV;
+
+	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+	if (id != data->id) {
+		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
+		return -ENODEV;
+	}
+
+	priv->switch_id = id;
+
+	/* Save revision to communicate to the internal PHY driver */
+	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
+
+	return 0;
+}
+
 static int
 qca8k_setup(struct dsa_switch *ds)
 {
@@ -1080,6 +1110,11 @@ qca8k_setup(struct dsa_switch *ds)
 	int cpu_port, ret, i;
 	u32 mask;
 
+	/* Check the detected switch id */
+	ret = qca8k_read_switch_id(priv);
+	if (ret)
+		return ret;
+
 	cpu_port = qca8k_find_cpu_port(ds);
 	if (cpu_port < 0) {
 		dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
@@ -2023,41 +2058,10 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.get_phy_flags		= qca8k_get_phy_flags,
 };
 
-static int qca8k_read_switch_id(struct qca8k_priv *priv)
-{
-	const struct qca8k_match_data *data;
-	u32 val;
-	u8 id;
-	int ret;
-
-	/* get the switches ID from the compatible */
-	data = of_device_get_match_data(priv->dev);
-	if (!data)
-		return -ENODEV;
-
-	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
-	if (ret < 0)
-		return -ENODEV;
-
-	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
-	if (id != data->id) {
-		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
-		return -ENODEV;
-	}
-
-	priv->switch_id = id;
-
-	/* Save revision to communicate to the internal PHY driver */
-	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
-
-	return 0;
-}
-
 static int
 qca8k_sw_probe(struct mdio_device *mdiodev)
 {
 	struct qca8k_priv *priv;
-	int ret;
 
 	/* allocate the private data struct so that we can probe the switches
 	 * ID register
@@ -2083,11 +2087,6 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
 		gpiod_set_value_cansleep(priv->reset_gpio, 0);
 	}
 
-	/* Check the detected switch id */
-	ret = qca8k_read_switch_id(priv);
-	if (ret)
-		return ret;
-
 	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
 	if (!priv->ds)
 		return -ENOMEM;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 06/19] net: dsa: qca8k: remove extra mutex_init in qca8k_setup
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (4 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-17 21:04 ` [net-next PATCH 07/19] net: dsa: qca8k: set regmap init as mandatory for regmap conversion Ansuel Smith
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Mutex is already init in sw_probe. Remove the extra init in qca8k_setup.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index be98d11b17ec..ee04b48875e7 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1126,8 +1126,6 @@ qca8k_setup(struct dsa_switch *ds)
 	if (ret)
 		return ret;
 
-	mutex_init(&priv->reg_mutex);
-
 	/* Start by setting up the register mapping */
 	priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
 					&qca8k_regmap_config);
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 07/19] net: dsa: qca8k: set regmap init as mandatory for regmap conversion
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (5 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 06/19] net: dsa: qca8k: remove extra mutex_init " Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:09   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper Ansuel Smith
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

In preparation for regmap conversion, make regmap init mandatory and
fail if any error occurs.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index ee04b48875e7..792b999da37c 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1110,6 +1110,14 @@ qca8k_setup(struct dsa_switch *ds)
 	int cpu_port, ret, i;
 	u32 mask;
 
+	/* Start by setting up the register mapping */
+	priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+					&qca8k_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		dev_err(priv->dev, "regmap initialization failed");
+		return PTR_ERR(priv->regmap);
+	}
+
 	/* Check the detected switch id */
 	ret = qca8k_read_switch_id(priv);
 	if (ret)
@@ -1126,12 +1134,6 @@ qca8k_setup(struct dsa_switch *ds)
 	if (ret)
 		return ret;
 
-	/* Start by setting up the register mapping */
-	priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
-					&qca8k_regmap_config);
-	if (IS_ERR(priv->regmap))
-		dev_warn(priv->dev, "regmap initialization failed");
-
 	ret = qca8k_setup_mdio_bus(priv);
 	if (ret)
 		return ret;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (6 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 07/19] net: dsa: qca8k: set regmap init as mandatory for regmap conversion Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:14   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 09/19] net: dsa: qca8k: add additional MIB counter and make it dynamic Ansuel Smith
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Convert any qca8k read/write/rmw/set/clear/pool to regmap helper and add
missing config to regmap_config struct.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 289 ++++++++++++++++++----------------------
 1 file changed, 131 insertions(+), 158 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 792b999da37c..7f71607bec3f 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -10,6 +10,7 @@
 #include <linux/phy.h>
 #include <linux/netdevice.h>
 #include <linux/bitfield.h>
+#include <linux/regmap.h>
 #include <net/dsa.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
@@ -150,8 +151,9 @@ qca8k_set_page(struct mii_bus *bus, u16 page)
 }
 
 static int
-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+qca8k_regmap_read(void *ctx, 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;
@@ -172,8 +174,9 @@ qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
 }
 
 static int
-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+qca8k_regmap_write(void *ctx, 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;
@@ -194,8 +197,9 @@ qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
 }
 
 static int
-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+qca8k_regmap_update_bits(void *ctx, 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;
@@ -223,34 +227,6 @@ qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
 	return ret;
 }
 
-static int
-qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
-{
-	return qca8k_rmw(priv, reg, 0, val);
-}
-
-static int
-qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
-{
-	return qca8k_rmw(priv, reg, val, 0);
-}
-
-static int
-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
-
-	return qca8k_read(priv, reg, val);
-}
-
-static int
-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
-
-	return qca8k_write(priv, reg, val);
-}
-
 static const struct regmap_range qca8k_readable_ranges[] = {
 	regmap_reg_range(0x0000, 0x00e4), /* Global control */
 	regmap_reg_range(0x0100, 0x0168), /* EEE control */
@@ -282,26 +258,19 @@ static struct regmap_config qca8k_regmap_config = {
 	.max_register = 0x16ac, /* end MIB - Port6 range */
 	.reg_read = qca8k_regmap_read,
 	.reg_write = qca8k_regmap_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 */
 };
 
 static int
 qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
 {
-	int ret, ret1;
 	u32 val;
 
-	ret = read_poll_timeout(qca8k_read, ret1, !(val & mask),
-				0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
-				priv, reg, &val);
-
-	/* Check if qca8k_read has failed for a different reason
-	 * before returning -ETIMEDOUT
-	 */
-	if (ret < 0 && ret1 < 0)
-		return ret1;
-
-	return ret;
+	return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
+				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
 }
 
 static int
@@ -312,7 +281,7 @@ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
 
 	/* load the ARL table into an array */
 	for (i = 0; i < 4; i++) {
-		ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val);
+		ret = regmap_read(priv->regmap, QCA8K_REG_ATU_DATA0 + (i * 4), &val);
 		if (ret < 0)
 			return ret;
 
@@ -359,7 +328,7 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
 
 	/* load the array into the ARL table */
 	for (i = 0; i < 3; i++)
-		qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
+		regmap_write(priv->regmap, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
 }
 
 static int
@@ -377,7 +346,7 @@ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
 	}
 
 	/* Write the function register triggering the table access */
-	ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
+	ret = regmap_write(priv->regmap, QCA8K_REG_ATU_FUNC, reg);
 	if (ret)
 		return ret;
 
@@ -388,7 +357,7 @@ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
 
 	/* Check for table full violation when adding an entry */
 	if (cmd == QCA8K_FDB_LOAD) {
-		ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
+		ret = regmap_read(priv->regmap, QCA8K_REG_ATU_FUNC, &reg);
 		if (ret < 0)
 			return ret;
 		if (reg & QCA8K_ATU_FUNC_FULL)
@@ -458,7 +427,7 @@ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
 	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
 
 	/* Write the function register triggering the table access */
-	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+	ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC1, reg);
 	if (ret)
 		return ret;
 
@@ -469,7 +438,7 @@ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
 
 	/* Check for table full violation when adding an entry */
 	if (cmd == QCA8K_VLAN_LOAD) {
-		ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
+		ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC1, &reg);
 		if (ret < 0)
 			return ret;
 		if (reg & QCA8K_VTU_FUNC1_FULL)
@@ -497,7 +466,7 @@ qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
 	if (ret < 0)
 		goto out;
 
-	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+	ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, &reg);
 	if (ret < 0)
 		goto out;
 	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
@@ -507,7 +476,7 @@ qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
 	else
 		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
 
-	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+	ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg);
 	if (ret)
 		goto out;
 	ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
@@ -530,7 +499,7 @@ qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
 	if (ret < 0)
 		goto out;
 
-	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+	ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, &reg);
 	if (ret < 0)
 		goto out;
 	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
@@ -550,7 +519,7 @@ qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
 	if (del) {
 		ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
 	} else {
-		ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+		ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg);
 		if (ret)
 			goto out;
 		ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
@@ -568,7 +537,7 @@ qca8k_mib_init(struct qca8k_priv *priv)
 	int ret;
 
 	mutex_lock(&priv->reg_mutex);
-	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
+	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
 	if (ret)
 		goto exit;
 
@@ -576,11 +545,11 @@ qca8k_mib_init(struct qca8k_priv *priv)
 	if (ret)
 		goto exit;
 
-	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
 	if (ret)
 		goto exit;
 
-	ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+	ret = regmap_write(priv->regmap, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
 
 exit:
 	mutex_unlock(&priv->reg_mutex);
@@ -597,9 +566,9 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
 		mask |= QCA8K_PORT_STATUS_LINK_AUTO;
 
 	if (enable)
-		qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port), mask);
+		regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
 	else
-		qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
+		regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
 }
 
 static u32
@@ -861,8 +830,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv)
 		 * a dt-overlay and driver reload changed the configuration
 		 */
 
-		return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
-				       QCA8K_MDIO_MASTER_EN);
+		return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
+					 QCA8K_MDIO_MASTER_EN);
 	}
 
 	/* Check if the devicetree declare the port:phy mapping */
@@ -903,10 +872,10 @@ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
 		mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
 
 	if (mask) {
-		ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
-				QCA8K_MAC_PWR_RGMII0_1_8V |
-				QCA8K_MAC_PWR_RGMII1_1_8V,
-				mask);
+		ret = regmap_update_bits(priv->regmap, QCA8K_REG_MAC_PWR_SEL,
+					 QCA8K_MAC_PWR_RGMII0_1_8V |
+					 QCA8K_MAC_PWR_RGMII1_1_8V,
+					 mask);
 	}
 
 	return ret;
@@ -947,8 +916,9 @@ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
 		if (data->reduced_package)
 			val |= QCA8327_PWS_PACKAGE148_EN;
 
-		ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
-				val);
+		ret = regmap_update_bits(priv->regmap, QCA8K_REG_PWS,
+					 QCA8327_PWS_PACKAGE148_EN,
+					 val);
 		if (ret)
 			return ret;
 	}
@@ -965,9 +935,10 @@ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
 		val |= QCA8K_PWS_LED_OPEN_EN_CSR;
 	}
 
-	return qca8k_rmw(priv, QCA8K_REG_PWS,
-			QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
-			val);
+	return regmap_update_bits(priv->regmap, QCA8K_REG_PWS,
+				  QCA8K_PWS_LED_OPEN_EN_CSR |
+				  QCA8K_PWS_POWER_ON_SEL,
+				  val);
 }
 
 static int
@@ -1085,7 +1056,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *priv)
 	if (!data)
 		return -ENODEV;
 
-	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
+	ret = regmap_read(priv->regmap, QCA8K_REG_MASK_CTRL, &val);
 	if (ret < 0)
 		return -ENODEV;
 
@@ -1147,16 +1118,16 @@ qca8k_setup(struct dsa_switch *ds)
 		return ret;
 
 	/* Make sure MAC06 is disabled */
-	ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL,
-			      QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
+	ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
+				QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
 	if (ret) {
 		dev_err(priv->dev, "failed disabling MAC06 exchange");
 		return ret;
 	}
 
 	/* Enable CPU Port */
-	ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
-			    QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+	ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+			      QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
 	if (ret) {
 		dev_err(priv->dev, "failed enabling CPU port");
 		return ret;
@@ -1170,16 +1141,18 @@ qca8k_setup(struct dsa_switch *ds)
 	/* Initial setup of all ports */
 	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
 		/* Disable forwarding by default on all ports */
-		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-				QCA8K_PORT_LOOKUP_MEMBER, 0);
+		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+					 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));
+			ret = regmap_write(priv->regmap, 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;
@@ -1195,11 +1168,11 @@ qca8k_setup(struct dsa_switch *ds)
 	 * Notice that in multi-cpu config only one port should be set
 	 * for igmp, unknown, multicast and broadcast packet
 	 */
-	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)));
+	ret = regmap_write(priv->regmap, 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)));
 	if (ret)
 		return ret;
 
@@ -1209,38 +1182,38 @@ qca8k_setup(struct dsa_switch *ds)
 	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));
+			ret = regmap_update_bits(priv->regmap, 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),
-					QCA8K_PORT_LOOKUP_MEMBER,
-					BIT(cpu_port));
+			ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+						 QCA8K_PORT_LOOKUP_MEMBER,
+						 BIT(cpu_port));
 			if (ret)
 				return ret;
 
 			/* Enable ARP Auto-learning by default */
-			ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-					    QCA8K_PORT_LOOKUP_LEARN);
+			ret = regmap_set_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));
+			ret = regmap_update_bits(priv->regmap, 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));
+			ret = regmap_write(priv->regmap, 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;
 		}
@@ -1274,18 +1247,18 @@ qca8k_setup(struct dsa_switch *ds)
 					QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
 					QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
 			}
-			qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
+			regmap_write(priv->regmap, 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);
+			regmap_update_bits(priv->regmap, 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);
 		}
 
 		/* Set initial MTU for every port.
@@ -1299,14 +1272,14 @@ qca8k_setup(struct dsa_switch *ds)
 	if (priv->switch_id == QCA8K_ID_QCA8327) {
 		mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
 		       QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
-		qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
-			  QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
-			  QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
-			  mask);
+		regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FC_THRESH,
+				   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
+				   QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
+				   mask);
 	}
 
 	/* Setup our port MTUs to match power on defaults */
-	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
+	ret = regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
 	if (ret)
 		dev_warn(priv->dev, "failed setting MTU settings");
 
@@ -1349,12 +1322,12 @@ qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_inde
 	}
 
 	/* Set RGMII delay based on the selected values */
-	ret = qca8k_rmw(priv, reg,
-			QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
-			QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
-			QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
-			QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
-			val);
+	ret = regmap_update_bits(priv->regmap, reg,
+				 QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
+				 QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
+				 QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
+				 QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
+				 val);
 	if (ret)
 		dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
 			cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
@@ -1415,7 +1388,7 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 	case PHY_INTERFACE_MODE_RGMII_ID:
 	case PHY_INTERFACE_MODE_RGMII_TXID:
 	case PHY_INTERFACE_MODE_RGMII_RXID:
-		qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+		regmap_write(priv->regmap, reg, QCA8K_PORT_PAD_RGMII_EN);
 
 		/* Configure rgmii delay */
 		qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
@@ -1425,26 +1398,26 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 		 * rather than individual port registers.
 		 */
 		if (priv->switch_id == QCA8K_ID_QCA8337)
-			qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
-				    QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+			regmap_write(priv->regmap, QCA8K_REG_PORT5_PAD_CTRL,
+				     QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
 		break;
 	case PHY_INTERFACE_MODE_SGMII:
 	case PHY_INTERFACE_MODE_1000BASEX:
 		/* Enable SGMII on the port */
-		qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
+		regmap_write(priv->regmap, reg, QCA8K_PORT_PAD_SGMII_EN);
 
 		/* Enable/disable SerDes auto-negotiation as necessary */
-		ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
+		ret = regmap_read(priv->regmap, QCA8K_REG_PWS, &val);
 		if (ret)
 			return;
 		if (phylink_autoneg_inband(mode))
 			val &= ~QCA8K_PWS_SERDES_AEN_DIS;
 		else
 			val |= QCA8K_PWS_SERDES_AEN_DIS;
-		qca8k_write(priv, QCA8K_REG_PWS, val);
+		regmap_write(priv->regmap, QCA8K_REG_PWS, val);
 
 		/* Configure the SGMII parameters */
-		ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
+		ret = regmap_read(priv->regmap, QCA8K_REG_SGMII_CTRL, &val);
 		if (ret)
 			return;
 
@@ -1466,7 +1439,7 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 			val |= QCA8K_SGMII_MODE_CTRL_BASEX;
 		}
 
-		qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
+		regmap_write(priv->regmap, QCA8K_REG_SGMII_CTRL, val);
 
 		/* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
 		 * falling edge is set writing in the PORT0 PAD reg
@@ -1485,10 +1458,10 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 			val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
 
 		if (val)
-			ret = qca8k_rmw(priv, reg,
-					QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
-					QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
-					val);
+			ret = regmap_update_bits(priv->regmap, reg,
+						 QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+						 QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+						 val);
 
 		/* From original code is reported port instability as SGMII also
 		 * require delay set. Apply advised values here or take them from DT.
@@ -1578,7 +1551,7 @@ qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
 	u32 reg;
 	int ret;
 
-	ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), &reg);
+	ret = regmap_read(priv->regmap, QCA8K_REG_PORT_STATUS(port), &reg);
 	if (ret < 0)
 		return ret;
 
@@ -1659,7 +1632,7 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
 
 	reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
 
-	qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
+	regmap_write(priv->regmap, QCA8K_REG_PORT_STATUS(port), reg);
 }
 
 static void
@@ -1689,12 +1662,12 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
 		mib = &ar8327_mib[i];
 		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
 
-		ret = qca8k_read(priv, reg, &val);
+		ret = regmap_read(priv->regmap, reg, &val);
 		if (ret < 0)
 			continue;
 
 		if (mib->size == 2) {
-			ret = qca8k_read(priv, reg + 4, &hi);
+			ret = regmap_read(priv->regmap, reg + 4, &hi);
 			if (ret < 0)
 				continue;
 		}
@@ -1723,7 +1696,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
 	int ret;
 
 	mutex_lock(&priv->reg_mutex);
-	ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
+	ret = regmap_read(priv->regmap, QCA8K_REG_EEE_CTRL, &reg);
 	if (ret < 0)
 		goto exit;
 
@@ -1731,7 +1704,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
 		reg |= lpi_en;
 	else
 		reg &= ~lpi_en;
-	ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+	ret = regmap_write(priv->regmap, QCA8K_REG_EEE_CTRL, reg);
 
 exit:
 	mutex_unlock(&priv->reg_mutex);
@@ -1770,8 +1743,8 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 		break;
 	}
 
-	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-		  QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+	regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+			   QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
 }
 
 static int
@@ -1792,9 +1765,9 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
 		/* Add this port to the portvlan mask of the other ports
 		 * in the bridge
 		 */
-		ret = qca8k_reg_set(priv,
-				    QCA8K_PORT_LOOKUP_CTRL(i),
-				    BIT(port));
+		ret = regmap_set_bits(priv->regmap,
+				      QCA8K_PORT_LOOKUP_CTRL(i),
+				      BIT(port));
 		if (ret)
 			return ret;
 		if (i != port)
@@ -1802,8 +1775,8 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
 	}
 
 	/* Add all other ports to this ports portvlan mask */
-	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-			QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+	ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+				 QCA8K_PORT_LOOKUP_MEMBER, port_mask);
 
 	return ret;
 }
@@ -1824,16 +1797,16 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
 		/* Remove this port to the portvlan mask of the other ports
 		 * in the bridge
 		 */
-		qca8k_reg_clear(priv,
-				QCA8K_PORT_LOOKUP_CTRL(i),
-				BIT(port));
+		regmap_clear_bits(priv->regmap,
+				  QCA8K_PORT_LOOKUP_CTRL(i),
+				  BIT(port));
 	}
 
 	/* Set the cpu port to be the only one in the portvlan mask of
 	 * this port
 	 */
-	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+	regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+			   QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
 }
 
 static int
@@ -1873,7 +1846,7 @@ qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
 			mtu = priv->port_mtu[i];
 
 	/* Include L2 header / FCS length */
-	return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
+	return regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
 }
 
 static int
@@ -1949,13 +1922,13 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 	int ret;
 
 	if (vlan_filtering) {
-		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
-				QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
 	} else {
-		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
-				QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
 	}
 
 	return ret;
@@ -1978,15 +1951,15 @@ qca8k_port_vlan_add(struct dsa_switch *ds, int port,
 	}
 
 	if (pvid) {
-		ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
-				QCA8K_EGREES_VLAN_PORT_MASK(port),
-				QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+		ret = regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(port),
+					 QCA8K_EGREES_VLAN_PORT_MASK(port),
+					 QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
 		if (ret)
 			return ret;
 
-		ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
-				  QCA8K_PORT_VLAN_CVID(vlan->vid) |
-				  QCA8K_PORT_VLAN_SVID(vlan->vid));
+		ret = regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(port),
+				   QCA8K_PORT_VLAN_CVID(vlan->vid) |
+				   QCA8K_PORT_VLAN_SVID(vlan->vid));
 	}
 
 	return ret;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 09/19] net: dsa: qca8k: add additional MIB counter and make it dynamic
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (7 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:17   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 10/19] net: dsa: qca8k: add support for port fast aging Ansuel Smith
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

We are currently missing 2 additional MIB counter present in QCA833x
switch.
QC832x switch have 39 MIB counter and QCA833X have 41 MIB counter.
Add the additional MIB counter and rework the MIB function to print the
correct supported counter from the match_data struct.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++---
 drivers/net/dsa/qca8k.h |  4 ++++
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 7f71607bec3f..cf4f69b36b47 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -70,6 +70,8 @@ static const struct qca8k_mib_desc ar8327_mib[] = {
 	MIB_DESC(1, 0x9c, "TxExcDefer"),
 	MIB_DESC(1, 0xa0, "TxDefer"),
 	MIB_DESC(1, 0xa4, "TxLateCol"),
+	MIB_DESC(1, 0xa8, "RXUnicast"),
+	MIB_DESC(1, 0xac, "TXUnicast"),
 };
 
 /* The 32bit switch registers are accessed indirectly. To achieve this we need
@@ -1638,12 +1640,16 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
 static void
 qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
 {
+	const struct qca8k_match_data *match_data;
+	struct qca8k_priv *priv = ds->priv;
 	int i;
 
 	if (stringset != ETH_SS_STATS)
 		return;
 
-	for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++)
+	match_data = of_device_get_match_data(priv->dev);
+
+	for (i = 0; i < match_data->mib_count; i++)
 		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
 			ETH_GSTRING_LEN);
 }
@@ -1653,12 +1659,15 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
 			uint64_t *data)
 {
 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	const struct qca8k_match_data *match_data;
 	const struct qca8k_mib_desc *mib;
 	u32 reg, i, val;
 	u32 hi = 0;
 	int ret;
 
-	for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
+	match_data = of_device_get_match_data(priv->dev);
+
+	for (i = 0; i < match_data->mib_count; i++) {
 		mib = &ar8327_mib[i];
 		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
 
@@ -1681,10 +1690,15 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
 static int
 qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
 {
+	const struct qca8k_match_data *match_data;
+	struct qca8k_priv *priv = ds->priv;
+
 	if (sset != ETH_SS_STATS)
 		return 0;
 
-	return ARRAY_SIZE(ar8327_mib);
+	match_data = of_device_get_match_data(priv->dev);
+
+	return match_data->mib_count;
 }
 
 static int
@@ -2143,14 +2157,17 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
 static const struct qca8k_match_data qca8327 = {
 	.id = QCA8K_ID_QCA8327,
 	.reduced_package = true,
+	.mib_count = QCA8K_QCA832X_MIB_COUNT,
 };
 
 static const struct qca8k_match_data qca8328 = {
 	.id = QCA8K_ID_QCA8327,
+	.mib_count = QCA8K_QCA832X_MIB_COUNT,
 };
 
 static const struct qca8k_match_data qca833x = {
 	.id = QCA8K_ID_QCA8337,
+	.mib_count = QCA8K_QCA833X_MIB_COUNT,
 };
 
 static const struct of_device_id qca8k_of_match[] = {
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index 3722fbb6b461..c5d83514ad2e 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -21,6 +21,9 @@
 #define PHY_ID_QCA8337					0x004dd036
 #define QCA8K_ID_QCA8337				0x13
 
+#define QCA8K_QCA832X_MIB_COUNT				39
+#define QCA8K_QCA833X_MIB_COUNT				41
+
 #define QCA8K_BUSY_WAIT_TIMEOUT				2000
 
 #define QCA8K_NUM_FDB_RECORDS				2048
@@ -279,6 +282,7 @@ struct ar8xxx_port_status {
 struct qca8k_match_data {
 	u8 id;
 	bool reduced_package;
+	u8 mib_count;
 };
 
 enum {
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 10/19] net: dsa: qca8k: add support for port fast aging
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (8 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 09/19] net: dsa: qca8k: add additional MIB counter and make it dynamic Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:20   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 11/19] net: dsa: qca8k: add support for mirror mode Ansuel Smith
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

The switch doesn't support fast aging but it does support the flush of
the ARL table for a specific port. Add this function to simulate
fast aging and proprely support stp state set.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index cf4f69b36b47..d73886b36e6a 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1823,6 +1823,16 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
 			   QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
 }
 
+static void
+qca8k_port_fast_age(struct dsa_switch *ds, int port)
+{
+	struct qca8k_priv *priv = ds->priv;
+
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
+	mutex_unlock(&priv->reg_mutex);
+}
+
 static int
 qca8k_port_enable(struct dsa_switch *ds, int port,
 		  struct phy_device *phy)
@@ -2031,6 +2041,7 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.port_stp_state_set	= qca8k_port_stp_state_set,
 	.port_bridge_join	= qca8k_port_bridge_join,
 	.port_bridge_leave	= qca8k_port_bridge_leave,
+	.port_fast_age		= qca8k_port_fast_age,
 	.port_fdb_add		= qca8k_port_fdb_add,
 	.port_fdb_del		= qca8k_port_fdb_del,
 	.port_fdb_dump		= qca8k_port_fdb_dump,
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 11/19] net: dsa: qca8k: add support for mirror mode
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (9 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 10/19] net: dsa: qca8k: add support for port fast aging Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:42   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 12/19] net: dsa: qca8k: add set_ageing_time support Ansuel Smith
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

The switch support mirror mode. Only one port can set as mirror port and
every other port can set to both ingress and egress mode. The mirror
port is disabled and reverted to normal operation once every port is
removed from sending packet to the mirror mode.
Also modify the fdb logit to send packet to both destination and mirror
port by default to support mirror mode.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 95 +++++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/qca8k.h |  5 +++
 2 files changed, 100 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index d73886b36e6a..a74099131e3d 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1938,6 +1938,99 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+static int
+qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+		      struct dsa_mall_mirror_tc_entry *mirror,
+		      bool ingress)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int monitor_port, ret;
+	u32 reg, val;
+
+	/* Check for existent entry */
+	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
+		return -EEXIST;
+
+	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
+	if (ret)
+		return ret;
+
+	/* QCA83xx can have only one port set to mirror mode.
+	 * Check that the correct port is requested and return error otherwise.
+	 * When no mirror port is set, the values is set to 0xF
+	 */
+	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+	if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
+		return -EEXIST;
+
+	/* Set the monitor port */
+	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
+			 mirror->to_local_port);
+	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+	if (ret)
+		return ret;
+
+	if (ingress) {
+		reg = QCA8K_PORT_LOOKUP_CTRL(port);
+		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+	} else {
+		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+	}
+
+	ret = regmap_update_bits(priv->regmap, reg, val, val);
+	if (ret)
+		return ret;
+
+	/* Track mirror port for tx and rx to decide when the
+	 * mirror port has to be disabled.
+	 */
+	if (ingress)
+		priv->mirror_rx |= BIT(port);
+	else
+		priv->mirror_tx |= BIT(port);
+
+	return 0;
+}
+
+static void
+qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+		      struct dsa_mall_mirror_tc_entry *mirror)
+{
+	struct qca8k_priv *priv = ds->priv;
+	u32 reg, val;
+	int ret;
+
+	if (mirror->ingress) {
+		reg = QCA8K_PORT_LOOKUP_CTRL(port);
+		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+	} else {
+		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+	}
+
+	ret = regmap_clear_bits(priv->regmap, reg, val);
+	if (ret)
+		goto err;
+
+	if (mirror->ingress)
+		priv->mirror_rx &= ~BIT(port);
+	else
+		priv->mirror_tx &= ~BIT(port);
+
+	/* No port set to send packet to mirror port. Disable mirror port */
+	if (!priv->mirror_rx && !priv->mirror_tx) {
+		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
+		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+		if (ret)
+			goto err;
+	}
+err:
+	dev_err(priv->dev, "Failed to del mirror port from %d", port);
+}
+
 static int
 qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 			  struct netlink_ext_ack *extack)
@@ -2045,6 +2138,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.port_fdb_add		= qca8k_port_fdb_add,
 	.port_fdb_del		= qca8k_port_fdb_del,
 	.port_fdb_dump		= qca8k_port_fdb_dump,
+	.port_mirror_add	= qca8k_port_mirror_add,
+	.port_mirror_del	= qca8k_port_mirror_del,
 	.port_vlan_filtering	= qca8k_port_vlan_filtering,
 	.port_vlan_add		= qca8k_port_vlan_add,
 	.port_vlan_del		= qca8k_port_vlan_del,
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index c5d83514ad2e..d25afdab4dea 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -177,6 +177,7 @@
 #define   QCA8K_VTU_FUNC1_FULL				BIT(4)
 #define QCA8K_REG_GLOBAL_FW_CTRL0			0x620
 #define   QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN		BIT(10)
+#define   QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM		GENMASK(7, 4)
 #define QCA8K_REG_GLOBAL_FW_CTRL1			0x624
 #define   QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK		GENMASK(30, 24)
 #define   QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK		GENMASK(22, 16)
@@ -198,6 +199,7 @@
 #define   QCA8K_PORT_LOOKUP_STATE_LEARNING		QCA8K_PORT_LOOKUP_STATE(0x3)
 #define   QCA8K_PORT_LOOKUP_STATE_FORWARD		QCA8K_PORT_LOOKUP_STATE(0x4)
 #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20)
+#define   QCA8K_PORT_LOOKUP_ING_MIRROR_EN		BIT(25)
 
 #define QCA8K_REG_GLOBAL_FC_THRESH			0x800
 #define   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK		GENMASK(24, 16)
@@ -262,6 +264,7 @@ enum qca8k_fdb_cmd {
 	QCA8K_FDB_FLUSH	= 1,
 	QCA8K_FDB_LOAD = 2,
 	QCA8K_FDB_PURGE = 3,
+	QCA8K_FDB_FLUSH_PORT = 5,
 	QCA8K_FDB_NEXT = 6,
 	QCA8K_FDB_SEARCH = 7,
 };
@@ -302,6 +305,8 @@ struct qca8k_ports_config {
 struct qca8k_priv {
 	u8 switch_id;
 	u8 switch_revision;
+	u8 mirror_rx;
+	u8 mirror_tx;
 	bool legacy_phy_port_mapping;
 	struct qca8k_ports_config ports_config;
 	struct regmap *regmap;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 12/19] net: dsa: qca8k: add set_ageing_time support
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (10 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 11/19] net: dsa: qca8k: add support for mirror mode Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:47   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 13/19] net: dsa: qca8k: add min/max ageing time Ansuel Smith
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

qca8k support setting ageing time in set of 7s. Add support for it and
return error with value greater than the max value accepted of 7645m.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 18 ++++++++++++++++++
 drivers/net/dsa/qca8k.h |  3 +++
 2 files changed, 21 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index a74099131e3d..50f19549b97d 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1833,6 +1833,23 @@ qca8k_port_fast_age(struct dsa_switch *ds, int port)
 	mutex_unlock(&priv->reg_mutex);
 }
 
+static int
+qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+	struct qca8k_priv *priv = ds->priv;
+	unsigned int secs = msecs / 1000;
+	u32 val;
+
+	/* AGE_TIME reg is set in 7s step */
+	val = secs / 7;
+
+	if (val > FIELD_MAX(QCA8K_ATU_AGE_TIME_MASK))
+		return -ERANGE;
+
+	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
+				  QCA8K_ATU_AGE_TIME(val));
+}
+
 static int
 qca8k_port_enable(struct dsa_switch *ds, int port,
 		  struct phy_device *phy)
@@ -2125,6 +2142,7 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.get_strings		= qca8k_get_strings,
 	.get_ethtool_stats	= qca8k_get_ethtool_stats,
 	.get_sset_count		= qca8k_get_sset_count,
+	.set_ageing_time	= qca8k_set_ageing_time,
 	.get_mac_eee		= qca8k_get_mac_eee,
 	.set_mac_eee		= qca8k_set_mac_eee,
 	.port_enable		= qca8k_port_enable,
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index d25afdab4dea..e1298179d7cb 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -175,6 +175,9 @@
 #define   QCA8K_VTU_FUNC1_BUSY				BIT(31)
 #define   QCA8K_VTU_FUNC1_VID_MASK			GENMASK(27, 16)
 #define   QCA8K_VTU_FUNC1_FULL				BIT(4)
+#define QCA8K_REG_ATU_CTRL				0x618
+#define   QCA8K_ATU_AGE_TIME_MASK			GENMASK(15, 0)
+#define   QCA8K_ATU_AGE_TIME(x)				FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
 #define QCA8K_REG_GLOBAL_FW_CTRL0			0x620
 #define   QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN		BIT(10)
 #define   QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM		GENMASK(7, 4)
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 13/19] net: dsa: qca8k: add min/max ageing time
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (11 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 12/19] net: dsa: qca8k: add set_ageing_time support Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  1:49   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del Ansuel Smith
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Add min and max ageing value for qca8k switch.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 50f19549b97d..dda99263fe8c 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1291,6 +1291,10 @@ qca8k_setup(struct dsa_switch *ds)
 	/* We don't have interrupts for link changes, so we need to poll */
 	ds->pcs_poll = true;
 
+	/* Set min a max ageing value supported */
+	ds->ageing_time_min = 7000;
+	ds->ageing_time_max = 458745000;
+
 	return 0;
 }
 
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (12 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 13/19] net: dsa: qca8k: add min/max ageing time Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  2:06   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 15/19] net: dsa: qca8k: add LAG support Ansuel Smith
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Add support for mdb add/del function. The ARL table is used to insert
the rule. A new search function is introduced to search the rule and add
additional port to it. If every port is removed from the rule, it's
removed. It's set STATIC in the ARL table (aka it doesn't age) to not be
flushed by fast age function.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 82 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index dda99263fe8c..a217c842ab45 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -417,6 +417,23 @@ qca8k_fdb_flush(struct qca8k_priv *priv)
 	mutex_unlock(&priv->reg_mutex);
 }
 
+static int
+qca8k_fdb_search(struct qca8k_priv *priv, struct qca8k_fdb *fdb, const u8 *mac, u16 vid)
+{
+	int ret;
+
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_write(priv, vid, 0, mac, 0);
+	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+	if (ret < 0)
+		goto exit;
+
+	ret = qca8k_fdb_read(priv, fdb);
+exit:
+	mutex_unlock(&priv->reg_mutex);
+	return ret;
+}
+
 static int
 qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
 {
@@ -1959,6 +1976,69 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+static int
+qca8k_port_mdb_add(struct dsa_switch *ds, int port,
+		   const struct switchdev_obj_port_mdb *mdb)
+{
+	struct qca8k_priv *priv = ds->priv;
+	struct qca8k_fdb fdb = { 0 };
+	const u8 *addr = mdb->addr;
+	u8 port_mask = BIT(port);
+	u16 vid = mdb->vid;
+	int ret;
+
+	/* Check if entry already exist */
+	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
+	if (ret < 0)
+		return ret;
+
+	/* Rule exist. Delete first */
+	if (!fdb.aging) {
+		ret = qca8k_fdb_del(priv, addr, fdb.port_mask, vid);
+		if (ret)
+			return ret;
+	}
+
+	/* Add port to fdb portmask */
+	fdb.port_mask |= port_mask;
+
+	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
+}
+
+static int
+qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+		   const struct switchdev_obj_port_mdb *mdb)
+{
+	struct qca8k_priv *priv = ds->priv;
+	struct qca8k_fdb fdb = { 0 };
+	const u8 *addr = mdb->addr;
+	u8 port_mask = BIT(port);
+	u16 vid = mdb->vid;
+	int ret;
+
+	/* Check if entry already exist */
+	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
+	if (ret < 0)
+		return ret;
+
+	/* Rule doesn't exist. Why delete? */
+	if (!fdb.aging)
+		return -EINVAL;
+
+	ret = qca8k_fdb_del(priv, addr, port_mask, vid);
+	if (ret)
+		return ret;
+
+	/* Only port in the rule is this port. Don't re insert */
+	if (fdb.port_mask == port_mask)
+		return 0;
+
+	/* Remove port from port mask */
+	fdb.port_mask &= ~port_mask;
+
+	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
+}
+
 static int
 qca8k_port_mirror_add(struct dsa_switch *ds, int port,
 		      struct dsa_mall_mirror_tc_entry *mirror,
@@ -2160,6 +2240,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.port_fdb_add		= qca8k_port_fdb_add,
 	.port_fdb_del		= qca8k_port_fdb_del,
 	.port_fdb_dump		= qca8k_port_fdb_dump,
+	.port_mdb_add		= qca8k_port_mdb_add,
+	.port_mdb_del		= qca8k_port_mdb_del,
 	.port_mirror_add	= qca8k_port_mirror_add,
 	.port_mirror_del	= qca8k_port_mirror_del,
 	.port_vlan_filtering	= qca8k_port_vlan_filtering,
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 15/19] net: dsa: qca8k: add LAG support
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (13 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  2:13   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress Ansuel Smith
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Add LAG support to this switch. In Documentation this is described as
trunk mode. A max of 4 LAGs are supported and each can support up to 4
port. The only tx mode supported is Hash mode and no reference is
present for active backup or any other mode in Documentation.
When no port are present in the trunk, the trunk is disabled in the
switch.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 117 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/qca8k.h |  24 +++++++++
 2 files changed, 141 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index a217c842ab45..c3234988aabf 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1312,6 +1312,9 @@ qca8k_setup(struct dsa_switch *ds)
 	ds->ageing_time_min = 7000;
 	ds->ageing_time_max = 458745000;
 
+	/* Set max number of LAGs supported */
+	ds->num_lag_ids = QCA8K_NUM_LAGS;
+
 	return 0;
 }
 
@@ -2220,6 +2223,118 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
 	return DSA_TAG_PROTO_QCA;
 }
 
+static bool
+qca8k_lag_can_offload(struct dsa_switch *ds,
+		      struct net_device *lag,
+		      struct netdev_lag_upper_info *info)
+{
+	struct dsa_port *dp;
+	int id, members = 0;
+
+	id = dsa_lag_id(ds->dst, lag);
+	if (id < 0 || id >= ds->num_lag_ids)
+		return false;
+
+	dsa_lag_foreach_port(dp, ds->dst, lag)
+		/* Includes the port joining the LAG */
+		members++;
+
+	if (members > QCA8K_NUM_PORTS_FOR_LAG)
+		return false;
+
+	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+		return false;
+
+	return true;
+}
+
+static int
+qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
+			  struct net_device *lag, bool delete)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int ret, id, i;
+	u32 val;
+
+	id = dsa_lag_id(ds->dst, lag);
+
+	/* Read current port member */
+	ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
+	if (ret)
+		return ret;
+
+	/* Shift val to the correct trunk */
+	val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
+	val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
+	if (delete)
+		val &= ~BIT(port);
+	else
+		val |= BIT(port);
+
+	/* Update port member. With empty portmap disable trunk */
+	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
+				 QCA8K_REG_GOL_TRUNK_MEMBER(id) |
+				 QCA8K_REG_GOL_TRUNK_EN(id),
+				 !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
+				 val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
+
+	/* Search empty member if adding or port on deleting */
+	for (i = 0; i < 4; i++) {
+		ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
+		if (ret)
+			return ret;
+
+		val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
+		val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
+
+		if (delete) {
+			/* If port flagged to be disabled assume this member is
+			 * empty
+			 */
+			if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+				continue;
+
+			val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
+			if (val != port)
+				continue;
+		} else {
+			/* If port flagged to be enabled assume this member is
+			 * already set
+			 */
+			if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+				continue;
+		}
+
+		/* We find the member to remove */
+		break;
+	}
+
+	/* Set port in the correct port mask or disable port if in delate mode */
+	return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
+				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
+				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
+				  !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
+				  port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
+}
+
+static int
+qca8k_port_lag_join(struct dsa_switch *ds, int port,
+		    struct net_device *lag,
+		    struct netdev_lag_upper_info *info)
+{
+	if (!qca8k_lag_can_offload(ds, lag, info))
+		return -EOPNOTSUPP;
+
+	return qca8k_lag_refresh_portmap(ds, port, lag, false);
+}
+
+static int
+qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+		     struct net_device *lag)
+{
+	return qca8k_lag_refresh_portmap(ds, port, lag, true);
+}
+
 static const struct dsa_switch_ops qca8k_switch_ops = {
 	.get_tag_protocol	= qca8k_get_tag_protocol,
 	.setup			= qca8k_setup,
@@ -2253,6 +2368,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.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,
 };
 
 static int
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index e1298179d7cb..5310022569f3 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -15,6 +15,8 @@
 #define QCA8K_NUM_PORTS					7
 #define QCA8K_NUM_CPU_PORTS				2
 #define QCA8K_MAX_MTU					9000
+#define QCA8K_NUM_LAGS					4
+#define QCA8K_NUM_PORTS_FOR_LAG				4
 
 #define PHY_ID_QCA8327					0x004dd034
 #define QCA8K_ID_QCA8327				0x12
@@ -204,6 +206,28 @@
 #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20)
 #define   QCA8K_PORT_LOOKUP_ING_MIRROR_EN		BIT(25)
 
+#define QCA8K_REG_GOL_TRUNK_CTRL0			0x700
+/* 4 max trunk first
+ * first 6 bit for member bitmap
+ * 7th bit is to enable trunk port
+ */
+#define QCA8K_REG_GOL_TRUNK_SHIFT(_i)			((_i) * 8)
+#define QCA8K_REG_GOL_TRUNK_EN_MASK			BIT(7)
+#define QCA8K_REG_GOL_TRUNK_EN(_i)			(QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
+#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK			GENMASK(6, 0)
+#define QCA8K_REG_GOL_TRUNK_MEMBER(_i)			(QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
+/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */
+#define QCA8K_REG_GOL_TRUNK_CTRL(_i)			(0x704 + (((_i) / 2) * 4))
+#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK		GENMASK(3, 0)
+#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK		BIT(3)
+#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK		GENMASK(2, 0)
+#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)		(((_i) / 2) * 16)
+#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i)			((_i) * 4)
+/* Complex shift: FIRST shift for port THEN shift for trunk */
+#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)	(QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i))
+#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j)	(QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
+#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j)	(QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
+
 #define QCA8K_REG_GLOBAL_FC_THRESH			0x800
 #define   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK		GENMASK(24, 16)
 #define   QCA8K_GLOBAL_FC_GOL_XON_THRES(x)		FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (14 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 15/19] net: dsa: qca8k: add LAG support Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  2:20   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 17/19] net: dsa: qca8k: move qca8k to qca dir Ansuel Smith
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

qca8k have a global MTU. Inform DSA of this as the change MTU port
function checks the max MTU across all port and sets the max value
anyway as this switch doesn't support per port MTU.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca8k.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index c3234988aabf..cae58753bb1f 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -1315,6 +1315,9 @@ qca8k_setup(struct dsa_switch *ds)
 	/* Set max number of LAGs supported */
 	ds->num_lag_ids = QCA8K_NUM_LAGS;
 
+	/* Global MTU. Inform dsa that per port MTU is not supported */
+	ds->mtu_enforcement_ingress = true;
+
 	return 0;
 }
 
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 17/19] net: dsa: qca8k: move qca8k to qca dir
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (15 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-17 21:04 ` [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant Ansuel Smith
  2021-11-17 21:04 ` [net-next PATCH 19/19] net: dsa: qca8k: split qca8k in common and 8xxx specific code Ansuel Smith
  18 siblings, 0 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Move qca8k driver to qca dir in preparation for code split of common
code from specific code.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/Kconfig           | 8 --------
 drivers/net/dsa/Makefile          | 1 -
 drivers/net/dsa/qca/Kconfig       | 9 +++++++++
 drivers/net/dsa/qca/Makefile      | 1 +
 drivers/net/dsa/{ => qca}/qca8k.c | 0
 drivers/net/dsa/{ => qca}/qca8k.h | 0
 6 files changed, 10 insertions(+), 9 deletions(-)
 rename drivers/net/dsa/{ => qca}/qca8k.c (100%)
 rename drivers/net/dsa/{ => qca}/qca8k.h (100%)

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 7b1457a6e327..19587620ef14 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -59,14 +59,6 @@ source "drivers/net/dsa/sja1105/Kconfig"
 
 source "drivers/net/dsa/xrs700x/Kconfig"
 
-config NET_DSA_QCA8K
-	tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
-	select NET_DSA_TAG_QCA
-	select REGMAP
-	help
-	  This enables support for the Qualcomm Atheros QCA8K Ethernet
-	  switch chips.
-
 config NET_DSA_REALTEK_SMI
 	tristate "Realtek SMI Ethernet switch family support"
 	select NET_DSA_TAG_RTL4_A
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 8da1569a34e6..6c6fbb14eff8 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -8,7 +8,6 @@ endif
 obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
 obj-$(CONFIG_NET_DSA_MT7530)	+= mt7530.o
 obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
-obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o
 obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
 realtek-smi-objs		:= realtek-smi-core.o rtl8366.o rtl8366rb.o rtl8365mb.o
 obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
diff --git a/drivers/net/dsa/qca/Kconfig b/drivers/net/dsa/qca/Kconfig
index 13b7e679b8b5..7186f036678c 100644
--- a/drivers/net/dsa/qca/Kconfig
+++ b/drivers/net/dsa/qca/Kconfig
@@ -7,3 +7,12 @@ config NET_DSA_AR9331
 	help
 	  This enables support for the Qualcomm Atheros AR9331 built-in Ethernet
 	  switch.
+
+config NET_DSA_QCA8K
+	tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
+	depends on NET_DSA
+	select NET_DSA_TAG_QCA
+	select REGMAP
+	help
+	  This enables support for the Qualcomm Atheros QCA8K Ethernet
+	  switch chips.
\ No newline at end of file
diff --git a/drivers/net/dsa/qca/Makefile b/drivers/net/dsa/qca/Makefile
index 274022319066..16f84acf0246 100644
--- a/drivers/net/dsa/qca/Makefile
+++ b/drivers/net/dsa/qca/Makefile
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_NET_DSA_AR9331)	+= ar9331.o
+obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o
\ No newline at end of file
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca/qca8k.c
similarity index 100%
rename from drivers/net/dsa/qca8k.c
rename to drivers/net/dsa/qca/qca8k.c
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca/qca8k.h
similarity index 100%
rename from drivers/net/dsa/qca8k.h
rename to drivers/net/dsa/qca/qca8k.h
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (16 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 17/19] net: dsa: qca8k: move qca8k to qca dir Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  2021-11-19  2:21   ` Vladimir Oltean
  2021-11-17 21:04 ` [net-next PATCH 19/19] net: dsa: qca8k: split qca8k in common and 8xxx specific code Ansuel Smith
  18 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

Drop of_platform include and device_get_match_data instead of the OF
variant.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca/qca8k.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/net/dsa/qca/qca8k.c b/drivers/net/dsa/qca/qca8k.c
index cae58753bb1f..260cdac53990 100644
--- a/drivers/net/dsa/qca/qca8k.c
+++ b/drivers/net/dsa/qca/qca8k.c
@@ -14,7 +14,6 @@
 #include <net/dsa.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
-#include <linux/of_platform.h>
 #include <linux/if_bridge.h>
 #include <linux/mdio.h>
 #include <linux/phylink.h>
@@ -929,7 +928,7 @@ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
 	 * Should be applied by default but we set this just to make sure.
 	 */
 	if (priv->switch_id == QCA8K_ID_QCA8327) {
-		data = of_device_get_match_data(priv->dev);
+		data = device_get_match_data(priv->dev);
 
 		/* Set the correct package of 148 pin for QCA8327 */
 		if (data->reduced_package)
@@ -1071,7 +1070,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *priv)
 	int ret;
 
 	/* get the switches ID from the compatible */
-	data = of_device_get_match_data(priv->dev);
+	data = device_get_match_data(priv->dev);
 	if (!data)
 		return -ENODEV;
 
@@ -1674,7 +1673,7 @@ qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
 	if (stringset != ETH_SS_STATS)
 		return;
 
-	match_data = of_device_get_match_data(priv->dev);
+	match_data = device_get_match_data(priv->dev);
 
 	for (i = 0; i < match_data->mib_count; i++)
 		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
@@ -1692,7 +1691,7 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
 	u32 hi = 0;
 	int ret;
 
-	match_data = of_device_get_match_data(priv->dev);
+	match_data = device_get_match_data(priv->dev);
 
 	for (i = 0; i < match_data->mib_count; i++) {
 		mib = &ar8327_mib[i];
@@ -1723,7 +1722,7 @@ qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
 	if (sset != ETH_SS_STATS)
 		return 0;
 
-	match_data = of_device_get_match_data(priv->dev);
+	match_data = device_get_match_data(priv->dev);
 
 	return match_data->mib_count;
 }
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [net-next PATCH 19/19] net: dsa: qca8k: split qca8k in common and 8xxx specific code
  2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
                   ` (17 preceding siblings ...)
  2021-11-17 21:04 ` [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant Ansuel Smith
@ 2021-11-17 21:04 ` Ansuel Smith
  18 siblings, 0 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 21:04 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, Ansuel Smith,
	linux-kernel, netdev

The qca8k family reg structure is used also in the internal ipq40xx
switch. Split qca8k common code from specific code for future
implementation of ipq40xx internal switch based on qca8k.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/qca/Makefile                  |    3 +-
 drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} | 1168 +----------------
 drivers/net/dsa/qca/qca8k-common.c            | 1157 ++++++++++++++++
 drivers/net/dsa/qca/qca8k.h                   |   50 +
 4 files changed, 1222 insertions(+), 1156 deletions(-)
 rename drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} (56%)
 create mode 100644 drivers/net/dsa/qca/qca8k-common.c

diff --git a/drivers/net/dsa/qca/Makefile b/drivers/net/dsa/qca/Makefile
index 16f84acf0246..31ef72d4b20a 100644
--- a/drivers/net/dsa/qca/Makefile
+++ b/drivers/net/dsa/qca/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_NET_DSA_AR9331)	+= ar9331.o
-obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o
\ No newline at end of file
+obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o
+qca8k-y 			+= qca8k-common.o qca8k-8xxx.o
\ No newline at end of file
diff --git a/drivers/net/dsa/qca/qca8k.c b/drivers/net/dsa/qca/qca8k-8xxx.c
similarity index 56%
rename from drivers/net/dsa/qca/qca8k.c
rename to drivers/net/dsa/qca/qca8k-8xxx.c
index 260cdac53990..1dbb8fe52457 100644
--- a/drivers/net/dsa/qca/qca8k.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -22,57 +22,6 @@
 
 #include "qca8k.h"
 
-#define MIB_DESC(_s, _o, _n)	\
-	{			\
-		.size = (_s),	\
-		.offset = (_o),	\
-		.name = (_n),	\
-	}
-
-static const struct qca8k_mib_desc ar8327_mib[] = {
-	MIB_DESC(1, 0x00, "RxBroad"),
-	MIB_DESC(1, 0x04, "RxPause"),
-	MIB_DESC(1, 0x08, "RxMulti"),
-	MIB_DESC(1, 0x0c, "RxFcsErr"),
-	MIB_DESC(1, 0x10, "RxAlignErr"),
-	MIB_DESC(1, 0x14, "RxRunt"),
-	MIB_DESC(1, 0x18, "RxFragment"),
-	MIB_DESC(1, 0x1c, "Rx64Byte"),
-	MIB_DESC(1, 0x20, "Rx128Byte"),
-	MIB_DESC(1, 0x24, "Rx256Byte"),
-	MIB_DESC(1, 0x28, "Rx512Byte"),
-	MIB_DESC(1, 0x2c, "Rx1024Byte"),
-	MIB_DESC(1, 0x30, "Rx1518Byte"),
-	MIB_DESC(1, 0x34, "RxMaxByte"),
-	MIB_DESC(1, 0x38, "RxTooLong"),
-	MIB_DESC(2, 0x3c, "RxGoodByte"),
-	MIB_DESC(2, 0x44, "RxBadByte"),
-	MIB_DESC(1, 0x4c, "RxOverFlow"),
-	MIB_DESC(1, 0x50, "Filtered"),
-	MIB_DESC(1, 0x54, "TxBroad"),
-	MIB_DESC(1, 0x58, "TxPause"),
-	MIB_DESC(1, 0x5c, "TxMulti"),
-	MIB_DESC(1, 0x60, "TxUnderRun"),
-	MIB_DESC(1, 0x64, "Tx64Byte"),
-	MIB_DESC(1, 0x68, "Tx128Byte"),
-	MIB_DESC(1, 0x6c, "Tx256Byte"),
-	MIB_DESC(1, 0x70, "Tx512Byte"),
-	MIB_DESC(1, 0x74, "Tx1024Byte"),
-	MIB_DESC(1, 0x78, "Tx1518Byte"),
-	MIB_DESC(1, 0x7c, "TxMaxByte"),
-	MIB_DESC(1, 0x80, "TxOverSize"),
-	MIB_DESC(2, 0x84, "TxByte"),
-	MIB_DESC(1, 0x8c, "TxCollision"),
-	MIB_DESC(1, 0x90, "TxAbortCol"),
-	MIB_DESC(1, 0x94, "TxMultiCol"),
-	MIB_DESC(1, 0x98, "TxSingleCol"),
-	MIB_DESC(1, 0x9c, "TxExcDefer"),
-	MIB_DESC(1, 0xa0, "TxDefer"),
-	MIB_DESC(1, 0xa4, "TxLateCol"),
-	MIB_DESC(1, 0xa8, "RXUnicast"),
-	MIB_DESC(1, 0xac, "TXUnicast"),
-};
-
 /* The 32bit switch registers are accessed indirectly. To achieve this we need
  * to set the page of the register. Track the last page that was set to reduce
  * mdio writes
@@ -228,30 +177,6 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_
 	return ret;
 }
 
-static const struct regmap_range qca8k_readable_ranges[] = {
-	regmap_reg_range(0x0000, 0x00e4), /* Global control */
-	regmap_reg_range(0x0100, 0x0168), /* EEE control */
-	regmap_reg_range(0x0200, 0x0270), /* Parser control */
-	regmap_reg_range(0x0400, 0x0454), /* ACL */
-	regmap_reg_range(0x0600, 0x0718), /* Lookup */
-	regmap_reg_range(0x0800, 0x0b70), /* QM */
-	regmap_reg_range(0x0c00, 0x0c80), /* PKT */
-	regmap_reg_range(0x0e00, 0x0e98), /* L3 */
-	regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
-	regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
-	regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
-	regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
-	regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
-	regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
-	regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
-
-};
-
-static const struct regmap_access_table qca8k_readable_table = {
-	.yes_ranges = qca8k_readable_ranges,
-	.n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
-};
-
 static struct regmap_config qca8k_regmap_config = {
 	.reg_bits = 16,
 	.val_bits = 32,
@@ -265,330 +190,6 @@ static struct regmap_config qca8k_regmap_config = {
 	.cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
 };
 
-static int
-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
-{
-	u32 val;
-
-	return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
-				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
-}
-
-static int
-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
-{
-	u32 reg[4], val;
-	int i, ret;
-
-	/* load the ARL table into an array */
-	for (i = 0; i < 4; i++) {
-		ret = regmap_read(priv->regmap, QCA8K_REG_ATU_DATA0 + (i * 4), &val);
-		if (ret < 0)
-			return ret;
-
-		reg[i] = val;
-	}
-
-	/* vid - 83:72 */
-	fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
-	/* aging - 67:64 */
-	fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
-	/* portmask - 54:48 */
-	fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
-	/* mac - 47:0 */
-	fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
-	fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
-	fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
-	fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
-	fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
-	fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
-
-	return 0;
-}
-
-static void
-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
-		u8 aging)
-{
-	u32 reg[3] = { 0 };
-	int i;
-
-	/* vid - 83:72 */
-	reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
-	/* aging - 67:64 */
-	reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
-	/* portmask - 54:48 */
-	reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
-	/* mac - 47:0 */
-	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
-	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
-	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
-	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
-	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
-	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
-
-	/* load the array into the ARL table */
-	for (i = 0; i < 3; i++)
-		regmap_write(priv->regmap, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
-}
-
-static int
-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
-{
-	u32 reg;
-	int ret;
-
-	/* Set the command and FDB index */
-	reg = QCA8K_ATU_FUNC_BUSY;
-	reg |= cmd;
-	if (port >= 0) {
-		reg |= QCA8K_ATU_FUNC_PORT_EN;
-		reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
-	}
-
-	/* Write the function register triggering the table access */
-	ret = regmap_write(priv->regmap, QCA8K_REG_ATU_FUNC, reg);
-	if (ret)
-		return ret;
-
-	/* wait for completion */
-	ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
-	if (ret)
-		return ret;
-
-	/* Check for table full violation when adding an entry */
-	if (cmd == QCA8K_FDB_LOAD) {
-		ret = regmap_read(priv->regmap, QCA8K_REG_ATU_FUNC, &reg);
-		if (ret < 0)
-			return ret;
-		if (reg & QCA8K_ATU_FUNC_FULL)
-			return -1;
-	}
-
-	return 0;
-}
-
-static int
-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
-{
-	int ret;
-
-	qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
-	ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
-	if (ret < 0)
-		return ret;
-
-	return qca8k_fdb_read(priv, fdb);
-}
-
-static int
-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
-	      u16 vid, u8 aging)
-{
-	int ret;
-
-	mutex_lock(&priv->reg_mutex);
-	qca8k_fdb_write(priv, vid, port_mask, mac, aging);
-	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
-	mutex_unlock(&priv->reg_mutex);
-
-	return ret;
-}
-
-static int
-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
-{
-	int ret;
-
-	mutex_lock(&priv->reg_mutex);
-	qca8k_fdb_write(priv, vid, port_mask, mac, 0);
-	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
-	mutex_unlock(&priv->reg_mutex);
-
-	return ret;
-}
-
-static void
-qca8k_fdb_flush(struct qca8k_priv *priv)
-{
-	mutex_lock(&priv->reg_mutex);
-	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
-	mutex_unlock(&priv->reg_mutex);
-}
-
-static int
-qca8k_fdb_search(struct qca8k_priv *priv, struct qca8k_fdb *fdb, const u8 *mac, u16 vid)
-{
-	int ret;
-
-	mutex_lock(&priv->reg_mutex);
-	qca8k_fdb_write(priv, vid, 0, mac, 0);
-	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
-	if (ret < 0)
-		goto exit;
-
-	ret = qca8k_fdb_read(priv, fdb);
-exit:
-	mutex_unlock(&priv->reg_mutex);
-	return ret;
-}
-
-static int
-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
-{
-	u32 reg;
-	int ret;
-
-	/* Set the command and VLAN index */
-	reg = QCA8K_VTU_FUNC1_BUSY;
-	reg |= cmd;
-	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
-
-	/* Write the function register triggering the table access */
-	ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC1, reg);
-	if (ret)
-		return ret;
-
-	/* wait for completion */
-	ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
-	if (ret)
-		return ret;
-
-	/* Check for table full violation when adding an entry */
-	if (cmd == QCA8K_VLAN_LOAD) {
-		ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC1, &reg);
-		if (ret < 0)
-			return ret;
-		if (reg & QCA8K_VTU_FUNC1_FULL)
-			return -ENOMEM;
-	}
-
-	return 0;
-}
-
-static int
-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
-{
-	u32 reg;
-	int ret;
-
-	/*
-	   We do the right thing with VLAN 0 and treat it as untagged while
-	   preserving the tag on egress.
-	 */
-	if (vid == 0)
-		return 0;
-
-	mutex_lock(&priv->reg_mutex);
-	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
-	if (ret < 0)
-		goto out;
-
-	ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, &reg);
-	if (ret < 0)
-		goto out;
-	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
-	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
-	if (untagged)
-		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
-	else
-		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
-
-	ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg);
-	if (ret)
-		goto out;
-	ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
-
-out:
-	mutex_unlock(&priv->reg_mutex);
-
-	return ret;
-}
-
-static int
-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
-{
-	u32 reg, mask;
-	int ret, i;
-	bool del;
-
-	mutex_lock(&priv->reg_mutex);
-	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
-	if (ret < 0)
-		goto out;
-
-	ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, &reg);
-	if (ret < 0)
-		goto out;
-	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
-	reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
-
-	/* Check if we're the last member to be removed */
-	del = true;
-	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-		mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
-
-		if ((reg & mask) != mask) {
-			del = false;
-			break;
-		}
-	}
-
-	if (del) {
-		ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
-	} else {
-		ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg);
-		if (ret)
-			goto out;
-		ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
-	}
-
-out:
-	mutex_unlock(&priv->reg_mutex);
-
-	return ret;
-}
-
-static int
-qca8k_mib_init(struct qca8k_priv *priv)
-{
-	int ret;
-
-	mutex_lock(&priv->reg_mutex);
-	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
-	if (ret)
-		goto exit;
-
-	ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
-	if (ret)
-		goto exit;
-
-	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
-	if (ret)
-		goto exit;
-
-	ret = regmap_write(priv->regmap, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
-
-exit:
-	mutex_unlock(&priv->reg_mutex);
-	return ret;
-}
-
-static void
-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
-{
-	u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
-
-	/* Port 0 and 6 have no internal PHY */
-	if (port > 0 && port < 6)
-		mask |= QCA8K_PORT_STATUS_LINK_AUTO;
-
-	if (enable)
-		regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
-	else
-		regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
-}
-
 static u32
 qca8k_port_to_phy(int port)
 {
@@ -1571,772 +1172,29 @@ qca8k_phylink_validate(struct dsa_switch *ds, int port,
 	linkmode_and(state->advertising, state->advertising, mask);
 }
 
-static int
-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
-			     struct phylink_link_state *state)
-{
-	struct qca8k_priv *priv = ds->priv;
-	u32 reg;
-	int ret;
-
-	ret = regmap_read(priv->regmap, QCA8K_REG_PORT_STATUS(port), &reg);
-	if (ret < 0)
-		return ret;
-
-	state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
-	state->an_complete = state->link;
-	state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);
-	state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
-							   DUPLEX_HALF;
-
-	switch (reg & QCA8K_PORT_STATUS_SPEED) {
-	case QCA8K_PORT_STATUS_SPEED_10:
-		state->speed = SPEED_10;
-		break;
-	case QCA8K_PORT_STATUS_SPEED_100:
-		state->speed = SPEED_100;
-		break;
-	case QCA8K_PORT_STATUS_SPEED_1000:
-		state->speed = SPEED_1000;
-		break;
-	default:
-		state->speed = SPEED_UNKNOWN;
-		break;
-	}
-
-	state->pause = MLO_PAUSE_NONE;
-	if (reg & QCA8K_PORT_STATUS_RXFLOW)
-		state->pause |= MLO_PAUSE_RX;
-	if (reg & QCA8K_PORT_STATUS_TXFLOW)
-		state->pause |= MLO_PAUSE_TX;
-
-	return 1;
-}
-
-static void
-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
-			    phy_interface_t interface)
-{
-	struct qca8k_priv *priv = ds->priv;
-
-	qca8k_port_set_status(priv, port, 0);
-}
-
-static void
-qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
-			  phy_interface_t interface, struct phy_device *phydev,
-			  int speed, int duplex, bool tx_pause, bool rx_pause)
-{
-	struct qca8k_priv *priv = ds->priv;
-	u32 reg;
-
-	if (phylink_autoneg_inband(mode)) {
-		reg = QCA8K_PORT_STATUS_LINK_AUTO;
-	} else {
-		switch (speed) {
-		case SPEED_10:
-			reg = QCA8K_PORT_STATUS_SPEED_10;
-			break;
-		case SPEED_100:
-			reg = QCA8K_PORT_STATUS_SPEED_100;
-			break;
-		case SPEED_1000:
-			reg = QCA8K_PORT_STATUS_SPEED_1000;
-			break;
-		default:
-			reg = QCA8K_PORT_STATUS_LINK_AUTO;
-			break;
-		}
-
-		if (duplex == DUPLEX_FULL)
-			reg |= QCA8K_PORT_STATUS_DUPLEX;
-
-		if (rx_pause || dsa_is_cpu_port(ds, port))
-			reg |= QCA8K_PORT_STATUS_RXFLOW;
-
-		if (tx_pause || dsa_is_cpu_port(ds, port))
-			reg |= QCA8K_PORT_STATUS_TXFLOW;
-	}
-
-	reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
-
-	regmap_write(priv->regmap, QCA8K_REG_PORT_STATUS(port), reg);
-}
-
-static void
-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
-{
-	const struct qca8k_match_data *match_data;
-	struct qca8k_priv *priv = ds->priv;
-	int i;
-
-	if (stringset != ETH_SS_STATS)
-		return;
-
-	match_data = device_get_match_data(priv->dev);
-
-	for (i = 0; i < match_data->mib_count; i++)
-		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
-			ETH_GSTRING_LEN);
-}
-
-static void
-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
-			uint64_t *data)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	const struct qca8k_match_data *match_data;
-	const struct qca8k_mib_desc *mib;
-	u32 reg, i, val;
-	u32 hi = 0;
-	int ret;
-
-	match_data = device_get_match_data(priv->dev);
-
-	for (i = 0; i < match_data->mib_count; i++) {
-		mib = &ar8327_mib[i];
-		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
-
-		ret = regmap_read(priv->regmap, reg, &val);
-		if (ret < 0)
-			continue;
-
-		if (mib->size == 2) {
-			ret = regmap_read(priv->regmap, reg + 4, &hi);
-			if (ret < 0)
-				continue;
-		}
-
-		data[i] = val;
-		if (mib->size == 2)
-			data[i] |= (u64)hi << 32;
-	}
-}
-
-static int
-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
 {
-	const struct qca8k_match_data *match_data;
 	struct qca8k_priv *priv = ds->priv;
 
-	if (sset != ETH_SS_STATS)
-		return 0;
-
-	match_data = device_get_match_data(priv->dev);
+	/* Communicate to the phy internal driver the switch revision.
+	 * Based on the switch revision different values needs to be
+	 * set to the dbg and mmd reg on the phy.
+	 * The first 2 bit are used to communicate the switch revision
+	 * to the phy driver.
+	 */
+	if (port > 0 && port < 6)
+		return priv->switch_revision;
 
-	return match_data->mib_count;
+	return 0;
 }
 
-static int
-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
-	u32 reg;
-	int ret;
-
-	mutex_lock(&priv->reg_mutex);
-	ret = regmap_read(priv->regmap, QCA8K_REG_EEE_CTRL, &reg);
-	if (ret < 0)
-		goto exit;
-
-	if (eee->eee_enabled)
-		reg |= lpi_en;
-	else
-		reg &= ~lpi_en;
-	ret = regmap_write(priv->regmap, QCA8K_REG_EEE_CTRL, reg);
-
-exit:
-	mutex_unlock(&priv->reg_mutex);
-	return ret;
-}
-
-static int
-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
-{
-	/* Nothing to do on the port's MAC */
-	return 0;
-}
-
-static void
-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	u32 stp_state;
-
-	switch (state) {
-	case BR_STATE_DISABLED:
-		stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
-		break;
-	case BR_STATE_BLOCKING:
-		stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
-		break;
-	case BR_STATE_LISTENING:
-		stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
-		break;
-	case BR_STATE_LEARNING:
-		stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
-		break;
-	case BR_STATE_FORWARDING:
-	default:
-		stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
-		break;
-	}
-
-	regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
-			   QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
-}
-
-static int
-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	int port_mask, cpu_port;
-	int i, ret;
-
-	cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
-	port_mask = BIT(cpu_port);
-
-	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-		if (dsa_is_cpu_port(ds, i))
-			continue;
-		if (dsa_to_port(ds, i)->bridge_dev != br)
-			continue;
-		/* Add this port to the portvlan mask of the other ports
-		 * in the bridge
-		 */
-		ret = regmap_set_bits(priv->regmap,
-				      QCA8K_PORT_LOOKUP_CTRL(i),
-				      BIT(port));
-		if (ret)
-			return ret;
-		if (i != port)
-			port_mask |= BIT(i);
-	}
-
-	/* Add all other ports to this ports portvlan mask */
-	ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
-				 QCA8K_PORT_LOOKUP_MEMBER, port_mask);
-
-	return ret;
-}
-
-static void
-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	int cpu_port, i;
-
-	cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
-
-	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-		if (dsa_is_cpu_port(ds, i))
-			continue;
-		if (dsa_to_port(ds, i)->bridge_dev != br)
-			continue;
-		/* Remove this port to the portvlan mask of the other ports
-		 * in the bridge
-		 */
-		regmap_clear_bits(priv->regmap,
-				  QCA8K_PORT_LOOKUP_CTRL(i),
-				  BIT(port));
-	}
-
-	/* Set the cpu port to be the only one in the portvlan mask of
-	 * this port
-	 */
-	regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
-			   QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
-}
-
-static void
-qca8k_port_fast_age(struct dsa_switch *ds, int port)
-{
-	struct qca8k_priv *priv = ds->priv;
-
-	mutex_lock(&priv->reg_mutex);
-	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
-	mutex_unlock(&priv->reg_mutex);
-}
-
-static int
-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
-{
-	struct qca8k_priv *priv = ds->priv;
-	unsigned int secs = msecs / 1000;
-	u32 val;
-
-	/* AGE_TIME reg is set in 7s step */
-	val = secs / 7;
-
-	if (val > FIELD_MAX(QCA8K_ATU_AGE_TIME_MASK))
-		return -ERANGE;
-
-	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
-				  QCA8K_ATU_AGE_TIME(val));
-}
-
-static int
-qca8k_port_enable(struct dsa_switch *ds, int port,
-		  struct phy_device *phy)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-
-	qca8k_port_set_status(priv, port, 1);
-	priv->port_sts[port].enabled = 1;
-
-	if (dsa_is_user_port(ds, port))
-		phy_support_asym_pause(phy);
-
-	return 0;
-}
-
-static void
-qca8k_port_disable(struct dsa_switch *ds, int port)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-
-	qca8k_port_set_status(priv, port, 0);
-	priv->port_sts[port].enabled = 0;
-}
-
-static int
-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
-{
-	struct qca8k_priv *priv = ds->priv;
-	int i, mtu = 0;
-
-	priv->port_mtu[port] = new_mtu;
-
-	for (i = 0; i < QCA8K_NUM_PORTS; i++)
-		if (priv->port_mtu[i] > mtu)
-			mtu = priv->port_mtu[i];
-
-	/* Include L2 header / FCS length */
-	return regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
-}
-
-static int
-qca8k_port_max_mtu(struct dsa_switch *ds, int port)
-{
-	return QCA8K_MAX_MTU;
-}
-
-static int
-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
-		      u16 port_mask, u16 vid)
-{
-	/* Set the vid to the port vlan id if no vid is set */
-	if (!vid)
-		vid = QCA8K_PORT_VID_DEF;
-
-	return qca8k_fdb_add(priv, addr, port_mask, vid,
-			     QCA8K_ATU_STATUS_STATIC);
-}
-
-static int
-qca8k_port_fdb_add(struct dsa_switch *ds, int port,
-		   const unsigned char *addr, u16 vid)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	u16 port_mask = BIT(port);
-
-	return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
-}
-
-static int
-qca8k_port_fdb_del(struct dsa_switch *ds, int port,
-		   const unsigned char *addr, u16 vid)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	u16 port_mask = BIT(port);
-
-	if (!vid)
-		vid = QCA8K_PORT_VID_DEF;
-
-	return qca8k_fdb_del(priv, addr, port_mask, vid);
-}
-
-static int
-qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
-		    dsa_fdb_dump_cb_t *cb, void *data)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-	struct qca8k_fdb _fdb = { 0 };
-	int cnt = QCA8K_NUM_FDB_RECORDS;
-	bool is_static;
-	int ret = 0;
-
-	mutex_lock(&priv->reg_mutex);
-	while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
-		if (!_fdb.aging)
-			break;
-		is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
-		ret = cb(_fdb.mac, _fdb.vid, is_static, data);
-		if (ret)
-			break;
-	}
-	mutex_unlock(&priv->reg_mutex);
-
-	return 0;
-}
-
-static int
-qca8k_port_mdb_add(struct dsa_switch *ds, int port,
-		   const struct switchdev_obj_port_mdb *mdb)
-{
-	struct qca8k_priv *priv = ds->priv;
-	struct qca8k_fdb fdb = { 0 };
-	const u8 *addr = mdb->addr;
-	u8 port_mask = BIT(port);
-	u16 vid = mdb->vid;
-	int ret;
-
-	/* Check if entry already exist */
-	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
-	if (ret < 0)
-		return ret;
-
-	/* Rule exist. Delete first */
-	if (!fdb.aging) {
-		ret = qca8k_fdb_del(priv, addr, fdb.port_mask, vid);
-		if (ret)
-			return ret;
-	}
-
-	/* Add port to fdb portmask */
-	fdb.port_mask |= port_mask;
-
-	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
-}
-
-static int
-qca8k_port_mdb_del(struct dsa_switch *ds, int port,
-		   const struct switchdev_obj_port_mdb *mdb)
-{
-	struct qca8k_priv *priv = ds->priv;
-	struct qca8k_fdb fdb = { 0 };
-	const u8 *addr = mdb->addr;
-	u8 port_mask = BIT(port);
-	u16 vid = mdb->vid;
-	int ret;
-
-	/* Check if entry already exist */
-	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
-	if (ret < 0)
-		return ret;
-
-	/* Rule doesn't exist. Why delete? */
-	if (!fdb.aging)
-		return -EINVAL;
-
-	ret = qca8k_fdb_del(priv, addr, port_mask, vid);
-	if (ret)
-		return ret;
-
-	/* Only port in the rule is this port. Don't re insert */
-	if (fdb.port_mask == port_mask)
-		return 0;
-
-	/* Remove port from port mask */
-	fdb.port_mask &= ~port_mask;
-
-	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
-}
-
-static int
-qca8k_port_mirror_add(struct dsa_switch *ds, int port,
-		      struct dsa_mall_mirror_tc_entry *mirror,
-		      bool ingress)
-{
-	struct qca8k_priv *priv = ds->priv;
-	int monitor_port, ret;
-	u32 reg, val;
-
-	/* Check for existent entry */
-	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
-		return -EEXIST;
-
-	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
-	if (ret)
-		return ret;
-
-	/* QCA83xx can have only one port set to mirror mode.
-	 * Check that the correct port is requested and return error otherwise.
-	 * When no mirror port is set, the values is set to 0xF
-	 */
-	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
-	if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
-		return -EEXIST;
-
-	/* Set the monitor port */
-	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
-			 mirror->to_local_port);
-	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
-				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
-	if (ret)
-		return ret;
-
-	if (ingress) {
-		reg = QCA8K_PORT_LOOKUP_CTRL(port);
-		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
-	} else {
-		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
-		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
-	}
-
-	ret = regmap_update_bits(priv->regmap, reg, val, val);
-	if (ret)
-		return ret;
-
-	/* Track mirror port for tx and rx to decide when the
-	 * mirror port has to be disabled.
-	 */
-	if (ingress)
-		priv->mirror_rx |= BIT(port);
-	else
-		priv->mirror_tx |= BIT(port);
-
-	return 0;
-}
-
-static void
-qca8k_port_mirror_del(struct dsa_switch *ds, int port,
-		      struct dsa_mall_mirror_tc_entry *mirror)
-{
-	struct qca8k_priv *priv = ds->priv;
-	u32 reg, val;
-	int ret;
-
-	if (mirror->ingress) {
-		reg = QCA8K_PORT_LOOKUP_CTRL(port);
-		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
-	} else {
-		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
-		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
-	}
-
-	ret = regmap_clear_bits(priv->regmap, reg, val);
-	if (ret)
-		goto err;
-
-	if (mirror->ingress)
-		priv->mirror_rx &= ~BIT(port);
-	else
-		priv->mirror_tx &= ~BIT(port);
-
-	/* No port set to send packet to mirror port. Disable mirror port */
-	if (!priv->mirror_rx && !priv->mirror_tx) {
-		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
-		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
-					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
-		if (ret)
-			goto err;
-	}
-err:
-	dev_err(priv->dev, "Failed to del mirror port from %d", port);
-}
-
-static int
-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
-			  struct netlink_ext_ack *extack)
-{
-	struct qca8k_priv *priv = ds->priv;
-	int ret;
-
-	if (vlan_filtering) {
-		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
-					 QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
-					 QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
-	} else {
-		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
-					 QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
-					 QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
-	}
-
-	return ret;
-}
-
-static int
-qca8k_port_vlan_add(struct dsa_switch *ds, int port,
-		    const struct switchdev_obj_port_vlan *vlan,
-		    struct netlink_ext_ack *extack)
-{
-	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-	struct qca8k_priv *priv = ds->priv;
-	int ret;
-
-	ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
-	if (ret) {
-		dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
-		return ret;
-	}
-
-	if (pvid) {
-		ret = regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(port),
-					 QCA8K_EGREES_VLAN_PORT_MASK(port),
-					 QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
-		if (ret)
-			return ret;
-
-		ret = regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(port),
-				   QCA8K_PORT_VLAN_CVID(vlan->vid) |
-				   QCA8K_PORT_VLAN_SVID(vlan->vid));
-	}
-
-	return ret;
-}
-
-static int
-qca8k_port_vlan_del(struct dsa_switch *ds, int port,
-		    const struct switchdev_obj_port_vlan *vlan)
-{
-	struct qca8k_priv *priv = ds->priv;
-	int ret;
-
-	ret = qca8k_vlan_del(priv, port, vlan->vid);
-	if (ret)
-		dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
-
-	return ret;
-}
-
-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
-{
-	struct qca8k_priv *priv = ds->priv;
-
-	/* Communicate to the phy internal driver the switch revision.
-	 * Based on the switch revision different values needs to be
-	 * set to the dbg and mmd reg on the phy.
-	 * The first 2 bit are used to communicate the switch revision
-	 * to the phy driver.
-	 */
-	if (port > 0 && port < 6)
-		return priv->switch_revision;
-
-	return 0;
-}
-
-static enum dsa_tag_protocol
-qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
-		       enum dsa_tag_protocol mp)
+static enum dsa_tag_protocol
+qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
+		       enum dsa_tag_protocol mp)
 {
 	return DSA_TAG_PROTO_QCA;
 }
 
-static bool
-qca8k_lag_can_offload(struct dsa_switch *ds,
-		      struct net_device *lag,
-		      struct netdev_lag_upper_info *info)
-{
-	struct dsa_port *dp;
-	int id, members = 0;
-
-	id = dsa_lag_id(ds->dst, lag);
-	if (id < 0 || id >= ds->num_lag_ids)
-		return false;
-
-	dsa_lag_foreach_port(dp, ds->dst, lag)
-		/* Includes the port joining the LAG */
-		members++;
-
-	if (members > QCA8K_NUM_PORTS_FOR_LAG)
-		return false;
-
-	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
-		return false;
-
-	return true;
-}
-
-static int
-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
-			  struct net_device *lag, bool delete)
-{
-	struct qca8k_priv *priv = ds->priv;
-	int ret, id, i;
-	u32 val;
-
-	id = dsa_lag_id(ds->dst, lag);
-
-	/* Read current port member */
-	ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
-	if (ret)
-		return ret;
-
-	/* Shift val to the correct trunk */
-	val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
-	val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
-	if (delete)
-		val &= ~BIT(port);
-	else
-		val |= BIT(port);
-
-	/* Update port member. With empty portmap disable trunk */
-	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
-				 QCA8K_REG_GOL_TRUNK_MEMBER(id) |
-				 QCA8K_REG_GOL_TRUNK_EN(id),
-				 !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
-				 val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
-
-	/* Search empty member if adding or port on deleting */
-	for (i = 0; i < 4; i++) {
-		ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
-		if (ret)
-			return ret;
-
-		val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
-		val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
-
-		if (delete) {
-			/* If port flagged to be disabled assume this member is
-			 * empty
-			 */
-			if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
-				continue;
-
-			val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
-			if (val != port)
-				continue;
-		} else {
-			/* If port flagged to be enabled assume this member is
-			 * already set
-			 */
-			if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
-				continue;
-		}
-
-		/* We find the member to remove */
-		break;
-	}
-
-	/* Set port in the correct port mask or disable port if in delate mode */
-	return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
-				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
-				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
-				  !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
-				  port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
-}
-
-static int
-qca8k_port_lag_join(struct dsa_switch *ds, int port,
-		    struct net_device *lag,
-		    struct netdev_lag_upper_info *info)
-{
-	if (!qca8k_lag_can_offload(ds, lag, info))
-		return -EOPNOTSUPP;
-
-	return qca8k_lag_refresh_portmap(ds, port, lag, false);
-}
-
-static int
-qca8k_port_lag_leave(struct dsa_switch *ds, int port,
-		     struct net_device *lag)
-{
-	return qca8k_lag_refresh_portmap(ds, port, lag, true);
-}
-
 static const struct dsa_switch_ops qca8k_switch_ops = {
 	.get_tag_protocol	= qca8k_get_tag_protocol,
 	.setup			= qca8k_setup,
diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c
new file mode 100644
index 000000000000..fdd5c92a278f
--- /dev/null
+++ b/drivers/net/dsa/qca/qca8k-common.c
@@ -0,0 +1,1157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/bitfield.h>
+#include <net/dsa.h>
+#include <linux/if_bridge.h>
+
+#include "qca8k.h"
+
+static const struct regmap_range qca8k_readable_ranges[] = {
+	regmap_reg_range(0x0000, 0x00e4), /* Global control */
+	regmap_reg_range(0x0100, 0x0168), /* EEE control */
+	regmap_reg_range(0x0200, 0x0270), /* Parser control */
+	regmap_reg_range(0x0400, 0x0454), /* ACL */
+	regmap_reg_range(0x0600, 0x0718), /* Lookup */
+	regmap_reg_range(0x0800, 0x0b70), /* QM */
+	regmap_reg_range(0x0c00, 0x0c80), /* PKT */
+	regmap_reg_range(0x0e00, 0x0e98), /* L3 */
+	regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
+	regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
+	regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
+	regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
+	regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
+	regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
+	regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
+
+};
+
+const struct regmap_access_table qca8k_readable_table = {
+	.yes_ranges = qca8k_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+};
+
+#define MIB_DESC(_s, _o, _n)	\
+	{			\
+		.size = (_s),	\
+		.offset = (_o),	\
+		.name = (_n),	\
+	}
+
+static const struct qca8k_mib_desc ar8327_mib[] = {
+	MIB_DESC(1, 0x00, "RxBroad"),
+	MIB_DESC(1, 0x04, "RxPause"),
+	MIB_DESC(1, 0x08, "RxMulti"),
+	MIB_DESC(1, 0x0c, "RxFcsErr"),
+	MIB_DESC(1, 0x10, "RxAlignErr"),
+	MIB_DESC(1, 0x14, "RxRunt"),
+	MIB_DESC(1, 0x18, "RxFragment"),
+	MIB_DESC(1, 0x1c, "Rx64Byte"),
+	MIB_DESC(1, 0x20, "Rx128Byte"),
+	MIB_DESC(1, 0x24, "Rx256Byte"),
+	MIB_DESC(1, 0x28, "Rx512Byte"),
+	MIB_DESC(1, 0x2c, "Rx1024Byte"),
+	MIB_DESC(1, 0x30, "Rx1518Byte"),
+	MIB_DESC(1, 0x34, "RxMaxByte"),
+	MIB_DESC(1, 0x38, "RxTooLong"),
+	MIB_DESC(2, 0x3c, "RxGoodByte"),
+	MIB_DESC(2, 0x44, "RxBadByte"),
+	MIB_DESC(1, 0x4c, "RxOverFlow"),
+	MIB_DESC(1, 0x50, "Filtered"),
+	MIB_DESC(1, 0x54, "TxBroad"),
+	MIB_DESC(1, 0x58, "TxPause"),
+	MIB_DESC(1, 0x5c, "TxMulti"),
+	MIB_DESC(1, 0x60, "TxUnderRun"),
+	MIB_DESC(1, 0x64, "Tx64Byte"),
+	MIB_DESC(1, 0x68, "Tx128Byte"),
+	MIB_DESC(1, 0x6c, "Tx256Byte"),
+	MIB_DESC(1, 0x70, "Tx512Byte"),
+	MIB_DESC(1, 0x74, "Tx1024Byte"),
+	MIB_DESC(1, 0x78, "Tx1518Byte"),
+	MIB_DESC(1, 0x7c, "TxMaxByte"),
+	MIB_DESC(1, 0x80, "TxOverSize"),
+	MIB_DESC(2, 0x84, "TxByte"),
+	MIB_DESC(1, 0x8c, "TxCollision"),
+	MIB_DESC(1, 0x90, "TxAbortCol"),
+	MIB_DESC(1, 0x94, "TxMultiCol"),
+	MIB_DESC(1, 0x98, "TxSingleCol"),
+	MIB_DESC(1, 0x9c, "TxExcDefer"),
+	MIB_DESC(1, 0xa0, "TxDefer"),
+	MIB_DESC(1, 0xa4, "TxLateCol"),
+	MIB_DESC(1, 0xa8, "RXUnicast"),
+	MIB_DESC(1, 0xac, "TXUnicast"),
+};
+
+static int
+qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+{
+	u32 val;
+
+	return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
+				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+}
+
+static int
+qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+{
+	u32 reg[4], val;
+	int i, ret;
+
+	/* load the ARL table into an array */
+	for (i = 0; i < 4; i++) {
+		ret = regmap_read(priv->regmap, QCA8K_REG_ATU_DATA0 + (i * 4), &val);
+		if (ret < 0)
+			return ret;
+
+		reg[i] = val;
+	}
+
+	/* vid - 83:72 */
+	fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+	/* aging - 67:64 */
+	fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+	/* portmask - 54:48 */
+	fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+	/* mac - 47:0 */
+	fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
+	fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
+	fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
+	fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
+	fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
+	fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+
+	return 0;
+}
+
+static void
+qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
+		u8 aging)
+{
+	u32 reg[3] = { 0 };
+	int i;
+
+	/* vid - 83:72 */
+	reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+	/* aging - 67:64 */
+	reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+	/* portmask - 54:48 */
+	reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+	/* mac - 47:0 */
+	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
+	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
+	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+
+	/* load the array into the ARL table */
+	for (i = 0; i < 3; i++)
+		regmap_write(priv->regmap, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
+}
+
+static int
+qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
+{
+	u32 reg;
+	int ret;
+
+	/* Set the command and FDB index */
+	reg = QCA8K_ATU_FUNC_BUSY;
+	reg |= cmd;
+	if (port >= 0) {
+		reg |= QCA8K_ATU_FUNC_PORT_EN;
+		reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+	}
+
+	/* Write the function register triggering the table access */
+	ret = regmap_write(priv->regmap, QCA8K_REG_ATU_FUNC, reg);
+	if (ret)
+		return ret;
+
+	/* wait for completion */
+	ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
+	if (ret)
+		return ret;
+
+	/* Check for table full violation when adding an entry */
+	if (cmd == QCA8K_FDB_LOAD) {
+		ret = regmap_read(priv->regmap, QCA8K_REG_ATU_FUNC, &reg);
+		if (ret < 0)
+			return ret;
+		if (reg & QCA8K_ATU_FUNC_FULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int
+qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
+{
+	int ret;
+
+	qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
+	ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
+	if (ret < 0)
+		return ret;
+
+	return qca8k_fdb_read(priv, fdb);
+}
+
+static int
+qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
+	      u16 vid, u8 aging)
+{
+	int ret;
+
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_write(priv, vid, port_mask, mac, aging);
+	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+	mutex_unlock(&priv->reg_mutex);
+
+	return ret;
+}
+
+static int
+qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
+{
+	int ret;
+
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_write(priv, vid, port_mask, mac, 0);
+	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+	mutex_unlock(&priv->reg_mutex);
+
+	return ret;
+}
+
+void
+qca8k_fdb_flush(struct qca8k_priv *priv)
+{
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
+	mutex_unlock(&priv->reg_mutex);
+}
+
+static int
+qca8k_fdb_search(struct qca8k_priv *priv, struct qca8k_fdb *fdb, const u8 *mac, u16 vid)
+{
+	int ret;
+
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_write(priv, vid, 0, mac, 0);
+	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+	if (ret < 0)
+		goto exit;
+
+	ret = qca8k_fdb_read(priv, fdb);
+exit:
+	mutex_unlock(&priv->reg_mutex);
+	return ret;
+}
+
+static int
+qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+{
+	u32 reg;
+	int ret;
+
+	/* Set the command and VLAN index */
+	reg = QCA8K_VTU_FUNC1_BUSY;
+	reg |= cmd;
+	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+
+	/* Write the function register triggering the table access */
+	ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC1, reg);
+	if (ret)
+		return ret;
+
+	/* wait for completion */
+	ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
+	if (ret)
+		return ret;
+
+	/* Check for table full violation when adding an entry */
+	if (cmd == QCA8K_VLAN_LOAD) {
+		ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC1, &reg);
+		if (ret < 0)
+			return ret;
+		if (reg & QCA8K_VTU_FUNC1_FULL)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int
+qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
+{
+	u32 reg;
+	int ret;
+
+	/* We do the right thing with VLAN 0 and treat it as untagged while
+	 * preserving the tag on egress.
+	 */
+	if (vid == 0)
+		return 0;
+
+	mutex_lock(&priv->reg_mutex);
+	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, &reg);
+	if (ret < 0)
+		goto out;
+	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+	if (untagged)
+		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+	else
+		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+
+	ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg);
+	if (ret)
+		goto out;
+	ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+
+out:
+	mutex_unlock(&priv->reg_mutex);
+
+	return ret;
+}
+
+static int
+qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
+{
+	u32 reg, mask;
+	int ret, i;
+	bool del;
+
+	mutex_lock(&priv->reg_mutex);
+	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_read(priv->regmap, QCA8K_REG_VTU_FUNC0, &reg);
+	if (ret < 0)
+		goto out;
+	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+	reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+
+	/* Check if we're the last member to be removed */
+	del = true;
+	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+		mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+
+		if ((reg & mask) != mask) {
+			del = false;
+			break;
+		}
+	}
+
+	if (del) {
+		ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+	} else {
+		ret = regmap_write(priv->regmap, QCA8K_REG_VTU_FUNC0, reg);
+		if (ret)
+			goto out;
+		ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+	}
+
+out:
+	mutex_unlock(&priv->reg_mutex);
+
+	return ret;
+}
+
+int
+qca8k_mib_init(struct qca8k_priv *priv)
+{
+	int ret;
+
+	mutex_lock(&priv->reg_mutex);
+	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
+	if (ret)
+		goto exit;
+
+	ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+	if (ret)
+		goto exit;
+
+	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+	if (ret)
+		goto exit;
+
+	ret = regmap_write(priv->regmap, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+
+exit:
+	mutex_unlock(&priv->reg_mutex);
+	return ret;
+}
+
+void
+qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
+{
+	u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+
+	/* Port 0 and 6 have no internal PHY */
+	if (port > 0 && port < 6)
+		mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+
+	if (enable)
+		regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+	else
+		regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+}
+
+int
+qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
+			     struct phylink_link_state *state)
+{
+	struct qca8k_priv *priv = ds->priv;
+	u32 reg;
+	int ret;
+
+	ret = regmap_read(priv->regmap, QCA8K_REG_PORT_STATUS(port), &reg);
+	if (ret < 0)
+		return ret;
+
+	state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
+	state->an_complete = state->link;
+	state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);
+	state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
+							   DUPLEX_HALF;
+
+	switch (reg & QCA8K_PORT_STATUS_SPEED) {
+	case QCA8K_PORT_STATUS_SPEED_10:
+		state->speed = SPEED_10;
+		break;
+	case QCA8K_PORT_STATUS_SPEED_100:
+		state->speed = SPEED_100;
+		break;
+	case QCA8K_PORT_STATUS_SPEED_1000:
+		state->speed = SPEED_1000;
+		break;
+	default:
+		state->speed = SPEED_UNKNOWN;
+		break;
+	}
+
+	state->pause = MLO_PAUSE_NONE;
+	if (reg & QCA8K_PORT_STATUS_RXFLOW)
+		state->pause |= MLO_PAUSE_RX;
+	if (reg & QCA8K_PORT_STATUS_TXFLOW)
+		state->pause |= MLO_PAUSE_TX;
+
+	return 1;
+}
+
+void
+qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+			    phy_interface_t interface)
+{
+	struct qca8k_priv *priv = ds->priv;
+
+	qca8k_port_set_status(priv, port, 0);
+}
+
+void
+qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
+			  phy_interface_t interface, struct phy_device *phydev,
+			  int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+	struct qca8k_priv *priv = ds->priv;
+	u32 reg;
+
+	if (phylink_autoneg_inband(mode)) {
+		reg = QCA8K_PORT_STATUS_LINK_AUTO;
+	} else {
+		switch (speed) {
+		case SPEED_10:
+			reg = QCA8K_PORT_STATUS_SPEED_10;
+			break;
+		case SPEED_100:
+			reg = QCA8K_PORT_STATUS_SPEED_100;
+			break;
+		case SPEED_1000:
+			reg = QCA8K_PORT_STATUS_SPEED_1000;
+			break;
+		default:
+			reg = QCA8K_PORT_STATUS_LINK_AUTO;
+			break;
+		}
+
+		if (duplex == DUPLEX_FULL)
+			reg |= QCA8K_PORT_STATUS_DUPLEX;
+
+		if (rx_pause || dsa_is_cpu_port(ds, port))
+			reg |= QCA8K_PORT_STATUS_RXFLOW;
+
+		if (tx_pause || dsa_is_cpu_port(ds, port))
+			reg |= QCA8K_PORT_STATUS_TXFLOW;
+	}
+
+	reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+
+	regmap_write(priv->regmap, QCA8K_REG_PORT_STATUS(port), reg);
+}
+
+void
+qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+{
+	const struct qca8k_match_data *match_data;
+	struct qca8k_priv *priv = ds->priv;
+	int i;
+
+	if (stringset != ETH_SS_STATS)
+		return;
+
+	match_data = device_get_match_data(priv->dev);
+
+	for (i = 0; i < match_data->mib_count; i++)
+		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+			ETH_GSTRING_LEN);
+}
+
+void
+qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
+			uint64_t *data)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	const struct qca8k_match_data *match_data;
+	const struct qca8k_mib_desc *mib;
+	u32 reg, i, val;
+	u32 hi = 0;
+	int ret;
+
+	match_data = device_get_match_data(priv->dev);
+
+	for (i = 0; i < match_data->mib_count; i++) {
+		mib = &ar8327_mib[i];
+		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+		ret = regmap_read(priv->regmap, reg, &val);
+		if (ret < 0)
+			continue;
+
+		if (mib->size == 2) {
+			ret = regmap_read(priv->regmap, reg + 4, &hi);
+			if (ret < 0)
+				continue;
+		}
+
+		data[i] = val;
+		if (mib->size == 2)
+			data[i] |= (u64)hi << 32;
+	}
+}
+
+int
+qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+	const struct qca8k_match_data *match_data;
+	struct qca8k_priv *priv = ds->priv;
+
+	if (sset != ETH_SS_STATS)
+		return 0;
+
+	match_data = device_get_match_data(priv->dev);
+
+	return match_data->mib_count;
+}
+
+int
+qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
+	u32 reg;
+	int ret;
+
+	mutex_lock(&priv->reg_mutex);
+	ret = regmap_read(priv->regmap, QCA8K_REG_EEE_CTRL, &reg);
+	if (ret < 0)
+		goto exit;
+
+	if (eee->eee_enabled)
+		reg |= lpi_en;
+	else
+		reg &= ~lpi_en;
+	ret = regmap_write(priv->regmap, QCA8K_REG_EEE_CTRL, reg);
+
+exit:
+	mutex_unlock(&priv->reg_mutex);
+	return ret;
+}
+
+int
+qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
+{
+	/* Nothing to do on the port's MAC */
+	return 0;
+}
+
+void
+qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	u32 stp_state;
+
+	switch (state) {
+	case BR_STATE_DISABLED:
+		stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
+		break;
+	case BR_STATE_BLOCKING:
+		stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
+		break;
+	case BR_STATE_LISTENING:
+		stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
+		break;
+	case BR_STATE_LEARNING:
+		stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
+		break;
+	case BR_STATE_FORWARDING:
+	default:
+		stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
+		break;
+	}
+
+	regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+			   QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+}
+
+int
+qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	int port_mask, cpu_port;
+	int i, ret;
+
+	cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+	port_mask = BIT(cpu_port);
+
+	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+		if (dsa_is_cpu_port(ds, i))
+			continue;
+		if (dsa_to_port(ds, i)->bridge_dev != br)
+			continue;
+		/* Add this port to the portvlan mask of the other ports
+		 * in the bridge
+		 */
+		ret = regmap_set_bits(priv->regmap,
+				      QCA8K_PORT_LOOKUP_CTRL(i),
+				      BIT(port));
+		if (ret)
+			return ret;
+		if (i != port)
+			port_mask |= BIT(i);
+	}
+
+	/* Add all other ports to this ports portvlan mask */
+	ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+				 QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+
+	return ret;
+}
+
+void
+qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	int cpu_port, i;
+
+	cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+
+	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+		if (dsa_is_cpu_port(ds, i))
+			continue;
+		if (dsa_to_port(ds, i)->bridge_dev != br)
+			continue;
+		/* Remove this port to the portvlan mask of the other ports
+		 * in the bridge
+		 */
+		regmap_clear_bits(priv->regmap,
+				  QCA8K_PORT_LOOKUP_CTRL(i),
+				  BIT(port));
+	}
+
+	/* Set the cpu port to be the only one in the portvlan mask of
+	 * this port
+	 */
+	regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+			   QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+}
+
+void
+qca8k_port_fast_age(struct dsa_switch *ds, int port)
+{
+	struct qca8k_priv *priv = ds->priv;
+
+	mutex_lock(&priv->reg_mutex);
+	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
+	mutex_unlock(&priv->reg_mutex);
+}
+
+int
+qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+	struct qca8k_priv *priv = ds->priv;
+	unsigned int secs = msecs / 1000;
+	u32 val;
+
+	/* AGE_TIME reg is set in 7s step */
+	val = secs / 7;
+
+	if (val > FIELD_MAX(QCA8K_ATU_AGE_TIME_MASK))
+		return -ERANGE;
+
+	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL,
+				  QCA8K_ATU_AGE_TIME_MASK, QCA8K_ATU_AGE_TIME(val));
+}
+
+int
+qca8k_port_enable(struct dsa_switch *ds, int port,
+		  struct phy_device *phy)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+
+	qca8k_port_set_status(priv, port, 1);
+	priv->port_sts[port].enabled = 1;
+
+	if (dsa_is_user_port(ds, port))
+		phy_support_asym_pause(phy);
+
+	return 0;
+}
+
+void
+qca8k_port_disable(struct dsa_switch *ds, int port)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+
+	qca8k_port_set_status(priv, port, 0);
+	priv->port_sts[port].enabled = 0;
+}
+
+int
+qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int i, mtu = 0;
+
+	priv->port_mtu[port] = new_mtu;
+
+	for (i = 0; i < QCA8K_NUM_PORTS; i++)
+		if (priv->port_mtu[i] > mtu)
+			mtu = priv->port_mtu[i];
+
+	/* Include L2 header / FCS length */
+	return regmap_write(priv->regmap, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
+}
+
+int
+qca8k_port_max_mtu(struct dsa_switch *ds, int port)
+{
+	return QCA8K_MAX_MTU;
+}
+
+int
+qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
+		      u16 port_mask, u16 vid)
+{
+	/* Set the vid to the port vlan id if no vid is set */
+	if (!vid)
+		vid = QCA8K_PORT_VID_DEF;
+
+	return qca8k_fdb_add(priv, addr, port_mask, vid,
+			     QCA8K_ATU_STATUS_STATIC);
+}
+
+int
+qca8k_port_fdb_add(struct dsa_switch *ds, int port,
+		   const unsigned char *addr, u16 vid)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	u16 port_mask = BIT(port);
+
+	return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+}
+
+int
+qca8k_port_fdb_del(struct dsa_switch *ds, int port,
+		   const unsigned char *addr, u16 vid)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	u16 port_mask = BIT(port);
+
+	if (!vid)
+		vid = QCA8K_PORT_VID_DEF;
+
+	return qca8k_fdb_del(priv, addr, port_mask, vid);
+}
+
+int
+qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
+		    dsa_fdb_dump_cb_t *cb, void *data)
+{
+	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+	struct qca8k_fdb _fdb = { 0 };
+	int cnt = QCA8K_NUM_FDB_RECORDS;
+	bool is_static;
+	int ret = 0;
+
+	mutex_lock(&priv->reg_mutex);
+	while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
+		if (!_fdb.aging)
+			break;
+		is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
+		ret = cb(_fdb.mac, _fdb.vid, is_static, data);
+		if (ret)
+			break;
+	}
+	mutex_unlock(&priv->reg_mutex);
+
+	return 0;
+}
+
+int
+qca8k_port_mdb_add(struct dsa_switch *ds, int port,
+		   const struct switchdev_obj_port_mdb *mdb)
+{
+	struct qca8k_priv *priv = ds->priv;
+	struct qca8k_fdb fdb = { 0 };
+	const u8 *addr = mdb->addr;
+	u8 port_mask = BIT(port);
+	u16 vid = mdb->vid;
+	int ret;
+
+	/* Check if entry already exist */
+	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
+	if (ret < 0)
+		return ret;
+
+	/* Rule exist. Delete first */
+	if (!fdb.aging) {
+		ret = qca8k_fdb_del(priv, addr, fdb.port_mask, vid);
+		if (ret)
+			return ret;
+	}
+
+	/* Add port to fdb portmask */
+	fdb.port_mask |= port_mask;
+
+	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
+}
+
+int
+qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+		   const struct switchdev_obj_port_mdb *mdb)
+{
+	struct qca8k_priv *priv = ds->priv;
+	struct qca8k_fdb fdb = { 0 };
+	const u8 *addr = mdb->addr;
+	u8 port_mask = BIT(port);
+	u16 vid = mdb->vid;
+	int ret;
+
+	/* Check if entry already exist */
+	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
+	if (ret < 0)
+		return ret;
+
+	/* Rule doesn't exist. Why delete? */
+	if (!fdb.aging)
+		return -EINVAL;
+
+	ret = qca8k_fdb_del(priv, addr, port_mask, vid);
+	if (ret)
+		return ret;
+
+	/* Only port in the rule is this port. Don't re insert */
+	if (fdb.port_mask == port_mask)
+		return 0;
+
+	/* Remove port from port mask */
+	fdb.port_mask &= ~port_mask;
+
+	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
+}
+
+int
+qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+		      struct dsa_mall_mirror_tc_entry *mirror,
+		      bool ingress)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int monitor_port, ret;
+	u32 reg, val;
+
+	/* Check for existent entry */
+	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
+		return -EEXIST;
+
+	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
+	if (ret)
+		return ret;
+
+	/* QCA83xx can have only one port set to mirror mode.
+	 * Check that the correct port is requested and return error otherwise.
+	 * When no mirror port is set, the values is set to 0xF
+	 */
+	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+	if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
+		return -EEXIST;
+
+	/* Set the monitor port */
+	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
+			 mirror->to_local_port);
+	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+	if (ret)
+		return ret;
+
+	if (ingress) {
+		reg = QCA8K_PORT_LOOKUP_CTRL(port);
+		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+	} else {
+		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+	}
+
+	ret = regmap_update_bits(priv->regmap, reg, val, val);
+	if (ret)
+		return ret;
+
+	/* Track mirror port for tx and rx to decide when the
+	 * mirror port has to be disabled.
+	 */
+	if (ingress)
+		priv->mirror_rx |= BIT(port);
+	else
+		priv->mirror_tx |= BIT(port);
+
+	return 0;
+}
+
+void
+qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+		      struct dsa_mall_mirror_tc_entry *mirror)
+{
+	struct qca8k_priv *priv = ds->priv;
+	u32 reg, val;
+	int ret;
+
+	if (mirror->ingress) {
+		reg = QCA8K_PORT_LOOKUP_CTRL(port);
+		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+	} else {
+		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+	}
+
+	ret = regmap_clear_bits(priv->regmap, reg, val);
+	if (ret)
+		goto err;
+
+	if (mirror->ingress)
+		priv->mirror_rx &= ~BIT(port);
+	else
+		priv->mirror_tx &= ~BIT(port);
+
+	/* No port set to send packet to mirror port. Disable mirror port */
+	if (!priv->mirror_rx && !priv->mirror_tx) {
+		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
+		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+		if (ret)
+			goto err;
+	}
+err:
+	dev_err(priv->dev, "Failed to del mirror port from %d", port);
+}
+
+int
+qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+			  struct netlink_ext_ack *extack)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int ret;
+
+	if (vlan_filtering) {
+		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+	} else {
+		ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+					 QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+	}
+
+	return ret;
+}
+
+int
+qca8k_port_vlan_add(struct dsa_switch *ds, int port,
+		    const struct switchdev_obj_port_vlan *vlan,
+		    struct netlink_ext_ack *extack)
+{
+	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+	struct qca8k_priv *priv = ds->priv;
+	int ret;
+
+	ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+	if (ret) {
+		dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+		return ret;
+	}
+
+	if (pvid) {
+		ret = regmap_update_bits(priv->regmap, QCA8K_EGRESS_VLAN(port),
+					 QCA8K_EGREES_VLAN_PORT_MASK(port),
+					 QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+		if (ret)
+			return ret;
+
+		ret = regmap_write(priv->regmap, QCA8K_REG_PORT_VLAN_CTRL0(port),
+				   QCA8K_PORT_VLAN_CVID(vlan->vid) |
+				   QCA8K_PORT_VLAN_SVID(vlan->vid));
+	}
+
+	return ret;
+}
+
+int
+qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+		    const struct switchdev_obj_port_vlan *vlan)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int ret;
+
+	ret = qca8k_vlan_del(priv, port, vlan->vid);
+	if (ret)
+		dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
+
+	return ret;
+}
+
+bool
+qca8k_lag_can_offload(struct dsa_switch *ds,
+		      struct net_device *lag,
+		      struct netdev_lag_upper_info *info)
+{
+	struct dsa_port *dp;
+	int id, members = 0;
+
+	id = dsa_lag_id(ds->dst, lag);
+	if (id < 0 || id >= ds->num_lag_ids)
+		return false;
+
+	dsa_lag_foreach_port(dp, ds->dst, lag)
+		/* Includes the port joining the LAG */
+		members++;
+
+	if (members > QCA8K_NUM_PORTS_FOR_LAG)
+		return false;
+
+	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+		return false;
+
+	return true;
+}
+
+int
+qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
+			  struct net_device *lag, bool delete)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int ret, id, i;
+	u32 val;
+
+	id = dsa_lag_id(ds->dst, lag);
+
+	/* Read current port member */
+	ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
+	if (ret)
+		return ret;
+
+	/* Shift val to the correct trunk */
+	val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
+	val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
+	if (delete)
+		val &= ~BIT(port);
+	else
+		val |= BIT(port);
+
+	/* Update port member. With empty portmap disable trunk */
+	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
+				 QCA8K_REG_GOL_TRUNK_MEMBER(id) |
+				 QCA8K_REG_GOL_TRUNK_EN(id),
+				 !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
+				 val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
+
+	/* Search empty member if adding or port on deleting */
+	for (i = 0; i < 4; i++) {
+		ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
+		if (ret)
+			return ret;
+
+		val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
+		val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
+
+		if (delete) {
+			/* If port flagged to be disabled assume this member is
+			 * empty
+			 */
+			if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+				continue;
+
+			val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
+			if (val != port)
+				continue;
+		} else {
+			/* If port flagged to be enabled assume this member is
+			 * already set
+			 */
+			if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+				continue;
+		}
+
+		/* We find the member to remove */
+		break;
+	}
+
+	/* Set port in the correct port mask or disable port if in delate mode */
+	return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
+				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
+				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
+				  !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
+				  port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
+}
+
+int
+qca8k_port_lag_join(struct dsa_switch *ds, int port,
+		    struct net_device *lag,
+		    struct netdev_lag_upper_info *info)
+{
+	if (!qca8k_lag_can_offload(ds, lag, info))
+		return -EOPNOTSUPP;
+
+	return qca8k_lag_refresh_portmap(ds, port, lag, false);
+}
+
+int
+qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+		     struct net_device *lag)
+{
+	return qca8k_lag_refresh_portmap(ds, port, lag, true);
+}
diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h
index 5310022569f3..8e3888bf6823 100644
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -360,4 +360,54 @@ struct qca8k_fdb {
 	u8 mac[6];
 };
 
+/* Common setup function */
+extern const struct regmap_access_table qca8k_readable_table;
+
+void qca8k_fdb_flush(struct qca8k_priv *priv);
+int qca8k_mib_init(struct qca8k_priv *priv);
+void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable);
+void qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
+			       phy_interface_t interface, struct phy_device *phydev, int speed,
+			       int duplex, bool tx_pause, bool rx_pause);
+void qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+				 phy_interface_t interface);
+int qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, struct phylink_link_state *state);
+
+/* Common ops function */
+void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data);
+void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
+int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset);
+int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee);
+int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
+void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
+int qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br);
+void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br);
+void qca8k_port_fast_age(struct dsa_switch *ds, int port);
+int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs);
+int qca8k_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy);
+void qca8k_port_disable(struct dsa_switch *ds, int port);
+int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu);
+int qca8k_port_max_mtu(struct dsa_switch *ds, int port);
+int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, u16 port_mask, u16 vid);
+int qca8k_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid);
+int qca8k_port_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid);
+int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data);
+int qca8k_port_mdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb);
+int qca8k_port_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb);
+int qca8k_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror,
+			  bool ingress);
+void qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+			   struct dsa_mall_mirror_tc_entry *mirror);
+int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+			      struct netlink_ext_ack *extack);
+int qca8k_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan,
+			struct netlink_ext_ack *extack);
+int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+			const struct switchdev_obj_port_vlan *vlan);
+bool qca8k_lag_can_offload(struct dsa_switch *ds, struct net_device *lag,
+			   struct netdev_lag_upper_info *info);
+int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag,
+			struct netdev_lag_upper_info *info);
+int qca8k_port_lag_leave(struct dsa_switch *ds, int port, struct net_device *lag);
+
 #endif /* __QCA8K_H */
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: regmap: allow to define reg_update_bits for no bus configuration
  2021-11-17 21:04 ` regmap: allow to define reg_update_bits for no bus configuration Ansuel Smith
@ 2021-11-17 22:15   ` Mark Brown
  2021-11-17 22:19     ` Ansuel Smith
  2021-11-19  2:00   ` patchwork-bot+netdevbpf
  1 sibling, 1 reply; 50+ messages in thread
From: Mark Brown @ 2021-11-17 22:15 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, linux-kernel,
	netdev

[-- Attachment #1: Type: text/plain, Size: 830 bytes --]

On Wed, Nov 17, 2021 at 10:04:33PM +0100, Ansuel Smith wrote:
> Some device requires a special handling for reg_update_bits and can't use
> the normal regmap read write logic. An example is when locking is
> handled by the device and rmw operations requires to do atomic operations.
> Allow to declare a dedicated function in regmap_config for
> reg_update_bits in no bus configuration.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
>  drivers/base/regmap/regmap.c | 1 +
>  include/linux/regmap.h       | 7 +++++++
>  2 files changed, 8 insertions(+)

I've applied this already?  If it's needed by something in another tree
let me know and I'll make a signed tag for it.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: regmap: allow to define reg_update_bits for no bus configuration
  2021-11-17 22:15   ` Mark Brown
@ 2021-11-17 22:19     ` Ansuel Smith
  2021-11-17 22:33       ` Mark Brown
  0 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-17 22:19 UTC (permalink / raw)
  To: Mark Brown
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, linux-kernel,
	netdev

On Wed, Nov 17, 2021 at 10:15:53PM +0000, Mark Brown wrote:
> On Wed, Nov 17, 2021 at 10:04:33PM +0100, Ansuel Smith wrote:
> > Some device requires a special handling for reg_update_bits and can't use
> > the normal regmap read write logic. An example is when locking is
> > handled by the device and rmw operations requires to do atomic operations.
> > Allow to declare a dedicated function in regmap_config for
> > reg_update_bits in no bus configuration.
> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com
> > Signed-off-by: Mark Brown <broonie@kernel.org>
> > ---
> >  drivers/base/regmap/regmap.c | 1 +
> >  include/linux/regmap.h       | 7 +++++++
> >  2 files changed, 8 insertions(+)
> 
> I've applied this already?  If it's needed by something in another tree
> let me know and I'll make a signed tag for it.

Yes, I posted this in this series as net-next still doesn't have this
commit. Don't really know how to hanle this kind of corner
case. Do you have some hint about that and how to proceed?

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: regmap: allow to define reg_update_bits for no bus configuration
  2021-11-17 22:19     ` Ansuel Smith
@ 2021-11-17 22:33       ` Mark Brown
  2021-11-19  1:54         ` Jakub Kicinski
  0 siblings, 1 reply; 50+ messages in thread
From: Mark Brown @ 2021-11-17 22:33 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Russell King, linux-kernel,
	netdev

[-- Attachment #1: Type: text/plain, Size: 1399 bytes --]

On Wed, Nov 17, 2021 at 11:19:39PM +0100, Ansuel Smith wrote:
> On Wed, Nov 17, 2021 at 10:15:53PM +0000, Mark Brown wrote:

> > I've applied this already?  If it's needed by something in another tree
> > let me know and I'll make a signed tag for it.

> Yes, I posted this in this series as net-next still doesn't have this
> commit. Don't really know how to hanle this kind of corner
> case. Do you have some hint about that and how to proceed?

Ask for a tag like I said in the message you're replying to:

The following changes since commit fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf:

  Linux 5.16-rc1 (2021-11-14 13:56:52 -0800)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git tags/regmap-no-bus-update-bits

for you to fetch changes up to 02d6fdecb9c38de19065f6bed8d5214556fd061d:

  regmap: allow to define reg_update_bits for no bus configuration (2021-11-15 13:27:13 +0000)

----------------------------------------------------------------
regmap: Allow regmap_update_bits() to be offloaded with no bus

Some hardware can do this so let's use that capability.

----------------------------------------------------------------
Ansuel Smith (1):
      regmap: allow to define reg_update_bits for no bus configuration

 drivers/base/regmap/regmap.c | 1 +
 include/linux/regmap.h       | 7 +++++++
 2 files changed, 8 insertions(+)

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 02/19] net: dsa: qca8k: remove redundant check in parse_port_config
  2021-11-17 21:04 ` [net-next PATCH 02/19] net: dsa: qca8k: remove redundant check in parse_port_config Ansuel Smith
@ 2021-11-18 23:59   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-18 23:59 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev,
	kernel test robot, Dan Carpenter

On Wed, Nov 17, 2021 at 10:04:34PM +0100, Ansuel Smith wrote:
> The very next check for port 0 and 6 already make sure we don't go out
                                               ~~~~
                                               makes
> of bounds with the ports_config delay table.
> Remove the redundant check.
> 
> Reported-by: kernel test robot <lkp@intel.com>
> Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---

Reviewed-by: Vladimir Oltean <olteanv@gmail.com>

>  drivers/net/dsa/qca8k.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index a429c9750add..bfffc1fb7016 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -983,7 +983,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
>  	u32 delay;
>  
>  	/* We have 2 CPU port. Check them */
> -	for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
> +	for (port = 0; port < QCA8K_NUM_PORTS; port++) {
>  		/* Skip every other port */
>  		if (port != 0 && port != 6)
>  			continue;
> -- 
> 2.32.0
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 03/19] net: dsa: qca8k: skip sgmii delay on double cpu conf
  2021-11-17 21:04 ` [net-next PATCH 03/19] net: dsa: qca8k: skip sgmii delay on double cpu conf Ansuel Smith
@ 2021-11-19  0:58   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  0:58 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:35PM +0100, Ansuel Smith wrote:
> With a dual cpu configuration rgmii+sgmii, skip configuring sgmii delay
> as it does cause no traffic. Configure only rgmii delay in this
> specific configuration.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---

I feel that you owe a serious explanation about this SGMII delay
business, it's getting stranger and stranger. I chalked it up to the
fact that maybe QCA8334 has a strange SGMII implementation, where the
clock it is source-synchronous, as opposed to the data lanes themselves
being self-clocking. But the fact is, I don't really know, I never was
sure, never got an explanation, and now I am even less sure. And now
that I look in the datasheet for the pinout, I see a regular pair of
pins (SOP, SON) for the TX differential pair, and a regular pair of pins
(SIP, SIN) for the RX differential pair. No pin for any source
synchronous clock. So where is this SGMII_CLK125M clock localized, and
if it's inside the switch, and why do you need to configure its sampling
edge and delay, what is different between one board and another?

The RGMII and the SGMII CPU ports are different physical ports, are they not?
Why would the configuration of one affect the other? Do they share any
physical resource?

>  drivers/net/dsa/qca8k.c | 12 ++++++++++--
>  drivers/net/dsa/qca8k.h |  1 +
>  2 files changed, 11 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index bfffc1fb7016..ace465c878f8 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1041,8 +1041,13 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
>  			if (mode == PHY_INTERFACE_MODE_RGMII ||
>  			    mode == PHY_INTERFACE_MODE_RGMII_ID ||
>  			    mode == PHY_INTERFACE_MODE_RGMII_TXID ||
> -			    mode == PHY_INTERFACE_MODE_RGMII_RXID)
> +			    mode == PHY_INTERFACE_MODE_RGMII_RXID) {
> +				if (priv->ports_config.rgmii_tx_delay[cpu_port_index] ||
> +				    priv->ports_config.rgmii_rx_delay[cpu_port_index])
> +					priv->ports_config.skip_sgmii_delay = true;
> +
>  				break;
> +			}
>  
>  			if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
>  				priv->ports_config.sgmii_tx_clk_falling_edge = true;
> @@ -1457,8 +1462,11 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
>  
>  		/* From original code is reported port instability as SGMII also
>  		 * require delay set. Apply advised values here or take them from DT.
> +		 * In dual CPU configuration, apply only delay to rgmii as applying
> +		 * it also to the SGMII line cause no traffic to the entire switch.
>  		 */
> -		if (state->interface == PHY_INTERFACE_MODE_SGMII)
> +		if (state->interface == PHY_INTERFACE_MODE_SGMII &&
> +		    !priv->ports_config.skip_sgmii_delay)

I thought that the deal was that with the new "tx-internal-delay-ps" and
"rx-internal-delay-ps" properties, you would not enable any delays by
default in their absence. So if system is broken by the fact that delays
are applied on the SGMII port when they shouldn't have, the issue is in
the device tree and the fix is also there. This "skip_sgmii_delay" logic
on the other hand is fixing up the default delays that get applied in
lack of device tree properties.

>  			qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
>  
>  		break;
> diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
> index 128b8cf85e08..57c4c0d93558 100644
> --- a/drivers/net/dsa/qca8k.h
> +++ b/drivers/net/dsa/qca8k.h
> @@ -275,6 +275,7 @@ struct qca8k_ports_config {
>  	bool sgmii_rx_clk_falling_edge;
>  	bool sgmii_tx_clk_falling_edge;
>  	bool sgmii_enable_pll;
> +	bool skip_sgmii_delay;
>  	u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
>  	u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
>  };
> -- 
> 2.32.0
> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup
  2021-11-17 21:04 ` [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup Ansuel Smith
@ 2021-11-19  1:03   ` Vladimir Oltean
  2021-11-19  1:08     ` Ansuel Smith
  0 siblings, 1 reply; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:03 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:37PM +0100, Ansuel Smith wrote:
> Move read_switch_id function in qca8k_setup in preparation for regmap
> conversion. Sw probe should NOT contain function that depends on reading
> from switch regs.

It shouldn't? Why? We have plenty of switch drivers that use regmap in
the probe function.

> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 71 ++++++++++++++++++++---------------------
>  1 file changed, 35 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index 19331edf1fd4..be98d11b17ec 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1073,6 +1073,36 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
>  	return 0;
>  }
>  
> +static int qca8k_read_switch_id(struct qca8k_priv *priv)
> +{
> +	const struct qca8k_match_data *data;
> +	u32 val;
> +	u8 id;
> +	int ret;
> +
> +	/* get the switches ID from the compatible */
> +	data = of_device_get_match_data(priv->dev);
> +	if (!data)
> +		return -ENODEV;
> +
> +	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
> +	if (ret < 0)
> +		return -ENODEV;
> +
> +	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
> +	if (id != data->id) {
> +		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
> +		return -ENODEV;
> +	}
> +
> +	priv->switch_id = id;
> +
> +	/* Save revision to communicate to the internal PHY driver */
> +	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
> +
> +	return 0;
> +}
> +
>  static int
>  qca8k_setup(struct dsa_switch *ds)
>  {
> @@ -1080,6 +1110,11 @@ qca8k_setup(struct dsa_switch *ds)
>  	int cpu_port, ret, i;
>  	u32 mask;
>  
> +	/* Check the detected switch id */
> +	ret = qca8k_read_switch_id(priv);
> +	if (ret)
> +		return ret;
> +
>  	cpu_port = qca8k_find_cpu_port(ds);
>  	if (cpu_port < 0) {
>  		dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
> @@ -2023,41 +2058,10 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.get_phy_flags		= qca8k_get_phy_flags,
>  };
>  
> -static int qca8k_read_switch_id(struct qca8k_priv *priv)
> -{
> -	const struct qca8k_match_data *data;
> -	u32 val;
> -	u8 id;
> -	int ret;
> -
> -	/* get the switches ID from the compatible */
> -	data = of_device_get_match_data(priv->dev);
> -	if (!data)
> -		return -ENODEV;
> -
> -	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
> -	if (ret < 0)
> -		return -ENODEV;
> -
> -	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
> -	if (id != data->id) {
> -		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
> -		return -ENODEV;
> -	}
> -
> -	priv->switch_id = id;
> -
> -	/* Save revision to communicate to the internal PHY driver */
> -	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
> -
> -	return 0;
> -}
> -
>  static int
>  qca8k_sw_probe(struct mdio_device *mdiodev)
>  {
>  	struct qca8k_priv *priv;
> -	int ret;
>  
>  	/* allocate the private data struct so that we can probe the switches
>  	 * ID register
> @@ -2083,11 +2087,6 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
>  		gpiod_set_value_cansleep(priv->reset_gpio, 0);
>  	}
>  
> -	/* Check the detected switch id */
> -	ret = qca8k_read_switch_id(priv);
> -	if (ret)
> -		return ret;
> -
>  	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
>  	if (!priv->ds)
>  		return -ENOMEM;
> -- 
> 2.32.0
> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup
  2021-11-19  1:03   ` Vladimir Oltean
@ 2021-11-19  1:08     ` Ansuel Smith
  2021-11-21 18:34       ` Vladimir Oltean
  0 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-19  1:08 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 03:03:05AM +0200, Vladimir Oltean wrote:
> On Wed, Nov 17, 2021 at 10:04:37PM +0100, Ansuel Smith wrote:
> > Move read_switch_id function in qca8k_setup in preparation for regmap
> > conversion. Sw probe should NOT contain function that depends on reading
> > from switch regs.
> 
> It shouldn't? Why? We have plenty of switch drivers that use regmap in
> the probe function.
>

The initial idea was to make a shared probe function. (when the ipq40xx
code will be proposed)
Currently the regmap is init in the setup function so we can both 
move the switch id to setup or move regmap to probe.
What should be better in your opinion?

> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> >  drivers/net/dsa/qca8k.c | 71 ++++++++++++++++++++---------------------
> >  1 file changed, 35 insertions(+), 36 deletions(-)
> > 
> > diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> > index 19331edf1fd4..be98d11b17ec 100644
> > --- a/drivers/net/dsa/qca8k.c
> > +++ b/drivers/net/dsa/qca8k.c
> > @@ -1073,6 +1073,36 @@ qca8k_parse_port_config(struct qca8k_priv *priv)
> >  	return 0;
> >  }
> >  
> > +static int qca8k_read_switch_id(struct qca8k_priv *priv)
> > +{
> > +	const struct qca8k_match_data *data;
> > +	u32 val;
> > +	u8 id;
> > +	int ret;
> > +
> > +	/* get the switches ID from the compatible */
> > +	data = of_device_get_match_data(priv->dev);
> > +	if (!data)
> > +		return -ENODEV;
> > +
> > +	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
> > +	if (ret < 0)
> > +		return -ENODEV;
> > +
> > +	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
> > +	if (id != data->id) {
> > +		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
> > +		return -ENODEV;
> > +	}
> > +
> > +	priv->switch_id = id;
> > +
> > +	/* Save revision to communicate to the internal PHY driver */
> > +	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
> > +
> > +	return 0;
> > +}
> > +
> >  static int
> >  qca8k_setup(struct dsa_switch *ds)
> >  {
> > @@ -1080,6 +1110,11 @@ qca8k_setup(struct dsa_switch *ds)
> >  	int cpu_port, ret, i;
> >  	u32 mask;
> >  
> > +	/* Check the detected switch id */
> > +	ret = qca8k_read_switch_id(priv);
> > +	if (ret)
> > +		return ret;
> > +
> >  	cpu_port = qca8k_find_cpu_port(ds);
> >  	if (cpu_port < 0) {
> >  		dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
> > @@ -2023,41 +2058,10 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
> >  	.get_phy_flags		= qca8k_get_phy_flags,
> >  };
> >  
> > -static int qca8k_read_switch_id(struct qca8k_priv *priv)
> > -{
> > -	const struct qca8k_match_data *data;
> > -	u32 val;
> > -	u8 id;
> > -	int ret;
> > -
> > -	/* get the switches ID from the compatible */
> > -	data = of_device_get_match_data(priv->dev);
> > -	if (!data)
> > -		return -ENODEV;
> > -
> > -	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
> > -	if (ret < 0)
> > -		return -ENODEV;
> > -
> > -	id = QCA8K_MASK_CTRL_DEVICE_ID(val);
> > -	if (id != data->id) {
> > -		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
> > -		return -ENODEV;
> > -	}
> > -
> > -	priv->switch_id = id;
> > -
> > -	/* Save revision to communicate to the internal PHY driver */
> > -	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
> > -
> > -	return 0;
> > -}
> > -
> >  static int
> >  qca8k_sw_probe(struct mdio_device *mdiodev)
> >  {
> >  	struct qca8k_priv *priv;
> > -	int ret;
> >  
> >  	/* allocate the private data struct so that we can probe the switches
> >  	 * ID register
> > @@ -2083,11 +2087,6 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
> >  		gpiod_set_value_cansleep(priv->reset_gpio, 0);
> >  	}
> >  
> > -	/* Check the detected switch id */
> > -	ret = qca8k_read_switch_id(priv);
> > -	if (ret)
> > -		return ret;
> > -
> >  	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
> >  	if (!priv->ds)
> >  		return -ENOMEM;
> > -- 
> > 2.32.0
> > 
> 

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 07/19] net: dsa: qca8k: set regmap init as mandatory for regmap conversion
  2021-11-17 21:04 ` [net-next PATCH 07/19] net: dsa: qca8k: set regmap init as mandatory for regmap conversion Ansuel Smith
@ 2021-11-19  1:09   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:09 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:39PM +0100, Ansuel Smith wrote:
> In preparation for regmap conversion, make regmap init mandatory and
> fail if any error occurs.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---

Huh. Odd that someone would initialize a regmap in a driver and then
proceed to not use it for anything. Looks like it's been sitting there
since 6b93fb46480a ("net-next: dsa: add new driver for qca8xxx family").

Reviewed-by: Vladimir Oltean <olteanv@gmail.com>

>  drivers/net/dsa/qca8k.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index ee04b48875e7..792b999da37c 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1110,6 +1110,14 @@ qca8k_setup(struct dsa_switch *ds)
>  	int cpu_port, ret, i;
>  	u32 mask;
>  
> +	/* Start by setting up the register mapping */
> +	priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
> +					&qca8k_regmap_config);
> +	if (IS_ERR(priv->regmap)) {
> +		dev_err(priv->dev, "regmap initialization failed");
> +		return PTR_ERR(priv->regmap);
> +	}
> +
>  	/* Check the detected switch id */
>  	ret = qca8k_read_switch_id(priv);
>  	if (ret)
> @@ -1126,12 +1134,6 @@ qca8k_setup(struct dsa_switch *ds)
>  	if (ret)
>  		return ret;
>  
> -	/* Start by setting up the register mapping */
> -	priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
> -					&qca8k_regmap_config);
> -	if (IS_ERR(priv->regmap))
> -		dev_warn(priv->dev, "regmap initialization failed");
> -
>  	ret = qca8k_setup_mdio_bus(priv);
>  	if (ret)
>  		return ret;
> -- 
> 2.32.0
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper
  2021-11-17 21:04 ` [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper Ansuel Smith
@ 2021-11-19  1:14   ` Vladimir Oltean
  2021-11-19  1:28     ` Ansuel Smith
  0 siblings, 1 reply; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:14 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:40PM +0100, Ansuel Smith wrote:
> Convert any qca8k read/write/rmw/set/clear/pool to regmap helper and add
> missing config to regmap_config struct.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---

The important question is "why" and this commit message seems to omit that.
Using regmap will be slower than using the equivalent direct I/O.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 09/19] net: dsa: qca8k: add additional MIB counter and make it dynamic
  2021-11-17 21:04 ` [net-next PATCH 09/19] net: dsa: qca8k: add additional MIB counter and make it dynamic Ansuel Smith
@ 2021-11-19  1:17   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:17 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:41PM +0100, Ansuel Smith wrote:
> We are currently missing 2 additional MIB counter present in QCA833x
                                            ~~~~~~~

I know that 2 is less than 50, but please, it still counts as plural.

> switch.
> QC832x switch have 39 MIB counter and QCA833X have 41 MIB counter.
> Add the additional MIB counter and rework the MIB function to print the
> correct supported counter from the match_data struct.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---

Reviewed-by: Vladimir Oltean <olteanv@gmail.com>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 10/19] net: dsa: qca8k: add support for port fast aging
  2021-11-17 21:04 ` [net-next PATCH 10/19] net: dsa: qca8k: add support for port fast aging Ansuel Smith
@ 2021-11-19  1:20   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:20 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:42PM +0100, Ansuel Smith wrote:
> The switch doesn't support fast aging but it does support the flush of
> the ARL table for a specific port. Add this function to simulate
> fast aging and proprely support stp state set.
                 ~~~~~~~~                   ~~~
                 properly                   verb? noun? what are you saying here?

What difference do you see between fast ageing and ARL table flushing
for a specific port?

> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index cf4f69b36b47..d73886b36e6a 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1823,6 +1823,16 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
>  			   QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
>  }
>  
> +static void
> +qca8k_port_fast_age(struct dsa_switch *ds, int port)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +
> +	mutex_lock(&priv->reg_mutex);
> +	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
> +	mutex_unlock(&priv->reg_mutex);
> +}
> +
>  static int
>  qca8k_port_enable(struct dsa_switch *ds, int port,
>  		  struct phy_device *phy)
> @@ -2031,6 +2041,7 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.port_stp_state_set	= qca8k_port_stp_state_set,
>  	.port_bridge_join	= qca8k_port_bridge_join,
>  	.port_bridge_leave	= qca8k_port_bridge_leave,
> +	.port_fast_age		= qca8k_port_fast_age,
>  	.port_fdb_add		= qca8k_port_fdb_add,
>  	.port_fdb_del		= qca8k_port_fdb_del,
>  	.port_fdb_dump		= qca8k_port_fdb_dump,
> -- 
> 2.32.0
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper
  2021-11-19  1:14   ` Vladimir Oltean
@ 2021-11-19  1:28     ` Ansuel Smith
  2021-11-21 18:31       ` Vladimir Oltean
  0 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-19  1:28 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 03:14:10AM +0200, Vladimir Oltean wrote:
> On Wed, Nov 17, 2021 at 10:04:40PM +0100, Ansuel Smith wrote:
> > Convert any qca8k read/write/rmw/set/clear/pool to regmap helper and add
> > missing config to regmap_config struct.
> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> 
> The important question is "why" and this commit message seems to omit that.
> Using regmap will be slower than using the equivalent direct I/O.

Yes sorry, will improve the message.
The transition to regmap is needed to permit the use of common code by
different switch that have different read/write/rmw function.
It seems cleaner to use regmap instead of using some helper or putting
the read/write/rmw in the priv struct.
Also in theory the overhead created by using regmap should be marginal
as the internal mdio use dedicated function and bypass regmap. So the
overhead should be present only in the configuration operation or fdb
access.

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 11/19] net: dsa: qca8k: add support for mirror mode
  2021-11-17 21:04 ` [net-next PATCH 11/19] net: dsa: qca8k: add support for mirror mode Ansuel Smith
@ 2021-11-19  1:42   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:42 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:43PM +0100, Ansuel Smith wrote:
> The switch support mirror mode. Only one port can set as mirror port and
> every other port can set to both ingress and egress mode. The mirror
> port is disabled and reverted to normal operation once every port is
> removed from sending packet to the mirror mode.

> Also modify the fdb logit to send packet to both destination and mirror
> port by default to support mirror mode.

I don't see any FDB logic being modified by this patch. Also, port-based
mirroring should not require it.

> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 95 +++++++++++++++++++++++++++++++++++++++++
>  drivers/net/dsa/qca8k.h |  5 +++
>  2 files changed, 100 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index d73886b36e6a..a74099131e3d 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1938,6 +1938,99 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
>  	return 0;
>  }
>  
> +static int
> +qca8k_port_mirror_add(struct dsa_switch *ds, int port,
> +		      struct dsa_mall_mirror_tc_entry *mirror,
> +		      bool ingress)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	int monitor_port, ret;
> +	u32 reg, val;
> +
> +	/* Check for existent entry */
> +	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
> +		return -EEXIST;
> +
> +	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
> +	if (ret)
> +		return ret;
> +
> +	/* QCA83xx can have only one port set to mirror mode.
> +	 * Check that the correct port is requested and return error otherwise.
> +	 * When no mirror port is set, the values is set to 0xF
> +	 */
> +	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
> +	if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
> +		return -EEXIST;
> +
> +	/* Set the monitor port */
> +	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
> +			 mirror->to_local_port);
> +	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
> +				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
> +	if (ret)
> +		return ret;
> +
> +	if (ingress) {
> +		reg = QCA8K_PORT_LOOKUP_CTRL(port);
> +		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
> +	} else {
> +		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
> +		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
> +	}
> +
> +	ret = regmap_update_bits(priv->regmap, reg, val, val);
> +	if (ret)
> +		return ret;
> +
> +	/* Track mirror port for tx and rx to decide when the
> +	 * mirror port has to be disabled.
> +	 */
> +	if (ingress)
> +		priv->mirror_rx |= BIT(port);
> +	else
> +		priv->mirror_tx |= BIT(port);
> +
> +	return 0;
> +}
> +
> +static void
> +qca8k_port_mirror_del(struct dsa_switch *ds, int port,
> +		      struct dsa_mall_mirror_tc_entry *mirror)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	u32 reg, val;
> +	int ret;
> +
> +	if (mirror->ingress) {
> +		reg = QCA8K_PORT_LOOKUP_CTRL(port);
> +		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
> +	} else {
> +		reg = QCA8K_REG_PORT_HOL_CTRL1(port);
> +		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
> +	}
> +
> +	ret = regmap_clear_bits(priv->regmap, reg, val);
> +	if (ret)
> +		goto err;
> +
> +	if (mirror->ingress)
> +		priv->mirror_rx &= ~BIT(port);
> +	else
> +		priv->mirror_tx &= ~BIT(port);
> +
> +	/* No port set to send packet to mirror port. Disable mirror port */
> +	if (!priv->mirror_rx && !priv->mirror_tx) {
> +		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
> +		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
> +					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
> +		if (ret)
> +			goto err;
> +	}
> +err:
> +	dev_err(priv->dev, "Failed to del mirror port from %d", port);
> +}
> +
>  static int
>  qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
>  			  struct netlink_ext_ack *extack)
> @@ -2045,6 +2138,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.port_fdb_add		= qca8k_port_fdb_add,
>  	.port_fdb_del		= qca8k_port_fdb_del,
>  	.port_fdb_dump		= qca8k_port_fdb_dump,
> +	.port_mirror_add	= qca8k_port_mirror_add,
> +	.port_mirror_del	= qca8k_port_mirror_del,
>  	.port_vlan_filtering	= qca8k_port_vlan_filtering,
>  	.port_vlan_add		= qca8k_port_vlan_add,
>  	.port_vlan_del		= qca8k_port_vlan_del,
> diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
> index c5d83514ad2e..d25afdab4dea 100644
> --- a/drivers/net/dsa/qca8k.h
> +++ b/drivers/net/dsa/qca8k.h
> @@ -177,6 +177,7 @@
>  #define   QCA8K_VTU_FUNC1_FULL				BIT(4)
>  #define QCA8K_REG_GLOBAL_FW_CTRL0			0x620
>  #define   QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN		BIT(10)
> +#define   QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM		GENMASK(7, 4)
>  #define QCA8K_REG_GLOBAL_FW_CTRL1			0x624
>  #define   QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK		GENMASK(30, 24)
>  #define   QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK		GENMASK(22, 16)
> @@ -198,6 +199,7 @@
>  #define   QCA8K_PORT_LOOKUP_STATE_LEARNING		QCA8K_PORT_LOOKUP_STATE(0x3)
>  #define   QCA8K_PORT_LOOKUP_STATE_FORWARD		QCA8K_PORT_LOOKUP_STATE(0x4)
>  #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20)
> +#define   QCA8K_PORT_LOOKUP_ING_MIRROR_EN		BIT(25)
>  
>  #define QCA8K_REG_GLOBAL_FC_THRESH			0x800
>  #define   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK		GENMASK(24, 16)
> @@ -262,6 +264,7 @@ enum qca8k_fdb_cmd {
>  	QCA8K_FDB_FLUSH	= 1,
>  	QCA8K_FDB_LOAD = 2,
>  	QCA8K_FDB_PURGE = 3,
> +	QCA8K_FDB_FLUSH_PORT = 5,

This portion seems like the missing piece required for the previous
patch to actually compile.

>  	QCA8K_FDB_NEXT = 6,
>  	QCA8K_FDB_SEARCH = 7,
>  };
> @@ -302,6 +305,8 @@ struct qca8k_ports_config {
>  struct qca8k_priv {
>  	u8 switch_id;
>  	u8 switch_revision;
> +	u8 mirror_rx;
> +	u8 mirror_tx;
>  	bool legacy_phy_port_mapping;
>  	struct qca8k_ports_config ports_config;
>  	struct regmap *regmap;
> -- 
> 2.32.0
> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 12/19] net: dsa: qca8k: add set_ageing_time support
  2021-11-17 21:04 ` [net-next PATCH 12/19] net: dsa: qca8k: add set_ageing_time support Ansuel Smith
@ 2021-11-19  1:47   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:47 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:44PM +0100, Ansuel Smith wrote:
> qca8k support setting ageing time in set of 7s. Add support for it and
> return error with value greater than the max value accepted of 7645m.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 18 ++++++++++++++++++
>  drivers/net/dsa/qca8k.h |  3 +++
>  2 files changed, 21 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index a74099131e3d..50f19549b97d 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1833,6 +1833,23 @@ qca8k_port_fast_age(struct dsa_switch *ds, int port)
>  	mutex_unlock(&priv->reg_mutex);
>  }
>  
> +static int
> +qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	unsigned int secs = msecs / 1000;
> +	u32 val;
> +
> +	/* AGE_TIME reg is set in 7s step */
> +	val = secs / 7;

A typical bug is when the user gives you an ageing time smaller than 7
seconds, like 6, and you divide and end up writing 0 to hardware, which
effectively disables learning and is not what the user intended.
Maybe DIV_ROUND_UP? Or just catch "val == 0" and replace it with 1.

> +
> +	if (val > FIELD_MAX(QCA8K_ATU_AGE_TIME_MASK))
> +		return -ERANGE;

You can set ds->ageing_time_max and it could do the check for you.

> +
> +	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
> +				  QCA8K_ATU_AGE_TIME(val));
> +}
> +
>  static int
>  qca8k_port_enable(struct dsa_switch *ds, int port,
>  		  struct phy_device *phy)
> @@ -2125,6 +2142,7 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.get_strings		= qca8k_get_strings,
>  	.get_ethtool_stats	= qca8k_get_ethtool_stats,
>  	.get_sset_count		= qca8k_get_sset_count,
> +	.set_ageing_time	= qca8k_set_ageing_time,
>  	.get_mac_eee		= qca8k_get_mac_eee,
>  	.set_mac_eee		= qca8k_set_mac_eee,
>  	.port_enable		= qca8k_port_enable,
> diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
> index d25afdab4dea..e1298179d7cb 100644
> --- a/drivers/net/dsa/qca8k.h
> +++ b/drivers/net/dsa/qca8k.h
> @@ -175,6 +175,9 @@
>  #define   QCA8K_VTU_FUNC1_BUSY				BIT(31)
>  #define   QCA8K_VTU_FUNC1_VID_MASK			GENMASK(27, 16)
>  #define   QCA8K_VTU_FUNC1_FULL				BIT(4)
> +#define QCA8K_REG_ATU_CTRL				0x618
> +#define   QCA8K_ATU_AGE_TIME_MASK			GENMASK(15, 0)
> +#define   QCA8K_ATU_AGE_TIME(x)				FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
>  #define QCA8K_REG_GLOBAL_FW_CTRL0			0x620
>  #define   QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN		BIT(10)
>  #define   QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM		GENMASK(7, 4)
> -- 
> 2.32.0
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 13/19] net: dsa: qca8k: add min/max ageing time
  2021-11-17 21:04 ` [net-next PATCH 13/19] net: dsa: qca8k: add min/max ageing time Ansuel Smith
@ 2021-11-19  1:49   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  1:49 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:45PM +0100, Ansuel Smith wrote:
> Add min and max ageing value for qca8k switch.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index 50f19549b97d..dda99263fe8c 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1291,6 +1291,10 @@ qca8k_setup(struct dsa_switch *ds)
>  	/* We don't have interrupts for link changes, so we need to poll */
>  	ds->pcs_poll = true;
>  
> +	/* Set min a max ageing value supported */
> +	ds->ageing_time_min = 7000;
> +	ds->ageing_time_max = 458745000;
> +
>  	return 0;
>  }
>  
> -- 
> 2.32.0
> 

Squash with previous patch please, and you should be able to remove the
range check from qca8k_set_ageing_time.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: regmap: allow to define reg_update_bits for no bus configuration
  2021-11-17 22:33       ` Mark Brown
@ 2021-11-19  1:54         ` Jakub Kicinski
  2021-11-19  2:00           ` Ansuel Smith
  0 siblings, 1 reply; 50+ messages in thread
From: Jakub Kicinski @ 2021-11-19  1:54 UTC (permalink / raw)
  To: Mark Brown
  Cc: Ansuel Smith, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	Vladimir Oltean, David S. Miller, Russell King, linux-kernel,
	netdev

On Wed, 17 Nov 2021 22:33:30 +0000 Mark Brown wrote:
> On Wed, Nov 17, 2021 at 11:19:39PM +0100, Ansuel Smith wrote:
> > On Wed, Nov 17, 2021 at 10:15:53PM +0000, Mark Brown wrote:  
> 
> > > I've applied this already?  If it's needed by something in another tree
> > > let me know and I'll make a signed tag for it.  
> 
> > Yes, I posted this in this series as net-next still doesn't have this
> > commit. Don't really know how to hanle this kind of corner
> > case. Do you have some hint about that and how to proceed?  
> 
> Ask for a tag like I said in the message you're replying to:
> 
> The following changes since commit fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf:
> 
>   Linux 5.16-rc1 (2021-11-14 13:56:52 -0800)
> 
> are available in the Git repository at:
> 
>   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git tags/regmap-no-bus-update-bits
> 
> for you to fetch changes up to 02d6fdecb9c38de19065f6bed8d5214556fd061d:
> 
>   regmap: allow to define reg_update_bits for no bus configuration (2021-11-15 13:27:13 +0000)
> 
> ----------------------------------------------------------------
> regmap: Allow regmap_update_bits() to be offloaded with no bus
> 
> Some hardware can do this so let's use that capability.

Pulled into net-next, thanks Mark!

Ansuel, please make sure to post fewer than 15 patches at a time.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: regmap: allow to define reg_update_bits for no bus configuration
  2021-11-17 21:04 ` regmap: allow to define reg_update_bits for no bus configuration Ansuel Smith
  2021-11-17 22:15   ` Mark Brown
@ 2021-11-19  2:00   ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 50+ messages in thread
From: patchwork-bot+netdevbpf @ 2021-11-19  2:00 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: andrew, vivien.didelot, f.fainelli, olteanv, davem, kuba, linux,
	linux-kernel, netdev, broonie

Hello:

This patch was applied to netdev/net-next.git (master)
by Mark Brown <broonie@kernel.org>:

On Wed, 17 Nov 2021 22:04:33 +0100 you wrote:
> Some device requires a special handling for reg_update_bits and can't use
> the normal regmap read write logic. An example is when locking is
> handled by the device and rmw operations requires to do atomic operations.
> Allow to declare a dedicated function in regmap_config for
> reg_update_bits in no bus configuration.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com
> Signed-off-by: Mark Brown <broonie@kernel.org>
> 
> [...]

Here is the summary with links:
  - regmap: allow to define reg_update_bits for no bus configuration
    https://git.kernel.org/netdev/net-next/c/02d6fdecb9c3

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: regmap: allow to define reg_update_bits for no bus configuration
  2021-11-19  1:54         ` Jakub Kicinski
@ 2021-11-19  2:00           ` Ansuel Smith
  0 siblings, 0 replies; 50+ messages in thread
From: Ansuel Smith @ 2021-11-19  2:00 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Mark Brown, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	Vladimir Oltean, David S. Miller, Russell King, linux-kernel,
	netdev

On Thu, Nov 18, 2021 at 05:54:30PM -0800, Jakub Kicinski wrote:
> On Wed, 17 Nov 2021 22:33:30 +0000 Mark Brown wrote:
> > On Wed, Nov 17, 2021 at 11:19:39PM +0100, Ansuel Smith wrote:
> > > On Wed, Nov 17, 2021 at 10:15:53PM +0000, Mark Brown wrote:  
> > 
> > > > I've applied this already?  If it's needed by something in another tree
> > > > let me know and I'll make a signed tag for it.  
> > 
> > > Yes, I posted this in this series as net-next still doesn't have this
> > > commit. Don't really know how to hanle this kind of corner
> > > case. Do you have some hint about that and how to proceed?  
> > 
> > Ask for a tag like I said in the message you're replying to:
> > 
> > The following changes since commit fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf:
> > 
> >   Linux 5.16-rc1 (2021-11-14 13:56:52 -0800)
> > 
> > are available in the Git repository at:
> > 
> >   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git tags/regmap-no-bus-update-bits
> > 
> > for you to fetch changes up to 02d6fdecb9c38de19065f6bed8d5214556fd061d:
> > 
> >   regmap: allow to define reg_update_bits for no bus configuration (2021-11-15 13:27:13 +0000)
> > 
> > ----------------------------------------------------------------
> > regmap: Allow regmap_update_bits() to be offloaded with no bus
> > 
> > Some hardware can do this so let's use that capability.
> 
> Pulled into net-next, thanks Mark!
> 
> Ansuel, please make sure to post fewer than 15 patches at a time.

Thx for the merge. Ok will split the series, sorry. It was really to
get some idea about the regmap and split changes.

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del
  2021-11-17 21:04 ` [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del Ansuel Smith
@ 2021-11-19  2:06   ` Vladimir Oltean
  2021-11-19  2:19     ` Ansuel Smith
  0 siblings, 1 reply; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  2:06 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:46PM +0100, Ansuel Smith wrote:
> Add support for mdb add/del function. The ARL table is used to insert
> the rule. A new search function is introduced to search the rule and add
> additional port to it. If every port is removed from the rule, it's
> removed. It's set STATIC in the ARL table (aka it doesn't age) to not be
> flushed by fast age function.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 82 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 82 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index dda99263fe8c..a217c842ab45 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -417,6 +417,23 @@ qca8k_fdb_flush(struct qca8k_priv *priv)
>  	mutex_unlock(&priv->reg_mutex);
>  }
>  
> +static int
> +qca8k_fdb_search(struct qca8k_priv *priv, struct qca8k_fdb *fdb, const u8 *mac, u16 vid)
> +{
> +	int ret;
> +
> +	mutex_lock(&priv->reg_mutex);

If I were you, I'd create a locking scheme where the entire FDB entry is
updated under the same critical section. Right now you're relying on the
rtnl_mutex serializing calls to ->port_mdb_add()/->port_mdb_del(). But
that might change. Don't leave that task to someone that has non-expert
knowledge of the driver.

> +	qca8k_fdb_write(priv, vid, 0, mac, 0);
> +	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
> +	if (ret < 0)
> +		goto exit;
> +
> +	ret = qca8k_fdb_read(priv, fdb);
> +exit:
> +	mutex_unlock(&priv->reg_mutex);
> +	return ret;
> +}
> +
>  static int
>  qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
>  {
> @@ -1959,6 +1976,69 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
>  	return 0;
>  }
>  
> +static int
> +qca8k_port_mdb_add(struct dsa_switch *ds, int port,
> +		   const struct switchdev_obj_port_mdb *mdb)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	struct qca8k_fdb fdb = { 0 };
> +	const u8 *addr = mdb->addr;
> +	u8 port_mask = BIT(port);

This doesn't really need to be kept in a temporary variable as it is
only used once.

> +	u16 vid = mdb->vid;
> +	int ret;
> +
> +	/* Check if entry already exist */
> +	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Rule exist. Delete first */
> +	if (!fdb.aging) {
> +		ret = qca8k_fdb_del(priv, addr, fdb.port_mask, vid);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Add port to fdb portmask */
> +	fdb.port_mask |= port_mask;
> +
> +	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
> +}
> +
> +static int
> +qca8k_port_mdb_del(struct dsa_switch *ds, int port,
> +		   const struct switchdev_obj_port_mdb *mdb)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	struct qca8k_fdb fdb = { 0 };
> +	const u8 *addr = mdb->addr;
> +	u8 port_mask = BIT(port);
> +	u16 vid = mdb->vid;
> +	int ret;
> +
> +	/* Check if entry already exist */
> +	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Rule doesn't exist. Why delete? */

Because refcounting is hard. In fact with VLANs it is quite possible to
delete an absent entry. For MDBs and FDBs, the bridge should now error
out before it even reaches to you.

> +	if (!fdb.aging)
> +		return -EINVAL;
> +
> +	ret = qca8k_fdb_del(priv, addr, port_mask, vid);
> +	if (ret)
> +		return ret;
> +
> +	/* Only port in the rule is this port. Don't re insert */
> +	if (fdb.port_mask == port_mask)
> +		return 0;
> +
> +	/* Remove port from port mask */
> +	fdb.port_mask &= ~port_mask;
> +
> +	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
> +}
> +
>  static int
>  qca8k_port_mirror_add(struct dsa_switch *ds, int port,
>  		      struct dsa_mall_mirror_tc_entry *mirror,
> @@ -2160,6 +2240,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.port_fdb_add		= qca8k_port_fdb_add,
>  	.port_fdb_del		= qca8k_port_fdb_del,
>  	.port_fdb_dump		= qca8k_port_fdb_dump,
> +	.port_mdb_add		= qca8k_port_mdb_add,
> +	.port_mdb_del		= qca8k_port_mdb_del,
>  	.port_mirror_add	= qca8k_port_mirror_add,
>  	.port_mirror_del	= qca8k_port_mirror_del,
>  	.port_vlan_filtering	= qca8k_port_vlan_filtering,
> -- 
> 2.32.0
> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 15/19] net: dsa: qca8k: add LAG support
  2021-11-17 21:04 ` [net-next PATCH 15/19] net: dsa: qca8k: add LAG support Ansuel Smith
@ 2021-11-19  2:13   ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  2:13 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:47PM +0100, Ansuel Smith wrote:
> Add LAG support to this switch. In Documentation this is described as
> trunk mode. A max of 4 LAGs are supported and each can support up to 4
> port. The only tx mode supported is Hash mode and no reference is
> present for active backup or any other mode in Documentation.
> When no port are present in the trunk, the trunk is disabled in the
> switch.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 117 ++++++++++++++++++++++++++++++++++++++++
>  drivers/net/dsa/qca8k.h |  24 +++++++++
>  2 files changed, 141 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index a217c842ab45..c3234988aabf 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1312,6 +1312,9 @@ qca8k_setup(struct dsa_switch *ds)
>  	ds->ageing_time_min = 7000;
>  	ds->ageing_time_max = 458745000;
>  
> +	/* Set max number of LAGs supported */
> +	ds->num_lag_ids = QCA8K_NUM_LAGS;
> +
>  	return 0;
>  }
>  
> @@ -2220,6 +2223,118 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
>  	return DSA_TAG_PROTO_QCA;
>  }
>  
> +static bool
> +qca8k_lag_can_offload(struct dsa_switch *ds,
> +		      struct net_device *lag,
> +		      struct netdev_lag_upper_info *info)
> +{
> +	struct dsa_port *dp;
> +	int id, members = 0;
> +
> +	id = dsa_lag_id(ds->dst, lag);
> +	if (id < 0 || id >= ds->num_lag_ids)
> +		return false;
> +
> +	dsa_lag_foreach_port(dp, ds->dst, lag)
> +		/* Includes the port joining the LAG */
> +		members++;
> +
> +	if (members > QCA8K_NUM_PORTS_FOR_LAG)
> +		return false;
> +
> +	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
> +		return false;

You'll get bonus points if you also validate info->hash_type.

> +
> +	return true;
> +}
> +
> +static int
> +qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
> +			  struct net_device *lag, bool delete)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	int ret, id, i;
> +	u32 val;
> +
> +	id = dsa_lag_id(ds->dst, lag);
> +
> +	/* Read current port member */
> +	ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
> +	if (ret)
> +		return ret;
> +
> +	/* Shift val to the correct trunk */
> +	val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
> +	val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
> +	if (delete)
> +		val &= ~BIT(port);
> +	else
> +		val |= BIT(port);
> +
> +	/* Update port member. With empty portmap disable trunk */
> +	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
> +				 QCA8K_REG_GOL_TRUNK_MEMBER(id) |
> +				 QCA8K_REG_GOL_TRUNK_EN(id),
> +				 !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
> +				 val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
> +
> +	/* Search empty member if adding or port on deleting */
> +	for (i = 0; i < 4; i++) {
> +		ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
> +		if (ret)
> +			return ret;
> +
> +		val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
> +		val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
> +
> +		if (delete) {
> +			/* If port flagged to be disabled assume this member is
> +			 * empty
> +			 */
> +			if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
> +				continue;
> +
> +			val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
> +			if (val != port)
> +				continue;
> +		} else {
> +			/* If port flagged to be enabled assume this member is
> +			 * already set
> +			 */
> +			if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
> +				continue;
> +		}
> +
> +		/* We find the member to remove */
> +		break;
> +	}
> +
> +	/* Set port in the correct port mask or disable port if in delate mode */
> +	return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
> +				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
> +				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
> +				  !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
> +				  port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
> +}
> +
> +static int
> +qca8k_port_lag_join(struct dsa_switch *ds, int port,
> +		    struct net_device *lag,
> +		    struct netdev_lag_upper_info *info)
> +{
> +	if (!qca8k_lag_can_offload(ds, lag, info))
> +		return -EOPNOTSUPP;
> +
> +	return qca8k_lag_refresh_portmap(ds, port, lag, false);
> +}
> +
> +static int
> +qca8k_port_lag_leave(struct dsa_switch *ds, int port,
> +		     struct net_device *lag)
> +{
> +	return qca8k_lag_refresh_portmap(ds, port, lag, true);
> +}
> +
>  static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.get_tag_protocol	= qca8k_get_tag_protocol,
>  	.setup			= qca8k_setup,
> @@ -2253,6 +2368,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
>  	.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,

If you unplug a cable from the LAG, does the traffic that was going
through that port automatically get rebalanced towards the other ports
that are left? If not, shouldn't you also implement ->port_lag_change?

>  };
>  
>  static int
> diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
> index e1298179d7cb..5310022569f3 100644
> --- a/drivers/net/dsa/qca8k.h
> +++ b/drivers/net/dsa/qca8k.h
> @@ -15,6 +15,8 @@
>  #define QCA8K_NUM_PORTS					7
>  #define QCA8K_NUM_CPU_PORTS				2
>  #define QCA8K_MAX_MTU					9000
> +#define QCA8K_NUM_LAGS					4
> +#define QCA8K_NUM_PORTS_FOR_LAG				4
>  
>  #define PHY_ID_QCA8327					0x004dd034
>  #define QCA8K_ID_QCA8327				0x12
> @@ -204,6 +206,28 @@
>  #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20)
>  #define   QCA8K_PORT_LOOKUP_ING_MIRROR_EN		BIT(25)
>  
> +#define QCA8K_REG_GOL_TRUNK_CTRL0			0x700
> +/* 4 max trunk first
> + * first 6 bit for member bitmap
> + * 7th bit is to enable trunk port
> + */
> +#define QCA8K_REG_GOL_TRUNK_SHIFT(_i)			((_i) * 8)
> +#define QCA8K_REG_GOL_TRUNK_EN_MASK			BIT(7)
> +#define QCA8K_REG_GOL_TRUNK_EN(_i)			(QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
> +#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK			GENMASK(6, 0)
> +#define QCA8K_REG_GOL_TRUNK_MEMBER(_i)			(QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
> +/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */
> +#define QCA8K_REG_GOL_TRUNK_CTRL(_i)			(0x704 + (((_i) / 2) * 4))
> +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK		GENMASK(3, 0)
> +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK		BIT(3)
> +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK		GENMASK(2, 0)
> +#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)		(((_i) / 2) * 16)
> +#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i)			((_i) * 4)
> +/* Complex shift: FIRST shift for port THEN shift for trunk */
> +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)	(QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i))
> +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j)	(QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
> +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j)	(QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
> +
>  #define QCA8K_REG_GLOBAL_FC_THRESH			0x800
>  #define   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK		GENMASK(24, 16)
>  #define   QCA8K_GLOBAL_FC_GOL_XON_THRES(x)		FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
> -- 
> 2.32.0
> 


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del
  2021-11-19  2:06   ` Vladimir Oltean
@ 2021-11-19  2:19     ` Ansuel Smith
  2021-11-19  2:33       ` Vladimir Oltean
  0 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-19  2:19 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 04:06:57AM +0200, Vladimir Oltean wrote:
> On Wed, Nov 17, 2021 at 10:04:46PM +0100, Ansuel Smith wrote:
> > Add support for mdb add/del function. The ARL table is used to insert
> > the rule. A new search function is introduced to search the rule and add
> > additional port to it. If every port is removed from the rule, it's
> > removed. It's set STATIC in the ARL table (aka it doesn't age) to not be
> > flushed by fast age function.
> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> >  drivers/net/dsa/qca8k.c | 82 +++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 82 insertions(+)
> > 
> > diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> > index dda99263fe8c..a217c842ab45 100644
> > --- a/drivers/net/dsa/qca8k.c
> > +++ b/drivers/net/dsa/qca8k.c
> > @@ -417,6 +417,23 @@ qca8k_fdb_flush(struct qca8k_priv *priv)
> >  	mutex_unlock(&priv->reg_mutex);
> >  }
> >  
> > +static int
> > +qca8k_fdb_search(struct qca8k_priv *priv, struct qca8k_fdb *fdb, const u8 *mac, u16 vid)
> > +{
> > +	int ret;
> > +
> > +	mutex_lock(&priv->reg_mutex);
> 
> If I were you, I'd create a locking scheme where the entire FDB entry is
> updated under the same critical section. Right now you're relying on the
> rtnl_mutex serializing calls to ->port_mdb_add()/->port_mdb_del(). But
> that might change. Don't leave that task to someone that has non-expert
> knowledge of the driver.
>

Ok will change the logic and do the search and add/del operation in one
lock.

> > +	qca8k_fdb_write(priv, vid, 0, mac, 0);
> > +	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
> > +	if (ret < 0)
> > +		goto exit;
> > +
> > +	ret = qca8k_fdb_read(priv, fdb);
> > +exit:
> > +	mutex_unlock(&priv->reg_mutex);
> > +	return ret;
> > +}
> > +
> >  static int
> >  qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
> >  {
> > @@ -1959,6 +1976,69 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
> >  	return 0;
> >  }
> >  
> > +static int
> > +qca8k_port_mdb_add(struct dsa_switch *ds, int port,
> > +		   const struct switchdev_obj_port_mdb *mdb)
> > +{
> > +	struct qca8k_priv *priv = ds->priv;
> > +	struct qca8k_fdb fdb = { 0 };
> > +	const u8 *addr = mdb->addr;
> > +	u8 port_mask = BIT(port);
> 
> This doesn't really need to be kept in a temporary variable as it is
> only used once.
> 
> > +	u16 vid = mdb->vid;
> > +	int ret;
> > +
> > +	/* Check if entry already exist */
> > +	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* Rule exist. Delete first */
> > +	if (!fdb.aging) {
> > +		ret = qca8k_fdb_del(priv, addr, fdb.port_mask, vid);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	/* Add port to fdb portmask */
> > +	fdb.port_mask |= port_mask;
> > +
> > +	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
> > +}
> > +
> > +static int
> > +qca8k_port_mdb_del(struct dsa_switch *ds, int port,
> > +		   const struct switchdev_obj_port_mdb *mdb)
> > +{
> > +	struct qca8k_priv *priv = ds->priv;
> > +	struct qca8k_fdb fdb = { 0 };
> > +	const u8 *addr = mdb->addr;
> > +	u8 port_mask = BIT(port);
> > +	u16 vid = mdb->vid;
> > +	int ret;
> > +
> > +	/* Check if entry already exist */
> > +	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* Rule doesn't exist. Why delete? */
> 
> Because refcounting is hard. In fact with VLANs it is quite possible to
> delete an absent entry. For MDBs and FDBs, the bridge should now error
> out before it even reaches to you.
> 

So in this specific case I should simply return 0 to correctly decrement
the ref, correct? 

> > +	if (!fdb.aging)
> > +		return -EINVAL;
> > +
> > +	ret = qca8k_fdb_del(priv, addr, port_mask, vid);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Only port in the rule is this port. Don't re insert */
> > +	if (fdb.port_mask == port_mask)
> > +		return 0;
> > +
> > +	/* Remove port from port mask */
> > +	fdb.port_mask &= ~port_mask;
> > +
> > +	return qca8k_port_fdb_insert(priv, addr, fdb.port_mask, vid);
> > +}
> > +
> >  static int
> >  qca8k_port_mirror_add(struct dsa_switch *ds, int port,
> >  		      struct dsa_mall_mirror_tc_entry *mirror,
> > @@ -2160,6 +2240,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
> >  	.port_fdb_add		= qca8k_port_fdb_add,
> >  	.port_fdb_del		= qca8k_port_fdb_del,
> >  	.port_fdb_dump		= qca8k_port_fdb_dump,
> > +	.port_mdb_add		= qca8k_port_mdb_add,
> > +	.port_mdb_del		= qca8k_port_mdb_del,
> >  	.port_mirror_add	= qca8k_port_mirror_add,
> >  	.port_mirror_del	= qca8k_port_mirror_del,
> >  	.port_vlan_filtering	= qca8k_port_vlan_filtering,
> > -- 
> > 2.32.0
> > 
> 

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress
  2021-11-17 21:04 ` [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress Ansuel Smith
@ 2021-11-19  2:20   ` Vladimir Oltean
  2021-11-19  2:28     ` Ansuel Smith
  0 siblings, 1 reply; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  2:20 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:48PM +0100, Ansuel Smith wrote:
> qca8k have a global MTU. Inform DSA of this as the change MTU port
> function checks the max MTU across all port and sets the max value
> anyway as this switch doesn't support per port MTU.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  drivers/net/dsa/qca8k.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> index c3234988aabf..cae58753bb1f 100644
> --- a/drivers/net/dsa/qca8k.c
> +++ b/drivers/net/dsa/qca8k.c
> @@ -1315,6 +1315,9 @@ qca8k_setup(struct dsa_switch *ds)
>  	/* Set max number of LAGs supported */
>  	ds->num_lag_ids = QCA8K_NUM_LAGS;
>  
> +	/* Global MTU. Inform dsa that per port MTU is not supported */
> +	ds->mtu_enforcement_ingress = true;
> +
>  	return 0;
>  }
>  
> -- 
> 2.32.0
> 

This doesn't do what you think it does. If you want the dev->mtu of all
interfaces to get updated at once, you need to do that yourself. Setting
ds->mtu_enforcement_ingress will only update the MTU for ports belonging
to the same bridge, and for a different reason. Or I'm missing the
reason why you're making this change now.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant
  2021-11-17 21:04 ` [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant Ansuel Smith
@ 2021-11-19  2:21   ` Vladimir Oltean
  2021-11-19  2:32     ` Ansuel Smith
  0 siblings, 1 reply; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  2:21 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Wed, Nov 17, 2021 at 10:04:50PM +0100, Ansuel Smith wrote:
> Drop of_platform include and device_get_match_data instead of the OF
> variant.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---

Why? Any ACPI device coming?

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress
  2021-11-19  2:20   ` Vladimir Oltean
@ 2021-11-19  2:28     ` Ansuel Smith
  2021-11-21 18:11       ` Vladimir Oltean
  0 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-19  2:28 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 04:20:08AM +0200, Vladimir Oltean wrote:
> On Wed, Nov 17, 2021 at 10:04:48PM +0100, Ansuel Smith wrote:
> > qca8k have a global MTU. Inform DSA of this as the change MTU port
> > function checks the max MTU across all port and sets the max value
> > anyway as this switch doesn't support per port MTU.
> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> >  drivers/net/dsa/qca8k.c | 3 +++
> >  1 file changed, 3 insertions(+)
> > 
> > diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> > index c3234988aabf..cae58753bb1f 100644
> > --- a/drivers/net/dsa/qca8k.c
> > +++ b/drivers/net/dsa/qca8k.c
> > @@ -1315,6 +1315,9 @@ qca8k_setup(struct dsa_switch *ds)
> >  	/* Set max number of LAGs supported */
> >  	ds->num_lag_ids = QCA8K_NUM_LAGS;
> >  
> > +	/* Global MTU. Inform dsa that per port MTU is not supported */
> > +	ds->mtu_enforcement_ingress = true;
> > +
> >  	return 0;
> >  }
> >  
> > -- 
> > 2.32.0
> > 
> 
> This doesn't do what you think it does. If you want the dev->mtu of all
> interfaces to get updated at once, you need to do that yourself. Setting
> ds->mtu_enforcement_ingress will only update the MTU for ports belonging
> to the same bridge, and for a different reason. Or I'm missing the
> reason why you're making this change now.

Got confused by the Documentation. Just to confirm in DSA we don't have
a way to handle the case where we have one MTU reg that is applied to
every port, correct?

We already handle this by checking the max MTU set to all port in the
port_change_mtu but I was searching a cleaner way to handle this as
currently we use an array to store the MTU of all port and seems a bit
hacky and a waste of space. 

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant
  2021-11-19  2:21   ` Vladimir Oltean
@ 2021-11-19  2:32     ` Ansuel Smith
  2021-11-21 18:03       ` Vladimir Oltean
  0 siblings, 1 reply; 50+ messages in thread
From: Ansuel Smith @ 2021-11-19  2:32 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 04:21:36AM +0200, Vladimir Oltean wrote:
> On Wed, Nov 17, 2021 at 10:04:50PM +0100, Ansuel Smith wrote:
> > Drop of_platform include and device_get_match_data instead of the OF
> > variant.
> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> 
> Why? Any ACPI device coming?

No ACPI device coming. Notice we could drop an extra include.
Is using device API wrong for OF only drivers?
Just asking will drop if it can cause any problem or confusion.

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del
  2021-11-19  2:19     ` Ansuel Smith
@ 2021-11-19  2:33       ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-19  2:33 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 03:19:30AM +0100, Ansuel Smith wrote:
> > > +static int
> > > +qca8k_port_mdb_del(struct dsa_switch *ds, int port,
> > > +		   const struct switchdev_obj_port_mdb *mdb)
> > > +{
> > > +	struct qca8k_priv *priv = ds->priv;
> > > +	struct qca8k_fdb fdb = { 0 };
> > > +	const u8 *addr = mdb->addr;
> > > +	u8 port_mask = BIT(port);
> > > +	u16 vid = mdb->vid;
> > > +	int ret;
> > > +
> > > +	/* Check if entry already exist */
> > > +	ret = qca8k_fdb_search(priv, &fdb, addr, vid);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/* Rule doesn't exist. Why delete? */
> >
> > Because refcounting is hard. In fact with VLANs it is quite possible to
> > delete an absent entry. For MDBs and FDBs, the bridge should now error
> > out before it even reaches to you.
> >
>
> So in this specific case I should simply return 0 to correctly decrement
> the ref, correct?

No, it's fine, don't change anything.

See these?

[  365.648039] mscc_felix 0000:00:00.5 swp0: failed (err=-2) to del object (id=3)
[  365.648071] mscc_felix 0000:00:00.5 swp1: failed (err=-2) to del object (id=3)
[  365.648091] mscc_felix 0000:00:00.5 swp2: failed (err=-2) to del object (id=3)
[  365.648111] mscc_felix 0000:00:00.5 swp3: failed (err=-2) to del object (id=3)
[  365.648130] mscc_felix 0000:00:00.5 swp4: failed (err=-2) to del object (id=3)
[68736.079878] mscc_felix 0000:00:00.5 swp0: failed (err=-2) to del object (id=3)
[68736.079912] mscc_felix 0000:00:00.5 swp1: failed (err=-2) to del object (id=3)
[68736.079934] mscc_felix 0000:00:00.5 swp2: failed (err=-2) to del object (id=3)
[68736.079954] mscc_felix 0000:00:00.5 swp3: failed (err=-2) to del object (id=3)
[68736.079974] mscc_felix 0000:00:00.5 swp4: failed (err=-2) to del object (id=3)

err=-2 is -ENOENT, this driver is complaining about the fact that
->port_mdb_del() is called on MDB entries that are no longer in
hardware. And the system isn't doing anything, mind you, just idling.

Long story short, this used to be an issue until commit 3f6e32f92a02
("net: dsa: reference count the FDB addresses at the cross-chip notifier
level") - if you backport anything to v5.10 you'll notice that it'll
complain there, the refcounting is something that appeared in v5.14.

My comment was just to explain "why delete if there's no entry in
hardware" - because there isn't (wasn't) any logic to avoid doing so.

> > > +	if (!fdb.aging)
> > > +		return -EINVAL;

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant
  2021-11-19  2:32     ` Ansuel Smith
@ 2021-11-21 18:03       ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-21 18:03 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 03:32:06AM +0100, Ansuel Smith wrote:
> On Fri, Nov 19, 2021 at 04:21:36AM +0200, Vladimir Oltean wrote:
> > On Wed, Nov 17, 2021 at 10:04:50PM +0100, Ansuel Smith wrote:
> > > Drop of_platform include and device_get_match_data instead of the OF
> > > variant.
> > > 
> > > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > > ---
> > 
> > Why? Any ACPI device coming?
> 
> No ACPI device coming. Notice we could drop an extra include.
> Is using device API wrong for OF only drivers?
> Just asking will drop if it can cause any problem or confusion.

No problem, just that this patch is a bit weak on the justification side.
People usually don't make changes just for the fun of it.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress
  2021-11-19  2:28     ` Ansuel Smith
@ 2021-11-21 18:11       ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-21 18:11 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 03:28:48AM +0100, Ansuel Smith wrote:
> On Fri, Nov 19, 2021 at 04:20:08AM +0200, Vladimir Oltean wrote:
> > On Wed, Nov 17, 2021 at 10:04:48PM +0100, Ansuel Smith wrote:
> > > qca8k have a global MTU. Inform DSA of this as the change MTU port
> > > function checks the max MTU across all port and sets the max value
> > > anyway as this switch doesn't support per port MTU.
> > > 
> > > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > > ---
> > >  drivers/net/dsa/qca8k.c | 3 +++
> > >  1 file changed, 3 insertions(+)
> > > 
> > > diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
> > > index c3234988aabf..cae58753bb1f 100644
> > > --- a/drivers/net/dsa/qca8k.c
> > > +++ b/drivers/net/dsa/qca8k.c
> > > @@ -1315,6 +1315,9 @@ qca8k_setup(struct dsa_switch *ds)
> > >  	/* Set max number of LAGs supported */
> > >  	ds->num_lag_ids = QCA8K_NUM_LAGS;
> > >  
> > > +	/* Global MTU. Inform dsa that per port MTU is not supported */
> > > +	ds->mtu_enforcement_ingress = true;
> > > +
> > >  	return 0;
> > >  }
> > >  
> > > -- 
> > > 2.32.0
> > > 
> > 
> > This doesn't do what you think it does. If you want the dev->mtu of all
> > interfaces to get updated at once, you need to do that yourself. Setting
> > ds->mtu_enforcement_ingress will only update the MTU for ports belonging
> > to the same bridge, and for a different reason. Or I'm missing the
> > reason why you're making this change now.
> 
> Got confused by the Documentation. Just to confirm in DSA we don't have
> a way to handle the case where we have one MTU reg that is applied to
> every port, correct?

No we don't, because usually it is not a problem. The interface MTU
represents the minimum L2 payload size that can be accepted and
transmitted by the interface. It can be larger than that.

> We already handle this by checking the max MTU set to all port in the
> port_change_mtu but I was searching a cleaner way to handle this as
> currently we use an array to store the MTU of all port and seems a bit
> hacky and a waste of space. 

It depends on what your goal is. The current implementation seems to get
the job done fine, unless I'm missing something. If you set one
interface's MTU to e.g. 4000, the MTU of the other interfaces will
remain the same at 1500, although they can also receive larger frames.
But to also transmit larger frames on those other ports, you'd have to
increase their MTU as well, and even though that wouldn't increase
anything in hardware, it would tell the kernel to use the larger value.
This is in line with the user interaction that would be required if the
MTU was a per-port value, which seems fine to me.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper
  2021-11-19  1:28     ` Ansuel Smith
@ 2021-11-21 18:31       ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-21 18:31 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 02:28:23AM +0100, Ansuel Smith wrote:
> On Fri, Nov 19, 2021 at 03:14:10AM +0200, Vladimir Oltean wrote:
> > On Wed, Nov 17, 2021 at 10:04:40PM +0100, Ansuel Smith wrote:
> > > Convert any qca8k read/write/rmw/set/clear/pool to regmap helper and add
> > > missing config to regmap_config struct.
> > > 
> > > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > > ---
> > 
> > The important question is "why" and this commit message seems to omit that.
> > Using regmap will be slower than using the equivalent direct I/O.
> 
> Yes sorry, will improve the message.
> The transition to regmap is needed to permit the use of common code by
> different switch that have different read/write/rmw function.
> It seems cleaner to use regmap instead of using some helper or putting
> the read/write/rmw in the priv struct.
> Also in theory the overhead created by using regmap should be marginal
> as the internal mdio use dedicated function and bypass regmap. So the
> overhead should be present only in the configuration operation or fdb
> access.

Ok, no problem with that, just consider that reviewers have limited
attention span, and most of them are not in fact familiar with the
switches you're working with or with what you're trying to do with them,
so providing as much relevant information in the commit message as
possible is crucial to having a smooth way forward with your work.
IMHO I shouldn't have to ask "why" on every one of your patch, the "why"
should already be there.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup
  2021-11-19  1:08     ` Ansuel Smith
@ 2021-11-21 18:34       ` Vladimir Oltean
  0 siblings, 0 replies; 50+ messages in thread
From: Vladimir Oltean @ 2021-11-21 18:34 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, Russell King, linux-kernel, netdev

On Fri, Nov 19, 2021 at 02:08:59AM +0100, Ansuel Smith wrote:
> On Fri, Nov 19, 2021 at 03:03:05AM +0200, Vladimir Oltean wrote:
> > On Wed, Nov 17, 2021 at 10:04:37PM +0100, Ansuel Smith wrote:
> > > Move read_switch_id function in qca8k_setup in preparation for regmap
> > > conversion. Sw probe should NOT contain function that depends on reading
> > > from switch regs.
> > 
> > It shouldn't? Why? We have plenty of switch drivers that use regmap in
> > the probe function.
> >
> 
> The initial idea was to make a shared probe function. (when the ipq40xx
> code will be proposed)
> Currently the regmap is init in the setup function so we can both 
> move the switch id to setup or move regmap to probe.
> What should be better in your opinion?

Either one is fine, but it seems a bit backwards to call
dsa_register_switch() only to find out later, during the ->setup()
callback, that you're in fact talking to a potato. So from that point of
view maybe you could keep the device id check in ->probe, and therefore
also move the regmap initialization there.

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2021-11-21 18:34 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-17 21:04 [net-next PATCH 00/19] Multiple cleanup and feature for qca8k Ansuel Smith
2021-11-17 21:04 ` regmap: allow to define reg_update_bits for no bus configuration Ansuel Smith
2021-11-17 22:15   ` Mark Brown
2021-11-17 22:19     ` Ansuel Smith
2021-11-17 22:33       ` Mark Brown
2021-11-19  1:54         ` Jakub Kicinski
2021-11-19  2:00           ` Ansuel Smith
2021-11-19  2:00   ` patchwork-bot+netdevbpf
2021-11-17 21:04 ` [net-next PATCH 02/19] net: dsa: qca8k: remove redundant check in parse_port_config Ansuel Smith
2021-11-18 23:59   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 03/19] net: dsa: qca8k: skip sgmii delay on double cpu conf Ansuel Smith
2021-11-19  0:58   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 04/19] net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET Ansuel Smith
2021-11-17 21:04 ` [net-next PATCH 05/19] net: dsa: qca8k: move read switch id function in qca8k_setup Ansuel Smith
2021-11-19  1:03   ` Vladimir Oltean
2021-11-19  1:08     ` Ansuel Smith
2021-11-21 18:34       ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 06/19] net: dsa: qca8k: remove extra mutex_init " Ansuel Smith
2021-11-17 21:04 ` [net-next PATCH 07/19] net: dsa: qca8k: set regmap init as mandatory for regmap conversion Ansuel Smith
2021-11-19  1:09   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 08/19] net: dsa: qca8k: convert qca8k to regmap helper Ansuel Smith
2021-11-19  1:14   ` Vladimir Oltean
2021-11-19  1:28     ` Ansuel Smith
2021-11-21 18:31       ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 09/19] net: dsa: qca8k: add additional MIB counter and make it dynamic Ansuel Smith
2021-11-19  1:17   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 10/19] net: dsa: qca8k: add support for port fast aging Ansuel Smith
2021-11-19  1:20   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 11/19] net: dsa: qca8k: add support for mirror mode Ansuel Smith
2021-11-19  1:42   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 12/19] net: dsa: qca8k: add set_ageing_time support Ansuel Smith
2021-11-19  1:47   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 13/19] net: dsa: qca8k: add min/max ageing time Ansuel Smith
2021-11-19  1:49   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 14/19] net: dsa: qca8k: add support for mdb_add/del Ansuel Smith
2021-11-19  2:06   ` Vladimir Oltean
2021-11-19  2:19     ` Ansuel Smith
2021-11-19  2:33       ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 15/19] net: dsa: qca8k: add LAG support Ansuel Smith
2021-11-19  2:13   ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 16/19] net: dsa: qca8k: enable mtu_enforcement_ingress Ansuel Smith
2021-11-19  2:20   ` Vladimir Oltean
2021-11-19  2:28     ` Ansuel Smith
2021-11-21 18:11       ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 17/19] net: dsa: qca8k: move qca8k to qca dir Ansuel Smith
2021-11-17 21:04 ` [net-next PATCH 18/19] net: dsa: qca8k: use device_get_match_data instead of the OF variant Ansuel Smith
2021-11-19  2:21   ` Vladimir Oltean
2021-11-19  2:32     ` Ansuel Smith
2021-11-21 18:03       ` Vladimir Oltean
2021-11-17 21:04 ` [net-next PATCH 19/19] net: dsa: qca8k: split qca8k in common and 8xxx specific code Ansuel Smith

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.