kernel: 5.10: backport qca8k stability improvements
[openwrt/openwrt.git] / target / linux / generic / backport-5.10 / 785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch
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