aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
diff options
context:
space:
mode:
authorChristian Marangi <ansuelsmth@gmail.com>2022-09-12 18:40:16 +0200
committerChristian Marangi <ansuelsmth@gmail.com>2022-09-19 15:19:53 +0200
commit7ace30aeb6ff58b4de845dd4722b281afd0c7eae (patch)
tree41c627ab344f5b8012610cccf5592d5876bc6586 /target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
parentddcebda08b26142d47eb3c2009d48d12781d76a3 (diff)
downloadupstream-7ace30aeb6ff58b4de845dd4722b281afd0c7eae.tar.gz
upstream-7ace30aeb6ff58b4de845dd4722b281afd0c7eae.tar.bz2
upstream-7ace30aeb6ff58b4de845dd4722b281afd0c7eae.zip
generic: 5.15: qca8k: backport code split patch
Backport upstream code split patch for qca8k needed for ipq40xx target to correctly implement a DSA driver. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Diffstat (limited to 'target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch')
-rw-r--r--target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch448
1 files changed, 448 insertions, 0 deletions
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
index 0000000000..898010f950
--- /dev/null
+++ b/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
@@ -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 */