generic: 5.15: qca8k: backport code split patch
[openwrt/staging/svanheule.git] / target / linux / generic / backport-5.15 / 771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
diff --git a/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch b/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
new file mode 100644 (file)
index 0000000..898010f
--- /dev/null
@@ -0,0 +1,448 @@
+From c5290f636624b98e76a82bd63ffec0a8a9daa620 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:21 +0200
+Subject: [PATCH 12/14] net: dsa: qca8k: move port VLAN functions to common
+ code
+
+The same port VLAN functions are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+Also drop exposing busy_wait and make it static.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c   | 182 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 179 +++++++++++++++++++++++++++-
+ drivers/net/dsa/qca/qca8k.h        |  10 +-
+ 3 files changed, 187 insertions(+), 184 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -15,7 +15,6 @@
+ #include <linux/of_net.h>
+ #include <linux/of_mdio.h>
+ #include <linux/of_platform.h>
+-#include <linux/if_bridge.h>
+ #include <linux/mdio.h>
+ #include <linux/phylink.h>
+ #include <linux/gpio/consumer.h>
+@@ -442,122 +441,6 @@ static struct regmap_config qca8k_regmap
+ };
+ static int
+-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+-{
+-      u32 reg;
+-      int ret;
+-
+-      /* Set the command and VLAN index */
+-      reg = QCA8K_VTU_FUNC1_BUSY;
+-      reg |= cmd;
+-      reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+-
+-      /* Write the function register triggering the table access */
+-      ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+-      if (ret)
+-              return ret;
+-
+-      /* wait for completion */
+-      ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
+-      if (ret)
+-              return ret;
+-
+-      /* Check for table full violation when adding an entry */
+-      if (cmd == QCA8K_VLAN_LOAD) {
+-              ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
+-              if (ret < 0)
+-                      return ret;
+-              if (reg & QCA8K_VTU_FUNC1_FULL)
+-                      return -ENOMEM;
+-      }
+-
+-      return 0;
+-}
+-
+-static int
+-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
+-{
+-      u32 reg;
+-      int ret;
+-
+-      /*
+-         We do the right thing with VLAN 0 and treat it as untagged while
+-         preserving the tag on egress.
+-       */
+-      if (vid == 0)
+-              return 0;
+-
+-      mutex_lock(&priv->reg_mutex);
+-      ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+-      if (ret < 0)
+-              goto out;
+-
+-      ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+-      if (ret < 0)
+-              goto out;
+-      reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+-      reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+-      if (untagged)
+-              reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+-      else
+-              reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+-
+-      ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+-      if (ret)
+-              goto out;
+-      ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+-
+-out:
+-      mutex_unlock(&priv->reg_mutex);
+-
+-      return ret;
+-}
+-
+-static int
+-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
+-{
+-      u32 reg, mask;
+-      int ret, i;
+-      bool del;
+-
+-      mutex_lock(&priv->reg_mutex);
+-      ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+-      if (ret < 0)
+-              goto out;
+-
+-      ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+-      if (ret < 0)
+-              goto out;
+-      reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+-      reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+-
+-      /* Check if we're the last member to be removed */
+-      del = true;
+-      for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+-              mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+-
+-              if ((reg & mask) != mask) {
+-                      del = false;
+-                      break;
+-              }
+-      }
+-
+-      if (del) {
+-              ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+-      } else {
+-              ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+-              if (ret)
+-                      goto out;
+-              ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+-      }
+-
+-out:
+-      mutex_unlock(&priv->reg_mutex);
+-
+-      return ret;
+-}
+-
+-static int
+ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
+                       struct sk_buff *read_skb, u32 *val)
+ {
+@@ -1836,71 +1719,6 @@ exit:
+       return ret;
+ }
+-
+-static int
+-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+-                        struct netlink_ext_ack *extack)
+-{
+-      struct qca8k_priv *priv = ds->priv;
+-      int ret;
+-
+-      if (vlan_filtering) {
+-              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+-                              QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+-                              QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+-      } else {
+-              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+-                              QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+-                              QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+-      }
+-
+-      return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_add(struct dsa_switch *ds, int port,
+-                  const struct switchdev_obj_port_vlan *vlan,
+-                  struct netlink_ext_ack *extack)
+-{
+-      bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+-      bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+-      struct qca8k_priv *priv = ds->priv;
+-      int ret;
+-
+-      ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+-      if (ret) {
+-              dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+-              return ret;
+-      }
+-
+-      if (pvid) {
+-              ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+-                              QCA8K_EGREES_VLAN_PORT_MASK(port),
+-                              QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+-              if (ret)
+-                      return ret;
+-
+-              ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+-                                QCA8K_PORT_VLAN_CVID(vlan->vid) |
+-                                QCA8K_PORT_VLAN_SVID(vlan->vid));
+-      }
+-
+-      return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+-                  const struct switchdev_obj_port_vlan *vlan)
+-{
+-      struct qca8k_priv *priv = ds->priv;
+-      int ret;
+-
+-      ret = qca8k_vlan_del(priv, port, vlan->vid);
+-      if (ret)
+-              dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
+-
+-      return ret;
+-}
+ static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
+ {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -141,7 +141,7 @@ static int qca8k_bulk_write(struct qca8k
+       return 0;
+ }
+-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+       u32 val;
+@@ -354,6 +354,120 @@ exit:
+       return ret;
+ }
++static int qca8k_vlan_access(struct qca8k_priv *priv,
++                           enum qca8k_vlan_cmd cmd, u16 vid)
++{
++      u32 reg;
++      int ret;
++
++      /* Set the command and VLAN index */
++      reg = QCA8K_VTU_FUNC1_BUSY;
++      reg |= cmd;
++      reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
++
++      /* Write the function register triggering the table access */
++      ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++      if (ret)
++              return ret;
++
++      /* wait for completion */
++      ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
++      if (ret)
++              return ret;
++
++      /* Check for table full violation when adding an entry */
++      if (cmd == QCA8K_VLAN_LOAD) {
++              ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
++              if (ret < 0)
++                      return ret;
++              if (reg & QCA8K_VTU_FUNC1_FULL)
++                      return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static int qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid,
++                        bool untagged)
++{
++      u32 reg;
++      int ret;
++
++      /* We do the right thing with VLAN 0 and treat it as untagged while
++       * preserving the tag on egress.
++       */
++      if (vid == 0)
++              return 0;
++
++      mutex_lock(&priv->reg_mutex);
++      ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++      if (ret < 0)
++              goto out;
++
++      ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++      if (ret < 0)
++              goto out;
++      reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
++      reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++      if (untagged)
++              reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
++      else
++              reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
++
++      ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++      if (ret)
++              goto out;
++      ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++
++out:
++      mutex_unlock(&priv->reg_mutex);
++
++      return ret;
++}
++
++static int qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
++{
++      u32 reg, mask;
++      int ret, i;
++      bool del;
++
++      mutex_lock(&priv->reg_mutex);
++      ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++      if (ret < 0)
++              goto out;
++
++      ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++      if (ret < 0)
++              goto out;
++      reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++      reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
++
++      /* Check if we're the last member to be removed */
++      del = true;
++      for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++              mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
++
++              if ((reg & mask) != mask) {
++                      del = false;
++                      break;
++              }
++      }
++
++      if (del) {
++              ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
++      } else {
++              ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++              if (ret)
++                      goto out;
++              ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++      }
++
++out:
++      mutex_unlock(&priv->reg_mutex);
++
++      return ret;
++}
++
+ int qca8k_mib_init(struct qca8k_priv *priv)
+ {
+       int ret;
+@@ -832,3 +946,66 @@ void qca8k_port_mirror_del(struct dsa_sw
+ err:
+       dev_err(priv->dev, "Failed to del mirror port from %d", port);
+ }
++
++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port,
++                            bool vlan_filtering,
++                            struct netlink_ext_ack *extack)
++{
++      struct qca8k_priv *priv = ds->priv;
++      int ret;
++
++      if (vlan_filtering) {
++              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++                              QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++                              QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
++      } else {
++              ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++                              QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++                              QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
++      }
++
++      return ret;
++}
++
++int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
++                      const struct switchdev_obj_port_vlan *vlan,
++                      struct netlink_ext_ack *extack)
++{
++      bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
++      bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
++      struct qca8k_priv *priv = ds->priv;
++      int ret;
++
++      ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
++      if (ret) {
++              dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
++              return ret;
++      }
++
++      if (pvid) {
++              ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
++                              QCA8K_EGREES_VLAN_PORT_MASK(port),
++                              QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
++              if (ret)
++                      return ret;
++
++              ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
++                                QCA8K_PORT_VLAN_CVID(vlan->vid) |
++                                QCA8K_PORT_VLAN_SVID(vlan->vid));
++      }
++
++      return ret;
++}
++
++int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
++                      const struct switchdev_obj_port_vlan *vlan)
++{
++      struct qca8k_priv *priv = ds->priv;
++      int ret;
++
++      ret = qca8k_vlan_del(priv, port, vlan->vid);
++      if (ret)
++              dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
++
++      return ret;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -431,7 +431,6 @@ int qca8k_write(struct qca8k_priv *priv,
+ int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
+ /* Common ops function */
+-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask);
+ void qca8k_fdb_flush(struct qca8k_priv *priv);
+ /* Common ethtool stats function */
+@@ -487,4 +486,13 @@ int qca8k_port_mirror_add(struct dsa_swi
+ void qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+                          struct dsa_mall_mirror_tc_entry *mirror);
++/* Common port VLAN function */
++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
++                            struct netlink_ext_ack *extack);
++int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
++                      const struct switchdev_obj_port_vlan *vlan,
++                      struct netlink_ext_ack *extack);
++int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
++                      const struct switchdev_obj_port_vlan *vlan);
++
+ #endif /* __QCA8K_H */