[PATCH net-next v7 8/9] net: dsa: lan9645x: add mdb management
flat view
STALE134d
SUPERSEDED: 1 (1M)
From: Jens Emil Schulz Østergaard <hidden>
Date: 2026-06-03 07:26:37
Also in:
linux-devicetree, lkml
Subsystem:
microchip ksz series ethernet switch driver, networking drivers, networking [dsa], the rest · Maintainers:
Woojung Huh, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn, Vladimir Oltean, Linus Torvalds
Revision v7 of 15 in this series; 1 review trailer.
Revisions (15)
- v1 [diff vs current]
- v2 [diff vs current]
- v3 [diff vs current]
- v4 [diff vs current]
- v5 [diff vs current]
- v6 [diff vs current]
- v7 current
- v8 [diff vs current]
- v9 [diff vs current]
- v10 [diff vs current]
- v11 [diff vs current]
- v12 [diff vs current]
- v13 [diff vs current]
- v14 [diff vs current]
- v15 [diff vs current]
Add support for dsa mdb callbacks. L2 multicast and IP multicast is handled differently. IP multicast stores the port group mask inline in the mac table. L2 multicast points to a PGID index, which encodes the port group mask. Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com> Signed-off-by: Jens Emil Schulz Østergaard <redacted> --- Changes in v7: - refactor IGMP/MLD/IPMC_CTRL to use new LAN9645X_CPUQ_DEF, LAN9645X_CPUQ_TRAP and LAN9645X_CPUQ_COPY queues - add __aligned(2) to mac variable in lan9645x_mdb_update_dest Changes in v5: - skip igmp/mld redir for npi port Changes in v4: - clean up fresh mdb when hw mac table write fails. - avoid void return in lan9645x_offload_fwd_mark Changes in v3: - avoid mdb add/del dealloc when mac table writes fail - dealloc mdb entries on deinit Changes in v2: - New file: selftests required implementation of the mdb callbacks. --- drivers/net/dsa/microchip/lan9645x/Makefile | 1 + drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c | 14 + drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 62 ++++ drivers/net/dsa/microchip/lan9645x/lan9645x_main.h | 21 ++ drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c | 397 +++++++++++++++++++++ drivers/net/dsa/microchip/lan9645x/lan9645x_port.c | 11 +- 6 files changed, 505 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/microchip/lan9645x/Makefile
index 70815edca5b9..2413d11fe849 100644
--- a/drivers/net/dsa/microchip/lan9645x/Makefile
+++ b/drivers/net/dsa/microchip/lan9645x/Makefile@@ -4,6 +4,7 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_LAN9645X) += mchp-lan9645x.o mchp-lan9645x-objs := \ lan9645x_mac.o \ lan9645x_main.o \ + lan9645x_mdb.o \ lan9645x_npi.o \ lan9645x_phylink.o \ lan9645x_port.o \
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
index 1079e49f6271..1470ac38b635 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c@@ -142,6 +142,20 @@ int lan9645x_mact_learn(struct lan9645x *lan9645x, int port, return err; } +int lan9645x_mact_learn_cpu_copy(struct lan9645x *lan9645x, int port, + const unsigned char *addr, u16 vid, + enum macaccess_entry_type type, bool cpu_copy) +{ + int err; + + mutex_lock(&lan9645x->mact_lock); + err = __lan9645x_mact_learn_cpu_copy(lan9645x, port, addr, vid, type, + cpu_copy); + mutex_unlock(&lan9645x->mact_lock); + + return err; +} + int lan9645x_mact_flush(struct lan9645x *lan9645x, int port) { int err;
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
index f9fb41692c6b..d8926e65618a 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c@@ -70,6 +70,7 @@ static void lan9645x_teardown(struct dsa_switch *ds) destroy_workqueue(lan9645x->owq); lan9645x_npi_port_deinit(lan9645x, lan9645x->npi); lan9645x_mac_deinit(lan9645x); + lan9645x_mdb_deinit(lan9645x); mutex_destroy(&lan9645x->fwd_domain_lock); }
@@ -156,6 +157,7 @@ static int lan9645x_setup(struct dsa_switch *ds) err = lan9645x_mac_init(lan9645x); if (err) goto err_mutex; + lan9645x_mdb_init(lan9645x); /* Link Aggregation Mode: NETDEV_LAG_HASH_L2 */ lan_wr(ANA_AGGR_CFG_AC_SMAC_ENA |
@@ -301,6 +303,7 @@ static int lan9645x_setup(struct dsa_switch *ds) err_mac: lan9645x_mac_deinit(lan9645x); + lan9645x_mdb_deinit(lan9645x); err_mutex: mutex_destroy(&lan9645x->fwd_domain_lock); lan9645x_npi_port_deinit(lan9645x, lan9645x->npi);
@@ -700,6 +703,61 @@ static int lan9645x_fdb_del(struct dsa_switch *ds, int port, return lan9645x_mact_forget(lan9645x, addr, vid, ENTRYTYPE_LOCKED); } +static int lan9645x_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct net_device *bridge_dev = lan9645x_db2bridge(db); + struct lan9645x *lan9645x = ds->priv; + + dev_dbg(lan9645x->dev, "port=%d addr=%pM vid=%u\n", port, mdb->addr, + mdb->vid); + + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_mdb_present_in_other_db(ds, port, mdb, db)) + return 0; + + if (port == lan9645x->npi) + port = CPU_PORT; + + return lan9645x_mdb_port_add(lan9645x, port, mdb, bridge_dev); +} + +static int lan9645x_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct net_device *bridge_dev = lan9645x_db2bridge(db); + struct lan9645x *lan9645x = ds->priv; + int err; + + dev_dbg(lan9645x->dev, "port=%d addr=%pM vid=%u\n", port, mdb->addr, + mdb->vid); + + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_mdb_present_in_other_db(ds, port, mdb, db)) + return 0; + + if (port == lan9645x->npi) + port = CPU_PORT; + + err = lan9645x_mdb_port_del(lan9645x, port, mdb, bridge_dev); + if (err == -ENOENT) { + dev_dbg(lan9645x->dev, + "mdb not found port=%d addr=%pM vid=%u\n", port, + mdb->addr, mdb->vid); + return 0; + } + + return err; +} + static const struct dsa_switch_ops lan9645x_switch_ops = { .get_tag_protocol = lan9645x_get_tag_protocol,
@@ -733,6 +791,10 @@ static const struct dsa_switch_ops lan9645x_switch_ops = { .port_fdb_dump = lan9645x_fdb_dump, .port_fdb_add = lan9645x_fdb_add, .port_fdb_del = lan9645x_fdb_del, + + /* Multicast database */ + .port_mdb_add = lan9645x_mdb_add, + .port_mdb_del = lan9645x_mdb_del, }; static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x)
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
index f5d8b7042d57..40d9de23e4bc 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h@@ -205,6 +205,14 @@ struct lan9645x { /* VLAN entries */ struct lan9645x_vlan vlans[VLAN_N_VID]; + /* Multicast Forwarding Database */ + struct list_head mdb_entries; + struct list_head pgid_entries; + /* lock for mdb_entries and pgid_entries. Must be taken before mact_lock + * if both are taken. + */ + struct mutex mdb_lock; + int num_port_dis; };
@@ -407,5 +415,18 @@ int lan9645x_mac_init(struct lan9645x *lan9645x); void lan9645x_mac_deinit(struct lan9645x *lan9645x); int lan9645x_mact_dsa_dump(struct lan9645x *lan9645x, int port, dsa_fdb_dump_cb_t *cb, void *data); +int lan9645x_mact_learn_cpu_copy(struct lan9645x *lan9645x, int port, + const unsigned char *addr, u16 vid, + enum macaccess_entry_type type, bool cpu_copy); + +/* Multicast Database lan9645x_mdb.c */ +int lan9645x_mdb_port_add(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge); +int lan9645x_mdb_port_del(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge); +void lan9645x_mdb_init(struct lan9645x *lan9645x); +void lan9645x_mdb_deinit(struct lan9645x *lan9645x); #endif /* __LAN9645X_MAIN_H__ */
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c
new file mode 100644
index 000000000000..11a1d8d98101
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c@@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2026 Microchip Technology Inc. + */ + +#include "lan9645x_main.h" + +/* HW ignores dest_idx for IPv4/IPv6 types, so we use this dummy index */ +#define IP_ENTRY_PGID 0 +#define PGID_INDEX(pgid) ((pgid) ? (pgid)->index : IP_ENTRY_PGID) + +struct lan9645x_pgid_entry { + struct list_head list; + int index; + refcount_t refcount; + u16 ports; +}; + +struct lan9645x_mdb_entry { + struct list_head list; + unsigned char mac[ETH_ALEN]; + u16 vid; + u16 ports; + struct lan9645x_pgid_entry *pgid; +}; + +void lan9645x_mdb_init(struct lan9645x *lan9645x) +{ + INIT_LIST_HEAD(&lan9645x->mdb_entries); + INIT_LIST_HEAD(&lan9645x->pgid_entries); + mutex_init(&lan9645x->mdb_lock); + + /* Use CPU queues to communicate frame classification to the CPU */ + lan_rmw(ANA_CPUQ_CFG_CPUQ_IGMP_SET(LAN9645X_CPUQ_TRAP) | + ANA_CPUQ_CFG_CPUQ_MLD_SET(LAN9645X_CPUQ_TRAP) | + ANA_CPUQ_CFG_CPUQ_IPMC_CTRL_SET(LAN9645X_CPUQ_COPY), + ANA_CPUQ_CFG_CPUQ_IGMP | + ANA_CPUQ_CFG_CPUQ_MLD | + ANA_CPUQ_CFG_CPUQ_IPMC_CTRL, + lan9645x, ANA_CPUQ_CFG); +} + +static enum macaccess_entry_type lan9645x_mdb_classify(const unsigned char *mac) +{ + if (ether_addr_is_ipv4_mcast(mac)) + return ENTRYTYPE_MACV4; + if (ether_addr_is_ipv6_mcast(mac)) + return ENTRYTYPE_MACV6; + return ENTRYTYPE_LOCKED; +} + +static struct lan9645x_mdb_entry * +lan9645x_mdb_entry_lookup(struct lan9645x *lan9645x, const unsigned char *mac, + u16 vid) +{ + struct lan9645x_mdb_entry *mdb; + + list_for_each_entry(mdb, &lan9645x->mdb_entries, list) { + if (ether_addr_equal(mdb->mac, mac) && mdb->vid == vid) + return mdb; + } + + return NULL; +} + +static struct lan9645x_mdb_entry * +lan9645x_mdb_entry_alloc(struct lan9645x *lan9645x, + const unsigned char addr[ETH_ALEN], u16 vid) +{ + struct lan9645x_mdb_entry *mdb_entry; + + mdb_entry = kzalloc_obj(*mdb_entry); + if (!mdb_entry) + return ERR_PTR(-ENOMEM); + + ether_addr_copy(mdb_entry->mac, addr); + mdb_entry->vid = vid; + + list_add_tail(&mdb_entry->list, &lan9645x->mdb_entries); + + dev_dbg(lan9645x->dev, "vid=%u addr=%pM\n", mdb_entry->vid, + mdb_entry->mac); + + return mdb_entry; +} + +static void lan9645x_mdb_encode_mac(unsigned char *dst, unsigned char *mac, + u16 ports, enum macaccess_entry_type type) +{ + ether_addr_copy(dst, mac); + + /* The HW encodes the portmask in the high bits of the mac for ip + * multicast entries, to save on the limited PGID resources. + * + * IPv4 Multicast DMAC: 0x01005Exxxxxx + * IPv6 Multicast DMAC: 0x3333xxxxxxxx + * + * which gives us 24 or 16 bits to encode the portmask. + */ + if (type == ENTRYTYPE_MACV4) { + dst[0] = 0; + dst[1] = ports >> 8; + dst[2] = ports & 0xff; + } else if (type == ENTRYTYPE_MACV6) { + dst[0] = ports >> 8; + dst[1] = ports & 0xff; + } +} + +static void lan9645x_pgid_entry_put(struct lan9645x *lan9645x, + struct lan9645x_pgid_entry *pgid_entry) +{ + if (!pgid_entry) + return; + + if (!refcount_dec_and_test(&pgid_entry->refcount)) + return; + + dev_dbg(lan9645x->dev, "pgid=%d ports=0x%x", pgid_entry->index, + pgid_entry->ports); + /* We leave the PGID written in HW, as no entry is pointing to it. */ + list_del(&pgid_entry->list); + kfree(pgid_entry); +} + +static void lan9645x_mdb_entry_dealloc(struct lan9645x *lan9645x, + struct lan9645x_mdb_entry *mdb_entry) +{ + dev_dbg(lan9645x->dev, "vid=%u addr=%pM\n", mdb_entry->vid, + mdb_entry->mac); + list_del(&mdb_entry->list); + lan9645x_pgid_entry_put(lan9645x, mdb_entry->pgid); + kfree(mdb_entry); +} + +static struct lan9645x_pgid_entry * +lan9645x_mdb_pgid_entry_lookup(struct lan9645x *lan9645x, u16 ports) +{ + struct lan9645x_pgid_entry *pgid_entry; + + list_for_each_entry(pgid_entry, &lan9645x->pgid_entries, list) { + if (pgid_entry->ports == ports && + refcount_inc_not_zero(&pgid_entry->refcount)) + return pgid_entry; + } + + return NULL; +} + +static struct lan9645x_pgid_entry * +lan9645x_pgid_entry_alloc(struct lan9645x *lan9645x, int index, u16 ports) +{ + struct lan9645x_pgid_entry *pgid_entry; + + pgid_entry = kzalloc_obj(*pgid_entry); + 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, &lan9645x->pgid_entries); + + dev_dbg(lan9645x->dev, "index=%d ports=0x%x", pgid_entry->index, + pgid_entry->ports); + + lan_rmw(ANA_PGID_PGID_SET(pgid_entry->ports), + ANA_PGID_PGID, lan9645x, + ANA_PGID(pgid_entry->index)); + + return pgid_entry; +} + +static struct lan9645x_pgid_entry * +lan9645x_mdb_pgid_entry_create(struct lan9645x *lan9645x, u16 ports) +{ + struct lan9645x_pgid_entry *pgid_entry = NULL; + int index; + + for (index = PGID_GP_START; index < PGID_GP_END; index++) { + bool used = false; + + list_for_each_entry(pgid_entry, &lan9645x->pgid_entries, list) { + if (pgid_entry->index == index) { + used = true; + break; + } + } + + if (!used) + return lan9645x_pgid_entry_alloc(lan9645x, index, + ports); + } + + return ERR_PTR(-ENOSPC); +} + +static struct lan9645x_pgid_entry * +lan9645x_mdb_pgid_entry_get(struct lan9645x *lan9645x, u16 ports, + enum macaccess_entry_type type) +{ + struct lan9645x_pgid_entry *pgid_entry; + u16 pgid_ports; + + if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6 || !ports) + return NULL; + + /* CPU_PORT forwarding is handled by cpu_copy flag on mac table entry. + * So we can strip CPU_PORT here to allow better PGID sharing. + */ + pgid_ports = ports & ~BIT(CPU_PORT); + + pgid_entry = lan9645x_mdb_pgid_entry_lookup(lan9645x, pgid_ports); + if (!pgid_entry) + return lan9645x_mdb_pgid_entry_create(lan9645x, pgid_ports); + + return pgid_entry; +} + +static int lan9645x_mdb_update_dest(struct lan9645x *lan9645x, + struct lan9645x_mdb_entry *mdb_entry, + enum macaccess_entry_type type, + struct lan9645x_pgid_entry *new_pgid, + u16 new_ports) +{ + unsigned char mac[ETH_ALEN] __aligned(2); + struct lan9645x_pgid_entry *old_pgid; + int err, pgid_index; + bool cpu_copy; + + old_pgid = mdb_entry->pgid; + lan9645x_mdb_encode_mac(mac, mdb_entry->mac, new_ports, type); + cpu_copy = !!(new_ports & BIT(CPU_PORT)); + pgid_index = PGID_INDEX(new_pgid); + + /* For IP multicast, the hardware lookup uses the DMAC + * (01:00:5E:.. / 33:33:..) as the (mac, vid) key, not the encoded mac. + * Therefore, this CMD_LEARN will atomically rewrite the existing + * hardware entry. We intentionally do not do a forget before learn + * sequence, as that would not be atomic, and leave a forwarding gap. + */ + err = lan9645x_mact_learn_cpu_copy(lan9645x, pgid_index, mac, + mdb_entry->vid, type, cpu_copy); + if (err) { + lan9645x_pgid_entry_put(lan9645x, new_pgid); + return err; + } + mdb_entry->pgid = new_pgid; + mdb_entry->ports = new_ports; + lan9645x_pgid_entry_put(lan9645x, old_pgid); + return 0; +} + +static int __lan9645x_mdb_add(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + struct lan9645x_pgid_entry *new_pgid; + struct lan9645x_mdb_entry *mdb_entry; + u16 new_ports; + int err; + + mdb_entry = lan9645x_mdb_entry_lookup(lan9645x, addr, vid); + if (!mdb_entry) { + mdb_entry = lan9645x_mdb_entry_alloc(lan9645x, addr, vid); + if (IS_ERR(mdb_entry)) + return PTR_ERR(mdb_entry); + } + + if (mdb_entry->ports & BIT(chip_port)) + return 0; + + new_ports = mdb_entry->ports | BIT(chip_port); + + /* Update PGID ptr for non-IP entries (L2 multicast) */ + new_pgid = lan9645x_mdb_pgid_entry_get(lan9645x, new_ports, type); + if (IS_ERR(new_pgid)) { + /* Out of PGIDs or mem. Continue forwarding to old port + * group, or remove if fresh mdb_entry. + */ + if (!mdb_entry->ports) + lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry); + + return PTR_ERR(new_pgid); + } + + err = lan9645x_mdb_update_dest(lan9645x, mdb_entry, type, new_pgid, + new_ports); + if (err && !mdb_entry->ports) + lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry); + + return err; +} + +static int __lan9645x_mdb_del(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + struct lan9645x_pgid_entry *new_pgid; + struct lan9645x_mdb_entry *mdb_entry; + u16 new_ports; + int err; + + mdb_entry = lan9645x_mdb_entry_lookup(lan9645x, addr, vid); + if (!mdb_entry) + return -ENOENT; + + if (!(mdb_entry->ports & BIT(chip_port))) + return 0; + + new_ports = mdb_entry->ports & ~BIT(chip_port); + + if (!new_ports) { + /* For IP multicast hardware uses DMAC as key (mac,vid) not + * encoded mac. + */ + err = lan9645x_mact_forget(lan9645x, mdb_entry->mac, + mdb_entry->vid, type); + if (err) + return err; + lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry); + return 0; + } + + /* Update PGID ptr for non-IP entries (L2 multicast) */ + new_pgid = lan9645x_mdb_pgid_entry_get(lan9645x, new_ports, type); + if (IS_ERR(new_pgid)) + /* Continue forwarding to old port group. */ + return PTR_ERR(new_pgid); + + return lan9645x_mdb_update_dest(lan9645x, mdb_entry, type, new_pgid, + new_ports); +} + +static int lan9645x_mdb_add(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + int err; + + mutex_lock(&lan9645x->mdb_lock); + err = __lan9645x_mdb_add(lan9645x, chip_port, addr, vid, type); + mutex_unlock(&lan9645x->mdb_lock); + return err; +} + +static int lan9645x_mdb_del(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + int err; + + mutex_lock(&lan9645x->mdb_lock); + err = __lan9645x_mdb_del(lan9645x, chip_port, addr, vid, type); + mutex_unlock(&lan9645x->mdb_lock); + return err; +} + +int lan9645x_mdb_port_add(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge) +{ + enum macaccess_entry_type type; + u16 vid = mdb->vid; + + type = lan9645x_mdb_classify(mdb->addr); + + if (!vid) + vid = lan9645x_vlan_unaware_pvid(!!bridge); + + return lan9645x_mdb_add(lan9645x, port, mdb->addr, vid, type); +} + +int lan9645x_mdb_port_del(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge) +{ + enum macaccess_entry_type type; + u16 vid = mdb->vid; + + type = lan9645x_mdb_classify(mdb->addr); + + if (!vid) + vid = lan9645x_vlan_unaware_pvid(!!bridge); + + return lan9645x_mdb_del(lan9645x, port, mdb->addr, vid, type); +} + +void lan9645x_mdb_deinit(struct lan9645x *lan9645x) +{ + struct lan9645x_mdb_entry *mdb, *tmp; + + list_for_each_entry_safe(mdb, tmp, &lan9645x->mdb_entries, list) + lan9645x_mdb_entry_dealloc(lan9645x, mdb); + + mutex_destroy(&lan9645x->mdb_lock); +}
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
index 87eb0f6dce4c..b8300745ec40 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c@@ -208,8 +208,17 @@ int lan9645x_port_setup(struct dsa_switch *ds, int port) ANA_PORT_CFG_PORTID_VAL, lan9645x, ANA_PORT_CFG(p->chip_port)); - if (p->chip_port != lan9645x->npi) + if (p->chip_port != lan9645x->npi) { lan9645x_vlan_set_hostmode(p); + lan_rmw(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(true) | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(true) | + ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(true), + ANA_CPU_FWD_CFG_IGMP_REDIR_ENA | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA | + ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, + lan9645x, ANA_CPU_FWD_CFG(p->chip_port)); + } + return 0; }
--
2.52.0