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>
705 lines
19 KiB
Diff
705 lines
19 KiB
Diff
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, ®);
|
|
- 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, ®);
|
|
+ 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 */
|