Commit c5bcdd82 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'lan966x-extend-switchdev-and-mdb-support'



Horatiu Vultur says:

====================
net: lan966x: Extend switchdev with mdb support

This patch series extends lan966x with mdb support by implementing
the switchdev callbacks: SWITCHDEV_OBJ_ID_PORT_MDB and
SWITCHDEV_OBJ_ID_HOST_MDB.
It adds support for both ipv4/ipv6 entries and l2 entries.

v2->v3:
- rename PGID_FIRST and PGID_LAST to PGID_GP_START and PGID_GP_END
- don't forget and relearn an entry for the CPU if there are more
  references to the cpu.

v1->v2:
- rename lan966x_mac_learn_impl to __lan966x_mac_learn
- rename lan966x_mac_cpu_copy to lan966x_mac_ip_learn
- fix grammar and typos in comments and commit messages
- add reference counter for entries that copy frames to CPU
====================

Reviewed-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2a5ab39b 7aacb894
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -7,4 +7,4 @@ obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o

lan966x-switch-objs  := lan966x_main.o lan966x_phylink.o lan966x_port.o \
			lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \
			lan966x_vlan.o lan966x_fdb.o
			lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o
+31 −5
Original line number Diff line number Diff line
@@ -68,7 +68,8 @@ static void lan966x_mac_select(struct lan966x *lan966x,
	lan_wr(mach, lan966x, ANA_MACHDATA);
}

int lan966x_mac_learn(struct lan966x *lan966x, int port,
static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid,
			       bool cpu_copy,
			       const unsigned char mac[ETH_ALEN],
			       unsigned int vid,
			       enum macaccess_entry_type type)
@@ -78,7 +79,8 @@ int lan966x_mac_learn(struct lan966x *lan966x, int port,
	/* Issue a write command */
	lan_wr(ANA_MACACCESS_VALID_SET(1) |
	       ANA_MACACCESS_CHANGE2SW_SET(0) |
	       ANA_MACACCESS_DEST_IDX_SET(port) |
	       ANA_MACACCESS_MAC_CPU_COPY_SET(cpu_copy) |
	       ANA_MACACCESS_DEST_IDX_SET(pgid) |
	       ANA_MACACCESS_ENTRYTYPE_SET(type) |
	       ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),
	       lan966x, ANA_MACACCESS);
@@ -86,6 +88,30 @@ int lan966x_mac_learn(struct lan966x *lan966x, int port,
	return lan966x_mac_wait_for_completion(lan966x);
}

/* The mask of the front ports is encoded inside the mac parameter via a call
 * to lan966x_mdb_encode_mac().
 */
int lan966x_mac_ip_learn(struct lan966x *lan966x,
			 bool cpu_copy,
			 const unsigned char mac[ETH_ALEN],
			 unsigned int vid,
			 enum macaccess_entry_type type)
{
	WARN_ON(type != ENTRYTYPE_MACV4 && type != ENTRYTYPE_MACV6);

	return __lan966x_mac_learn(lan966x, 0, cpu_copy, mac, vid, type);
}

int lan966x_mac_learn(struct lan966x *lan966x, int port,
		      const unsigned char mac[ETH_ALEN],
		      unsigned int vid,
		      enum macaccess_entry_type type)
{
	WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED);

	return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);
}

int lan966x_mac_forget(struct lan966x *lan966x,
		       const unsigned char mac[ETH_ALEN],
		       unsigned int vid,
+2 −0
Original line number Diff line number Diff line
@@ -926,6 +926,7 @@ static int lan966x_probe(struct platform_device *pdev)
		lan966x_port_init(lan966x->ports[p]);
	}

	lan966x_mdb_init(lan966x);
	err = lan966x_fdb_init(lan966x);
	if (err)
		goto cleanup_ports;
@@ -955,6 +956,7 @@ static int lan966x_remove(struct platform_device *pdev)
	mutex_destroy(&lan966x->stats_lock);

	lan966x_mac_purge_entries(lan966x);
	lan966x_mdb_deinit(lan966x);
	lan966x_fdb_deinit(lan966x);

	return 0;
+24 −2
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
/* Reserved amount for (SRC, PRIO) at index 8*SRC + PRIO */
#define QSYS_Q_RSRV			95

#define CPU_PORT			8

/* Reserved PGIDs */
#define PGID_CPU			(PGID_AGGR - 6)
#define PGID_UC				(PGID_AGGR - 5)
@@ -38,14 +40,16 @@
#define PGID_MCIPV4			(PGID_AGGR - 2)
#define PGID_MCIPV6			(PGID_AGGR - 1)

/* Non-reserved PGIDs, used for general purpose */
#define PGID_GP_START			(CPU_PORT + 1)
#define PGID_GP_END			PGID_CPU

#define LAN966X_SPEED_NONE		0
#define LAN966X_SPEED_2500		1
#define LAN966X_SPEED_1000		1
#define LAN966X_SPEED_100		2
#define LAN966X_SPEED_10		3

#define CPU_PORT			8

/* MAC table entry types.
 * ENTRYTYPE_NORMAL is subject to aging.
 * ENTRYTYPE_LOCKED is not subject to aging.
@@ -105,6 +109,10 @@ struct lan966x {
	/* worqueue for fdb */
	struct workqueue_struct *fdb_work;
	struct list_head fdb_entries;

	/* mdb */
	struct list_head mdb_entries;
	struct list_head pgid_entries;
};

struct lan966x_port_config {
@@ -157,6 +165,11 @@ int lan966x_port_pcs_set(struct lan966x_port *port,
			 struct lan966x_port_config *config);
void lan966x_port_init(struct lan966x_port *port);

int lan966x_mac_ip_learn(struct lan966x *lan966x,
			 bool cpu_copy,
			 const unsigned char mac[ETH_ALEN],
			 unsigned int vid,
			 enum macaccess_entry_type type);
int lan966x_mac_learn(struct lan966x *lan966x, int port,
		      const unsigned char mac[ETH_ALEN],
		      unsigned int vid,
@@ -206,6 +219,15 @@ int lan966x_handle_fdb(struct net_device *dev,
		       unsigned long event, const void *ctx,
		       const struct switchdev_notifier_fdb_info *fdb_info);

void lan966x_mdb_init(struct lan966x *lan966x);
void lan966x_mdb_deinit(struct lan966x *lan966x);
int lan966x_handle_port_mdb_add(struct lan966x_port *port,
				const struct switchdev_obj *obj);
int lan966x_handle_port_mdb_del(struct lan966x_port *port,
				const struct switchdev_obj *obj);
void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid);
void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid);

static inline void __iomem *lan_addr(void __iomem *base[],
				     int id, int tinst, int tcnt,
				     int gbase, int ginst,
+506 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+

#include <net/switchdev.h>

#include "lan966x_main.h"

struct lan966x_pgid_entry {
	struct list_head list;
	int index;
	refcount_t refcount;
	u16 ports;
};

struct lan966x_mdb_entry {
	struct list_head list;
	unsigned char mac[ETH_ALEN];
	u16 vid;
	u16 ports;
	struct lan966x_pgid_entry *pgid;
	u8 cpu_copy;
};

void lan966x_mdb_init(struct lan966x *lan966x)
{
	INIT_LIST_HEAD(&lan966x->mdb_entries);
	INIT_LIST_HEAD(&lan966x->pgid_entries);
}

static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
{
	struct lan966x_mdb_entry *mdb_entry, *tmp;

	list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
		list_del(&mdb_entry->list);
		kfree(mdb_entry);
	}
}

static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
{
	struct lan966x_pgid_entry *pgid_entry, *tmp;

	list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
		list_del(&pgid_entry->list);
		kfree(pgid_entry);
	}
}

void lan966x_mdb_deinit(struct lan966x *lan966x)
{
	lan966x_mdb_purge_mdb_entries(lan966x);
	lan966x_mdb_purge_pgid_entries(lan966x);
}

static struct lan966x_mdb_entry *
lan966x_mdb_entry_get(struct lan966x *lan966x,
		      const unsigned char *mac,
		      u16 vid)
{
	struct lan966x_mdb_entry *mdb_entry;

	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
		if (ether_addr_equal(mdb_entry->mac, mac) &&
		    mdb_entry->vid == vid)
			return mdb_entry;
	}

	return NULL;
}

static struct lan966x_mdb_entry *
lan966x_mdb_entry_add(struct lan966x *lan966x,
		      const struct switchdev_obj_port_mdb *mdb)
{
	struct lan966x_mdb_entry *mdb_entry;

	mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
	if (!mdb_entry)
		return ERR_PTR(-ENOMEM);

	ether_addr_copy(mdb_entry->mac, mdb->addr);
	mdb_entry->vid = mdb->vid;

	list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);

	return mdb_entry;
}

static void lan966x_mdb_encode_mac(unsigned char *mac,
				   struct lan966x_mdb_entry *mdb_entry,
				   enum macaccess_entry_type type)
{
	ether_addr_copy(mac, mdb_entry->mac);

	if (type == ENTRYTYPE_MACV4) {
		mac[0] = 0;
		mac[1] = mdb_entry->ports >> 8;
		mac[2] = mdb_entry->ports & 0xff;
	} else if (type == ENTRYTYPE_MACV6) {
		mac[0] = mdb_entry->ports >> 8;
		mac[1] = mdb_entry->ports & 0xff;
	}
}

static int lan966x_mdb_ip_add(struct lan966x_port *port,
			      const struct switchdev_obj_port_mdb *mdb,
			      enum macaccess_entry_type type)
{
	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
	struct lan966x *lan966x = port->lan966x;
	struct lan966x_mdb_entry *mdb_entry;
	unsigned char mac[ETH_ALEN];
	bool cpu_copy = false;

	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
	if (!mdb_entry) {
		mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
		if (IS_ERR(mdb_entry))
			return PTR_ERR(mdb_entry);
	} else {
		lan966x_mdb_encode_mac(mac, mdb_entry, type);
		lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
	}

	if (cpu_port)
		mdb_entry->cpu_copy++;
	else
		mdb_entry->ports |= BIT(port->chip_port);

	/* Copy the frame to CPU only if the CPU is in the VLAN */
	if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
	    mdb_entry->cpu_copy)
		cpu_copy = true;

	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	return lan966x_mac_ip_learn(lan966x, cpu_copy,
				    mac, mdb_entry->vid, type);
}

static int lan966x_mdb_ip_del(struct lan966x_port *port,
			      const struct switchdev_obj_port_mdb *mdb,
			      enum macaccess_entry_type type)
{
	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
	struct lan966x *lan966x = port->lan966x;
	struct lan966x_mdb_entry *mdb_entry;
	unsigned char mac[ETH_ALEN];
	u16 ports;

	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
	if (!mdb_entry)
		return -ENOENT;

	ports = mdb_entry->ports;
	if (cpu_port) {
		/* If there are still other references to the CPU port then
		 * there is no point to delete and add again the same entry
		 */
		mdb_entry->cpu_copy--;
		if (mdb_entry->cpu_copy)
			return 0;
	} else {
		ports &= ~BIT(port->chip_port);
	}

	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);

	mdb_entry->ports = ports;

	if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
		list_del(&mdb_entry->list);
		kfree(mdb_entry);
		return 0;
	}

	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
				    mac, mdb_entry->vid, type);
}

static struct lan966x_pgid_entry *
lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
{
	struct lan966x_pgid_entry *pgid_entry;

	pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
	if (!pgid_entry)
		return ERR_PTR(-ENOMEM);

	pgid_entry->ports = ports;
	pgid_entry->index = index;
	refcount_set(&pgid_entry->refcount, 1);

	list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);

	return pgid_entry;
}

static struct lan966x_pgid_entry *
lan966x_pgid_entry_get(struct lan966x *lan966x,
		       struct lan966x_mdb_entry *mdb_entry)
{
	struct lan966x_pgid_entry *pgid_entry;
	int index;

	/* Try to find an existing pgid that uses the same ports as the
	 * mdb_entry
	 */
	list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
		if (pgid_entry->ports == mdb_entry->ports) {
			refcount_inc(&pgid_entry->refcount);
			return pgid_entry;
		}
	}

	/* Try to find an empty pgid entry and allocate one in case it finds it,
	 * otherwise it means that there are no more resources
	 */
	for (index = PGID_GP_START; index < PGID_GP_END; index++) {
		bool used = false;

		list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
			if (pgid_entry->index == index) {
				used = true;
				break;
			}
		}

		if (!used)
			return lan966x_pgid_entry_add(lan966x, index,
						      mdb_entry->ports);
	}

	return ERR_PTR(-ENOSPC);
}

static void lan966x_pgid_entry_del(struct lan966x *lan966x,
				   struct lan966x_pgid_entry *pgid_entry)
{
	if (!refcount_dec_and_test(&pgid_entry->refcount))
		return;

	list_del(&pgid_entry->list);
	kfree(pgid_entry);
}

static int lan966x_mdb_l2_add(struct lan966x_port *port,
			      const struct switchdev_obj_port_mdb *mdb,
			      enum macaccess_entry_type type)
{
	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
	struct lan966x *lan966x = port->lan966x;
	struct lan966x_pgid_entry *pgid_entry;
	struct lan966x_mdb_entry *mdb_entry;
	unsigned char mac[ETH_ALEN];

	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
	if (!mdb_entry) {
		mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
		if (IS_ERR(mdb_entry))
			return PTR_ERR(mdb_entry);
	} else {
		lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
		lan966x_mdb_encode_mac(mac, mdb_entry, type);
		lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
	}

	if (cpu_port) {
		mdb_entry->ports |= BIT(CPU_PORT);
		mdb_entry->cpu_copy++;
	} else {
		mdb_entry->ports |= BIT(port->chip_port);
	}

	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
	if (IS_ERR(pgid_entry)) {
		list_del(&mdb_entry->list);
		kfree(mdb_entry);
		return PTR_ERR(pgid_entry);
	}
	mdb_entry->pgid = pgid_entry;

	/* Copy the frame to CPU only if the CPU is in the VLAN */
	if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
	    mdb_entry->cpu_copy)
		mdb_entry->ports &= BIT(CPU_PORT);

	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
		ANA_PGID_PGID,
		lan966x, ANA_PGID(pgid_entry->index));

	return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
				 mdb_entry->vid, type);
}

static int lan966x_mdb_l2_del(struct lan966x_port *port,
			      const struct switchdev_obj_port_mdb *mdb,
			      enum macaccess_entry_type type)
{
	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
	struct lan966x *lan966x = port->lan966x;
	struct lan966x_pgid_entry *pgid_entry;
	struct lan966x_mdb_entry *mdb_entry;
	unsigned char mac[ETH_ALEN];
	u16 ports;

	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
	if (!mdb_entry)
		return -ENOENT;

	ports = mdb_entry->ports;
	if (cpu_port) {
		/* If there are still other references to the CPU port then
		 * there is no point to delete and add again the same entry
		 */
		mdb_entry->cpu_copy--;
		if (mdb_entry->cpu_copy)
			return 0;

		ports &= ~BIT(CPU_PORT);
	} else {
		ports &= ~BIT(port->chip_port);
	}

	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
	lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);

	mdb_entry->ports = ports;

	if (!mdb_entry->ports) {
		list_del(&mdb_entry->list);
		kfree(mdb_entry);
		return 0;
	}

	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
	if (IS_ERR(pgid_entry)) {
		list_del(&mdb_entry->list);
		kfree(mdb_entry);
		return PTR_ERR(pgid_entry);
	}
	mdb_entry->pgid = pgid_entry;

	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
		ANA_PGID_PGID,
		lan966x, ANA_PGID(pgid_entry->index));

	return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
				 mdb_entry->vid, type);
}

static enum macaccess_entry_type
lan966x_mdb_classify(const unsigned char *mac)
{
	if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
		return ENTRYTYPE_MACV4;
	if (mac[0] == 0x33 && mac[1] == 0x33)
		return ENTRYTYPE_MACV6;
	return ENTRYTYPE_LOCKED;
}

int lan966x_handle_port_mdb_add(struct lan966x_port *port,
				const struct switchdev_obj *obj)
{
	const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
	enum macaccess_entry_type type;

	/* Split the way the entries are added for ipv4/ipv6 and for l2. The
	 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
	 * entry, while for l2 is required to use pgid entries
	 */
	type = lan966x_mdb_classify(mdb->addr);
	if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
		return lan966x_mdb_ip_add(port, mdb, type);

	return lan966x_mdb_l2_add(port, mdb, type);
}

int lan966x_handle_port_mdb_del(struct lan966x_port *port,
				const struct switchdev_obj *obj)
{
	const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
	enum macaccess_entry_type type;

	/* Split the way the entries are removed for ipv4/ipv6 and for l2. The
	 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
	 * entry, while for l2 is required to use pgid entries
	 */
	type = lan966x_mdb_classify(mdb->addr);
	if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
		return lan966x_mdb_ip_del(port, mdb, type);

	return lan966x_mdb_l2_del(port, mdb, type);
}

static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
				    struct lan966x_mdb_entry *mdb_entry,
				    enum macaccess_entry_type type)
{
	unsigned char mac[ETH_ALEN];

	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
	lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
}

static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
				    struct lan966x_mdb_entry *mdb_entry,
				    enum macaccess_entry_type type)
{
	struct lan966x_pgid_entry *pgid_entry;
	unsigned char mac[ETH_ALEN];

	lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);

	mdb_entry->ports |= BIT(CPU_PORT);

	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
	if (IS_ERR(pgid_entry))
		return;

	mdb_entry->pgid = pgid_entry;

	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
		ANA_PGID_PGID,
		lan966x, ANA_PGID(pgid_entry->index));

	lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
			  mdb_entry->vid, type);
}

void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
{
	struct lan966x_mdb_entry *mdb_entry;
	enum macaccess_entry_type type;

	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
		if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
			continue;

		type = lan966x_mdb_classify(mdb_entry->mac);
		if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
			lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
		else
			lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
	}
}

static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
				      struct lan966x_mdb_entry *mdb_entry,
				      enum macaccess_entry_type type)
{
	unsigned char mac[ETH_ALEN];

	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
	lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
}

static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
				      struct lan966x_mdb_entry *mdb_entry,
				      enum macaccess_entry_type type)
{
	struct lan966x_pgid_entry *pgid_entry;
	unsigned char mac[ETH_ALEN];

	lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
	lan966x_mdb_encode_mac(mac, mdb_entry, type);
	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);

	mdb_entry->ports &= ~BIT(CPU_PORT);

	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
	if (IS_ERR(pgid_entry))
		return;

	mdb_entry->pgid = pgid_entry;

	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
		ANA_PGID_PGID,
		lan966x, ANA_PGID(pgid_entry->index));

	lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
			  mdb_entry->vid, type);
}

void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
{
	struct lan966x_mdb_entry *mdb_entry;
	enum macaccess_entry_type type;

	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
		if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
			continue;

		type = lan966x_mdb_classify(mdb_entry->mac);
		if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
			lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
		else
			lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
	}
}
Loading