diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
new file mode 100644
index 000000000000..63e339091fcb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include "session-protect.h"
+#include "fw/api/time-event.h"
+#include "fw/api/context.h"
+#include "iface.h"
+#include <net/mac80211.h>
+
+void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
+				       struct iwl_rx_packet *pkt)
+{
+	struct iwl_session_prot_notif *notif = (void *)pkt->data;
+	int fw_link_id = le32_to_cpu(notif->mac_link_id);
+	struct ieee80211_bss_conf *link_conf =
+		iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+	struct ieee80211_vif *vif;
+	struct iwl_mld_vif *mld_vif;
+	struct iwl_mld_session_protect *session_protect;
+
+	if (WARN_ON(!link_conf))
+		return;
+
+	vif = link_conf->vif;
+	mld_vif = iwl_mld_vif_from_mac80211(vif);
+	session_protect = &mld_vif->session_protect;
+
+	if (!le32_to_cpu(notif->status)) {
+		memset(session_protect, 0, sizeof(*session_protect));
+	} else if (le32_to_cpu(notif->start)) {
+		/* End_jiffies indicates an active session */
+		session_protect->session_requested = false;
+		session_protect->end_jiffies =
+			TU_TO_EXP_TIME(session_protect->duration);
+		/* !session_protect->end_jiffies means inactive session */
+		if (!session_protect->end_jiffies)
+			session_protect->end_jiffies = 1;
+	} else {
+		memset(session_protect, 0, sizeof(*session_protect));
+	}
+}
+
+static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+						struct ieee80211_vif *vif,
+						u32 duration, u32 min_duration,
+						int link_id)
+{
+	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+	struct iwl_mld_link *link =
+		iwl_mld_link_dereference_check(mld_vif, link_id);
+	struct iwl_mld_session_protect *session_protect =
+		&mld_vif->session_protect;
+	struct iwl_session_prot_cmd cmd = {
+		.id_and_color = cpu_to_le32(link->fw_id),
+		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
+		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
+	};
+	int ret;
+
+	lockdep_assert_wiphy(mld->wiphy);
+
+	WARN(hweight16(vif->active_links) > 1,
+	     "Session protection isn't allowed with more than one active link");
+
+	if (session_protect->end_jiffies &&
+	    time_after(session_protect->end_jiffies,
+		       TU_TO_EXP_TIME(min_duration))) {
+		IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n",
+			     jiffies_to_msecs(session_protect->end_jiffies -
+					      jiffies));
+		return -EALREADY;
+	}
+
+	IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n",
+		     le32_to_cpu(cmd.duration_tu));
+
+	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP,
+						SESSION_PROTECTION_CMD), &cmd);
+
+	if (ret)
+		return ret;
+
+	/* end_jiffies will be updated when handling session_prot_notif */
+	session_protect->end_jiffies = 0;
+	session_protect->duration = duration;
+	session_protect->session_requested = true;
+
+	return 0;
+}
+
+void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+					 struct ieee80211_vif *vif,
+					 u32 duration, u32 min_duration,
+					 int link_id)
+{
+	int ret;
+
+	ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
+						   min_duration, link_id);
+	if (ret && ret != -EALREADY)
+		IWL_ERR(mld,
+			"Couldn't send the SESSION_PROTECTION_CMD (%d)\n",
+			ret);
+}
+
+struct iwl_mld_session_start_data {
+	struct iwl_mld *mld;
+	struct ieee80211_bss_conf *link_conf;
+	bool success;
+};
+
+static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait,
+				     struct iwl_rx_packet *pkt, void *_data)
+{
+	struct iwl_session_prot_notif *notif = (void *)pkt->data;
+	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
+	struct iwl_mld_session_start_data *data = _data;
+	struct ieee80211_bss_conf *link_conf;
+	struct iwl_mld *mld = data->mld;
+	int fw_link_id;
+
+	if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif),
+			 "short session prot notif (%d)\n",
+			 pkt_len))
+		return false;
+
+	fw_link_id = le32_to_cpu(notif->mac_link_id);
+	link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+
+	if (link_conf != data->link_conf)
+		return false;
+
+	if (!le32_to_cpu(notif->status))
+		return true;
+
+	if (notif->start) {
+		data->success = true;
+		return true;
+	}
+
+	return false;
+}
+
+int iwl_mld_start_session_protection(struct iwl_mld *mld,
+				     struct ieee80211_vif *vif,
+				     u32 duration, u32 min_duration,
+				     int link_id, unsigned long timeout)
+{
+	static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF };
+	struct iwl_notification_wait start_wait;
+	struct iwl_mld_session_start_data data = {
+		.mld = mld,
+		.link_conf = wiphy_dereference(mld->wiphy,
+					       vif->link_conf[link_id]),
+	};
+	int ret;
+
+	if (WARN_ON(!data.link_conf))
+		return -EINVAL;
+
+	iwl_init_notification_wait(&mld->notif_wait, &start_wait,
+				   start_notif, ARRAY_SIZE(start_notif),
+				   iwl_mld_session_start_fn, &data);
+
+	ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
+						   min_duration, link_id);
+
+	if (ret) {
+		iwl_remove_notification(&mld->notif_wait, &start_wait);
+		return ret == -EALREADY ? 0 : ret;
+	}
+
+	ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout);
+	if (ret)
+		return ret;
+	return data.success ? 0 : -EIO;
+}
+
+int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
+				      struct ieee80211_vif *vif,
+				      int link_id)
+{
+	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+	struct iwl_mld_link *link =
+		iwl_mld_link_dereference_check(mld_vif, link_id);
+	struct iwl_mld_session_protect *session_protect =
+		&mld_vif->session_protect;
+	struct iwl_session_prot_cmd cmd = {
+		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
+	};
+	int ret;
+
+	lockdep_assert_wiphy(mld->wiphy);
+
+	/* If there isn't an active session or a requested one for this
+	 * link do nothing
+	 */
+	if (!session_protect->session_requested &&
+	    !session_protect->end_jiffies)
+		return 0;
+
+	if (WARN_ON(!link))
+		return -EINVAL;
+
+	cmd.id_and_color = cpu_to_le32(link->fw_id),
+
+	ret = iwl_mld_send_cmd_pdu(mld,
+				   WIDE_ID(MAC_CONF_GROUP,
+					   SESSION_PROTECTION_CMD), &cmd);
+	if (ret) {
+		IWL_ERR(mld,
+			"Couldn't send the SESSION_PROTECTION_CMD\n");
+		return ret;
+	}
+
+	memset(session_protect, 0, sizeof(*session_protect));
+
+	return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h
new file mode 100644
index 000000000000..642bec8451a1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#ifndef __session_protect_h__
+#define __session_protect_h__
+
+#include "mld.h"
+#include "hcmd.h"
+#include <net/mac80211.h>
+#include "fw/api/mac-cfg.h"
+
+/**
+ * DOC: session protection
+ *
+ * Session protection is an API from the firmware that allows the driver to
+ * request time on medium. This is needed before the association when we need
+ * to be on medium for the association frame exchange. Once we configure the
+ * firmware as 'associated', the firmware will allocate time on medium without
+ * needed a session protection.
+ *
+ * TDLS discover uses this API as well even after association to ensure that
+ * other activities internal to the firmware will not interrupt our presence
+ * on medium.
+ */
+
+/**
+ * struct iwl_mld_session_protect - session protection parameters
+ * @end_jiffies: expected end_jiffies of current session protection.
+ *	0 if not active
+ * @duration: the duration in tu of current session
+ * @session_requested: A session protection command was sent and wasn't yet
+ *	answered
+ */
+struct iwl_mld_session_protect {
+	unsigned long end_jiffies;
+	u32 duration;
+	bool session_requested;
+};
+
+#define IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS 900
+#define IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS 400
+
+/**
+ * iwl_mld_handle_session_prot_notif - handles %SESSION_PROTECTION_NOTIF
+ * @mld: the mld component
+ * @pkt: the RX packet containing the notification
+ */
+void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
+				       struct iwl_rx_packet *pkt);
+
+/**
+ * iwl_mld_schedule_session_protection - schedule a session protection
+ * @mld: the mld component
+ * @vif: the virtual interface for which the protection issued
+ * @duration: the requested duration of the protection
+ * @min_duration: the minimum duration of the protection
+ * @link_id: The link to schedule a session protection for
+ */
+void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+					 struct ieee80211_vif *vif,
+					 u32 duration, u32 min_duration,
+					 int link_id);
+
+/**
+ * iwl_mld_start_session_protection - start a session protection
+ * @mld: the mld component
+ * @vif: the virtual interface for which the protection issued
+ * @duration: the requested duration of the protection
+ * @min_duration: the minimum duration of the protection
+ * @link_id: The link to schedule a session protection for
+ * @timeout: timeout for waiting
+ *
+ * This schedules the session protection, and waits for it to start
+ * (with timeout)
+ *
+ * Returns: 0 if successful, error code otherwise
+ */
+int iwl_mld_start_session_protection(struct iwl_mld *mld,
+				     struct ieee80211_vif *vif,
+				     u32 duration, u32 min_duration,
+				     int link_id, unsigned long timeout);
+
+/**
+ * iwl_mld_cancel_session_protection - cancel the session protection.
+ * @mld: the mld component
+ * @vif: the virtual interface for which the session is issued
+ * @link_id: cancel the session protection for given link
+ *
+ * This functions cancels the session protection which is an act of good
+ * citizenship. If it is not needed any more it should be canceled because
+ * the other mac contexts wait for the medium during that time.
+ *
+ * Returns: 0 if successful, error code otherwise
+ *
+ */
+int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
+				      struct ieee80211_vif *vif,
+				      int link_id);
+
+#endif /* __session_protect_h__ */
