#include <linux/mii.h>
 #include <linux/phy.h>
 #include <linux/if_bridge.h>
+#include <linux/etherdevice.h>
 
 #include "lan9303.h"
 
 static const int alrport_2_portmap[] = {1, 2, 4, 0, 3, 5, 6, 7 };
 static const int portmap_2_alrport[] = {3, 0, 1, 4, 2, 5, 6, 7 };
 
+/* Return pointer to first free ALR cache entry, return NULL if none */
+static struct lan9303_alr_cache_entry *
+lan9303_alr_cache_find_free(struct lan9303 *chip)
+{
+       int i;
+       struct lan9303_alr_cache_entry *entr = chip->alr_cache;
+
+       for (i = 0; i < LAN9303_NUM_ALR_RECORDS; i++, entr++)
+               if (entr->port_map == 0)
+                       return entr;
+
+       return NULL;
+}
+
+/* Return pointer to ALR cache entry matching MAC address */
+static struct lan9303_alr_cache_entry *
+lan9303_alr_cache_find_mac(struct lan9303 *chip, const u8 *mac_addr)
+{
+       int i;
+       struct lan9303_alr_cache_entry *entr = chip->alr_cache;
+
+       BUILD_BUG_ON_MSG(sizeof(struct lan9303_alr_cache_entry) & 1,
+                        "ether_addr_equal require u16 alignment");
+
+       for (i = 0; i < LAN9303_NUM_ALR_RECORDS; i++, entr++)
+               if (ether_addr_equal(entr->mac_addr, mac_addr))
+                       return entr;
+
+       return NULL;
+}
+
 /* Wait a while until mask & reg == value. Otherwise return timeout. */
 static int lan9303_csr_reg_wait(struct lan9303 *chip, int regno,
                                int mask, char value)
        dump_ctx->cb(mac, 0, is_static, dump_ctx->data);
 }
 
+/* Set a static ALR entry. Delete entry if port_map is zero */
+static void lan9303_alr_set_entry(struct lan9303 *chip, const u8 *mac,
+                                 u8 port_map, bool stp_override)
+{
+       u32 dat0, dat1, alr_port;
+
+       dev_dbg(chip->dev, "%s(%pM, %d)\n", __func__, mac, port_map);
+       dat1 = LAN9303_ALR_DAT1_STATIC;
+       if (port_map)
+               dat1 |= LAN9303_ALR_DAT1_VALID;
+       /* otherwise no ports: delete entry */
+       if (stp_override)
+               dat1 |= LAN9303_ALR_DAT1_AGE_OVERRID;
+
+       alr_port = portmap_2_alrport[port_map & 7];
+       dat1 &= ~LAN9303_ALR_DAT1_PORT_MASK;
+       dat1 |= alr_port << LAN9303_ALR_DAT1_PORT_BITOFFS;
+
+       dat0 = 0;
+       dat0 |= (mac[0] << 0);
+       dat0 |= (mac[1] << 8);
+       dat0 |= (mac[2] << 16);
+       dat0 |= (mac[3] << 24);
+
+       dat1 |= (mac[4] << 0);
+       dat1 |= (mac[5] << 8);
+
+       lan9303_alr_make_entry_raw(chip, dat0, dat1);
+}
+
+/* Add port to static ALR entry, create new static entry if needed */
+static int lan9303_alr_add_port(struct lan9303 *chip, const u8 *mac, int port,
+                               bool stp_override)
+{
+       struct lan9303_alr_cache_entry *entr;
+
+       entr = lan9303_alr_cache_find_mac(chip, mac);
+       if (!entr) { /*New entry */
+               entr = lan9303_alr_cache_find_free(chip);
+               if (!entr)
+                       return -ENOSPC;
+               ether_addr_copy(entr->mac_addr, mac);
+       }
+       entr->port_map |= BIT(port);
+       entr->stp_override = stp_override;
+       lan9303_alr_set_entry(chip, mac, entr->port_map, stp_override);
+
+       return 0;
+}
+
+/* Delete static port from ALR entry, delete entry if last port */
+static int lan9303_alr_del_port(struct lan9303 *chip, const u8 *mac, int port)
+{
+       struct lan9303_alr_cache_entry *entr;
+
+       entr = lan9303_alr_cache_find_mac(chip, mac);
+       if (!entr)
+               return 0;  /* no static entry found */
+
+       entr->port_map &= ~BIT(port);
+       if (entr->port_map == 0) /* zero means its free again */
+               eth_zero_addr(&entr->port_map);
+       lan9303_alr_set_entry(chip, mac, entr->port_map, entr->stp_override);
+
+       return 0;
+}
+
 static int lan9303_disable_processing_port(struct lan9303 *chip,
                                           unsigned int port)
 {
        lan9303_alr_loop(chip, alr_loop_cb_del_port_learned, &del_ctx);
 }
 
+static int lan9303_port_fdb_add(struct dsa_switch *ds, int port,
+                               const unsigned char *addr, u16 vid)
+{
+       struct lan9303 *chip = ds->priv;
+
+       dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid);
+       if (vid)
+               return -EOPNOTSUPP;
+
+       return lan9303_alr_add_port(chip, addr, port, false);
+}
+
+static int lan9303_port_fdb_del(struct dsa_switch *ds, int port,
+                               const unsigned char *addr, u16 vid)
+
+{
+       struct lan9303 *chip = ds->priv;
+
+       dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid);
+       if (vid)
+               return -EOPNOTSUPP;
+       lan9303_alr_del_port(chip, addr, port);
+
+       return 0;
+}
+
 static int lan9303_port_fdb_dump(struct dsa_switch *ds, int port,
                                 dsa_fdb_dump_cb_t *cb, void *data)
 {
        return 0;
 }
 
+static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port,
+                                   const struct switchdev_obj_port_mdb *mdb,
+                                   struct switchdev_trans *trans)
+{
+       struct lan9303 *chip = ds->priv;
+
+       dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr,
+               mdb->vid);
+       if (mdb->vid)
+               return -EOPNOTSUPP;
+       if (lan9303_alr_cache_find_mac(chip, mdb->addr))
+               return 0;
+       if (!lan9303_alr_cache_find_free(chip))
+               return -ENOSPC;
+
+       return 0;
+}
+
+static void lan9303_port_mdb_add(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_mdb *mdb,
+                                struct switchdev_trans *trans)
+{
+       struct lan9303 *chip = ds->priv;
+
+       dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr,
+               mdb->vid);
+       lan9303_alr_add_port(chip, mdb->addr, port, false);
+}
+
+static int lan9303_port_mdb_del(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb)
+{
+       struct lan9303 *chip = ds->priv;
+
+       dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr,
+               mdb->vid);
+       if (mdb->vid)
+               return -EOPNOTSUPP;
+       lan9303_alr_del_port(chip, mdb->addr, port);
+
+       return 0;
+}
+
 static const struct dsa_switch_ops lan9303_switch_ops = {
        .get_tag_protocol = lan9303_get_tag_protocol,
        .setup = lan9303_setup,
        .port_bridge_leave      = lan9303_port_bridge_leave,
        .port_stp_state_set     = lan9303_port_stp_state_set,
        .port_fast_age          = lan9303_port_fast_age,
+       .port_fdb_add           = lan9303_port_fdb_add,
+       .port_fdb_del           = lan9303_port_fdb_del,
        .port_fdb_dump          = lan9303_port_fdb_dump,
+       .port_mdb_prepare       = lan9303_port_mdb_prepare,
+       .port_mdb_add           = lan9303_port_mdb_add,
+       .port_mdb_del           = lan9303_port_mdb_del,
 };
 
 static int lan9303_register_switch(struct lan9303 *chip)