diff --git a/drivers/net/sfc/sfc.c b/drivers/net/sfc/sfc.c
index 274a98e228..cd2c97f3b2 100644
--- a/drivers/net/sfc/sfc.c
+++ b/drivers/net/sfc/sfc.c
@@ -895,6 +895,10 @@ sfc_attach(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_mae_attach;
 
+	rc = sfc_mae_switchdev_init(sa);
+	if (rc != 0)
+		goto fail_mae_switchdev_init;
+
 	sfc_log_init(sa, "fini nic");
 	efx_nic_fini(enp);
 
@@ -923,6 +927,9 @@ sfc_attach(struct sfc_adapter *sa)
 
 fail_sw_xstats_init:
 	sfc_flow_fini(sa);
+	sfc_mae_switchdev_fini(sa);
+
+fail_mae_switchdev_init:
 	sfc_mae_detach(sa);
 
 fail_mae_attach:
@@ -969,6 +976,7 @@ sfc_detach(struct sfc_adapter *sa)
 
 	sfc_flow_fini(sa);
 
+	sfc_mae_switchdev_fini(sa);
 	sfc_mae_detach(sa);
 	sfc_mae_counter_rxq_detach(sa);
 	sfc_filter_detach(sa);
diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c
index 4b520bc619..b3607a178b 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -44,6 +44,139 @@ sfc_mae_counter_registry_fini(struct sfc_mae_counter_registry *registry)
 	sfc_mae_counters_fini(&registry->counters);
 }
 
+static int
+sfc_mae_internal_rule_find_empty_slot(struct sfc_adapter *sa,
+				      struct sfc_mae_rule **rule)
+{
+	struct sfc_mae *mae = &sa->mae;
+	struct sfc_mae_internal_rules *internal_rules = &mae->internal_rules;
+	unsigned int entry;
+	int rc;
+
+	for (entry = 0; entry < SFC_MAE_NB_RULES_MAX; entry++) {
+		if (internal_rules->rules[entry].spec == NULL)
+			break;
+	}
+
+	if (entry == SFC_MAE_NB_RULES_MAX) {
+		rc = ENOSPC;
+		sfc_err(sa, "failed too many rules (%u rules used)", entry);
+		goto fail_too_many_rules;
+	}
+
+	*rule = &internal_rules->rules[entry];
+
+	return 0;
+
+fail_too_many_rules:
+	return rc;
+}
+
+int
+sfc_mae_rule_add_mport_match_deliver(struct sfc_adapter *sa,
+				     const efx_mport_sel_t *mport_match,
+				     const efx_mport_sel_t *mport_deliver,
+				     int prio, struct sfc_mae_rule **rulep)
+{
+	struct sfc_mae *mae = &sa->mae;
+	struct sfc_mae_rule *rule;
+	int rc;
+
+	sfc_log_init(sa, "entry");
+
+	if (prio > 0 && (unsigned int)prio >= mae->nb_action_rule_prios_max) {
+		rc = EINVAL;
+		sfc_err(sa, "failed: invalid priority %d (max %u)", prio,
+			mae->nb_action_rule_prios_max);
+		goto fail_invalid_prio;
+	}
+	if (prio < 0)
+		prio = mae->nb_action_rule_prios_max - 1;
+
+	rc = sfc_mae_internal_rule_find_empty_slot(sa, &rule);
+	if (rc != 0)
+		goto fail_find_empty_slot;
+
+	sfc_log_init(sa, "init MAE match spec");
+	rc = efx_mae_match_spec_init(sa->nic, EFX_MAE_RULE_ACTION,
+				     (uint32_t)prio, &rule->spec);
+	if (rc != 0) {
+		sfc_err(sa, "failed to init MAE match spec");
+		goto fail_match_init;
+	}
+
+	rc = efx_mae_match_spec_mport_set(rule->spec, mport_match, NULL);
+	if (rc != 0) {
+		sfc_err(sa, "failed to get MAE match mport selector");
+		goto fail_mport_set;
+	}
+
+	rc = efx_mae_action_set_spec_init(sa->nic, &rule->actions);
+	if (rc != 0) {
+		sfc_err(sa, "failed to init MAE action set");
+		goto fail_action_init;
+	}
+
+	rc = efx_mae_action_set_populate_deliver(rule->actions,
+						 mport_deliver);
+	if (rc != 0) {
+		sfc_err(sa, "failed to populate deliver action");
+		goto fail_populate_deliver;
+	}
+
+	rc = efx_mae_action_set_alloc(sa->nic, rule->actions,
+				      &rule->action_set);
+	if (rc != 0) {
+		sfc_err(sa, "failed to allocate action set");
+		goto fail_action_set_alloc;
+	}
+
+	rc = efx_mae_action_rule_insert(sa->nic, rule->spec, NULL,
+					&rule->action_set,
+					&rule->rule_id);
+	if (rc != 0) {
+		sfc_err(sa, "failed to insert action rule");
+		goto fail_rule_insert;
+	}
+
+	*rulep = rule;
+
+	sfc_log_init(sa, "done");
+
+	return 0;
+
+fail_rule_insert:
+	efx_mae_action_set_free(sa->nic, &rule->action_set);
+
+fail_action_set_alloc:
+fail_populate_deliver:
+	efx_mae_action_set_spec_fini(sa->nic, rule->actions);
+
+fail_action_init:
+fail_mport_set:
+	efx_mae_match_spec_fini(sa->nic, rule->spec);
+
+fail_match_init:
+fail_find_empty_slot:
+fail_invalid_prio:
+	sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+	return rc;
+}
+
+void
+sfc_mae_rule_del(struct sfc_adapter *sa, struct sfc_mae_rule *rule)
+{
+	if (rule == NULL || rule->spec == NULL)
+		return;
+
+	efx_mae_action_rule_remove(sa->nic, &rule->rule_id);
+	efx_mae_action_set_free(sa->nic, &rule->action_set);
+	efx_mae_action_set_spec_fini(sa->nic, rule->actions);
+	efx_mae_match_spec_fini(sa->nic, rule->spec);
+
+	rule->spec = NULL;
+}
+
 int
 sfc_mae_attach(struct sfc_adapter *sa)
 {
@@ -3443,3 +3576,81 @@ sfc_mae_flow_query(struct rte_eth_dev *dev,
 			"Query for action of this type is not supported");
 	}
 }
+
+int
+sfc_mae_switchdev_init(struct sfc_adapter *sa)
+{
+	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
+	struct sfc_mae *mae = &sa->mae;
+	efx_mport_sel_t pf;
+	efx_mport_sel_t phy;
+	int rc;
+
+	sfc_log_init(sa, "entry");
+
+	if (!sa->switchdev) {
+		sfc_log_init(sa, "switchdev is not enabled - skip");
+		return 0;
+	}
+
+	if (mae->status != SFC_MAE_STATUS_SUPPORTED) {
+		rc = ENOTSUP;
+		sfc_err(sa, "failed to init switchdev - no MAE support");
+		goto fail_no_mae;
+	}
+
+	rc = efx_mae_mport_by_pcie_function(encp->enc_pf, EFX_PCI_VF_INVALID,
+					    &pf);
+	if (rc != 0) {
+		sfc_err(sa, "failed get PF mport");
+		goto fail_pf_get;
+	}
+
+	rc = efx_mae_mport_by_phy_port(encp->enc_assigned_port, &phy);
+	if (rc != 0) {
+		sfc_err(sa, "failed get PHY mport");
+		goto fail_phy_get;
+	}
+
+	rc = sfc_mae_rule_add_mport_match_deliver(sa, &pf, &phy,
+			SFC_MAE_RULE_PRIO_LOWEST,
+			&mae->switchdev_rule_pf_to_ext);
+	if (rc != 0) {
+		sfc_err(sa, "failed add MAE rule to forward from PF to PHY");
+		goto fail_pf_add;
+	}
+
+	rc = sfc_mae_rule_add_mport_match_deliver(sa, &phy, &pf,
+			SFC_MAE_RULE_PRIO_LOWEST,
+			&mae->switchdev_rule_ext_to_pf);
+	if (rc != 0) {
+		sfc_err(sa, "failed add MAE rule to forward from PHY to PF");
+		goto fail_phy_add;
+	}
+
+	sfc_log_init(sa, "done");
+
+	return 0;
+
+fail_phy_add:
+	sfc_mae_rule_del(sa, mae->switchdev_rule_pf_to_ext);
+
+fail_pf_add:
+fail_phy_get:
+fail_pf_get:
+fail_no_mae:
+	sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+	return rc;
+}
+
+void
+sfc_mae_switchdev_fini(struct sfc_adapter *sa)
+{
+	struct sfc_mae *mae = &sa->mae;
+
+	if (!sa->switchdev)
+		return;
+
+	sfc_mae_rule_del(sa, mae->switchdev_rule_pf_to_ext);
+	sfc_mae_rule_del(sa, mae->switchdev_rule_ext_to_pf);
+}
diff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h
index 7e3b6a7a97..684f0daf7a 100644
--- a/drivers/net/sfc/sfc_mae.h
+++ b/drivers/net/sfc/sfc_mae.h
@@ -139,6 +139,26 @@ struct sfc_mae_counter_registry {
 	uint32_t			service_id;
 };
 
+/** Rules to forward traffic from PHY port to PF and from PF to PHY port */
+#define SFC_MAE_NB_SWITCHDEV_RULES	(2)
+/** Maximum required internal MAE rules */
+#define SFC_MAE_NB_RULES_MAX		(SFC_MAE_NB_SWITCHDEV_RULES)
+
+struct sfc_mae_rule {
+	efx_mae_match_spec_t		*spec;
+	efx_mae_actions_t		*actions;
+	efx_mae_aset_id_t		action_set;
+	efx_mae_rule_id_t		rule_id;
+};
+
+struct sfc_mae_internal_rules {
+	/*
+	 * Rules required to sustain switchdev mode or to provide
+	 * port representor functionality.
+	 */
+	struct sfc_mae_rule		rules[SFC_MAE_NB_RULES_MAX];
+};
+
 struct sfc_mae {
 	/** Assigned switch domain identifier */
 	uint16_t			switch_domain_id;
@@ -164,6 +184,14 @@ struct sfc_mae {
 	bool				counter_rxq_running;
 	/** Counter registry */
 	struct sfc_mae_counter_registry	counter_registry;
+	/** Driver-internal flow rules */
+	struct sfc_mae_internal_rules	internal_rules;
+	/**
+	 * Switchdev default rules. They forward traffic from PHY port
+	 * to PF and vice versa.
+	 */
+	struct sfc_mae_rule		*switchdev_rule_pf_to_ext;
+	struct sfc_mae_rule		*switchdev_rule_ext_to_pf;
 };
 
 struct sfc_adapter;
@@ -306,6 +334,27 @@ sfc_flow_insert_cb_t sfc_mae_flow_insert;
 sfc_flow_remove_cb_t sfc_mae_flow_remove;
 sfc_flow_query_cb_t sfc_mae_flow_query;
 
+/**
+ * The value used to represent the lowest priority.
+ * Used in MAE rule API.
+ */
+#define SFC_MAE_RULE_PRIO_LOWEST	(-1)
+
+/**
+ * Insert a driver-internal flow rule that matches traffic originating from
+ * some m-port selector and redirects it to another one
+ * (eg. PF --> PHY, PHY --> PF).
+ *
+ * If requested priority is negative, use the lowest priority.
+ */
+int sfc_mae_rule_add_mport_match_deliver(struct sfc_adapter *sa,
+					 const efx_mport_sel_t *mport_match,
+					 const efx_mport_sel_t *mport_deliver,
+					 int prio, struct sfc_mae_rule **rulep);
+void sfc_mae_rule_del(struct sfc_adapter *sa, struct sfc_mae_rule *rule);
+int sfc_mae_switchdev_init(struct sfc_adapter *sa);
+void sfc_mae_switchdev_fini(struct sfc_adapter *sa);
+
 #ifdef __cplusplus
 }
 #endif
