[openwrt/openwrt] kernel: 5.10: backport qca8k stability improvements

LEDE Commits lede-commits at lists.infradead.org
Sun Oct 24 08:35:22 PDT 2021


hauke pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/e3c47ff90d765c1fe683b5422006ab1fe3ab718d

commit e3c47ff90d765c1fe683b5422006ab1fe3ab718d
Author: Matthew Hagan <mnhagan88 at gmail.com>
AuthorDate: Thu Sep 9 17:12:30 2021 +0000

    kernel: 5.10: backport qca8k stability improvements
    
    This is a backport of Ansuel Smith's "Multiple improvement to qca8k stability"
    series. The QCA8337 switch is available on multiple platforms including
    ipq806x, ath79 and bcm53xx.
    
    Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
    Signed-off-by: Matthew Hagan <mnhagan88 at gmail.com>
---
 ...-qca8k-change-simple-print-to-dev-variant.patch |  35 +++
 ...ca8k-use-iopoll-macro-for-qca8k_busy_wait.patch |  61 +++++
 ...8k-improve-qca8k-read-write-rmw-bus-acces.patch |  86 +++++++
 ...et-dsa-qca8k-handle-qca8k_set_page-errors.patch | 101 ++++++++
 ...8k-handle-error-with-qca8k_read-operation.patch | 207 ++++++++++++++++
 ...8k-handle-error-with-qca8k_write-operatio.patch | 263 ++++++++++++++++++++
 ...a8k-handle-error-with-qca8k_rmw-operation.patch | 226 +++++++++++++++++
 ...a-qca8k-handle-error-from-qca8k_busy_wait.patch |  66 +++++
 ...-dsa-qca8k-add-support-for-qca8327-switch.patch |  96 ++++++++
 ...net-dsa-qca8k-Document-new-compatible-qca.patch |  26 ++
 ...ca8k-add-priority-tweak-to-qca8337-switch.patch | 130 ++++++++++
 ...et-dsa-qca8k-limit-port5-delay-to-qca8337.patch |  31 +++
 ...8k-add-GLOBAL_FC-settings-needed-for-qca8.patch |  48 ++++
 ...-net-dsa-qca8k-add-support-for-switch-rev.patch | 114 +++++++++
 ...8k-add-ethernet-ports-fallback-to-setup_m.patch |  28 +++
 ...t-dsa-qca8k-make-rgmii-delay-configurable.patch | 188 +++++++++++++++
 ...ca8k-clear-MASTER_EN-after-phy-read-write.patch |  50 ++++
 ...8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch | 128 ++++++++++
 ...-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch |  39 +++
 ...8k-add-support-for-internal-phy-and-inter.patch | 267 +++++++++++++++++++++
 ...bindings-dsa-qca8k-Document-internal-mdio.patch |  93 +++++++
 ...8k-improve-internal-mdio-read-write-bus-a.patch |  95 ++++++++
 ...8k-pass-switch_revision-info-to-phy-dev_f.patch |  48 ++++
 ...-support-for-qca8k-switch-internal-PHY-in.patch | 229 ++++++++++++++++++
 .../730-net-phy-at803x-fix-feature-detection.patch |   4 +-
 .../707-net-phy-Add-Qualcom-QCA807x-driver.patch   |   6 +-
 26 files changed, 2660 insertions(+), 5 deletions(-)

diff --git a/target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch b/target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch
new file mode 100644
index 0000000000..b8e6d9b613
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch
@@ -0,0 +1,35 @@
+From 5d9e068402dcf7354cc8ee66c2152845306d2ccb Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:51 +0200
+Subject: [PATCH] net: dsa: qca8k: change simple print to dev variant
+
+Change pr_err and pr_warn to dev variant.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -701,7 +701,7 @@ qca8k_setup(struct dsa_switch *ds)
+ 
+ 	/* Make sure that port 0 is the cpu port */
+ 	if (!dsa_is_cpu_port(ds, 0)) {
+-		pr_err("port 0 is not the CPU port\n");
++		dev_err(priv->dev, "port 0 is not the CPU port");
+ 		return -EINVAL;
+ 	}
+ 
+@@ -711,7 +711,7 @@ qca8k_setup(struct dsa_switch *ds)
+ 	priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+ 					&qca8k_regmap_config);
+ 	if (IS_ERR(priv->regmap))
+-		pr_warn("regmap initialization failed");
++		dev_warn(priv->dev, "regmap initialization failed");
+ 
+ 	ret = qca8k_setup_mdio_bus(priv);
+ 	if (ret)
diff --git a/target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch b/target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch
new file mode 100644
index 0000000000..ff8288d484
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch
@@ -0,0 +1,61 @@
+From 2ad255f2faaffb3af786031fba2e7955454b558a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:52 +0200
+Subject: [PATCH] net: dsa: qca8k: use iopoll macro for qca8k_busy_wait
+
+Use iopoll macro instead of while loop.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 23 +++++++++++------------
+ drivers/net/dsa/qca8k.h |  2 ++
+ 2 files changed, 13 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -262,21 +262,20 @@ static struct regmap_config qca8k_regmap
+ static int
+ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+-	unsigned long timeout;
++	u32 val;
++	int ret;
+ 
+-	timeout = jiffies + msecs_to_jiffies(20);
++	ret = read_poll_timeout(qca8k_read, val, !(val & mask),
++				0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++				priv, reg);
+ 
+-	/* loop until the busy flag has cleared */
+-	do {
+-		u32 val = qca8k_read(priv, reg);
+-		int busy = val & mask;
++	/* Check if qca8k_read has failed for a different reason
++	 * before returning -ETIMEDOUT
++	 */
++	if (ret < 0 && val < 0)
++		return val;
+ 
+-		if (!busy)
+-			break;
+-		cond_resched();
+-	} while (!time_after_eq(jiffies, timeout));
+-
+-	return time_after_eq(jiffies, timeout);
++	return ret;
+ }
+ 
+ static void
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -18,6 +18,8 @@
+ #define PHY_ID_QCA8337					0x004dd036
+ #define QCA8K_ID_QCA8337				0x13
+ 
++#define QCA8K_BUSY_WAIT_TIMEOUT				20
++
+ #define QCA8K_NUM_FDB_RECORDS				2048
+ 
+ #define QCA8K_CPU_PORT					0
diff --git a/target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch b/target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch
new file mode 100644
index 0000000000..c403589874
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch
@@ -0,0 +1,86 @@
+From 504bf65931824eda83494e5b5d75686e27ace03e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:53 +0200
+Subject: [PATCH] net: dsa: qca8k: improve qca8k read/write/rmw bus access
+
+Put bus in local variable to improve faster access to the mdio bus.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 29 ++++++++++++++++-------------
+ 1 file changed, 16 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -142,17 +142,18 @@ qca8k_set_page(struct mii_bus *bus, u16
+ static u32
+ qca8k_read(struct qca8k_priv *priv, u32 reg)
+ {
++	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
+ 	u32 val;
+ 
+ 	qca8k_split_addr(reg, &r1, &r2, &page);
+ 
+-	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	qca8k_set_page(priv->bus, page);
+-	val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
++	qca8k_set_page(bus, page);
++	val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ 
+-	mutex_unlock(&priv->bus->mdio_lock);
++	mutex_unlock(&bus->mdio_lock);
+ 
+ 	return val;
+ }
+@@ -160,35 +161,37 @@ qca8k_read(struct qca8k_priv *priv, u32
+ static void
+ qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
++	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
+ 
+ 	qca8k_split_addr(reg, &r1, &r2, &page);
+ 
+-	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	qca8k_set_page(priv->bus, page);
+-	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++	qca8k_set_page(bus, page);
++	qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+ 
+-	mutex_unlock(&priv->bus->mdio_lock);
++	mutex_unlock(&bus->mdio_lock);
+ }
+ 
+ static u32
+ qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val)
+ {
++	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
+ 	u32 ret;
+ 
+ 	qca8k_split_addr(reg, &r1, &r2, &page);
+ 
+-	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	qca8k_set_page(priv->bus, page);
+-	ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
++	qca8k_set_page(bus, page);
++	ret = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ 	ret &= ~mask;
+ 	ret |= val;
+-	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret);
++	qca8k_mii_write32(bus, 0x10 | r2, r1, ret);
+ 
+-	mutex_unlock(&priv->bus->mdio_lock);
++	mutex_unlock(&bus->mdio_lock);
+ 
+ 	return ret;
+ }
diff --git a/target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch b/target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch
new file mode 100644
index 0000000000..6be494a8c7
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch
@@ -0,0 +1,101 @@
+From ba5707ec58cfb6853dff41c2aae72deb6a03d389 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:54 +0200
+Subject: [PATCH] net: dsa: qca8k: handle qca8k_set_page errors
+
+With a remote possibility, the set_page function can fail. Since this is
+a critical part of the write/read qca8k regs, propagate the error and
+terminate any read/write operation.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 33 ++++++++++++++++++++++++++-------
+ 1 file changed, 26 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -127,16 +127,23 @@ qca8k_mii_write32(struct mii_bus *bus, i
+ 				    "failed to write qca8k 32bit register\n");
+ }
+ 
+-static void
++static int
+ qca8k_set_page(struct mii_bus *bus, u16 page)
+ {
++	int ret;
++
+ 	if (page == qca8k_current_page)
+-		return;
++		return 0;
+ 
+-	if (bus->write(bus, 0x18, 0, page) < 0)
++	ret = bus->write(bus, 0x18, 0, page);
++	if (ret < 0) {
+ 		dev_err_ratelimited(&bus->dev,
+ 				    "failed to set qca8k page\n");
++		return ret;
++	}
++
+ 	qca8k_current_page = page;
++	return 0;
+ }
+ 
+ static u32
+@@ -150,11 +157,14 @@ qca8k_read(struct qca8k_priv *priv, u32
+ 
+ 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	qca8k_set_page(bus, page);
++	val = qca8k_set_page(bus, page);
++	if (val < 0)
++		goto exit;
++
+ 	val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ 
++exit:
+ 	mutex_unlock(&bus->mdio_lock);
+-
+ 	return val;
+ }
+ 
+@@ -163,14 +173,19 @@ qca8k_write(struct qca8k_priv *priv, u32
+ {
+ 	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
++	int ret;
+ 
+ 	qca8k_split_addr(reg, &r1, &r2, &page);
+ 
+ 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	qca8k_set_page(bus, page);
++	ret = qca8k_set_page(bus, page);
++	if (ret < 0)
++		goto exit;
++
+ 	qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+ 
++exit:
+ 	mutex_unlock(&bus->mdio_lock);
+ }
+ 
+@@ -185,12 +200,16 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+ 
+ 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	qca8k_set_page(bus, page);
++	ret = qca8k_set_page(bus, page);
++	if (ret < 0)
++		goto exit;
++
+ 	ret = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ 	ret &= ~mask;
+ 	ret |= val;
+ 	qca8k_mii_write32(bus, 0x10 | r2, r1, ret);
+ 
++exit:
+ 	mutex_unlock(&bus->mdio_lock);
+ 
+ 	return ret;
diff --git a/target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch b/target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch
new file mode 100644
index 0000000000..3349b7897a
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch
@@ -0,0 +1,207 @@
+From 028f5f8ef44fcf87a456772cbb9f0d90a0a22884 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:55 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_read operation
+
+qca8k_read can fail. Rework any user to handle error values and
+correctly return.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 73 ++++++++++++++++++++++++++++++++---------
+ 1 file changed, 58 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -231,8 +231,13 @@ static int
+ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++	int ret;
++
++	ret = qca8k_read(priv, reg);
++	if (ret < 0)
++		return ret;
+ 
+-	*val = qca8k_read(priv, reg);
++	*val = ret;
+ 
+ 	return 0;
+ }
+@@ -300,15 +305,20 @@ qca8k_busy_wait(struct qca8k_priv *priv,
+ 	return ret;
+ }
+ 
+-static void
++static int
+ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+ {
+-	u32 reg[4];
++	u32 reg[4], val;
+ 	int i;
+ 
+ 	/* load the ARL table into an array */
+-	for (i = 0; i < 4; i++)
+-		reg[i] = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
++	for (i = 0; i < 4; i++) {
++		val = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
++		if (val < 0)
++			return val;
++
++		reg[i] = val;
++	}
+ 
+ 	/* vid - 83:72 */
+ 	fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
+@@ -323,6 +333,8 @@ qca8k_fdb_read(struct qca8k_priv *priv,
+ 	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;
++
++	return 0;
+ }
+ 
+ static void
+@@ -374,6 +386,8 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ 	/* Check for table full violation when adding an entry */
+ 	if (cmd == QCA8K_FDB_LOAD) {
+ 		reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC);
++		if (reg < 0)
++			return reg;
+ 		if (reg & QCA8K_ATU_FUNC_FULL)
+ 			return -1;
+ 	}
+@@ -388,10 +402,10 @@ qca8k_fdb_next(struct qca8k_priv *priv,
+ 
+ 	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)
+-		qca8k_fdb_read(priv, fdb);
++	if (ret < 0)
++		return ret;
+ 
+-	return ret;
++	return qca8k_fdb_read(priv, fdb);
+ }
+ 
+ static int
+@@ -449,6 +463,8 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ 	/* Check for table full violation when adding an entry */
+ 	if (cmd == QCA8K_VLAN_LOAD) {
+ 		reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1);
++		if (reg < 0)
++			return reg;
+ 		if (reg & QCA8K_VTU_FUNC1_FULL)
+ 			return -ENOMEM;
+ 	}
+@@ -475,6 +491,8 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ 		goto out;
+ 
+ 	reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
++	if (reg < 0)
++		return reg;
+ 	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+ 	reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ 	if (untagged)
+@@ -506,6 +524,8 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ 		goto out;
+ 
+ 	reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0);
++	if (reg < 0)
++		return reg;
+ 	reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+ 	reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
+ 			QCA8K_VTU_FUNC0_EG_MODE_S(port);
+@@ -621,8 +641,11 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ 			    QCA8K_MDIO_MASTER_BUSY))
+ 		return -ETIMEDOUT;
+ 
+-	val = (qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL) &
+-		QCA8K_MDIO_MASTER_DATA_MASK);
++	val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
++	if (val < 0)
++		return val;
++
++	val &= QCA8K_MDIO_MASTER_DATA_MASK;
+ 
+ 	return val;
+ }
+@@ -978,6 +1001,8 @@ qca8k_phylink_mac_link_state(struct dsa_
+ 	u32 reg;
+ 
+ 	reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port));
++	if (reg < 0)
++		return reg;
+ 
+ 	state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
+ 	state->an_complete = state->link;
+@@ -1078,18 +1103,26 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ 	const struct qca8k_mib_desc *mib;
+-	u32 reg, i;
++	u32 reg, i, val;
+ 	u64 hi;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
+ 		mib = &ar8327_mib[i];
+ 		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+ 
+-		data[i] = qca8k_read(priv, reg);
++		val = qca8k_read(priv, reg);
++		if (val < 0)
++			continue;
++
+ 		if (mib->size == 2) {
+ 			hi = qca8k_read(priv, reg + 4);
+-			data[i] |= hi << 32;
++			if (hi < 0)
++				continue;
+ 		}
++
++		data[i] = val;
++		if (mib->size == 2)
++			data[i] |= hi << 32;
+ 	}
+ }
+ 
+@@ -1107,18 +1140,25 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ 	u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
++	int ret = 0;
+ 	u32 reg;
+ 
+ 	mutex_lock(&priv->reg_mutex);
+ 	reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL);
++	if (reg < 0) {
++		ret = reg;
++		goto exit;
++	}
++
+ 	if (eee->eee_enabled)
+ 		reg |= lpi_en;
+ 	else
+ 		reg &= ~lpi_en;
+ 	qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+-	mutex_unlock(&priv->reg_mutex);
+ 
+-	return 0;
++exit:
++	mutex_unlock(&priv->reg_mutex);
++	return ret;
+ }
+ 
+ static int
+@@ -1456,6 +1496,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ 
+ 	/* read the switches ID register */
+ 	id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
++	if (id < 0)
++		return id;
++
+ 	id >>= QCA8K_MASK_CTRL_ID_S;
+ 	id &= QCA8K_MASK_CTRL_ID_M;
+ 	if (id != QCA8K_ID_QCA8337)
diff --git a/target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch b/target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch
new file mode 100644
index 0000000000..1e0e224c39
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch
@@ -0,0 +1,263 @@
+From d7805757c75c76e9518fc1023a29f0c4eed5b581 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:56 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_write operation
+
+qca8k_write can fail. Rework any user to handle error values and
+correctly return.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 102 ++++++++++++++++++++++++++--------------
+ 1 file changed, 67 insertions(+), 35 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -168,7 +168,7 @@ exit:
+ 	return val;
+ }
+ 
+-static void
++static int
+ qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+ 	struct mii_bus *bus = priv->bus;
+@@ -187,6 +187,7 @@ qca8k_write(struct qca8k_priv *priv, u32
+ 
+ exit:
+ 	mutex_unlock(&bus->mdio_lock);
++	return ret;
+ }
+ 
+ static u32
+@@ -247,9 +248,7 @@ qca8k_regmap_write(void *ctx, uint32_t r
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ 
+-	qca8k_write(priv, reg, val);
+-
+-	return 0;
++	return qca8k_write(priv, reg, val);
+ }
+ 
+ static const struct regmap_range qca8k_readable_ranges[] = {
+@@ -367,6 +366,7 @@ 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;
+@@ -377,7 +377,9 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ 	}
+ 
+ 	/* Write the function register triggering the table access */
+-	qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++	ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++	if (ret)
++		return ret;
+ 
+ 	/* wait for completion */
+ 	if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY))
+@@ -447,6 +449,7 @@ 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;
+@@ -454,7 +457,9 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ 	reg |= vid << QCA8K_VTU_FUNC1_VID_S;
+ 
+ 	/* Write the function register triggering the table access */
+-	qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++	if (ret)
++		return ret;
+ 
+ 	/* wait for completion */
+ 	if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY))
+@@ -502,7 +507,9 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ 		reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG <<
+ 				QCA8K_VTU_FUNC0_EG_MODE_S(port);
+ 
+-	qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++	if (ret)
++		return ret;
+ 	ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+ 
+ out:
+@@ -545,7 +552,9 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ 	if (del) {
+ 		ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+ 	} else {
+-		qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++		ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++		if (ret)
++			return ret;
+ 		ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+ 	}
+ 
+@@ -555,15 +564,20 @@ out:
+ 	return ret;
+ }
+ 
+-static void
++static int
+ qca8k_mib_init(struct qca8k_priv *priv)
+ {
++	int ret;
++
+ 	mutex_lock(&priv->reg_mutex);
+ 	qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
+ 	qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+ 	qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+-	qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
++	ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
+ 	mutex_unlock(&priv->reg_mutex);
++	return ret;
+ }
+ 
+ static void
+@@ -600,6 +614,7 @@ static int
+ qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
+ {
+ 	u32 phy, val;
++	int ret;
+ 
+ 	if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ 		return -EINVAL;
+@@ -613,7 +628,9 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ 	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+ 	      QCA8K_MDIO_MASTER_DATA(data);
+ 
+-	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++	ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++	if (ret)
++		return ret;
+ 
+ 	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+ 		QCA8K_MDIO_MASTER_BUSY);
+@@ -623,6 +640,7 @@ static int
+ qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
+ {
+ 	u32 phy, val;
++	int ret;
+ 
+ 	if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ 		return -EINVAL;
+@@ -635,7 +653,9 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ 	      QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ 	      QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+ 
+-	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++	ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++	if (ret)
++		return ret;
+ 
+ 	if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+ 			    QCA8K_MDIO_MASTER_BUSY))
+@@ -766,12 +786,18 @@ qca8k_setup(struct dsa_switch *ds)
+ 		      QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+ 
+ 	/* Enable MIB counters */
+-	qca8k_mib_init(priv);
++	ret = qca8k_mib_init(priv);
++	if (ret)
++		dev_warn(priv->dev, "mib init failed");
+ 
+ 	/* Enable QCA header mode on the cpu port */
+-	qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
+-		    QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+-		    QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++	ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
++			  QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
++			  QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++	if (ret) {
++		dev_err(priv->dev, "failed enabling QCA header mode");
++		return ret;
++	}
+ 
+ 	/* Disable forwarding by default on all ports */
+ 	for (i = 0; i < QCA8K_NUM_PORTS; i++)
+@@ -783,11 +809,13 @@ qca8k_setup(struct dsa_switch *ds)
+ 		qca8k_port_set_status(priv, i, 0);
+ 
+ 	/* Forward all unknown frames to CPU port for Linux processing */
+-	qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+-		    BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+-		    BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+-		    BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+-		    BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++	ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
++			  BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
++			  BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
++			  BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
++			  BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++	if (ret)
++		return ret;
+ 
+ 	/* Setup connection between CPU port & user ports */
+ 	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+@@ -815,16 +843,20 @@ qca8k_setup(struct dsa_switch *ds)
+ 			qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+ 				  0xfff << shift,
+ 				  QCA8K_PORT_VID_DEF << shift);
+-			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 = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
++					  QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
++					  QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
++			if (ret)
++				return ret;
+ 		}
+ 	}
+ 
+ 	/* Setup our port MTUs to match power on defaults */
+ 	for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ 		priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+-	qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
++	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
++	if (ret)
++		dev_warn(priv->dev, "failed setting MTU settings");
+ 
+ 	/* Flush the FDB table */
+ 	qca8k_fdb_flush(priv);
+@@ -1140,8 +1172,8 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ 	u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
+-	int ret = 0;
+ 	u32 reg;
++	int ret;
+ 
+ 	mutex_lock(&priv->reg_mutex);
+ 	reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL);
+@@ -1154,7 +1186,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds,
+ 		reg |= lpi_en;
+ 	else
+ 		reg &= ~lpi_en;
+-	qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
++	ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+ 
+ exit:
+ 	mutex_unlock(&priv->reg_mutex);
+@@ -1284,9 +1316,7 @@ qca8k_port_change_mtu(struct dsa_switch
+ 			mtu = priv->port_mtu[i];
+ 
+ 	/* Include L2 header / FCS length */
+-	qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
+-
+-	return 0;
++	return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
+ }
+ 
+ static int
diff --git a/target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch b/target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch
new file mode 100644
index 0000000000..506966f1af
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch
@@ -0,0 +1,226 @@
+From aaf421425cbdec4eb6fd75a29e65c2867b0b7bbd Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:57 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_rmw operation
+
+qca8k_rmw can fail. Rework any user to handle error values and
+correctly return. Change qca8k_rmw to return the error code or 0 instead
+of the reg value. The reg returned by qca8k_rmw wasn't used anywhere,
+so this doesn't cause any functional change.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 133 +++++++++++++++++++++++++---------------
+ 1 file changed, 83 insertions(+), 50 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -190,12 +190,13 @@ exit:
+ 	return ret;
+ }
+ 
+-static u32
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val)
++static int
++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+ {
+ 	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
+-	u32 ret;
++	u32 val;
++	int ret;
+ 
+ 	qca8k_split_addr(reg, &r1, &r2, &page);
+ 
+@@ -205,10 +206,15 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+ 	if (ret < 0)
+ 		goto exit;
+ 
+-	ret = qca8k_mii_read32(bus, 0x10 | r2, r1);
+-	ret &= ~mask;
+-	ret |= val;
+-	qca8k_mii_write32(bus, 0x10 | r2, r1, ret);
++	val = qca8k_mii_read32(bus, 0x10 | r2, r1);
++	if (val < 0) {
++		ret = val;
++		goto exit;
++	}
++
++	val &= ~mask;
++	val |= write_val;
++	qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+ 
+ exit:
+ 	mutex_unlock(&bus->mdio_lock);
+@@ -216,16 +222,16 @@ exit:
+ 	return ret;
+ }
+ 
+-static void
++static int
+ qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+-	qca8k_rmw(priv, reg, 0, val);
++	return qca8k_rmw(priv, reg, 0, val);
+ }
+ 
+-static void
++static int
+ qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
+ {
+-	qca8k_rmw(priv, reg, val, 0);
++	return qca8k_rmw(priv, reg, val, 0);
+ }
+ 
+ static int
+@@ -570,12 +576,19 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ 	int ret;
+ 
+ 	mutex_lock(&priv->reg_mutex);
+-	qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++	if (ret)
++		goto exit;
++
+ 	qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+-	qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++
++	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++	if (ret)
++		goto exit;
+ 
+ 	ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+ 
++exit:
+ 	mutex_unlock(&priv->reg_mutex);
+ 	return ret;
+ }
+@@ -747,9 +760,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ 		 * a dt-overlay and driver reload changed the configuration
+ 		 */
+ 
+-		qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+-				QCA8K_MDIO_MASTER_EN);
+-		return 0;
++		return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
++				       QCA8K_MDIO_MASTER_EN);
+ 	}
+ 
+ 	priv->ops.phy_read = qca8k_phy_read;
+@@ -782,8 +794,12 @@ qca8k_setup(struct dsa_switch *ds)
+ 		return ret;
+ 
+ 	/* Enable CPU Port */
+-	qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+-		      QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++	ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
++			    QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++	if (ret) {
++		dev_err(priv->dev, "failed enabling CPU port");
++		return ret;
++	}
+ 
+ 	/* Enable MIB counters */
+ 	ret = qca8k_mib_init(priv);
+@@ -800,9 +816,12 @@ qca8k_setup(struct dsa_switch *ds)
+ 	}
+ 
+ 	/* Disable forwarding by default on all ports */
+-	for (i = 0; i < QCA8K_NUM_PORTS; i++)
+-		qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+-			  QCA8K_PORT_LOOKUP_MEMBER, 0);
++	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++				QCA8K_PORT_LOOKUP_MEMBER, 0);
++		if (ret)
++			return ret;
++	}
+ 
+ 	/* Disable MAC by default on all ports */
+ 	for (i = 1; i < QCA8K_NUM_PORTS; i++)
+@@ -821,28 +840,37 @@ 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)) {
+-			qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
+-				  QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
++			ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
++					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)) {
+ 			int shift = 16 * (i % 2);
+ 
+-			qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+-				  QCA8K_PORT_LOOKUP_MEMBER,
+-				  BIT(QCA8K_CPU_PORT));
++			ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++					QCA8K_PORT_LOOKUP_MEMBER,
++					BIT(QCA8K_CPU_PORT));
++			if (ret)
++				return ret;
+ 
+ 			/* Enable ARP Auto-learning by default */
+-			qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+-				      QCA8K_PORT_LOOKUP_LEARN);
++			ret = qca8k_reg_set(priv, 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
+ 			 */
+-			qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+-				  0xfff << shift,
+-				  QCA8K_PORT_VID_DEF << shift);
++			ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
++					0xfff << shift,
++					QCA8K_PORT_VID_DEF << shift);
++			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));
+@@ -1234,7 +1262,7 @@ qca8k_port_bridge_join(struct dsa_switch
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ 	int port_mask = BIT(QCA8K_CPU_PORT);
+-	int i;
++	int i, ret;
+ 
+ 	for (i = 1; i < QCA8K_NUM_PORTS; i++) {
+ 		if (dsa_to_port(ds, i)->bridge_dev != br)
+@@ -1242,17 +1270,20 @@ qca8k_port_bridge_join(struct dsa_switch
+ 		/* Add this port to the portvlan mask of the other ports
+ 		 * in the bridge
+ 		 */
+-		qca8k_reg_set(priv,
+-			      QCA8K_PORT_LOOKUP_CTRL(i),
+-			      BIT(port));
++		ret = qca8k_reg_set(priv,
++				    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 */
+-	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+-		  QCA8K_PORT_LOOKUP_MEMBER, port_mask);
++	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++			QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+ 
+-	return 0;
++	return ret;
+ }
+ 
+ static void
diff --git a/target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch b/target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch
new file mode 100644
index 0000000000..360ce1d947
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch
@@ -0,0 +1,66 @@
+From b7c818d194927bdc60ed15db55bb8654496a36b7 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:58 +0200
+Subject: [PATCH] net: dsa: qca8k: handle error from qca8k_busy_wait
+
+Propagate errors from qca8k_busy_wait instead of hardcoding return
+value.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -388,8 +388,9 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ 		return ret;
+ 
+ 	/* wait for completion */
+-	if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY))
+-		return -1;
++	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) {
+@@ -468,8 +469,9 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ 		return ret;
+ 
+ 	/* wait for completion */
+-	if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY))
+-		return -ETIMEDOUT;
++	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) {
+@@ -580,7 +582,9 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ 	if (ret)
+ 		goto exit;
+ 
+-	qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++	ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++	if (ret)
++		goto exit;
+ 
+ 	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+ 	if (ret)
+@@ -670,9 +674,10 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ 	if (ret)
+ 		return ret;
+ 
+-	if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+-			    QCA8K_MDIO_MASTER_BUSY))
+-		return -ETIMEDOUT;
++	ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++			      QCA8K_MDIO_MASTER_BUSY);
++	if (ret)
++		return ret;
+ 
+ 	val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
+ 	if (val < 0)
diff --git a/target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch b/target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch
new file mode 100644
index 0000000000..72305850ca
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch
@@ -0,0 +1,96 @@
+From 6e82a457e06252b59102486767539cc9c2aba60b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 22:59:59 +0200
+Subject: [PATCH] net: dsa: qca8k: add support for qca8327 switch
+
+qca8327 switch is a low tier version of the more recent qca8337.
+It does share the same regs used by the qca8k driver and can be
+supported with minimal change.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Reviewed-by: Vladimir Oltean <olteanv at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++---
+ drivers/net/dsa/qca8k.h |  6 ++++++
+ 2 files changed, 26 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1533,6 +1533,7 @@ static const struct dsa_switch_ops qca8k
+ static int
+ qca8k_sw_probe(struct mdio_device *mdiodev)
+ {
++	const struct qca8k_match_data *data;
+ 	struct qca8k_priv *priv;
+ 	u32 id;
+ 
+@@ -1560,6 +1561,11 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ 		gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ 	}
+ 
++	/* get the switches ID from the compatible */
++	data = of_device_get_match_data(&mdiodev->dev);
++	if (!data)
++		return -ENODEV;
++
+ 	/* read the switches ID register */
+ 	id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
+ 	if (id < 0)
+@@ -1567,8 +1573,10 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ 
+ 	id >>= QCA8K_MASK_CTRL_ID_S;
+ 	id &= QCA8K_MASK_CTRL_ID_M;
+-	if (id != QCA8K_ID_QCA8337)
++	if (id != data->id) {
++		dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id);
+ 		return -ENODEV;
++	}
+ 
+ 	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ 	if (!priv->ds)
+@@ -1634,9 +1642,18 @@ static int qca8k_resume(struct device *d
+ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ 			 qca8k_suspend, qca8k_resume);
+ 
++static const struct qca8k_match_data qca832x = {
++	.id = QCA8K_ID_QCA8327,
++};
++
++static const struct qca8k_match_data qca833x = {
++	.id = QCA8K_ID_QCA8337,
++};
++
+ static const struct of_device_id qca8k_of_match[] = {
+-	{ .compatible = "qca,qca8334" },
+-	{ .compatible = "qca,qca8337" },
++	{ .compatible = "qca,qca8327", .data = &qca832x },
++	{ .compatible = "qca,qca8334", .data = &qca833x },
++	{ .compatible = "qca,qca8337", .data = &qca833x },
+ 	{ /* sentinel */ },
+ };
+ 
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -15,6 +15,8 @@
+ #define QCA8K_NUM_PORTS					7
+ #define QCA8K_MAX_MTU					9000
+ 
++#define PHY_ID_QCA8327					0x004dd034
++#define QCA8K_ID_QCA8327				0x12
+ #define PHY_ID_QCA8337					0x004dd036
+ #define QCA8K_ID_QCA8337				0x13
+ 
+@@ -213,6 +215,10 @@ struct ar8xxx_port_status {
+ 	int enabled;
+ };
+ 
++struct qca8k_match_data {
++	u8 id;
++};
++
+ struct qca8k_priv {
+ 	struct regmap *regmap;
+ 	struct mii_bus *bus;
diff --git a/target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch b/target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch
new file mode 100644
index 0000000000..3c4a14bd0b
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch
@@ -0,0 +1,26 @@
+From 227a9ffc1bc77037339530607fe129af3824620e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:00 +0200
+Subject: [PATCH] devicetree: net: dsa: qca8k: Document new compatible qca8327
+
+Add support for qca8327 in the compatible list.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Acked-by: Rob Herring <robh at kernel.org>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -3,6 +3,7 @@
+ Required properties:
+ 
+ - compatible: should be one of:
++    "qca,qca8327"
+     "qca,qca8334"
+     "qca,qca8337"
+ 
diff --git a/target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch b/target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch
new file mode 100644
index 0000000000..cd3050ef71
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch
@@ -0,0 +1,130 @@
+From 83a3ceb39b2495171aabe9446271b94c678354f3 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:01 +0200
+Subject: [PATCH] net: dsa: qca8k: add priority tweak to qca8337 switch
+
+The port 5 of the qca8337 have some problem in flood condition. The
+original legacy driver had some specific buffer and priority settings
+for the different port suggested by the QCA switch team. Add this
+missing settings to improve switch stability under load condition.
+The packet priority tweak is only needed for the qca8337 switch and
+other qca8k switch are not affected.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 47 +++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 25 ++++++++++++++++++++++
+ 2 files changed, 72 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -779,6 +779,7 @@ qca8k_setup(struct dsa_switch *ds)
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ 	int ret, i;
++	u32 mask;
+ 
+ 	/* Make sure that port 0 is the cpu port */
+ 	if (!dsa_is_cpu_port(ds, 0)) {
+@@ -884,6 +885,51 @@ qca8k_setup(struct dsa_switch *ds)
+ 		}
+ 	}
+ 
++	/* The port 5 of the qca8337 have some problem in flood condition. The
++	 * original legacy driver had some specific buffer and priority settings
++	 * for the different port suggested by the QCA switch team. Add this
++	 * missing settings to improve switch stability under load condition.
++	 * This problem is limited to qca8337 and other qca8k switch are not affected.
++	 */
++	if (priv->switch_id == QCA8K_ID_QCA8337) {
++		for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++			switch (i) {
++			/* The 2 CPU port and port 5 requires some different
++			 * priority than any other ports.
++			 */
++			case 0:
++			case 5:
++			case 6:
++				mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
++					QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
++				break;
++			default:
++				mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
++					QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
++					QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
++			}
++			qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
++
++			mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
++			QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++			QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++			QCA8K_PORT_HOL_CTRL1_WRED_EN;
++			qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
++				  QCA8K_PORT_HOL_CTRL1_ING_BUF |
++				  QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++				  QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++				  QCA8K_PORT_HOL_CTRL1_WRED_EN,
++				  mask);
++		}
++	}
++
+ 	/* Setup our port MTUs to match power on defaults */
+ 	for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ 		priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+@@ -1578,6 +1624,7 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ 		return -ENODEV;
+ 	}
+ 
++	priv->switch_id = id;
+ 	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ 	if (!priv->ds)
+ 		return -ENOMEM;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -168,6 +168,30 @@
+ #define   QCA8K_PORT_LOOKUP_STATE			GENMASK(18, 16)
+ #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20)
+ 
++#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_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_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_EGRESS_VLAN(x)				(0x0c70 + (4 * (x / 2)))
+ 
+@@ -220,6 +244,7 @@ struct qca8k_match_data {
+ };
+ 
+ struct qca8k_priv {
++	u8 switch_id;
+ 	struct regmap *regmap;
+ 	struct mii_bus *bus;
+ 	struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
diff --git a/target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch b/target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch
new file mode 100644
index 0000000000..d25edbb1aa
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch
@@ -0,0 +1,31 @@
+From 5bf9ff3b9fb5ecb67a1a3517b26db3a00f2a2f11 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:02 +0200
+Subject: [PATCH] net: dsa: qca8k: limit port5 delay to qca8337
+
+Limit port5 rx delay to qca8337. This is taken from the legacy QSDK code
+that limits the rx delay on port5 to only this particular switch version,
+on other switch only the tx and rx delay for port0 are needed.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1003,8 +1003,10 @@ qca8k_phylink_mac_config(struct dsa_swit
+ 			    QCA8K_PORT_PAD_RGMII_EN |
+ 			    QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) |
+ 			    QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY));
+-		qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+-			    QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
++		/* QCA8337 requires to set rgmii rx delay */
++		if (priv->switch_id == QCA8K_ID_QCA8337)
++			qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
++				    QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+ 		break;
+ 	case PHY_INTERFACE_MODE_SGMII:
+ 	case PHY_INTERFACE_MODE_1000BASEX:
diff --git a/target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch b/target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch
new file mode 100644
index 0000000000..2b393d242a
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch
@@ -0,0 +1,48 @@
+From 0fc57e4b5e39461fc0a54aae0afe4241363a7267 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:03 +0200
+Subject: [PATCH] net: dsa: qca8k: add GLOBAL_FC settings needed for qca8327
+
+Switch qca8327 needs special settings for the GLOBAL_FC_THRES regs.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 10 ++++++++++
+ drivers/net/dsa/qca8k.h |  6 ++++++
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -930,6 +930,16 @@ qca8k_setup(struct dsa_switch *ds)
+ 		}
+ 	}
+ 
++	/* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
++	if (priv->switch_id == QCA8K_ID_QCA8327) {
++		mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
++		       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,
++			  mask);
++	}
++
+ 	/* Setup our port MTUs to match power on defaults */
+ 	for (i = 0; i < QCA8K_NUM_PORTS; i++)
+ 		priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -168,6 +168,12 @@
+ #define   QCA8K_PORT_LOOKUP_STATE			GENMASK(18, 16)
+ #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_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)
diff --git a/target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch b/target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch
new file mode 100644
index 0000000000..ed9b8188de
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch
@@ -0,0 +1,114 @@
+From 95ffeaf18b3bb90eeef52cbf7d79ccc9d0345ff5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:04 +0200
+Subject: [PATCH] net: dsa: qca8k: add support for switch rev
+
+qca8k internal phy driver require some special debug value to be set
+based on the switch revision. Rework the switch id read function to
+also read the chip revision.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 53 ++++++++++++++++++++++++++---------------
+ drivers/net/dsa/qca8k.h |  7 ++++--
+ 2 files changed, 39 insertions(+), 21 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1588,12 +1588,40 @@ static const struct dsa_switch_ops qca8k
+ 	.phylink_mac_link_up	= qca8k_phylink_mac_link_up,
+ };
+ 
++static int qca8k_read_switch_id(struct qca8k_priv *priv)
++{
++	const struct qca8k_match_data *data;
++	u32 val;
++	u8 id;
++
++	/* get the switches ID from the compatible */
++	data = of_device_get_match_data(priv->dev);
++	if (!data)
++		return -ENODEV;
++
++	val = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
++	if (val < 0)
++		return -ENODEV;
++
++	id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
++	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 = (val & QCA8K_MASK_CTRL_REV_ID_MASK);
++
++	return 0;
++}
++
+ static int
+ qca8k_sw_probe(struct mdio_device *mdiodev)
+ {
+-	const struct qca8k_match_data *data;
+ 	struct qca8k_priv *priv;
+-	u32 id;
++	int ret;
+ 
+ 	/* allocate the private data struct so that we can probe the switches
+ 	 * ID register
+@@ -1619,24 +1647,11 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ 		gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ 	}
+ 
+-	/* get the switches ID from the compatible */
+-	data = of_device_get_match_data(&mdiodev->dev);
+-	if (!data)
+-		return -ENODEV;
++	/* Check the detected switch id */
++	ret = qca8k_read_switch_id(priv);
++	if (ret)
++		return ret;
+ 
+-	/* read the switches ID register */
+-	id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
+-	if (id < 0)
+-		return id;
+-
+-	id >>= QCA8K_MASK_CTRL_ID_S;
+-	id &= QCA8K_MASK_CTRL_ID_M;
+-	if (id != data->id) {
+-		dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id);
+-		return -ENODEV;
+-	}
+-
+-	priv->switch_id = id;
+ 	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+ 	if (!priv->ds)
+ 		return -ENOMEM;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -30,8 +30,10 @@
+ 
+ /* Global control registers */
+ #define QCA8K_REG_MASK_CTRL				0x000
+-#define   QCA8K_MASK_CTRL_ID_M				0xff
+-#define   QCA8K_MASK_CTRL_ID_S				8
++#define   QCA8K_MASK_CTRL_REV_ID_MASK			GENMASK(7, 0)
++#define   QCA8K_MASK_CTRL_REV_ID(x)			((x) >> 0)
++#define   QCA8K_MASK_CTRL_DEVICE_ID_MASK		GENMASK(15, 8)
++#define   QCA8K_MASK_CTRL_DEVICE_ID(x)			((x) >> 8)
+ #define QCA8K_REG_PORT0_PAD_CTRL			0x004
+ #define QCA8K_REG_PORT5_PAD_CTRL			0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL			0x00c
+@@ -251,6 +253,7 @@ struct qca8k_match_data {
+ 
+ struct qca8k_priv {
+ 	u8 switch_id;
++	u8 switch_revision;
+ 	struct regmap *regmap;
+ 	struct mii_bus *bus;
+ 	struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
diff --git a/target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch b/target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch
new file mode 100644
index 0000000000..629cb324e0
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch
@@ -0,0 +1,28 @@
+From 1ee0591a1093c2448642c33433483e9260275f7b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:05 +0200
+Subject: [PATCH] net: dsa: qca8k: add ethernet-ports fallback to
+ setup_mdio_bus
+
+Dsa now also supports ethernet-ports. Add this new binding as a fallback
+if the ports node can't be found.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -719,6 +719,9 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ 
+ 	ports = of_get_child_by_name(priv->dev->of_node, "ports");
+ 	if (!ports)
++		ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
++
++	if (!ports)
+ 		return -EINVAL;
+ 
+ 	for_each_available_child_of_node(ports, port) {
diff --git a/target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch b/target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch
new file mode 100644
index 0000000000..6dc2dc6e3e
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch
@@ -0,0 +1,188 @@
+From e4b9977cee1583da38a6e9118078bb728aaccf7b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:06 +0200
+Subject: [PATCH] net: dsa: qca8k: make rgmii delay configurable
+
+The legacy qsdk code used a different delay instead of the max value.
+Qsdk use 1 ns for rx and 2 ns for tx. Make these values configurable
+using the standard rx/tx-internal-delay-ps ethernet binding and apply
+qsdk values by default. The connected gmac doesn't add any delay so no
+additional delay is added to tx/rx.
+On this switch the delay is actually in ns so value should be in the
+1000 order. Any value converted from ps to ns by dividing it by 1000
+as the switch max value for delay is 3ns.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 82 ++++++++++++++++++++++++++++++++++++++++-
+ drivers/net/dsa/qca8k.h | 11 +++---
+ 2 files changed, 86 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -778,6 +778,68 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ }
+ 
+ static int
++qca8k_setup_of_rgmii_delay(struct qca8k_priv *priv)
++{
++	struct device_node *port_dn;
++	phy_interface_t mode;
++	struct dsa_port *dp;
++	u32 val;
++
++	/* CPU port is already checked */
++	dp = dsa_to_port(priv->ds, 0);
++
++	port_dn = dp->dn;
++
++	/* Check if port 0 is set to the correct type */
++	of_get_phy_mode(port_dn, &mode);
++	if (mode != PHY_INTERFACE_MODE_RGMII_ID &&
++	    mode != PHY_INTERFACE_MODE_RGMII_RXID &&
++	    mode != PHY_INTERFACE_MODE_RGMII_TXID) {
++		return 0;
++	}
++
++	switch (mode) {
++	case PHY_INTERFACE_MODE_RGMII_ID:
++	case PHY_INTERFACE_MODE_RGMII_RXID:
++		if (of_property_read_u32(port_dn, "rx-internal-delay-ps", &val))
++			val = 2;
++		else
++			/* Switch regs accept value in ns, convert ps to ns */
++			val = val / 1000;
++
++		if (val > QCA8K_MAX_DELAY) {
++			dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
++			val = 3;
++		}
++
++		priv->rgmii_rx_delay = val;
++		/* Stop here if we need to check only for rx delay */
++		if (mode != PHY_INTERFACE_MODE_RGMII_ID)
++			break;
++
++		fallthrough;
++	case PHY_INTERFACE_MODE_RGMII_TXID:
++		if (of_property_read_u32(port_dn, "tx-internal-delay-ps", &val))
++			val = 1;
++		else
++			/* Switch regs accept value in ns, convert ps to ns */
++			val = val / 1000;
++
++		if (val > QCA8K_MAX_DELAY) {
++			dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
++			val = 3;
++		}
++
++		priv->rgmii_tx_delay = val;
++		break;
++	default:
++		return 0;
++	}
++
++	return 0;
++}
++
++static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ 	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+@@ -802,6 +864,10 @@ qca8k_setup(struct dsa_switch *ds)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = qca8k_setup_of_rgmii_delay(priv);
++	if (ret)
++		return ret;
++
+ 	/* Enable CPU Port */
+ 	ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ 			    QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+@@ -970,6 +1036,8 @@ qca8k_phylink_mac_config(struct dsa_swit
+ 	case 0: /* 1st CPU port */
+ 		if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+ 		    state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ 		    state->interface != PHY_INTERFACE_MODE_SGMII)
+ 			return;
+ 
+@@ -985,6 +1053,8 @@ qca8k_phylink_mac_config(struct dsa_swit
+ 	case 6: /* 2nd CPU port / external PHY */
+ 		if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+ 		    state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ 		    state->interface != PHY_INTERFACE_MODE_SGMII &&
+ 		    state->interface != PHY_INTERFACE_MODE_1000BASEX)
+ 			return;
+@@ -1008,14 +1078,18 @@ qca8k_phylink_mac_config(struct dsa_swit
+ 		qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+ 		break;
+ 	case PHY_INTERFACE_MODE_RGMII_ID:
++	case PHY_INTERFACE_MODE_RGMII_TXID:
++	case PHY_INTERFACE_MODE_RGMII_RXID:
+ 		/* RGMII_ID needs internal delay. This is enabled through
+ 		 * PORT5_PAD_CTRL for all ports, rather than individual port
+ 		 * registers
+ 		 */
+ 		qca8k_write(priv, reg,
+ 			    QCA8K_PORT_PAD_RGMII_EN |
+-			    QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) |
+-			    QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY));
++			    QCA8K_PORT_PAD_RGMII_TX_DELAY(priv->rgmii_tx_delay) |
++			    QCA8K_PORT_PAD_RGMII_RX_DELAY(priv->rgmii_rx_delay) |
++			    QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
++			    QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+ 		/* QCA8337 requires to set rgmii rx delay */
+ 		if (priv->switch_id == QCA8K_ID_QCA8337)
+ 			qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+@@ -1073,6 +1147,8 @@ qca8k_phylink_validate(struct dsa_switch
+ 		if (state->interface != PHY_INTERFACE_MODE_NA &&
+ 		    state->interface != PHY_INTERFACE_MODE_RGMII &&
+ 		    state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ 		    state->interface != PHY_INTERFACE_MODE_SGMII)
+ 			goto unsupported;
+ 		break;
+@@ -1090,6 +1166,8 @@ qca8k_phylink_validate(struct dsa_switch
+ 		if (state->interface != PHY_INTERFACE_MODE_NA &&
+ 		    state->interface != PHY_INTERFACE_MODE_RGMII &&
+ 		    state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++		    state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+ 		    state->interface != PHY_INTERFACE_MODE_SGMII &&
+ 		    state->interface != PHY_INTERFACE_MODE_1000BASEX)
+ 			goto unsupported;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -38,12 +38,11 @@
+ #define QCA8K_REG_PORT5_PAD_CTRL			0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL			0x00c
+ #define   QCA8K_PORT_PAD_RGMII_EN			BIT(26)
+-#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)		\
+-						((0x8 + (x & 0x3)) << 22)
+-#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)		\
+-						((0x10 + (x & 0x3)) << 20)
+-#define   QCA8K_MAX_DELAY				3
++#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)		((x) << 22)
++#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)		((x) << 20)
++#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_SERDES_AEN_DIS			BIT(7)
+@@ -254,6 +253,8 @@ struct qca8k_match_data {
+ struct qca8k_priv {
+ 	u8 switch_id;
+ 	u8 switch_revision;
++	u8 rgmii_tx_delay;
++	u8 rgmii_rx_delay;
+ 	struct regmap *regmap;
+ 	struct mii_bus *bus;
+ 	struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
diff --git a/target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch b/target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch
new file mode 100644
index 0000000000..4593da032b
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch
@@ -0,0 +1,50 @@
+From 63c33bbfeb6842a956a0eb12901e28eb335bdb18 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:07 +0200
+Subject: [PATCH] net: dsa: qca8k: clear MASTER_EN after phy read/write
+
+Clear MDIO_MASTER_EN bit from MDIO_MASTER_CTRL after read/write
+operation. The MDIO_MASTER_EN bit is not reset after read/write
+operation and the next operation can be wrongly interpreted by the
+switch as a mdio operation. This cause a production of wrong/garbage
+data from the switch and underfined bheavior. (random port drop,
+unplugged port flagged with link up, wrong port speed)
+Also on driver remove the MASTER_CTRL can be left set and cause the
+malfunction of any next driver using the mdio device.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -649,8 +649,14 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ 	if (ret)
+ 		return ret;
+ 
+-	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+-		QCA8K_MDIO_MASTER_BUSY);
++	ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++			      QCA8K_MDIO_MASTER_BUSY);
++
++	/* even if the busy_wait timeouts try to clear the MASTER_EN */
++	qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
++			QCA8K_MDIO_MASTER_EN);
++
++	return ret;
+ }
+ 
+ static int
+@@ -685,6 +691,10 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ 
+ 	val &= QCA8K_MDIO_MASTER_DATA_MASK;
+ 
++	/* even if the busy_wait timeouts try to clear the MASTER_EN */
++	qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
++			QCA8K_MDIO_MASTER_EN);
++
+ 	return val;
+ }
+ 
diff --git a/target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch b/target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch
new file mode 100644
index 0000000000..b6684d7210
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch
@@ -0,0 +1,128 @@
+From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:08 +0200
+Subject: [PATCH] net: dsa: qca8k: dsa: qca8k: protect MASTER busy_wait with
+ mdio mutex
+
+MDIO_MASTER operation have a dedicated busy wait that is not protected
+by the mdio mutex. This can cause situation where the MASTER operation
+is done and a normal operation is executed between the MASTER read/write
+and the MASTER busy_wait. Rework the qca8k_mdio_read/write function to
+address this issue by binding the lock for the whole MASTER operation
+and not only the mdio read/write common operation.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 68 +++++++++++++++++++++++++++++++++--------
+ 1 file changed, 55 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -628,8 +628,31 @@ qca8k_port_to_phy(int port)
+ }
+ 
+ static int
++qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++{
++	u16 r1, r2, page;
++	u32 val;
++	int ret;
++
++	qca8k_split_addr(reg, &r1, &r2, &page);
++
++	ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0,
++				QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++				priv->bus, 0x10 | r2, r1);
++
++	/* Check if qca8k_read has failed for a different reason
++	 * before returnting -ETIMEDOUT
++	 */
++	if (ret < 0 && val < 0)
++		return val;
++
++	return ret;
++}
++
++static int
+ qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
+ {
++	u16 r1, r2, page;
+ 	u32 phy, val;
+ 	int ret;
+ 
+@@ -645,12 +668,21 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ 	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+ 	      QCA8K_MDIO_MASTER_DATA(data);
+ 
+-	ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
++	qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++	ret = qca8k_set_page(priv->bus, page);
+ 	if (ret)
+-		return ret;
++		goto exit;
++
++	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+ 
+-	ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+-			      QCA8K_MDIO_MASTER_BUSY);
++	ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++				   QCA8K_MDIO_MASTER_BUSY);
++
++exit:
++	mutex_unlock(&priv->bus->mdio_lock);
+ 
+ 	/* even if the busy_wait timeouts try to clear the MASTER_EN */
+ 	qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+@@ -662,6 +694,7 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ static int
+ qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
+ {
++	u16 r1, r2, page;
+ 	u32 phy, val;
+ 	int ret;
+ 
+@@ -676,21 +709,30 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ 	      QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ 	      QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+ 
+-	ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
+-	if (ret)
+-		return ret;
++	qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+-			      QCA8K_MDIO_MASTER_BUSY);
++	ret = qca8k_set_page(priv->bus, page);
+ 	if (ret)
+-		return ret;
++		goto exit;
+ 
+-	val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
+-	if (val < 0)
+-		return val;
++	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++
++	ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++				   QCA8K_MDIO_MASTER_BUSY);
++	if (ret)
++		goto exit;
+ 
++	val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
+ 	val &= QCA8K_MDIO_MASTER_DATA_MASK;
+ 
++exit:
++	mutex_unlock(&priv->bus->mdio_lock);
++
++	if (val >= 0)
++		val &= QCA8K_MDIO_MASTER_DATA_MASK;
++
+ 	/* even if the busy_wait timeouts try to clear the MASTER_EN */
+ 	qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+ 			QCA8K_MDIO_MASTER_EN);
diff --git a/target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch b/target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch
new file mode 100644
index 0000000000..30eeed361e
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch
@@ -0,0 +1,39 @@
+From 617960d72e93de0f3fa52407e2d39e8c43e73b0a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:09 +0200
+Subject: [PATCH] net: dsa: qca8k: enlarge mdio delay and timeout
+
+The witch require some extra delay after setting page or the next
+read/write can use still use the old page. Add a delay after the
+set_page function to address this as it's done in QSDK legacy driver.
+Some timeouts were notice with VLAN and phy function, enlarge the
+mdio busy wait timeout to fix these problems.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 1 +
+ drivers/net/dsa/qca8k.h | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -143,6 +143,7 @@ qca8k_set_page(struct mii_bus *bus, u16
+ 	}
+ 
+ 	qca8k_current_page = page;
++	usleep_range(1000, 2000);
+ 	return 0;
+ }
+ 
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -20,7 +20,7 @@
+ #define PHY_ID_QCA8337					0x004dd036
+ #define QCA8K_ID_QCA8337				0x13
+ 
+-#define QCA8K_BUSY_WAIT_TIMEOUT				20
++#define QCA8K_BUSY_WAIT_TIMEOUT				2000
+ 
+ #define QCA8K_NUM_FDB_RECORDS				2048
+ 
diff --git a/target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch b/target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch
new file mode 100644
index 0000000000..88d3c1ef43
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch
@@ -0,0 +1,267 @@
+From 759bafb8a3226326ca357613bc90acf738f80c32 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:10 +0200
+Subject: [PATCH] net: dsa: qca8k: add support for internal phy and internal
+ mdio
+
+Add support to setup_mdio_bus for internal phy declaration. Introduce a
+flag to use the legacy port phy mapping by default and use the direct
+mapping if a mdio node is detected in the switch node. Register a
+dedicated mdio internal mdio bus to address the different mapping
+between port and phy if the mdio node is detected.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 112 +++++++++++++++++++++++++++++-----------
+ drivers/net/dsa/qca8k.h |   1 +
+ 2 files changed, 83 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -11,6 +11,7 @@
+ #include <linux/netdevice.h>
+ #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>
+@@ -629,7 +630,7 @@ qca8k_port_to_phy(int port)
+ }
+ 
+ static int
+-qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
+ {
+ 	u16 r1, r2, page;
+ 	u32 val;
+@@ -639,7 +640,7 @@ qca8k_mdio_busy_wait(struct qca8k_priv *
+ 
+ 	ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0,
+ 				QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+-				priv->bus, 0x10 | r2, r1);
++				bus, 0x10 | r2, r1);
+ 
+ 	/* Check if qca8k_read has failed for a different reason
+ 	 * before returnting -ETIMEDOUT
+@@ -651,19 +652,16 @@ qca8k_mdio_busy_wait(struct qca8k_priv *
+ }
+ 
+ static int
+-qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
++qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data)
+ {
++	struct qca8k_priv *priv = salve_bus->priv;
+ 	u16 r1, r2, page;
+-	u32 phy, val;
++	u32 val;
+ 	int ret;
+ 
+ 	if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ 		return -EINVAL;
+ 
+-	/* callee is responsible for not passing bad ports,
+-	 * but we still would like to make spills impossible.
+-	 */
+-	phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+ 	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+ 	      QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ 	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+@@ -679,33 +677,29 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ 
+ 	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+ 
+-	ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++	ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
+ 				   QCA8K_MDIO_MASTER_BUSY);
+ 
+ exit:
+-	mutex_unlock(&priv->bus->mdio_lock);
+-
+ 	/* even if the busy_wait timeouts try to clear the MASTER_EN */
+-	qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+-			QCA8K_MDIO_MASTER_EN);
++	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++
++	mutex_unlock(&priv->bus->mdio_lock);
+ 
+ 	return ret;
+ }
+ 
+ static int
+-qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
++qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum)
+ {
++	struct qca8k_priv *priv = salve_bus->priv;
+ 	u16 r1, r2, page;
+-	u32 phy, val;
++	u32 val;
+ 	int ret;
+ 
+ 	if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ 		return -EINVAL;
+ 
+-	/* callee is responsible for not passing bad ports,
+-	 * but we still would like to make spills impossible.
+-	 */
+-	phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+ 	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+ 	      QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ 	      QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+@@ -720,24 +714,22 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ 
+ 	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
+ 
+-	ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
++	ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
+ 				   QCA8K_MDIO_MASTER_BUSY);
+ 	if (ret)
+ 		goto exit;
+ 
+ 	val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
+-	val &= QCA8K_MDIO_MASTER_DATA_MASK;
+ 
+ exit:
++	/* even if the busy_wait timeouts try to clear the MASTER_EN */
++	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++
+ 	mutex_unlock(&priv->bus->mdio_lock);
+ 
+ 	if (val >= 0)
+ 		val &= QCA8K_MDIO_MASTER_DATA_MASK;
+ 
+-	/* even if the busy_wait timeouts try to clear the MASTER_EN */
+-	qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+-			QCA8K_MDIO_MASTER_EN);
+-
+ 	return val;
+ }
+ 
+@@ -746,7 +738,14 @@ qca8k_phy_write(struct dsa_switch *ds, i
+ {
+ 	struct qca8k_priv *priv = ds->priv;
+ 
+-	return qca8k_mdio_write(priv, port, regnum, data);
++	/* Check if the legacy mapping should be used and the
++	 * port is not correctly mapped to the right PHY in the
++	 * devicetree
++	 */
++	if (priv->legacy_phy_port_mapping)
++		port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++	return qca8k_mdio_write(priv->bus, port, regnum, data);
+ }
+ 
+ static int
+@@ -755,7 +754,14 @@ qca8k_phy_read(struct dsa_switch *ds, in
+ 	struct qca8k_priv *priv = ds->priv;
+ 	int ret;
+ 
+-	ret = qca8k_mdio_read(priv, port, regnum);
++	/* Check if the legacy mapping should be used and the
++	 * port is not correctly mapped to the right PHY in the
++	 * devicetree
++	 */
++	if (priv->legacy_phy_port_mapping)
++		port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++	ret = qca8k_mdio_read(priv->bus, port, regnum);
+ 
+ 	if (ret < 0)
+ 		return 0xffff;
+@@ -764,10 +770,37 @@ qca8k_phy_read(struct dsa_switch *ds, in
+ }
+ 
+ static int
++qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio)
++{
++	struct dsa_switch *ds = priv->ds;
++	struct mii_bus *bus;
++
++	bus = devm_mdiobus_alloc(ds->dev);
++
++	if (!bus)
++		return -ENOMEM;
++
++	bus->priv = (void *)priv;
++	bus->name = "qca8k slave mii";
++	bus->read = qca8k_mdio_read;
++	bus->write = qca8k_mdio_write;
++	snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d",
++		 ds->index);
++
++	bus->parent = ds->dev;
++	bus->phy_mask = ~ds->phys_mii_mask;
++
++	ds->slave_mii_bus = bus;
++
++	return devm_of_mdiobus_register(priv->dev, bus, mdio);
++}
++
++static int
+ qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+ {
+ 	u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
+-	struct device_node *ports, *port;
++	struct device_node *ports, *port, *mdio;
++	phy_interface_t mode;
+ 	int err;
+ 
+ 	ports = of_get_child_by_name(priv->dev->of_node, "ports");
+@@ -788,7 +821,10 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ 		if (!dsa_is_user_port(priv->ds, reg))
+ 			continue;
+ 
+-		if (of_property_read_bool(port, "phy-handle"))
++		of_get_phy_mode(port, &mode);
++
++		if (of_property_read_bool(port, "phy-handle") &&
++		    mode != PHY_INTERFACE_MODE_INTERNAL)
+ 			external_mdio_mask |= BIT(reg);
+ 		else
+ 			internal_mdio_mask |= BIT(reg);
+@@ -825,8 +861,23 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ 				       QCA8K_MDIO_MASTER_EN);
+ 	}
+ 
++	/* Check if the devicetree declare the port:phy mapping */
++	mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
++	if (of_device_is_available(mdio)) {
++		err = qca8k_mdio_register(priv, mdio);
++		if (err)
++			of_node_put(mdio);
++
++		return err;
++	}
++
++	/* If a mapping can't be found the legacy mapping is used,
++	 * using the qca8k_port_to_phy function
++	 */
++	priv->legacy_phy_port_mapping = true;
+ 	priv->ops.phy_read = qca8k_phy_read;
+ 	priv->ops.phy_write = qca8k_phy_write;
++
+ 	return 0;
+ }
+ 
+@@ -1212,7 +1263,8 @@ qca8k_phylink_validate(struct dsa_switch
+ 	case 5:
+ 		/* Internal PHY */
+ 		if (state->interface != PHY_INTERFACE_MODE_NA &&
+-		    state->interface != PHY_INTERFACE_MODE_GMII)
++		    state->interface != PHY_INTERFACE_MODE_GMII &&
++		    state->interface != PHY_INTERFACE_MODE_INTERNAL)
+ 			goto unsupported;
+ 		break;
+ 	case 6: /* 2nd CPU port / external PHY */
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -255,6 +255,7 @@ struct qca8k_priv {
+ 	u8 switch_revision;
+ 	u8 rgmii_tx_delay;
+ 	u8 rgmii_rx_delay;
++	bool legacy_phy_port_mapping;
+ 	struct regmap *regmap;
+ 	struct mii_bus *bus;
+ 	struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
diff --git a/target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch b/target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch
new file mode 100644
index 0000000000..6db01b4b41
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch
@@ -0,0 +1,93 @@
+From 0c994a28e7518f098c84a3049cb2915780db873a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:11 +0200
+Subject: [PATCH] devicetree: bindings: dsa: qca8k: Document internal mdio
+ definition
+
+Document new way of declare mapping of internal PHY to port.
+The new implementation directly declare the PHY connected to the port
+by adding a node in the switch node. The driver detect this and register
+an internal mdiobus using the mapping defined in the mdio node.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Rob Herring <robh at kernel.org>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ .../devicetree/bindings/net/dsa/qca8k.txt     | 39 +++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -21,6 +21,10 @@ described in dsa/dsa.txt. If the QCA8K s
+ mdio-bus each subnode describing a port needs to have a valid phandle
+ referencing the internal PHY it is connected to. This is because there's no
+ N:N mapping of port and PHY id.
++To declare the internal mdio-bus configuration, declare a mdio node in the
++switch node and declare the phandle for the port referencing the internal
++PHY is connected to. In this config a internal mdio-bus is registered and
++the mdio MASTER is used as communication.
+ 
+ Don't use mixed external and internal mdio-bus configurations, as this is
+ not supported by the hardware.
+@@ -150,26 +154,61 @@ for the internal master mdio-bus configu
+ 				port at 1 {
+ 					reg = <1>;
+ 					label = "lan1";
++					phy-mode = "internal";
++					phy-handle = <&phy_port1>;
+ 				};
+ 
+ 				port at 2 {
+ 					reg = <2>;
+ 					label = "lan2";
++					phy-mode = "internal";
++					phy-handle = <&phy_port2>;
+ 				};
+ 
+ 				port at 3 {
+ 					reg = <3>;
+ 					label = "lan3";
++					phy-mode = "internal";
++					phy-handle = <&phy_port3>;
+ 				};
+ 
+ 				port at 4 {
+ 					reg = <4>;
+ 					label = "lan4";
++					phy-mode = "internal";
++					phy-handle = <&phy_port4>;
+ 				};
+ 
+ 				port at 5 {
+ 					reg = <5>;
+ 					label = "wan";
++					phy-mode = "internal";
++					phy-handle = <&phy_port5>;
++				};
++			};
++
++			mdio {
++				#address-cells = <1>;
++				#size-cells = <0>;
++
++				phy_port1: phy at 0 {
++					reg = <0>;
++				};
++
++				phy_port2: phy at 1 {
++					reg = <1>;
++				};
++
++				phy_port3: phy at 2 {
++					reg = <2>;
++				};
++
++				phy_port4: phy at 3 {
++					reg = <3>;
++				};
++
++				phy_port5: phy at 4 {
++					reg = <4>;
+ 				};
+ 			};
+ 		};
diff --git a/target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch b/target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch
new file mode 100644
index 0000000000..da8d5b3462
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch
@@ -0,0 +1,95 @@
+From b7ebac354d54f1657bb89b7a7ca149db50203e6a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:12 +0200
+Subject: [PATCH] net: dsa: qca8k: improve internal mdio read/write bus access
+
+Improve the internal mdio read/write bus access by caching the value
+without accessing it for every read/write.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -655,6 +655,7 @@ static int
+ qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data)
+ {
+ 	struct qca8k_priv *priv = salve_bus->priv;
++	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
+ 	u32 val;
+ 	int ret;
+@@ -669,22 +670,22 @@ qca8k_mdio_write(struct mii_bus *salve_b
+ 
+ 	qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+ 
+-	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	ret = qca8k_set_page(priv->bus, page);
++	ret = qca8k_set_page(bus, page);
+ 	if (ret)
+ 		goto exit;
+ 
+-	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++	qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+ 
+-	ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
++	ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ 				   QCA8K_MDIO_MASTER_BUSY);
+ 
+ exit:
+ 	/* even if the busy_wait timeouts try to clear the MASTER_EN */
+-	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++	qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
+ 
+-	mutex_unlock(&priv->bus->mdio_lock);
++	mutex_unlock(&bus->mdio_lock);
+ 
+ 	return ret;
+ }
+@@ -693,6 +694,7 @@ static int
+ qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum)
+ {
+ 	struct qca8k_priv *priv = salve_bus->priv;
++	struct mii_bus *bus = priv->bus;
+ 	u16 r1, r2, page;
+ 	u32 val;
+ 	int ret;
+@@ -706,26 +708,26 @@ qca8k_mdio_read(struct mii_bus *salve_bu
+ 
+ 	qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+ 
+-	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+ 
+-	ret = qca8k_set_page(priv->bus, page);
++	ret = qca8k_set_page(bus, page);
+ 	if (ret)
+ 		goto exit;
+ 
+-	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
++	qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+ 
+-	ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL,
++	ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ 				   QCA8K_MDIO_MASTER_BUSY);
+ 	if (ret)
+ 		goto exit;
+ 
+-	val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
++	val = qca8k_mii_read32(bus, 0x10 | r2, r1);
+ 
+ exit:
+ 	/* even if the busy_wait timeouts try to clear the MASTER_EN */
+-	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0);
++	qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
+ 
+-	mutex_unlock(&priv->bus->mdio_lock);
++	mutex_unlock(&bus->mdio_lock);
+ 
+ 	if (val >= 0)
+ 		val &= QCA8K_MDIO_MASTER_DATA_MASK;
diff --git a/target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch b/target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch
new file mode 100644
index 0000000000..1179cf152d
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch
@@ -0,0 +1,48 @@
+From a46aec02bc06ac2c33f326339e4ef88c735dc30d Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:13 +0200
+Subject: [PATCH] net: dsa: qca8k: pass switch_revision info to phy dev_flags
+
+Define get_phy_flags to pass switch_Revision needed to tweak the
+internal PHY with debug values based on the revision.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1740,6 +1740,22 @@ qca8k_port_vlan_del(struct dsa_switch *d
+ 	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)
+@@ -1774,6 +1790,7 @@ static const struct dsa_switch_ops qca8k
+ 	.phylink_mac_config	= qca8k_phylink_mac_config,
+ 	.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,
+ };
+ 
+ static int qca8k_read_switch_id(struct qca8k_priv *priv)
diff --git a/target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch b/target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch
new file mode 100644
index 0000000000..20325f564d
--- /dev/null
+++ b/target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch
@@ -0,0 +1,229 @@
+From 272833b9b3b3969be7a91839121d86662c8c4253 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth at gmail.com>
+Date: Fri, 14 May 2021 23:00:15 +0200
+Subject: [PATCH] net: phy: add support for qca8k switch internal PHY in at803x
+
+Since the at803x share the same regs, it's assumed they are based on the
+same implementation. Make it part of the at803x PHY driver to skip
+having redudant code.
+Add initial support for qca8k internal PHYs. The internal PHYs requires
+special mmd and debug values to be set based on the switch revision
+passwd using the dev_flags. Supports output of idle, receive and eee_wake
+errors stats.
+Some debug values sets can't be translated as the documentation lacks any
+reference about them.
+
+Signed-off-by: Ansuel Smith <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/Kconfig  |   5 +-
+ drivers/net/phy/at803x.c | 132 ++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 134 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -235,10 +235,11 @@ config NXP_TJA11XX_PHY
+ 	  Currently supports the NXP TJA1100 and TJA1101 PHY.
+ 
+ config AT803X_PHY
+-	tristate "Qualcomm Atheros AR803X PHYs"
++	tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
+ 	depends on REGULATOR
+ 	help
+-	  Currently supports the AR8030, AR8031, AR8033 and AR8035 model
++	  Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
++	  QCA8337(Internal qca8k PHY) model
+ 
+ config QSEMI_PHY
+ 	tristate "Quality Semiconductor PHYs"
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -92,10 +92,16 @@
+ #define AT803X_DEBUG_REG_5			0x05
+ #define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
+ 
++#define AT803X_DEBUG_REG_3C			0x3C
++
++#define AT803X_DEBUG_REG_3D			0x3D
++
+ #define AT803X_DEBUG_REG_1F			0x1F
+ #define AT803X_DEBUG_PLL_ON			BIT(2)
+ #define AT803X_DEBUG_RGMII_1V8			BIT(3)
+ 
++#define MDIO_AZ_DEBUG				0x800D
++
+ /* AT803x supports either the XTAL input pad, an internal PLL or the
+  * DSP as clock reference for the clock output pad. The XTAL reference
+  * is only used for 25 MHz output, all other frequencies need the PLL.
+@@ -142,10 +148,34 @@
+ #define AT803X_PAGE_FIBER		0
+ #define AT803X_PAGE_COPPER		1
+ 
++#define QCA8327_PHY_ID				0x004dd034
++#define QCA8337_PHY_ID				0x004dd036
++#define QCA8K_PHY_ID_MASK			0xffffffff
++
++#define QCA8K_DEVFLAGS_REVISION_MASK		GENMASK(2, 0)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+ 
++enum stat_access_type {
++	PHY,
++	MMD
++};
++
++struct at803x_hw_stat {
++	const char *string;
++	u8 reg;
++	u32 mask;
++	enum stat_access_type access_type;
++};
++
++static struct at803x_hw_stat at803x_hw_stats[] = {
++	{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
++	{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
++	{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
++};
++
+ struct at803x_priv {
+ 	int flags;
+ #define AT803X_KEEP_PLL_ENABLED	BIT(0)	/* don't turn off internal PLL */
+@@ -154,6 +184,7 @@ struct at803x_priv {
+ 	struct regulator_dev *vddio_rdev;
+ 	struct regulator_dev *vddh_rdev;
+ 	struct regulator *vddio;
++	u64 stats[ARRAY_SIZE(at803x_hw_stats)];
+ };
+ 
+ struct at803x_context {
+@@ -165,6 +196,17 @@ struct at803x_context {
+ 	u16 led_control;
+ };
+ 
++static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
++{
++	int ret;
++
++	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
++	if (ret < 0)
++		return ret;
++
++	return phy_write(phydev, AT803X_DEBUG_DATA, data);
++}
++
+ static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
+ {
+ 	int ret;
+@@ -327,6 +369,53 @@ static void at803x_get_wol(struct phy_de
+ 		wol->wolopts |= WAKE_MAGIC;
+ }
+ 
++static int at803x_get_sset_count(struct phy_device *phydev)
++{
++	return ARRAY_SIZE(at803x_hw_stats);
++}
++
++static void at803x_get_strings(struct phy_device *phydev, u8 *data)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
++		strscpy(data + i * ETH_GSTRING_LEN,
++			at803x_hw_stats[i].string, ETH_GSTRING_LEN);
++	}
++}
++
++static u64 at803x_get_stat(struct phy_device *phydev, int i)
++{
++	struct at803x_hw_stat stat = at803x_hw_stats[i];
++	struct at803x_priv *priv = phydev->priv;
++	int val;
++	u64 ret;
++
++	if (stat.access_type == MMD)
++		val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
++	else
++		val = phy_read(phydev, stat.reg);
++
++	if (val < 0) {
++		ret = U64_MAX;
++	} else {
++		val = val & stat.mask;
++		priv->stats[i] += val;
++		ret = priv->stats[i];
++	}
++
++	return ret;
++}
++
++static void at803x_get_stats(struct phy_device *phydev,
++			     struct ethtool_stats *stats, u64 *data)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
++		data[i] = at803x_get_stat(phydev, i);
++}
++
+ static int at803x_suspend(struct phy_device *phydev)
+ {
+ 	int value;
+@@ -1102,6 +1191,34 @@ static int at803x_cable_test_start(struc
+ 	return 0;
+ }
+ 
++static int qca83xx_config_init(struct phy_device *phydev)
++{
++	u8 switch_revision;
++
++	switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
++
++	switch (switch_revision) {
++	case 1:
++		/* For 100M waveform */
++		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea);
++		/* Turn on Gigabit clock */
++		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0);
++		break;
++
++	case 2:
++		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
++		fallthrough;
++	case 4:
++		phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
++		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x6860);
++		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46);
++		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
++		break;
++	}
++
++	return 0;
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ 	/* Qualcomm Atheros AR8035 */
+@@ -1198,7 +1315,20 @@ static struct phy_driver at803x_driver[]
+ 	.read_status		= at803x_read_status,
+ 	.soft_reset		= genphy_soft_reset,
+ 	.config_aneg		= at803x_config_aneg,
+-} };
++}, {
++	/* QCA8337 */
++	.phy_id = QCA8337_PHY_ID,
++	.phy_id_mask = QCA8K_PHY_ID_MASK,
++	.name = "QCA PHY 8337",
++	/* PHY_GBIT_FEATURES */
++	.probe = at803x_probe,
++	.flags = PHY_IS_INTERNAL,
++	.config_init = qca83xx_config_init,
++	.soft_reset = genphy_soft_reset,
++	.get_sset_count = at803x_get_sset_count,
++	.get_strings = at803x_get_strings,
++	.get_stats = at803x_get_stats,
++}, };
+ 
+ module_phy_driver(at803x_driver);
+ 
diff --git a/target/linux/generic/pending-5.10/730-net-phy-at803x-fix-feature-detection.patch b/target/linux/generic/pending-5.10/730-net-phy-at803x-fix-feature-detection.patch
index 1d4783e382..a00416ec3e 100644
--- a/target/linux/generic/pending-5.10/730-net-phy-at803x-fix-feature-detection.patch
+++ b/target/linux/generic/pending-5.10/730-net-phy-at803x-fix-feature-detection.patch
@@ -20,7 +20,7 @@ Signed-off-by: David Bauer <mail at david-bauer.net>
 
 --- a/drivers/net/phy/at803x.c
 +++ b/drivers/net/phy/at803x.c
-@@ -935,6 +935,34 @@ static int at803x_set_tunable(struct phy
+@@ -1024,6 +1024,34 @@ static int at803x_set_tunable(struct phy
  	}
  }
  
@@ -55,7 +55,7 @@ Signed-off-by: David Bauer <mail at david-bauer.net>
  static int at803x_cable_test_result_trans(u16 status)
  {
  	switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
-@@ -1156,7 +1184,7 @@ static struct phy_driver at803x_driver[]
+@@ -1273,7 +1301,7 @@ static struct phy_driver at803x_driver[]
  	.resume			= at803x_resume,
  	.read_page		= at803x_read_page,
  	.write_page		= at803x_write_page,
diff --git a/target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch b/target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch
index b58797c321..c81aa3bb73 100644
--- a/target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch
+++ b/target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch
@@ -25,9 +25,9 @@ Signed-off-by: Robert Marko <robert.marko at sartura.hr>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -314,6 +314,12 @@ config AT803X_PHY
- 	help
- 	  Currently supports the AR8030, AR8031, AR8033 and AR8035 model
+@@ -315,6 +315,12 @@ config AT803X_PHY
+ 	  Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
+ 	  QCA8337(Internal qca8k PHY) model
  
 +config QCA807X_PHY
 +	tristate "Qualcomm QCA807X PHYs"



More information about the lede-commits mailing list