.maxage = SJA1105_AGEING_TIME_MS(300000),
                /* All entries within a FDB bin are available for learning */
                .dyn_tbsz = SJA1105ET_FDB_BIN_SIZE,
+               /* And the P/Q/R/S equivalent setting: */
+               .start_dynspc = 0,
                /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
                .poly = 0x97,
                /* This selects between Independent VLAN Learning (IVL) and
                 * Maybe correlate with no_linklocal_learn from bridge driver?
                 */
                .no_mgmt_learn = true,
+               /* P/Q/R/S only */
+               .use_static = true,
+               /* Dynamically learned FDB entries can overwrite other (older)
+                * dynamic FDB entries
+                */
+               .owr_dyn = true,
+               .drpnolearn = true,
        };
 
        table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS];
 int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
                        const unsigned char *addr, u16 vid)
 {
-       return -EOPNOTSUPP;
+       struct sja1105_l2_lookup_entry l2_lookup = {0};
+       struct sja1105_private *priv = ds->priv;
+       int rc, i;
+
+       /* Search for an existing entry in the FDB table */
+       l2_lookup.macaddr = ether_addr_to_u64(addr);
+       l2_lookup.vlanid = vid;
+       l2_lookup.iotag = SJA1105_S_TAG;
+       l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
+       l2_lookup.mask_vlanid = VLAN_VID_MASK;
+       l2_lookup.mask_iotag = BIT(0);
+       l2_lookup.destports = BIT(port);
+
+       rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
+                                        SJA1105_SEARCH, &l2_lookup);
+       if (rc == 0) {
+               /* Found and this port is already in the entry's
+                * port mask => job done
+                */
+               if (l2_lookup.destports & BIT(port))
+                       return 0;
+               /* l2_lookup.index is populated by the switch in case it
+                * found something.
+                */
+               l2_lookup.destports |= BIT(port);
+               goto skip_finding_an_index;
+       }
+
+       /* Not found, so try to find an unused spot in the FDB.
+        * This is slightly inefficient because the strategy is knock-knock at
+        * every possible position from 0 to 1023.
+        */
+       for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
+               rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
+                                                i, NULL);
+               if (rc < 0)
+                       break;
+       }
+       if (i == SJA1105_MAX_L2_LOOKUP_COUNT) {
+               dev_err(ds->dev, "FDB is full, cannot add entry.\n");
+               return -EINVAL;
+       }
+       l2_lookup.index = i;
+
+skip_finding_an_index:
+       return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
+                                           l2_lookup.index, &l2_lookup,
+                                           true);
 }
 
 int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
                        const unsigned char *addr, u16 vid)
 {
-       return -EOPNOTSUPP;
+       struct sja1105_l2_lookup_entry l2_lookup = {0};
+       struct sja1105_private *priv = ds->priv;
+       bool keep;
+       int rc;
+
+       l2_lookup.macaddr = ether_addr_to_u64(addr);
+       l2_lookup.vlanid = vid;
+       l2_lookup.iotag = SJA1105_S_TAG;
+       l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
+       l2_lookup.mask_vlanid = VLAN_VID_MASK;
+       l2_lookup.mask_iotag = BIT(0);
+       l2_lookup.destports = BIT(port);
+
+       rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
+                                        SJA1105_SEARCH, &l2_lookup);
+       if (rc < 0)
+               return 0;
+
+       l2_lookup.destports &= ~BIT(port);
+
+       /* Decide whether we remove just this port from the FDB entry,
+        * or if we remove it completely.
+        */
+       if (l2_lookup.destports)
+               keep = true;
+       else
+               keep = false;
+
+       return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
+                                           l2_lookup.index, &l2_lookup, keep);
 }
 
 static int sja1105_fdb_add(struct dsa_switch *ds, int port,