kernel: 5.10: backport qca8k stability improvements
authorMatthew Hagan <mnhagan88@gmail.com>
Thu, 9 Sep 2021 17:12:30 +0000 (17:12 +0000)
committerHauke Mehrtens <hauke@hauke-m.de>
Sun, 24 Oct 2021 14:56:17 +0000 (16:56 +0200)
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@gmail.com>
Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
26 files changed:
target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/730-net-phy-at803x-fix-feature-detection.patch
target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch

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 (file)
index 0000000..b8e6d9b
--- /dev/null
@@ -0,0 +1,35 @@
+From 5d9e068402dcf7354cc8ee66c2152845306d2ccb Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..ff8288d
--- /dev/null
@@ -0,0 +1,61 @@
+From 2ad255f2faaffb3af786031fba2e7955454b558a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..c403589
--- /dev/null
@@ -0,0 +1,86 @@
+From 504bf65931824eda83494e5b5d75686e27ace03e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..6be494a
--- /dev/null
@@ -0,0 +1,101 @@
+From ba5707ec58cfb6853dff41c2aae72deb6a03d389 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..3349b78
--- /dev/null
@@ -0,0 +1,207 @@
+From 028f5f8ef44fcf87a456772cbb9f0d90a0a22884 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..1e0e224
--- /dev/null
@@ -0,0 +1,263 @@
+From d7805757c75c76e9518fc1023a29f0c4eed5b581 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..506966f
--- /dev/null
@@ -0,0 +1,226 @@
+From aaf421425cbdec4eb6fd75a29e65c2867b0b7bbd Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..360ce1d
--- /dev/null
@@ -0,0 +1,66 @@
+From b7c818d194927bdc60ed15db55bb8654496a36b7 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..7230585
--- /dev/null
@@ -0,0 +1,96 @@
+From 6e82a457e06252b59102486767539cc9c2aba60b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..3c4a14b
--- /dev/null
@@ -0,0 +1,26 @@
+From 227a9ffc1bc77037339530607fe129af3824620e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Acked-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..cd3050e
--- /dev/null
@@ -0,0 +1,130 @@
+From 83a3ceb39b2495171aabe9446271b94c678354f3 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..d25edbb
--- /dev/null
@@ -0,0 +1,31 @@
+From 5bf9ff3b9fb5ecb67a1a3517b26db3a00f2a2f11 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..2b393d2
--- /dev/null
@@ -0,0 +1,48 @@
+From 0fc57e4b5e39461fc0a54aae0afe4241363a7267 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..ed9b818
--- /dev/null
@@ -0,0 +1,114 @@
+From 95ffeaf18b3bb90eeef52cbf7d79ccc9d0345ff5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..629cb32
--- /dev/null
@@ -0,0 +1,28 @@
+From 1ee0591a1093c2448642c33433483e9260275f7b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..6dc2dc6
--- /dev/null
@@ -0,0 +1,188 @@
+From e4b9977cee1583da38a6e9118078bb728aaccf7b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..4593da0
--- /dev/null
@@ -0,0 +1,50 @@
+From 63c33bbfeb6842a956a0eb12901e28eb335bdb18 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..b6684d7
--- /dev/null
@@ -0,0 +1,128 @@
+From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..30eeed3
--- /dev/null
@@ -0,0 +1,39 @@
+From 617960d72e93de0f3fa52407e2d39e8c43e73b0a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..88d3c1e
--- /dev/null
@@ -0,0 +1,267 @@
+From 759bafb8a3226326ca357613bc90acf738f80c32 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..6db01b4
--- /dev/null
@@ -0,0 +1,93 @@
+From 0c994a28e7518f098c84a3049cb2915780db873a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: David S. Miller <davem@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@1 {
+                                       reg = <1>;
+                                       label = "lan1";
++                                      phy-mode = "internal";
++                                      phy-handle = <&phy_port1>;
+                               };
+                               port@2 {
+                                       reg = <2>;
+                                       label = "lan2";
++                                      phy-mode = "internal";
++                                      phy-handle = <&phy_port2>;
+                               };
+                               port@3 {
+                                       reg = <3>;
+                                       label = "lan3";
++                                      phy-mode = "internal";
++                                      phy-handle = <&phy_port3>;
+                               };
+                               port@4 {
+                                       reg = <4>;
+                                       label = "lan4";
++                                      phy-mode = "internal";
++                                      phy-handle = <&phy_port4>;
+                               };
+                               port@5 {
+                                       reg = <5>;
+                                       label = "wan";
++                                      phy-mode = "internal";
++                                      phy-handle = <&phy_port5>;
++                              };
++                      };
++
++                      mdio {
++                              #address-cells = <1>;
++                              #size-cells = <0>;
++
++                              phy_port1: phy@0 {
++                                      reg = <0>;
++                              };
++
++                              phy_port2: phy@1 {
++                                      reg = <1>;
++                              };
++
++                              phy_port3: phy@2 {
++                                      reg = <2>;
++                              };
++
++                              phy_port4: phy@3 {
++                                      reg = <3>;
++                              };
++
++                              phy_port5: phy@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 (file)
index 0000000..da8d5b3
--- /dev/null
@@ -0,0 +1,95 @@
+From b7ebac354d54f1657bb89b7a7ca149db50203e6a Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..1179cf1
--- /dev/null
@@ -0,0 +1,48 @@
+From a46aec02bc06ac2c33f326339e4ef88c735dc30d Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..20325f5
--- /dev/null
@@ -0,0 +1,229 @@
+From 272833b9b3b3969be7a91839121d86662c8c4253 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@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@gmail.com>
+Signed-off-by: David S. Miller <davem@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);
index 1d4783e382c53d6aaf4a01c046201b9537efc88d..a00416ec3e1d227d4610ec5fb7c194623b3e2c71 100644 (file)
@@ -20,7 +20,7 @@ Signed-off-by: David Bauer <mail@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@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,
index b58797c3216fbc6678c3eaec71187086439bf8a5..c81aa3bb734d4252f627e53d2577990538e84f9c 100644 (file)
@@ -25,9 +25,9 @@ Signed-off-by: Robert Marko <robert.marko@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"