#include <linux/refcount.h>
 
 #include "spectrum.h"
+#include "spectrum_router.h"
 #include "reg.h"
 
 struct mlxsw_sp_fid_family;
 
 enum mlxsw_sp_fid_flood_profile_id {
        MLXSW_SP_FID_FLOOD_PROFILE_ID_BRIDGE = 1,
+       MLXSW_SP_FID_FLOOD_PROFILE_ID_RSP,
 };
 
 struct mlxsw_sp_fid_flood_profile {
        return 0;
 }
 
+static unsigned int mlxsw_sp_fid_rfid_port_offset_cff(unsigned int local_port)
+{
+       /* Port 0 is the CPU port. Since we never create RIFs based off that
+        * port, we don't need to count it.
+        */
+       return WARN_ON_ONCE(!local_port) ? 0 : local_port - 1;
+}
+
+static int
+mlxsw_sp_fid_rfid_pgt_size_cff(const struct mlxsw_sp_fid_family *fid_family,
+                              u16 *p_pgt_size)
+{
+       struct mlxsw_core *core = fid_family->mlxsw_sp->core;
+       unsigned int max_ports;
+       u16 pgt_size;
+       u16 max_lags;
+       int err;
+
+       max_ports = mlxsw_core_max_ports(core);
+
+       err = mlxsw_core_max_lag(core, &max_lags);
+       if (err)
+               return err;
+
+       pgt_size = (mlxsw_sp_fid_rfid_port_offset_cff(max_ports) + max_lags) *
+                  fid_family->flood_profile->nr_flood_tables;
+       *p_pgt_size = pgt_size;
+       return 0;
+}
+
 static u16
 mlxsw_sp_fid_pgt_base_ctl(const struct mlxsw_sp_fid_family *fid_family,
                          const struct mlxsw_sp_flood_table *flood_table)
                                      fid_family->flood_profile->profile_id);
 }
 
+static u16 mlxsw_sp_fid_rfid_fid_offset_cff(struct mlxsw_sp *mlxsw_sp,
+                                           u16 port_lag_id, bool is_lag)
+{
+       u16 max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
+
+       if (is_lag)
+               return mlxsw_sp_fid_rfid_port_offset_cff(max_ports) +
+                      port_lag_id;
+       else
+               return mlxsw_sp_fid_rfid_port_offset_cff(port_lag_id);
+}
+
 static int mlxsw_sp_fid_op(const struct mlxsw_sp_fid *fid, bool valid)
 {
        struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
        .profile_id             = MLXSW_SP_FID_FLOOD_PROFILE_ID_BRIDGE,
 };
 
+static const struct mlxsw_sp_flood_table mlxsw_sp_fid_rsp_flood_tables_cff[] = {
+       {
+               .packet_type    = MLXSW_SP_FLOOD_TYPE_UC,
+               .table_index    = 0,
+       },
+       {
+               .packet_type    = MLXSW_SP_FLOOD_TYPE_NOT_UC,
+               .table_index    = 1,
+       },
+};
+
+static const
+struct mlxsw_sp_fid_flood_profile mlxsw_sp_fid_rsp_flood_profile_cff = {
+       .flood_tables           = mlxsw_sp_fid_rsp_flood_tables_cff,
+       .nr_flood_tables        = ARRAY_SIZE(mlxsw_sp_fid_rsp_flood_tables_cff),
+       .profile_id             = MLXSW_SP_FID_FLOOD_PROFILE_ID_RSP,
+};
+
 static bool
 mlxsw_sp_fid_8021q_compare(const struct mlxsw_sp_fid *fid, const void *arg)
 {
        return 0;
 }
 
+static int mlxsw_sp_fid_rfid_setup_cff(struct mlxsw_sp_fid *fid,
+                                      const void *arg)
+{
+       struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
+       u16 rif_index = *(const u16 *)arg;
+       struct mlxsw_sp_rif *rif;
+       bool is_lag;
+       u16 port;
+       int err;
+
+       rif = mlxsw_sp_rif_by_index(mlxsw_sp, rif_index);
+       if (!rif)
+               return -ENOENT;
+
+       err = mlxsw_sp_rif_subport_port(rif, &port, &is_lag);
+       if (err)
+               return err;
+
+       fid->fid_offset = mlxsw_sp_fid_rfid_fid_offset_cff(mlxsw_sp, port,
+                                                          is_lag);
+       return 0;
+}
+
 static int mlxsw_sp_fid_rfid_configure(struct mlxsw_sp_fid *fid)
 {
        return mlxsw_sp_fid_op(fid, true);
        .fid_pack               = mlxsw_sp_fid_pack_ctl,
 };
 
+static int
+mlxsw_sp_fid_rfid_port_add_cff(struct mlxsw_sp *mlxsw_sp,
+                              const struct mlxsw_sp_flood_table *flood_table,
+                              u16 pgt_addr, u16 smpe, unsigned int local_port)
+{
+       int err;
+
+       err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe,
+                                         local_port, true);
+       if (err)
+               return err;
+
+       if (flood_table->packet_type == MLXSW_SP_FLOOD_TYPE_NOT_UC) {
+               u16 router_port = mlxsw_sp_router_port(mlxsw_sp);
+
+               err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe,
+                                                 router_port, true);
+               if (err)
+                       goto err_entry_port_set;
+       }
+
+       return 0;
+
+err_entry_port_set:
+       mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe, local_port,
+                                   false);
+       return err;
+}
+
+static void
+mlxsw_sp_fid_rfid_port_del_cff(struct mlxsw_sp *mlxsw_sp,
+                              const struct mlxsw_sp_flood_table *flood_table,
+                              u16 pgt_addr, u16 smpe, u16 local_port)
+{
+       if (flood_table->packet_type == MLXSW_SP_FLOOD_TYPE_NOT_UC) {
+               u16 router_port = mlxsw_sp_router_port(mlxsw_sp);
+
+               mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe,
+                                           router_port, false);
+       }
+       mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe, local_port,
+                                   false);
+}
+
+static int
+mlxsw_sp_fid_rfid_port_memb_ft_cff(const struct mlxsw_sp_fid_family *fid_family,
+                                  const struct mlxsw_sp_flood_table *flood_table,
+                                  const struct mlxsw_sp_port *mlxsw_sp_port,
+                                  bool member)
+{
+       struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
+       u16 local_port = mlxsw_sp_port->local_port;
+       u16 fid_pgt_base;
+       u16 fid_offset;
+       u16 pgt_addr;
+       u16 smpe;
+       u16 port;
+
+       /* In-PGT SMPE is only valid on Spectrum-1, CFF only on Spectrum>1. */
+       smpe = 0;
+
+       port = mlxsw_sp_port->lagged ? mlxsw_sp_port->lag_id : local_port;
+       fid_offset = mlxsw_sp_fid_rfid_fid_offset_cff(mlxsw_sp, port,
+                                                     mlxsw_sp_port->lagged);
+       fid_pgt_base = mlxsw_sp_fid_off_pgt_base_cff(fid_family, fid_offset);
+       pgt_addr = fid_pgt_base + flood_table->table_index;
+
+       if (member)
+               return mlxsw_sp_fid_rfid_port_add_cff(mlxsw_sp, flood_table,
+                                                     pgt_addr, smpe,
+                                                     local_port);
+
+       mlxsw_sp_fid_rfid_port_del_cff(mlxsw_sp, flood_table, pgt_addr, smpe,
+                                      local_port);
+       return 0;
+}
+
+static int
+mlxsw_sp_fid_rfid_port_memb_cff(const struct mlxsw_sp_fid_family *fid_family,
+                               const struct mlxsw_sp_port *mlxsw_sp_port,
+                               bool member)
+{
+       int i;
+
+       for (i = 0; i < fid_family->flood_profile->nr_flood_tables; i++) {
+               const struct mlxsw_sp_flood_table *flood_table =
+                       &fid_family->flood_profile->flood_tables[i];
+               int err;
+
+               err = mlxsw_sp_fid_rfid_port_memb_ft_cff(fid_family,
+                                                        flood_table,
+                                                        mlxsw_sp_port, member);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int
+mlxsw_sp_fid_rfid_port_init_cff(const struct mlxsw_sp_fid_family *fid_family,
+                               const struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       return mlxsw_sp_fid_rfid_port_memb_cff(fid_family, mlxsw_sp_port, true);
+}
+
+static void
+mlxsw_sp_fid_rfid_port_fini_cff(const struct mlxsw_sp_fid_family *fid_family,
+                               const struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       mlxsw_sp_fid_rfid_port_memb_cff(fid_family, mlxsw_sp_port, false);
+}
+
+static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_rfid_ops_cff = {
+       .setup                  = mlxsw_sp_fid_rfid_setup_cff,
+       .configure              = mlxsw_sp_fid_rfid_configure,
+       .deconfigure            = mlxsw_sp_fid_rfid_deconfigure,
+       .index_alloc            = mlxsw_sp_fid_rfid_index_alloc,
+       .compare                = mlxsw_sp_fid_rfid_compare,
+       .port_vid_map           = mlxsw_sp_fid_rfid_port_vid_map,
+       .port_vid_unmap         = mlxsw_sp_fid_rfid_port_vid_unmap,
+       .vni_set                = mlxsw_sp_fid_rfid_vni_set,
+       .vni_clear              = mlxsw_sp_fid_rfid_vni_clear,
+       .nve_flood_index_set    = mlxsw_sp_fid_rfid_nve_flood_index_set,
+       .nve_flood_index_clear  = mlxsw_sp_fid_rfid_nve_flood_index_clear,
+       .vid_to_fid_rif_update  = mlxsw_sp_fid_rfid_vid_to_fid_rif_update,
+       .pgt_size               = mlxsw_sp_fid_rfid_pgt_size_cff,
+       .fid_port_init          = mlxsw_sp_fid_rfid_port_init_cff,
+       .fid_port_fini          = mlxsw_sp_fid_rfid_port_fini_cff,
+       .fid_mid                = mlxsw_sp_fid_fid_mid_cff,
+       .fid_pack               = mlxsw_sp_fid_fid_pack_cff,
+};
+
 static int mlxsw_sp_fid_dummy_setup(struct mlxsw_sp_fid *fid, const void *arg)
 {
        fid->fid_offset = 0;
        .smpe_index_valid       = true,
 };
 
+static const struct mlxsw_sp_fid_family mlxsw_sp_fid_rfid_family_cff = {
+       .type                   = MLXSW_SP_FID_TYPE_RFID,
+       .fid_size               = sizeof(struct mlxsw_sp_fid),
+       .start_index            = MLXSW_SP_RFID_START,
+       .end_index              = MLXSW_SP_RFID_END,
+       .flood_profile          = &mlxsw_sp_fid_rsp_flood_profile_cff,
+       .rif_type               = MLXSW_SP_RIF_TYPE_SUBPORT,
+       .ops                    = &mlxsw_sp_fid_rfid_ops_cff,
+       .smpe_index_valid       = false,
+};
+
 static const struct mlxsw_sp_fid_family *mlxsw_sp2_fid_family_arr_cff[] = {
        [MLXSW_SP_FID_TYPE_8021Q]       = &mlxsw_sp2_fid_8021q_family_cff,
        [MLXSW_SP_FID_TYPE_8021D]       = &mlxsw_sp2_fid_8021d_family_cff,
        [MLXSW_SP_FID_TYPE_DUMMY]       = &mlxsw_sp2_fid_dummy_family,
+       [MLXSW_SP_FID_TYPE_RFID]        = &mlxsw_sp_fid_rfid_family_cff,
 };
 
 static struct mlxsw_sp_fid *mlxsw_sp_fid_lookup(struct mlxsw_sp *mlxsw_sp,
 static const
 struct mlxsw_sp_fid_flood_profile *mlxsw_sp_fid_flood_profiles[] = {
        &mlxsw_sp_fid_8021d_flood_profile,
+       &mlxsw_sp_fid_rsp_flood_profile_cff,
 };
 
 static int