aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.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-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.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-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch')
-rw-r--r--target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch704
1 files changed, 704 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch b/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch
new file mode 100644
index 0000000000..96468ae74e
--- /dev/null
+++ b/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch
@@ -0,0 +1,704 @@
+From 2e5bd96eea86a246b4de3bf756f7a11b43e6187d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:19 +0200
+Subject: [PATCH 10/14] net: dsa: qca8k: move port FDB/MDB function to common
+ code
+
+The same port FDB/MDB function are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+Also drop bulk read/write functions and make them 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 | 306 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 297 +++++++++++++++++++++++++++-
+ drivers/net/dsa/qca/qca8k.h | 25 ++-
+ 3 files changed, 317 insertions(+), 311 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -442,217 +442,6 @@ static struct regmap_config qca8k_regmap
+ };
+
+ static int
+-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+-{
+- u32 reg[3];
+- int ret;
+-
+- /* load the ARL table into an array */
+- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+- if (ret)
+- return ret;
+-
+- /* vid - 83:72 */
+- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+- /* aging - 67:64 */
+- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+- /* portmask - 54:48 */
+- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+- /* mac - 47:0 */
+- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
+- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
+- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
+- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
+- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
+- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
+- u8 aging)
+-{
+- u32 reg[3] = { 0 };
+-
+- /* vid - 83:72 */
+- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+- /* aging - 67:64 */
+- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+- /* portmask - 54:48 */
+- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+- /* mac - 47:0 */
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+-
+- /* load the array into the ARL table */
+- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+-}
+-
+-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;
+- reg |= cmd;
+- if (port >= 0) {
+- reg |= QCA8K_ATU_FUNC_PORT_EN;
+- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+- }
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- 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) {
+- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_ATU_FUNC_FULL)
+- return -1;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
+-{
+- int ret;
+-
+- 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)
+- return ret;
+-
+- return qca8k_fdb_read(priv, fdb);
+-}
+-
+-static int
+-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
+- u16 vid, u8 aging)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_fdb_flush(struct qca8k_priv *priv)
+-{
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_fdb_read(priv, &fdb);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule exist. Delete first */
+- if (!fdb.aging) {
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+- }
+-
+- /* Add port to fdb portmask */
+- fdb.port_mask |= port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule doesn't exist. Why delete? */
+- if (!fdb.aging) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+-
+- /* Only port in the rule is this port. Don't re insert */
+- if (fdb.port_mask == port_mask)
+- goto exit;
+-
+- /* Remove port from port mask */
+- fdb.port_mask &= ~port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+ {
+ u32 reg;
+@@ -2048,97 +1837,6 @@ exit:
+ return ret;
+ }
+
+-static void
+-qca8k_port_fast_age(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
+- u16 port_mask, u16 vid)
+-{
+- /* Set the vid to the port vlan id if no vid is set */
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_add(priv, addr, port_mask, vid,
+- QCA8K_ATU_STATUS_STATIC);
+-}
+-
+-static int
+-qca8k_port_fdb_add(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_del(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_del(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
+- dsa_fdb_dump_cb_t *cb, void *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- struct qca8k_fdb _fdb = { 0 };
+- int cnt = QCA8K_NUM_FDB_RECORDS;
+- bool is_static;
+- int ret = 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
+- if (!_fdb.aging)
+- break;
+- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
+- ret = cb(_fdb.mac, _fdb.vid, is_static, data);
+- if (ret)
+- break;
+- }
+- mutex_unlock(&priv->reg_mutex);
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_port_mdb_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
+-}
+-
+-static int
+-qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+-}
+-
+ static int
+ qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -103,7 +103,7 @@ const struct regmap_access_table qca8k_r
+ };
+
+ /* TODO: remove these extra ops when we can support regmap bulk read/write */
+-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+ {
+ int i, count = len / sizeof(u32), ret;
+
+@@ -121,7 +121,7 @@ int qca8k_bulk_read(struct qca8k_priv *p
+ }
+
+ /* TODO: remove these extra ops when we can support regmap bulk read/write */
+-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+ {
+ int i, count = len / sizeof(u32), ret;
+ u32 tmp;
+@@ -149,6 +149,211 @@ int qca8k_busy_wait(struct qca8k_priv *p
+ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+ }
+
++static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
++{
++ u32 reg[3];
++ int ret;
++
++ /* load the ARL table into an array */
++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++ if (ret)
++ return ret;
++
++ /* vid - 83:72 */
++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
++ /* aging - 67:64 */
++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
++ /* portmask - 54:48 */
++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
++ /* mac - 47:0 */
++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
++
++ return 0;
++}
++
++static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask,
++ const u8 *mac, u8 aging)
++{
++ u32 reg[3] = { 0 };
++
++ /* vid - 83:72 */
++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
++ /* aging - 67:64 */
++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
++ /* portmask - 54:48 */
++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
++ /* mac - 47:0 */
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
++
++ /* load the array into the ARL table */
++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++}
++
++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;
++ reg |= cmd;
++ if (port >= 0) {
++ reg |= QCA8K_ATU_FUNC_PORT_EN;
++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
++ }
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ 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) {
++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_ATU_FUNC_FULL)
++ return -1;
++ }
++
++ return 0;
++}
++
++static int qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb,
++ int port)
++{
++ int ret;
++
++ 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)
++ return ret;
++
++ return qca8k_fdb_read(priv, fdb);
++}
++
++static int qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac,
++ u16 port_mask, u16 vid, u8 aging)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac,
++ u16 port_mask, u16 vid)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++void qca8k_fdb_flush(struct qca8k_priv *priv)
++{
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_fdb_read(priv, &fdb);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule exist. Delete first */
++ if (!fdb.aging) {
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++ }
++
++ /* Add port to fdb portmask */
++ fdb.port_mask |= port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule doesn't exist. Why delete? */
++ if (!fdb.aging) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++
++ /* Only port in the rule is this port. Don't re insert */
++ if (fdb.port_mask == port_mask)
++ goto exit;
++
++ /* Remove port from port mask */
++ fdb.port_mask &= ~port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
+ int qca8k_mib_init(struct qca8k_priv *priv)
+ {
+ int ret;
+@@ -368,6 +573,15 @@ void qca8k_port_bridge_leave(struct dsa_
+ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
+
++void qca8k_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
++ mutex_unlock(&priv->reg_mutex);
++}
++
+ int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+ {
+ struct qca8k_priv *priv = ds->priv;
+@@ -452,3 +666,78 @@ int qca8k_port_max_mtu(struct dsa_switch
+ {
+ return QCA8K_MAX_MTU;
+ }
++
++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
++ u16 port_mask, u16 vid)
++{
++ /* Set the vid to the port vlan id if no vid is set */
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_add(priv, addr, port_mask, vid,
++ QCA8K_ATU_STATUS_STATIC);
++}
++
++int qca8k_port_fdb_add(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
++}
++
++int qca8k_port_fdb_del(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_del(priv, addr, port_mask, vid);
++}
++
++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
++ dsa_fdb_dump_cb_t *cb, void *data)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ struct qca8k_fdb _fdb = { 0 };
++ int cnt = QCA8K_NUM_FDB_RECORDS;
++ bool is_static;
++ int ret = 0;
++
++ mutex_lock(&priv->reg_mutex);
++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
++ if (!_fdb.aging)
++ break;
++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
++ ret = cb(_fdb.mac, _fdb.vid, is_static, data);
++ if (ret)
++ break;
++ }
++ mutex_unlock(&priv->reg_mutex);
++
++ return 0;
++}
++
++int qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++int qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -430,11 +430,9 @@ int qca8k_read(struct qca8k_priv *priv,
+ int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val);
+ int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
+
+-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+-
+ /* 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 */
+ void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data);
+@@ -463,6 +461,23 @@ int qca8k_port_change_mtu(struct dsa_swi
+ int qca8k_port_max_mtu(struct dsa_switch *ds, int port);
+
+ /* Common fast age function */
++void qca8k_port_fast_age(struct dsa_switch *ds, int port);
+ int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs);
+
++/* Common FDB function */
++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
++ u16 port_mask, u16 vid);
++int qca8k_port_fdb_add(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid);
++int qca8k_port_fdb_del(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid);
++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
++ dsa_fdb_dump_cb_t *cb, void *data);
++
++/* Common MDB function */
++int qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb);
++int qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb);
++
+ #endif /* __QCA8K_H */