diff mbox series

[net-next,2/2] net: sparx5: Add mdb handlers

Message ID 20220321101446.2372093-3-casper.casan@gmail.com (mailing list archive)
State Accepted
Commit 3bacfccdcb2d3268adaf66d2ff4024251a93b916
Delegated to: Netdev Maintainers
Headers show
Series net: sparx5: Add multicast support | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 4 maintainers not CCed: vladimir.oltean@nxp.com bjarni.jonasson@microchip.com horatiu.vultur@microchip.com linux-arm-kernel@lists.infradead.org
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Casper Andersson March 21, 2022, 10:14 a.m. UTC
Adds mdb handlers. Uses the PGID arbiter to
find a free entry in the PGID table for the
multicast group port mask.

Signed-off-by: Casper Andersson <casper.casan@gmail.com>
---
 .../microchip/sparx5/sparx5_mactable.c        |  33 ++++--
 .../ethernet/microchip/sparx5/sparx5_main.h   |   2 +
 .../microchip/sparx5/sparx5_switchdev.c       | 111 ++++++++++++++++++
 3 files changed, 136 insertions(+), 10 deletions(-)

Comments

Steen Hegelund March 21, 2022, 1:24 p.m. UTC | #1
On Mon, 2022-03-21 at 11:14 +0100, Casper Andersson wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Adds mdb handlers. Uses the PGID arbiter to
> find a free entry in the PGID table for the
> multicast group port mask.
> 
> Signed-off-by: Casper Andersson <casper.casan@gmail.com>
> ---
>  .../microchip/sparx5/sparx5_mactable.c        |  33 ++++--
>  .../ethernet/microchip/sparx5/sparx5_main.h   |   2 +
>  .../microchip/sparx5/sparx5_switchdev.c       | 111 ++++++++++++++++++
>  3 files changed, 136 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
> b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
> index 82b1b3c9a065..35abb3d0ce19 100644
> --- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
> @@ -186,11 +186,11 @@ bool sparx5_mact_getnext(struct sparx5 *sparx5,
>         return ret == 0;
>  }
> 
> -static int sparx5_mact_lookup(struct sparx5 *sparx5,
> -                             const unsigned char mac[ETH_ALEN],
> -                             u16 vid)
> +bool sparx5_mact_find(struct sparx5 *sparx5,
> +                     const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
>  {
>         int ret;
> +       u32 cfg2;
> 
>         mutex_lock(&sparx5->lock);
> 
> @@ -202,16 +202,29 @@ static int sparx5_mact_lookup(struct sparx5 *sparx5,
>                 sparx5, LRN_COMMON_ACCESS_CTRL);
> 
>         ret = sparx5_mact_wait_for_completion(sparx5);
> -       if (ret)
> -               goto out;
> -
> -       ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET
> -               (spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2));
> +       if (ret == 0) {
> +               cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
> +               if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))
> +                       *pcfg2 = cfg2;
> +               else
> +                       ret = -ENOENT;
> +       }
> 
> -out:
>         mutex_unlock(&sparx5->lock);
> 
> -       return ret;
> +       return ret == 0;
> +}
> +
> +static int sparx5_mact_lookup(struct sparx5 *sparx5,
> +                             const unsigned char mac[ETH_ALEN],
> +                             u16 vid)
> +{
> +       u32 pcfg2;
> +
> +       if (sparx5_mact_find(sparx5, mac, vid, &pcfg2))
> +               return 1;
> +
> +       return 0;
>  }

I suggest to drop this and only use your new function (or at least let it return a real error code
like -ENOENT in case of an error).

> 
>  int sparx5_mact_forget(struct sparx5 *sparx5,
> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
> b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
> index e97fa091c740..7a04b8f2a546 100644
> --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
> @@ -310,6 +310,8 @@ int sparx5_mact_learn(struct sparx5 *sparx5, int port,
>                       const unsigned char mac[ETH_ALEN], u16 vid);
>  bool sparx5_mact_getnext(struct sparx5 *sparx5,
>                          unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2);
> +bool sparx5_mact_find(struct sparx5 *sparx5,
> +                     const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2);
>  int sparx5_mact_forget(struct sparx5 *sparx5,
>                        const unsigned char mac[ETH_ALEN], u16 vid);
>  int sparx5_add_mact_entry(struct sparx5 *sparx5,
> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
> b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
> index 2d5de1c06fab..9e1ea35d0c40 100644
> --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
> @@ -366,6 +366,109 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev,
>                                   v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
>  }
> 
> +static int sparx5_handle_port_mdb_add(struct net_device *dev,
> +                                     struct notifier_block *nb,
> +                                     const struct switchdev_obj_port_mdb *v)
> +{
> +       struct sparx5_port *port = netdev_priv(dev);
> +       struct sparx5 *spx5 = port->sparx5;
> +       u16 pgid_idx, vid;
> +       u32 mact_entry;
> +       int res, err;
> +
> +       /* When VLAN unaware the vlan value is not parsed and we receive vid 0.
> +        * Fall back to bridge vid 1.
> +        */
> +       if (!br_vlan_enabled(spx5->hw_bridge_dev))
> +               vid = 1;
> +       else
> +               vid = v->vid;
> +
> +       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
> +
> +       if (res) {
> +               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
> +
> +               /* MC_IDX has an offset of 65 in the PGID table. */
> +               pgid_idx += PGID_MCAST_START;

This will overlap some of the first ports with the flood masks according to:

https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html?select=ana_ac,pgid

You should use the custom area (PGID_BASE + 8 and onwards) for this new feature.

> +               sparx5_pgid_update_mask(port, pgid_idx, true);
> +       } else {
> +               err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx);
> +               if (err) {
> +                       netdev_warn(dev, "multicast pgid table full\n");
> +                       return err;
> +               }
> +               sparx5_pgid_update_mask(port, pgid_idx, true);
> +               err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid);
> +               if (err) {
> +                       netdev_warn(dev, "could not learn mac address %pM\n", v->addr);
> +                       sparx5_pgid_update_mask(port, pgid_idx, false);
> +                       return err;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int sparx5_mdb_del_entry(struct net_device *dev,
> +                               struct sparx5 *spx5,
> +                               const unsigned char mac[ETH_ALEN],
> +                               const u16 vid,
> +                               u16 pgid_idx)
> +{
> +       int err;
> +
> +       err = sparx5_mact_forget(spx5, mac, vid);
> +       if (err) {
> +               netdev_warn(dev, "could not forget mac address %pM", mac);
> +               return err;
> +       }
> +       err = sparx5_pgid_free(spx5, pgid_idx);
> +       if (err) {
> +               netdev_err(dev, "attempted to free already freed pgid\n");
> +               return err;
> +       }
> +       return 0;
> +}
> +
> +static int sparx5_handle_port_mdb_del(struct net_device *dev,
> +                                     struct notifier_block *nb,
> +                                     const struct switchdev_obj_port_mdb *v)
> +{
> +       struct sparx5_port *port = netdev_priv(dev);
> +       struct sparx5 *spx5 = port->sparx5;
> +       u16 pgid_idx, vid;
> +       u32 mact_entry, res, pgid_entry[3];
> +       int err;
> +
> +       if (!br_vlan_enabled(spx5->hw_bridge_dev))
> +               vid = 1;
> +       else
> +               vid = v->vid;
> +
> +       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
> +
> +       if (res) {
> +               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
> +
> +               /* MC_IDX has an offset of 65 in the PGID table. */
> +               pgid_idx += PGID_MCAST_START;
> +               sparx5_pgid_update_mask(port, pgid_idx, false);
> +
> +               pgid_entry[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid_idx));
> +               pgid_entry[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid_idx));
> +               pgid_entry[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid_idx));
> +               if (pgid_entry[0] == 0 && pgid_entry[1] == 0 && pgid_entry[2] == 0) {

Looks like you could use a function that gets the pgid port mask (the inverse of the
sparx5_pgid_update_mask() function)


> +                       /* No ports are in MC group. Remove entry */
> +                       err = sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx);
> +                       if (err)
> +                               return err;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
>  static int sparx5_handle_port_obj_add(struct net_device *dev,
>                                       struct notifier_block *nb,
>                                       struct switchdev_notifier_port_obj_info *info)
> @@ -378,6 +481,10 @@ static int sparx5_handle_port_obj_add(struct net_device *dev,
>                 err = sparx5_handle_port_vlan_add(dev, nb,
>                                                   SWITCHDEV_OBJ_PORT_VLAN(obj));
>                 break;
> +       case SWITCHDEV_OBJ_ID_PORT_MDB:
> +               err = sparx5_handle_port_mdb_add(dev, nb,
> +                                                SWITCHDEV_OBJ_PORT_MDB(obj));
> +               break;
>         default:
>                 err = -EOPNOTSUPP;
>                 break;
> @@ -426,6 +533,10 @@ static int sparx5_handle_port_obj_del(struct net_device *dev,
>                 err = sparx5_handle_port_vlan_del(dev, nb,
>                                                   SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);
>                 break;
> +       case SWITCHDEV_OBJ_ID_PORT_MDB:
> +               err = sparx5_handle_port_mdb_del(dev, nb,
> +                                                SWITCHDEV_OBJ_PORT_MDB(obj));
> +               break;
>         default:
>                 err = -EOPNOTSUPP;
>                 break;
> --
> 2.30.2
> 

Best Regards
Steen
Casper Andersson March 22, 2022, 9:59 a.m. UTC | #2
So this was already merged, but I have some comments on the feedback for
the follow up patch.

> > +static int sparx5_handle_port_mdb_add(struct net_device *dev,
> > +                                     struct notifier_block *nb,
> > +                                     const struct switchdev_obj_port_mdb *v)
> > +{
> > +       struct sparx5_port *port = netdev_priv(dev);
> > +       struct sparx5 *spx5 = port->sparx5;
> > +       u16 pgid_idx, vid;
> > +       u32 mact_entry;
> > +       int res, err;
> > +
> > +       /* When VLAN unaware the vlan value is not parsed and we receive vid 0.
> > +        * Fall back to bridge vid 1.
> > +        */
> > +       if (!br_vlan_enabled(spx5->hw_bridge_dev))
> > +               vid = 1;
> > +       else
> > +               vid = v->vid;
> > +
> > +       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
> > +
> > +       if (res) {
> > +               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
> > +
> > +               /* MC_IDX has an offset of 65 in the PGID table. */
> > +               pgid_idx += PGID_MCAST_START;
> 
> This will overlap some of the first ports with the flood masks according to:
> 
> https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html?select=ana_ac,pgid
> 
> You should use the custom area (PGID_BASE + 8 and onwards) for this new feature.

I'm aware of the overlap, hence why the PGID table has those fields
marked as reserved. But your datasheet says that the multicast index 
has an offset of 65 (ie. MC_IDX = 0 is at PGID = 65). This is already 
taken into account in the mact_learn function. I could set the
allocation to start at PGID_BASE + 8, but the offset still needs to 
be 65, right?

BR
Casper
Steen Hegelund March 22, 2022, 2:51 p.m. UTC | #3
Hi Casper

On Tue, 2022-03-22 at 10:59 +0100, Casper Andersson wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> So this was already merged, but I have some comments on the feedback for
> the follow up patch.
> 
> > > +static int sparx5_handle_port_mdb_add(struct net_device *dev,
> > > +                                     struct notifier_block *nb,
> > > +                                     const struct switchdev_obj_port_mdb *v)
> > > +{
> > > +       struct sparx5_port *port = netdev_priv(dev);
> > > +       struct sparx5 *spx5 = port->sparx5;
> > > +       u16 pgid_idx, vid;
> > > +       u32 mact_entry;
> > > +       int res, err;
> > > +
> > > +       /* When VLAN unaware the vlan value is not parsed and we receive vid 0.
> > > +        * Fall back to bridge vid 1.
> > > +        */
> > > +       if (!br_vlan_enabled(spx5->hw_bridge_dev))
> > > +               vid = 1;
> > > +       else
> > > +               vid = v->vid;
> > > +
> > > +       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
> > > +
> > > +       if (res) {
> > > +               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
> > > +
> > > +               /* MC_IDX has an offset of 65 in the PGID table. */
> > > +               pgid_idx += PGID_MCAST_START;
> > 
> > This will overlap some of the first ports with the flood masks according to:
> > 
> > https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html?select=ana_ac,pgid
> > 
> > You should use the custom area (PGID_BASE + 8 and onwards) for this new feature.
> 
> I'm aware of the overlap, hence why the PGID table has those fields
> marked as reserved. But your datasheet says that the multicast index
> has an offset of 65 (ie. MC_IDX = 0 is at PGID = 65). This is already
> taken into account in the mact_learn function. I could set the
> allocation to start at PGID_BASE + 8, but the offset still needs to
> be 65, right?

As I understand the PGID table functionality, you will need to start your custom table at PGID_BASE
+ 8 as the bitmasks at offset 65 to 70 are used as flood masks, so their purpose are already
defined.

BR
Steen

> 
> BR
> Casper
Casper Andersson March 22, 2022, 3:40 p.m. UTC | #4
On 2022-03-22 15:51, Steen Hegelund wrote:
> Hi Casper
> 
> On Tue, 2022-03-22 at 10:59 +0100, Casper Andersson wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> > 
> > So this was already merged, but I have some comments on the feedback for
> > the follow up patch.
> > 
> > > > +static int sparx5_handle_port_mdb_add(struct net_device *dev,
> > > > +                                     struct notifier_block *nb,
> > > > +                                     const struct switchdev_obj_port_mdb *v)
> > > > +{
> > > > +       struct sparx5_port *port = netdev_priv(dev);
> > > > +       struct sparx5 *spx5 = port->sparx5;
> > > > +       u16 pgid_idx, vid;
> > > > +       u32 mact_entry;
> > > > +       int res, err;
> > > > +
> > > > +       /* When VLAN unaware the vlan value is not parsed and we receive vid 0.
> > > > +        * Fall back to bridge vid 1.
> > > > +        */
> > > > +       if (!br_vlan_enabled(spx5->hw_bridge_dev))
> > > > +               vid = 1;
> > > > +       else
> > > > +               vid = v->vid;
> > > > +
> > > > +       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
> > > > +
> > > > +       if (res) {
> > > > +               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
> > > > +
> > > > +               /* MC_IDX has an offset of 65 in the PGID table. */
> > > > +               pgid_idx += PGID_MCAST_START;
> > > 
> > > This will overlap some of the first ports with the flood masks according to:
> > > 
> > > https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html?select=ana_ac,pgid
> > > 
> > > You should use the custom area (PGID_BASE + 8 and onwards) for this new feature.
> > 
> > I'm aware of the overlap, hence why the PGID table has those fields
> > marked as reserved. But your datasheet says that the multicast index
> > has an offset of 65 (ie. MC_IDX = 0 is at PGID = 65). This is already
> > taken into account in the mact_learn function. I could set the
> > allocation to start at PGID_BASE + 8, but the offset still needs to
> > be 65, right?
> 
> As I understand the PGID table functionality, you will need to start your custom table at PGID_BASE
> + 8 as the bitmasks at offset 65 to 70 are used as flood masks, so their purpose are already
> defined.

It is fine to start the allocation of multicast entries at PGID_BASE + 8,
but the multicast index (MC_IDX) in the mactable (that points at the
PGID table, to get the port mask) assumes that MC_IDX = 0 is at PGID = 65.
Not sure if it's appropriate to reference the datasheet here on Netdev,
but on figure 4-48 (PGID Layout) this is shown.

I tried setting the offset to base + 8 but it does not seem to find the 
right mask. Because if I say I put the mask on MC_IDX 0, and then place
it on base + 8 (73), then it will not find that mask because the hardware
will look for the mask at 65.

Even though the offset is 65, the PGID allocation will make sure
that it never allocates anything below 73. Meaning that the lowest
possible MC_IDX it can allocate will be 8.

I'm also a bit confused as to why the multicast offset is 65, but there
are a lot of overlapping areas in the PGID table and, e.g., GLAG has 
another offset where its index 0 is at PGID = 833 (as can be seen in
figure 4-48).

BR
Casper

> BR
> Steen
> 
> > 
> > BR
> > Casper
>
diff mbox series

Patch

diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
index 82b1b3c9a065..35abb3d0ce19 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
@@ -186,11 +186,11 @@  bool sparx5_mact_getnext(struct sparx5 *sparx5,
 	return ret == 0;
 }
 
-static int sparx5_mact_lookup(struct sparx5 *sparx5,
-			      const unsigned char mac[ETH_ALEN],
-			      u16 vid)
+bool sparx5_mact_find(struct sparx5 *sparx5,
+		      const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
 {
 	int ret;
+	u32 cfg2;
 
 	mutex_lock(&sparx5->lock);
 
@@ -202,16 +202,29 @@  static int sparx5_mact_lookup(struct sparx5 *sparx5,
 		sparx5, LRN_COMMON_ACCESS_CTRL);
 
 	ret = sparx5_mact_wait_for_completion(sparx5);
-	if (ret)
-		goto out;
-
-	ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET
-		(spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2));
+	if (ret == 0) {
+		cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
+		if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))
+			*pcfg2 = cfg2;
+		else
+			ret = -ENOENT;
+	}
 
-out:
 	mutex_unlock(&sparx5->lock);
 
-	return ret;
+	return ret == 0;
+}
+
+static int sparx5_mact_lookup(struct sparx5 *sparx5,
+			      const unsigned char mac[ETH_ALEN],
+			      u16 vid)
+{
+	u32 pcfg2;
+
+	if (sparx5_mact_find(sparx5, mac, vid, &pcfg2))
+		return 1;
+
+	return 0;
 }
 
 int sparx5_mact_forget(struct sparx5 *sparx5,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index e97fa091c740..7a04b8f2a546 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -310,6 +310,8 @@  int sparx5_mact_learn(struct sparx5 *sparx5, int port,
 		      const unsigned char mac[ETH_ALEN], u16 vid);
 bool sparx5_mact_getnext(struct sparx5 *sparx5,
 			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2);
+bool sparx5_mact_find(struct sparx5 *sparx5,
+		      const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2);
 int sparx5_mact_forget(struct sparx5 *sparx5,
 		       const unsigned char mac[ETH_ALEN], u16 vid);
 int sparx5_add_mact_entry(struct sparx5 *sparx5,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
index 2d5de1c06fab..9e1ea35d0c40 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
@@ -366,6 +366,109 @@  static int sparx5_handle_port_vlan_add(struct net_device *dev,
 				  v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
 }
 
+static int sparx5_handle_port_mdb_add(struct net_device *dev,
+				      struct notifier_block *nb,
+				      const struct switchdev_obj_port_mdb *v)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *spx5 = port->sparx5;
+	u16 pgid_idx, vid;
+	u32 mact_entry;
+	int res, err;
+
+	/* When VLAN unaware the vlan value is not parsed and we receive vid 0.
+	 * Fall back to bridge vid 1.
+	 */
+	if (!br_vlan_enabled(spx5->hw_bridge_dev))
+		vid = 1;
+	else
+		vid = v->vid;
+
+	res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
+
+	if (res) {
+		pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
+
+		/* MC_IDX has an offset of 65 in the PGID table. */
+		pgid_idx += PGID_MCAST_START;
+		sparx5_pgid_update_mask(port, pgid_idx, true);
+	} else {
+		err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx);
+		if (err) {
+			netdev_warn(dev, "multicast pgid table full\n");
+			return err;
+		}
+		sparx5_pgid_update_mask(port, pgid_idx, true);
+		err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid);
+		if (err) {
+			netdev_warn(dev, "could not learn mac address %pM\n", v->addr);
+			sparx5_pgid_update_mask(port, pgid_idx, false);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static int sparx5_mdb_del_entry(struct net_device *dev,
+				struct sparx5 *spx5,
+				const unsigned char mac[ETH_ALEN],
+				const u16 vid,
+				u16 pgid_idx)
+{
+	int err;
+
+	err = sparx5_mact_forget(spx5, mac, vid);
+	if (err) {
+		netdev_warn(dev, "could not forget mac address %pM", mac);
+		return err;
+	}
+	err = sparx5_pgid_free(spx5, pgid_idx);
+	if (err) {
+		netdev_err(dev, "attempted to free already freed pgid\n");
+		return err;
+	}
+	return 0;
+}
+
+static int sparx5_handle_port_mdb_del(struct net_device *dev,
+				      struct notifier_block *nb,
+				      const struct switchdev_obj_port_mdb *v)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *spx5 = port->sparx5;
+	u16 pgid_idx, vid;
+	u32 mact_entry, res, pgid_entry[3];
+	int err;
+
+	if (!br_vlan_enabled(spx5->hw_bridge_dev))
+		vid = 1;
+	else
+		vid = v->vid;
+
+	res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
+
+	if (res) {
+		pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
+
+		/* MC_IDX has an offset of 65 in the PGID table. */
+		pgid_idx += PGID_MCAST_START;
+		sparx5_pgid_update_mask(port, pgid_idx, false);
+
+		pgid_entry[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid_idx));
+		pgid_entry[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid_idx));
+		pgid_entry[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid_idx));
+		if (pgid_entry[0] == 0 && pgid_entry[1] == 0 && pgid_entry[2] == 0) {
+			/* No ports are in MC group. Remove entry */
+			err = sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
 static int sparx5_handle_port_obj_add(struct net_device *dev,
 				      struct notifier_block *nb,
 				      struct switchdev_notifier_port_obj_info *info)
@@ -378,6 +481,10 @@  static int sparx5_handle_port_obj_add(struct net_device *dev,
 		err = sparx5_handle_port_vlan_add(dev, nb,
 						  SWITCHDEV_OBJ_PORT_VLAN(obj));
 		break;
+	case SWITCHDEV_OBJ_ID_PORT_MDB:
+		err = sparx5_handle_port_mdb_add(dev, nb,
+						 SWITCHDEV_OBJ_PORT_MDB(obj));
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -426,6 +533,10 @@  static int sparx5_handle_port_obj_del(struct net_device *dev,
 		err = sparx5_handle_port_vlan_del(dev, nb,
 						  SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);
 		break;
+	case SWITCHDEV_OBJ_ID_PORT_MDB:
+		err = sparx5_handle_port_mdb_del(dev, nb,
+						 SWITCHDEV_OBJ_PORT_MDB(obj));
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;