mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-22 06:57:57 +00:00
7ace30aeb6
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>
449 lines
11 KiB
Diff
449 lines
11 KiB
Diff
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, ®);
|
|
- 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, ®);
|
|
- 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, ®);
|
|
- 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, ®);
|
|
+ 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, ®);
|
|
+ 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, ®);
|
|
+ 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 */
|