From 0fa55ca418c8afd6da242407a184c23548c553dc Mon Sep 17 00:00:00 2001
From: Sriram R <srirrama@codeaurora.org>
Date: Fri, 5 Jun 2020 12:21:15 +0530
Subject: [PATCH 2/3] ath11k: Add nss driver interface

This patch adds interface support for accessing nss driver with
support for initialization, teardown, vap up/down, peer create/delete,
tx/rx. NSS Stats addition is not part of this version.

Signed-off-by: Sriram R <srirrama@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/Kconfig  |    9 +
 drivers/net/wireless/ath/ath11k/Makefile |    1 +
 drivers/net/wireless/ath/ath11k/nss.c    | 1762 ++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath11k/nss.h    |  217 ++++
 4 files changed, 1989 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath11k/nss.c
 create mode 100644 drivers/net/wireless/ath/ath11k/nss.h

--- a/drivers/net/wireless/ath/ath11k/Kconfig
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
@@ -11,6 +11,15 @@ config ATH11K
 
 	  If you choose to build a module, it'll be called ath11k.
 
+config ATH11K_NSS_SUPPORT
+	bool "QCA ath11k nss support"
+	depends on ATH11K
+	default y
+	---help---
+	  Enables NSS offload support for ATH11K driver
+
+	  If unsure, say Y to enable NSS offload support.
+
 config ATH11K_AHB
 	tristate "Atheros ath11k AHB support"
 	depends on m
--- a/drivers/net/wireless/ath/ath11k/Makefile
+++ b/drivers/net/wireless/ath/ath11k/Makefile
@@ -27,6 +27,7 @@ ath11k-$(CONFIG_THERMAL) += thermal.o
 ath11k-$(CONFIG_WANT_DEV_COREDUMP) += coredump.o
 ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o
 ath11k-$(CPTCFG_ATH11K_PKTLOG) += pktlog.o
+ath11k-$(CPTCFG_ATH11K_NSS_SUPPORT) += nss.o
 
 obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o
 ath11k_ahb-y += ahb.o
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/nss.c
@@ -0,0 +1,2388 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include "mac.h"
+#include "nss.h"
+#include "core.h"
+#include "peer.h"
+#include "dp_rx.h"
+#include "dp_tx.h"
+#include "hif.h"
+#include "../../../../../net/mac80211/sta_info.h"
+
+/*-----------------------------ATH11K-NSS Helpers--------------------------*/
+
+static enum ath11k_nss_opmode
+ath11k_nss_get_vdev_opmode(struct ath11k_vif *arvif)
+{
+	switch (arvif->vdev_type) {
+	case WMI_VDEV_TYPE_AP:
+		return ATH11K_NSS_OPMODE_AP;
+	case WMI_VDEV_TYPE_STA:
+		return ATH11K_NSS_OPMODE_STA;
+	default:
+		ath11k_warn(arvif->ar->ab, "unsupported nss vdev type %d\n",
+			    arvif->vdev_type);
+	}
+
+	return ATH11K_NSS_OPMODE_UNKNOWN;
+}
+
+static void ath11k_nss_wifili_stats_sync(struct ath11k_base *ab,
+					 struct nss_wifili_stats_sync_msg *wlsoc_stats)
+{
+	struct nss_wifili_device_stats *devstats = &wlsoc_stats->stats;
+	struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
+	int i;
+
+	spin_lock_bh(&ab->base_lock);
+
+	soc_stats->err_ring_pkts += devstats->rxwbm_stats.err_src_rxdma;
+	soc_stats->invalid_rbm += devstats->rxwbm_stats.invalid_buf_mgr;
+
+	for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
+		soc_stats->rxdma_error[i] += devstats->rxwbm_stats.err_dma_codes[i];
+
+	for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
+		soc_stats->reo_error[i] += devstats->rxwbm_stats.err_reo_codes[i];
+
+	for (i = 0; i < DP_REO_DST_RING_MAX; i++)
+		soc_stats->hal_reo_error[i] += devstats->rxreo_stats[i].ring_error;
+
+	for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
+		soc_stats->tx_err.desc_na[i] += devstats->tcl_stats[i].tcl_ring_full;
+
+
+	for (i = 0; i < NSS_WIFILI_MAX_TCL_DATA_RINGS_MSG; i++)
+		atomic_add(devstats->txcomp_stats[i].invalid_bufsrc
+			  + devstats->txcomp_stats[i].invalid_cookie
+			  + devstats->tx_sw_pool_stats[i].desc_alloc_fail
+			  + devstats->tx_ext_sw_pool_stats[i].desc_alloc_fail,
+			  &soc_stats->tx_err.misc_fail);
+
+	for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
+		atomic_add(devstats->tx_data_stats[i].tx_send_fail_cnt,
+			   &soc_stats->tx_err.misc_fail);
+
+	spin_unlock_bh(&ab->base_lock);
+}
+
+static void ath11k_nss_get_peer_stats(struct ath11k_base *ab, struct nss_wifili_peer_stats *stats)
+{
+	struct ath11k_peer *peer;
+	struct nss_wifili_peer_ctrl_stats *pstats = NULL;
+	int i, j;
+	u64 tx_packets, tx_bytes, tx_dropped = 0;
+	u64 rx_packets, rx_bytes, rx_dropped;
+
+	if (!ab->nss.stats_enabled)
+		return;
+
+	for (i = 0; i < stats->npeers; i++) {
+		pstats = &stats->wpcs[i];
+
+		rcu_read_lock();
+		spin_lock_bh(&ab->base_lock);
+
+		peer = ath11k_peer_find_by_id(ab, pstats->peer_id);
+		if (!peer || !peer->sta) {
+			ath11k_dbg(ab, ATH11K_DBG_NSS, "nss wifili: unable to find peer %d\n", pstats->peer_id);
+			spin_unlock_bh(&ab->base_lock);
+			rcu_read_unlock();
+			continue;
+		}
+
+		if (!peer->nss.nss_stats) {
+			spin_unlock_bh(&ab->base_lock);
+			rcu_read_unlock();
+			return;
+		}
+
+		if (pstats->tx.tx_success_cnt)
+			peer->nss.nss_stats->last_ack = jiffies;
+
+		if (pstats->rx.rx_recvd) {
+			peer->nss.nss_stats->last_rx = jiffies;
+		}
+
+		tx_packets = pstats->tx.tx_mcast_cnt +
+			pstats->tx.tx_ucast_cnt +
+			pstats->tx.tx_bcast_cnt;
+		peer->nss.nss_stats->tx_packets += tx_packets;
+		tx_bytes = pstats->tx.tx_mcast_bytes +
+			pstats->tx.tx_ucast_bytes +
+			pstats->tx.tx_bcast_bytes;
+		peer->nss.nss_stats->tx_bytes += tx_bytes;
+		peer->nss.nss_stats->tx_retries += pstats->tx.retries;
+
+		for (j = 0; j < NSS_WIFILI_TQM_RR_MAX; j++)
+			tx_dropped += pstats->tx.dropped.drop_stats[j];
+
+		peer->nss.nss_stats->tx_failed += tx_dropped;
+
+		ATH11K_NSS_TXRX_NETDEV_STATS(tx, peer->vif, tx_bytes, tx_packets);
+
+		rx_packets = pstats->rx.rx_recvd;
+		peer->nss.nss_stats->rx_packets += rx_packets;
+		rx_bytes = pstats->rx.rx_recvd_bytes;
+		peer->nss.nss_stats->rx_bytes += rx_bytes;
+		rx_dropped = pstats->rx.err.mic_err +
+			pstats->rx.err.decrypt_err;
+		peer->nss.nss_stats->rx_dropped += rx_dropped;
+
+		ATH11K_NSS_TXRX_NETDEV_STATS(rx, peer->vif, rx_bytes, rx_packets);
+
+		spin_unlock_bh(&ab->base_lock);
+		rcu_read_unlock();
+	}
+}
+
+void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter)
+{
+	if (ab->nss.enabled)
+		tlv_filter->rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
+}
+
+static u32 ath11k_nss_cipher_type(struct ath11k_base *ab, u32 cipher)
+{
+	switch (cipher) {
+	case WLAN_CIPHER_SUITE_CCMP:
+		return PEER_SEC_TYPE_AES_CCMP;
+	case WLAN_CIPHER_SUITE_TKIP:
+		return PEER_SEC_TYPE_TKIP;
+	case WLAN_CIPHER_SUITE_CCMP_256:
+		return PEER_SEC_TYPE_AES_CCMP_256;
+	case WLAN_CIPHER_SUITE_GCMP:
+		return PEER_SEC_TYPE_AES_GCMP;
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		return PEER_SEC_TYPE_AES_GCMP_256;
+	default:
+		ath11k_warn(ab, "unknown cipher type %d\n", cipher);
+		return PEER_SEC_TYPE_NONE;
+	}
+}
+
+static void ath11k_nss_tx_encap_nwifi(struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	u8 *qos_ctl;
+
+	if (!ieee80211_is_data_qos(hdr->frame_control))
+		return;
+
+	qos_ctl = ieee80211_get_qos_ctl(hdr);
+	memmove(skb->data + IEEE80211_QOS_CTL_LEN,
+		skb->data, (void *)qos_ctl - (void *)skb->data);
+	skb_pull(skb, IEEE80211_QOS_CTL_LEN);
+
+	hdr = (void *)skb->data;
+	hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+}
+
+static void ath11k_nss_tx_encap_raw(struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	u32 cipher;
+
+	if (!ieee80211_has_protected(hdr->frame_control) || !info->control.hw_key)
+		return;
+
+	/* Include length for MIC */
+	skb_put(skb, IEEE80211_CCMP_MIC_LEN);
+
+	/* Include length for ICV if TKIP is used */
+	cipher = info->control.hw_key->cipher;
+	if (cipher == WLAN_CIPHER_SUITE_TKIP)
+		skb_put(skb, IEEE80211_TKIP_ICV_LEN);
+}
+
+static void ath11k_nss_peer_mem_free(struct ath11k_base *ab, u32 peer_id)
+{
+	struct ath11k_peer *peer;
+
+	spin_lock_bh(&ab->base_lock);
+
+	peer = ath11k_peer_find_by_id(ab, peer_id);
+	if (!peer) {
+		spin_unlock_bh(&ab->base_lock);
+		ath11k_warn(ab, "ath11k_nss: unable to free peer mem, peer_id:%d\n",
+			    peer_id);
+		return;
+	}
+
+	dma_unmap_single(ab->dev, peer->nss.paddr,
+			 WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE);
+
+	kfree(peer->nss.vaddr);
+	if (peer->nss.nss_stats) {
+		kfree(peer->nss.nss_stats);
+		peer->nss.nss_stats = NULL;
+	}
+
+	complete(&peer->nss.complete);
+	spin_unlock_bh(&ab->base_lock);
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer %d mem freed\n", peer_id);
+}
+
+/*-----------------------------Events/Callbacks------------------------------*/
+
+void ath11k_nss_wifili_event_receive(struct ath11k_base *ab, struct nss_wifili_msg *msg)
+{
+	u32 msg_type = msg->cm.type;
+	enum nss_cmn_response response = msg->cm.response;
+	u32 error =  msg->cm.error;
+	u32 peer_id;
+	struct nss_wifili_peer_stats *peer_stats;
+
+	if (!ab)
+		return;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "nss wifili event received %d response %d error %d\n",
+		   msg_type, response, error);
+
+	switch (msg_type) {
+	case NSS_WIFILI_INIT_MSG:
+	case NSS_WIFILI_PDEV_INIT_MSG:
+	case NSS_WIFILI_START_MSG:
+	case NSS_WIFILI_SOC_RESET_MSG:
+	case NSS_WIFILI_STOP_MSG:
+	case NSS_WIFILI_PDEV_DEINIT_MSG:
+		ab->nss.response = response;
+		complete(&ab->nss.complete);
+		break;
+
+	case NSS_WIFILI_PEER_CREATE_MSG:
+		if (response != NSS_CMN_RESPONSE_EMSG)
+			break;
+
+		peer_id = (&msg->msg.peermsg)->peer_id;
+
+		/* free peer memory allocated during peer create due to failure */
+		ath11k_nss_peer_mem_free(ab, peer_id);
+		break;
+	case NSS_WIFILI_PEER_DELETE_MSG:
+		peer_id = (&msg->msg.peermsg)->peer_id;
+
+		if (response == NSS_CMN_RESPONSE_EMSG)
+			ath11k_warn(ab, "ath11k_nss: peer delete failed%d\n",
+				    peer_id);
+
+		/* free peer memory allocated during peer create irrespective of
+		 * delete status
+		 */
+		ath11k_nss_peer_mem_free(ab, peer_id);
+		break;
+	case NSS_WIFILI_PEER_SECURITY_TYPE_MSG:
+		if (response == NSS_CMN_RESPONSE_EMSG)
+			ath11k_warn(ab, "peer securty config failed\n");
+
+		break;
+	case NSS_WIFILI_PEER_UPDATE_AUTH_FLAG:
+		if (response == NSS_CMN_RESPONSE_EMSG)
+			ath11k_warn(ab, "peer authorize config failed\n");
+
+		break;
+	case NSS_WIFILI_STATS_MSG:
+		if (response == NSS_CMN_RESPONSE_EMSG) {
+			ath11k_warn(ab, "soc_dp_stats failed to get updated\n");
+			break;
+		}
+		ath11k_nss_wifili_stats_sync(ab, &msg->msg.wlsoc_stats);
+		break;
+	case NSS_WIFILI_PEER_STATS_MSG:
+		peer_stats = &msg->msg.peer_stats.stats;
+		if (response == NSS_CMN_RESPONSE_EMSG) {
+			ath11k_warn(ab, "peer stats msg failed with error = %u\n", error);
+			break;
+		}
+		ath11k_nss_get_peer_stats(ab, peer_stats);
+		break;
+	case NSS_WIFILI_TID_REOQ_SETUP_MSG:
+		/* TODO setup tidq */
+		break;
+	default:
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type);
+		break;
+	}
+}
+
+void ath11k_nss_process_mic_error(struct ath11k_base *ab, struct sk_buff *skb)
+{
+	struct ath11k_vif *arvif;
+	struct ath11k_peer *peer = NULL;
+	struct hal_rx_desc *desc = (struct hal_rx_desc *)skb->data;
+	struct wireless_dev *wdev;
+	u16 peer_id;
+	u8 peer_addr[ETH_ALEN];
+	u8 ucast_keyidx, mcast_keyidx;
+	bool is_mcbc;
+
+	if (!ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc))
+		goto fail;
+
+	is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(ab, desc);
+	peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ab, desc);
+
+	spin_lock_bh(&ab->base_lock);
+	peer = ath11k_peer_find_by_id(ab, peer_id);
+	if (!peer) {
+		ath11k_info(ab, "ath11k_nss:peer not found");
+		spin_unlock_bh(&ab->base_lock);
+		goto fail;
+	}
+
+	if (!peer->vif) {
+		ath11k_warn(ab, "ath11k_nss:vif not found");
+		spin_unlock_bh(&ab->base_lock);
+		goto fail;
+	}
+
+	ether_addr_copy(peer_addr, peer->addr);
+	mcast_keyidx = peer->mcast_keyidx;
+	ucast_keyidx = peer->ucast_keyidx;
+	arvif = ath11k_vif_to_arvif(peer->vif);
+	spin_unlock_bh(&ab->base_lock);
+
+	if (!arvif->is_started) {
+		ath11k_warn(ab, "ath11k_nss:arvif not started");
+		goto fail;
+	}
+
+	wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif);
+	if (!wdev) {
+		ath11k_warn(ab, "ath11k_nss: wdev is null\n");
+		goto fail;
+	}
+
+	if (!wdev->netdev) {
+		ath11k_warn(ab, "ath11k_nss: netdev is null\n");
+		goto fail;
+	}
+
+	cfg80211_michael_mic_failure(wdev->netdev, peer_addr,
+				     is_mcbc ? NL80211_KEYTYPE_GROUP :
+				     NL80211_KEYTYPE_PAIRWISE,
+				     is_mcbc ? mcast_keyidx : ucast_keyidx,
+				     NULL, GFP_ATOMIC);
+	kfree(skb);
+	return;
+
+fail:
+	kfree(skb);
+	ath11k_warn(ab, "ath11k_nss: Failed to handle mic error\n");
+	return;
+}
+
+static void
+ath11k_nss_wifili_ext_callback_fn(struct ath11k_base *ab, struct sk_buff *skb,
+				  __attribute__((unused)) struct napi_struct *napi)
+{
+	struct nss_wifili_soc_per_packet_metadata *wepm;
+
+	wepm = (struct nss_wifili_soc_per_packet_metadata *)(skb->head +
+					      NSS_WIFILI_SOC_PER_PACKET_METADATA_OFFSET);
+
+	switch (wepm->pkt_type) {
+	case NSS_WIFILI_SOC_EXT_DATA_PKT_MIC_ERROR:
+		ath11k_nss_process_mic_error(ab, skb);
+		break;
+	default:
+		kfree(skb);
+		break;
+	}
+}
+
+void ath11k_nss_vdev_cfg_cb(void *app_data, struct nss_cmn_msg *msg)
+{
+	struct ath11k_vif *arvif = (struct ath11k_vif *)app_data;
+
+	if (!arvif)
+		return;
+
+	ath11k_dbg(arvif->ar->ab, ATH11K_DBG_NSS, "vdev cfg msg callback received msg:%d rsp:%d\n",
+		   msg->type, msg->response);
+
+	complete(&arvif->nss.complete);
+}
+
+static void ath11k_nss_vdev_event_receive(void *dev, struct nss_cmn_msg *vdev_msg)
+{
+	/*TODO*/
+}
+
+static void
+ath11k_nss_vdev_special_data_receive(struct net_device *dev, struct sk_buff *skb,
+				     __attribute__((unused)) struct napi_struct *napi)
+{
+	/* TODO */
+}
+
+/* TODO: move to mac80211 after cleanups/refactoring required after feature completion */
+static int ath11k_nss_deliver_rx(struct ieee80211_vif *vif, struct sk_buff *skb,
+				 bool eth,  int data_offs, struct napi_struct *napi)
+{
+	struct sk_buff_head subframe_list;
+	struct ieee80211_hdr *hdr;
+	struct sk_buff *subframe;
+	struct net_device *dev;
+	int hdr_len;
+	u8 *qc;
+
+	dev = skb->dev;
+
+	if (eth)
+		goto deliver_msdu;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		qc = ieee80211_get_qos_ctl(hdr);
+		if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
+			goto deliver_amsdu;
+	}
+
+	if (ieee80211_data_to_8023_exthdr(skb, NULL, vif->addr, vif->type,
+					  data_offs - hdr_len, false)) {
+		dev_kfree_skb_any(skb);
+		return -EINVAL;
+	}
+
+deliver_msdu:
+	skb->protocol = eth_type_trans(skb, dev);
+	napi_gro_receive(napi, skb);
+	return 0;
+
+deliver_amsdu:
+	/* Move to the start of the first subframe */
+	skb_pull(skb, data_offs);
+
+	__skb_queue_head_init(&subframe_list);
+
+	/* create list containing all the subframes */
+	ieee80211_amsdu_to_8023s(skb, &subframe_list, NULL,
+				 vif->type, 0, NULL, NULL);
+
+	/* This shouldn't happen, indicating error during defragmentation */
+	if (skb_queue_empty(&subframe_list))
+		return -EINVAL;
+
+	while (!skb_queue_empty(&subframe_list)) {
+		subframe = __skb_dequeue(&subframe_list);
+		subframe->protocol = eth_type_trans(subframe, dev);
+		napi_gro_receive(napi, subframe);
+	}
+
+	return 0;
+}
+
+static int ath11k_nss_undecap_raw(struct ath11k_vif *arvif, struct sk_buff *skb,
+				  int *data_offset)
+{
+	struct ath11k_base *ab = arvif->ar->ab;
+	struct ath11k *ar = arvif->ar;
+	enum hal_encrypt_type enctype;
+	struct ath11k_peer *peer = NULL;
+	struct ieee80211_hdr *hdr;
+	int hdr_len;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	*data_offset = hdr_len;
+
+	/* FCS is included in the raw mode skb, we can trim it, fcs error
+	 * packets are not expected to be received in this path
+	 */
+	skb_trim(skb, skb->len - FCS_LEN);
+
+	spin_lock_bh(&ab->base_lock);
+
+	peer = ath11k_peer_find_by_addr(ab, hdr->addr2);
+	if (!peer) {
+		ath11k_warn(ab, "peer not found for raw/nwifi undecap, drop this packet\n");
+		spin_unlock_bh(&ab->base_lock);
+		return -EINVAL;
+	}
+	enctype = peer->sec_type;
+
+	spin_unlock_bh(&ab->base_lock);
+
+	*data_offset += ath11k_dp_rx_crypto_param_len(ar, enctype);
+
+	/* Strip ICV, MIC, MMIC */
+	skb_trim(skb, skb->len -
+		 ath11k_dp_rx_crypto_mic_len(ar, enctype));
+
+	skb_trim(skb, skb->len -
+		 ath11k_dp_rx_crypto_icv_len(ar, enctype));
+
+	if (enctype == HAL_ENCRYPT_TYPE_TKIP_MIC)
+		skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN);
+
+	return 0;
+}
+
+static int ath11k_nss_undecap_nwifi(struct ath11k_vif *arvif, struct sk_buff *skb,
+				    int *data_offset)
+{
+	struct ieee80211_hdr *hdr;
+	int hdr_len;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	*data_offset = hdr_len;
+
+	/* We dont receive the IV from nss host on slow path
+	 * hence we can return only the header length as offset.
+	 **/
+	return 0;
+}
+
+static void
+ath11k_nss_vdev_data_receive(struct net_device *dev, struct sk_buff *skb,
+			     __attribute__((unused)) struct napi_struct *napi)
+{
+	enum ath11k_hw_txrx_mode decap_type;
+	struct wireless_dev *wdev;
+	struct ieee80211_vif *vif;
+	struct ath11k_vif *arvif;
+	struct ath11k_base *ab;
+	bool eth_decap = false;
+	int data_offs = 0;
+	int ret;
+
+	if (!dev) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	wdev = dev->ieee80211_ptr;
+	if (!wdev) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	vif = wdev_to_ieee80211_vif(wdev);
+	if (!vif) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	arvif = (struct ath11k_vif *)vif->drv_priv;
+	if (!arvif) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	ab = arvif->ar->ab;
+
+	skb->dev = dev;
+
+	/* log the original skb received from nss */
+	ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss: ",
+			skb->data, skb->len);
+
+	decap_type = arvif->nss.decap;
+
+	switch (decap_type) {
+	case ATH11K_HW_TXRX_RAW:
+		ret = ath11k_nss_undecap_raw(arvif, skb, &data_offs);
+		break;
+	case ATH11K_HW_TXRX_NATIVE_WIFI:
+		ret = ath11k_nss_undecap_nwifi(arvif, skb, &data_offs);
+		break;
+	case ATH11K_HW_TXRX_ETHERNET:
+		/* no changes required for ethernet decap */
+		ret = 0;
+		eth_decap = true;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		ath11k_warn(ab, "error in nss rx undecap, type %d err %d\n", decap_type,
+			    ret);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi);
+}
+
+int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb)
+{
+	struct ath11k *ar = arvif->ar;
+	nss_tx_status_t status;
+	int encap_type = ath11k_dp_tx_get_encap_type(arvif, skb);
+	struct ath11k_soc_dp_stats *soc_stats = &ar->ab->soc_stats;
+
+	if(encap_type != arvif->nss.encap) {
+		ath11k_warn(ar->ab, "encap mismatch in nss tx skb encap type %d" \
+			    "vif encap type %d\n", encap_type, arvif->nss.encap);
+		goto drop;
+	}
+
+	if (encap_type == HAL_TCL_ENCAP_TYPE_ETHERNET)
+		goto send;
+
+	if (encap_type == HAL_TCL_ENCAP_TYPE_RAW)
+		ath11k_nss_tx_encap_raw(skb);
+	else
+		ath11k_nss_tx_encap_nwifi(skb);
+
+send:
+	ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "", "nss tx msdu: ",
+			skb->data, skb->len);
+
+	status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, arvif->nss.if_num);
+
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_dbg(ar->ab, (ATH11K_DBG_NSS | ATH11K_DBG_DP_TX),
+			   "nss tx failure: %d\n", status);
+		atomic_inc(&soc_stats->tx_err.nss_tx_fail);
+	}
+
+	return status;
+drop:
+	atomic_inc(&soc_stats->tx_err.misc_fail);
+	WARN_ON_ONCE(1);
+	return -EINVAL;
+}
+
+int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val)
+{
+	struct nss_wifi_vdev_msg *vdev_msg = NULL;
+	struct nss_wifi_vdev_cmd_msg *vdev_cmd;
+	struct ath11k *ar = arvif->ar;
+	nss_tx_status_t status;
+
+	if (!ar->ab->nss.enabled)
+		return 0;
+
+	/* Monitor interface is not offloaded to nss */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		return 0;
+
+	vdev_msg = kzalloc(sizeof(*vdev_msg), GFP_ATOMIC);
+	if (!vdev_msg)
+		return -ENOMEM;
+
+	/* TODO: Convert to function for conversion in case of many
+	 * such commands
+	 */
+	if (cmd == NSS_WIFI_VDEV_SECURITY_TYPE_CMD)
+		val = ath11k_nss_cipher_type(ar->ab, val);
+
+	if (cmd == NSS_WIFI_VDEV_ENCAP_TYPE_CMD)
+		arvif->nss.encap = val;
+	else if (cmd == NSS_WIFI_VDEV_DECAP_TYPE_CMD)
+		arvif->nss.decap = val;
+
+	vdev_cmd = &vdev_msg->msg.vdev_cmd;
+	vdev_cmd->cmd = cmd;
+	vdev_cmd->value = val;
+
+	nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
+			       NSS_WIFI_VDEV_INTERFACE_CMD_MSG,
+			       sizeof(struct nss_wifi_vdev_cmd_msg),
+			       NULL, NULL);
+
+	status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "nss vdev set cmd failure cmd:%d val:%d",
+			    cmd, val);
+		goto free;
+	}
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev set cmd success cmd:%d val:%d\n",
+		   cmd, val);
+free:
+	kfree(vdev_msg);
+	return status;
+}
+
+static int ath11k_nss_vdev_configure(struct ath11k_vif *arvif)
+{
+	struct ath11k *ar = arvif->ar;
+	struct nss_wifi_vdev_msg *vdev_msg;
+	struct nss_wifi_vdev_config_msg *vdev_cfg;
+	nss_tx_status_t status;
+	int ret;
+
+	vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
+	if (!vdev_msg)
+		return -ENOMEM;
+
+	vdev_cfg = &vdev_msg->msg.vdev_config;
+
+	vdev_cfg->radio_ifnum = ar->nss.if_num;
+	vdev_cfg->vdev_id = arvif->vdev_id;
+
+	vdev_cfg->opmode = ath11k_nss_get_vdev_opmode(arvif);
+
+	ether_addr_copy(vdev_cfg->mac_addr, arvif->vif->addr);
+
+	reinit_completion(&arvif->nss.complete);
+
+	nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
+			       NSS_WIFI_VDEV_INTERFACE_CONFIGURE_MSG,
+			       sizeof(struct nss_wifi_vdev_config_msg),
+			       (nss_wifi_vdev_msg_callback_t *)ath11k_nss_vdev_cfg_cb,
+			       arvif);
+
+	status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "failed to configure nss vdev nss_err:%d\n",
+			    status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ret = wait_for_completion_timeout(&arvif->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ar->ab, "timeout in receiving nss vdev cfg response\n");
+		ret = -ETIMEDOUT;
+		goto free;
+	}
+
+	ret = 0;
+free:
+	kfree(vdev_msg);
+
+	return ret;
+}
+
+static void ath11k_nss_vdev_unregister(struct ath11k_vif *arvif)
+{
+	struct ath11k_base *ab = arvif->ar->ab;
+
+	switch (arvif->vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+		nss_unregister_wifi_vdev_if(arvif->nss.if_num);
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "unregistered nss vdev %d \n",
+			   arvif->nss.if_num);
+		break;
+	default:
+		ath11k_warn(ab, "unsupported interface type %d for nss vdev unregister\n",
+			    arvif->vif->type);
+		return;
+	}
+}
+
+static int ath11k_nss_vdev_register(struct ath11k_vif *arvif,
+				    struct net_device *netdev)
+{
+	struct ath11k *ar = arvif->ar;
+	struct ath11k_base *ab = ar->ab;
+	nss_tx_status_t status;
+	u32 features = 0;
+
+	switch (arvif->vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+		status = nss_register_wifi_vdev_if(ar->nss.ctx,
+					arvif->nss.if_num,
+					ath11k_nss_vdev_data_receive,
+					ath11k_nss_vdev_special_data_receive,
+					ath11k_nss_vdev_event_receive,
+					netdev, features);
+		if (status != NSS_TX_SUCCESS) {
+			ath11k_warn(ab, "failed to register nss vdev if_num %d nss_err:%d\n",
+				    arvif->nss.if_num, status);
+			return -EINVAL;
+		}
+
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "registered nss vdev if_num %d\n",
+			   arvif->nss.if_num);
+
+		break;
+	default:
+		ath11k_warn(ab, "unsupported interface type %d for nss vdev register\n",
+			    arvif->vif->type);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+void ath11k_nss_vdev_free(struct ath11k_vif *arvif)
+{
+	struct ath11k_base *ab = arvif->ar->ab;
+	nss_tx_status_t status;
+
+	switch (arvif->vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+		status = nss_dynamic_interface_dealloc_node(
+						arvif->nss.if_num,
+						NSS_DYNAMIC_INTERFACE_TYPE_VAP);
+		if (status != NSS_TX_SUCCESS)
+			ath11k_warn(ab, "failed to free nss vdev nss_err:%d\n",
+				    status);
+		else
+			ath11k_dbg(ab, ATH11K_DBG_NSS,
+				   "nss vdev interface deallocated\n");
+
+		return;
+	default:
+		ath11k_warn(ab, "unsupported interface type %d for nss vdev dealloc\n",
+			    arvif->vif->type);
+		return;
+	}
+}
+
+static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif)
+{
+	struct ath11k_base *ab = arvif->ar->ab;
+	enum nss_dynamic_interface_type if_type;
+	int if_num;
+
+	/* Initialize completion for verifying NSS message response */
+	init_completion(&arvif->nss.complete);
+
+	switch (arvif->vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+		if_type = NSS_DYNAMIC_INTERFACE_TYPE_VAP;
+		/* allocate interface context with NSS driver for the new vdev */
+		if_num = nss_dynamic_interface_alloc_node(if_type);
+		if (if_num < 0) {
+			ath11k_warn(ab, "failed to allocate nss vdev\n");
+			return -EINVAL;
+		}
+
+		arvif->nss.if_num = if_num;
+
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated nss vdev if_num %d\n",
+			   arvif->nss.if_num);
+
+		break;
+	default:
+		ath11k_warn(ab, "unsupported interface type %d for nss vdev alloc\n",
+			    arvif->vif->type);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+int ath11k_nss_vdev_create(struct ath11k_vif *arvif)
+{
+	struct ath11k *ar = arvif->ar;
+	struct ath11k_base *ab = ar->ab;
+	struct wireless_dev *wdev;
+	int ret;
+
+	if (!ab->nss.enabled)
+		return 0;
+
+	/* Monitor interface is not offloaded to nss */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		return 0;
+
+	if (arvif->nss.created)
+		return 0;
+
+	wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif);
+	if (!wdev) {
+		ath11k_warn(ab, "ath11k_nss: wdev is null\n");
+		return -EINVAL;
+	}
+
+	if (!wdev->netdev) {
+		ath11k_warn(ab, "ath11k_nss: netdev is null\n");
+		return -EINVAL;
+	}
+
+	ret = ath11k_nss_vdev_alloc(arvif);
+	if (ret)
+		return ret;
+
+	ret = ath11k_nss_vdev_register(arvif, wdev->netdev);
+	if (ret)
+		goto free_vdev;
+
+	switch (arvif->vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
+		ret = ath11k_nss_vdev_configure(arvif);
+		if (ret)
+			goto unregister_vdev;
+
+		break;
+	default:
+		ret = -ENOTSUPP;
+		goto unregister_vdev;
+	}
+
+	arvif->nss.created = true;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS,
+		   "nss vdev interface created ctx %pK, ifnum %d\n",
+		   ar->nss.ctx, arvif->nss.if_num);
+
+	return ret;
+
+unregister_vdev:
+	ath11k_nss_vdev_unregister(arvif);
+free_vdev:
+	ath11k_nss_vdev_free(arvif);
+
+	return ret;
+}
+
+void ath11k_nss_vdev_delete(struct ath11k_vif *arvif)
+{
+	if (!arvif->ar->ab->nss.enabled)
+		return;
+
+	/* Monitor interface is not offloaded to nss */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		return;
+
+	if (!arvif->nss.created)
+		return;
+
+	ath11k_nss_vdev_unregister(arvif);
+
+	ath11k_nss_vdev_free(arvif);
+
+	arvif->nss.created = false;
+}
+
+int ath11k_nss_vdev_up(struct ath11k_vif *arvif)
+{
+	struct nss_wifi_vdev_msg *vdev_msg = NULL;
+	struct nss_wifi_vdev_enable_msg *vdev_en;
+	struct ath11k *ar = arvif->ar;
+	nss_tx_status_t status;
+	int ret = 0;
+
+	if (!ar->ab->nss.enabled)
+		return 0;
+
+	/* Monitor interface is not offloaded to nss */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		return 0;
+
+	vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
+	if (!vdev_msg)
+		return -ENOMEM;
+
+	vdev_en = &vdev_msg->msg.vdev_enable;
+
+	ether_addr_copy(vdev_en->mac_addr, arvif->vif->addr);
+
+	nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
+			       NSS_WIFI_VDEV_INTERFACE_UP_MSG,
+			       sizeof(struct nss_wifi_vdev_enable_msg),
+			       NULL, NULL);
+
+	status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "nss vdev up tx msg error %d\n", status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev up tx msg success\n");
+free:
+	kfree(vdev_msg);
+	return ret;
+}
+
+int ath11k_nss_vdev_down(struct ath11k_vif *arvif)
+{
+	struct nss_wifi_vdev_msg *vdev_msg = NULL;
+	struct ath11k *ar = arvif->ar;
+	nss_tx_status_t status;
+	int ret = 0;
+
+	if (!ar->ab->nss.enabled)
+		return 0;
+
+	/* Monitor interface is not offloaded to nss */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		return 0;
+
+	vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
+	if (!vdev_msg)
+		return -ENOMEM;
+
+	nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
+			       NSS_WIFI_VDEV_INTERFACE_DOWN_MSG,
+			       sizeof(struct nss_wifi_vdev_disable_msg),
+			       NULL, NULL);
+
+	status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "nss vdev down tx msg error %d\n", status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev down tx msg success\n");
+free:
+	kfree(vdev_msg);
+	return ret;
+}
+
+/*----------------------------Peer Setup/Config -----------------------------*/
+
+int ath11k_nss_set_peer_sec_type(struct ath11k *ar,
+				 struct ath11k_peer *peer,
+				 struct ieee80211_key_conf *key_conf)
+{
+	struct nss_wifili_peer_security_type_msg *sec_msg;
+	nss_wifili_msg_callback_t msg_cb;
+	struct nss_wifili_msg *wlmsg;
+	nss_tx_status_t status;
+	u8 *mic_key;
+
+	if (!ar->ab->nss.enabled)
+		return 0;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	sec_msg = &wlmsg->msg.securitymsg;
+	sec_msg->peer_id = peer->peer_id;
+
+	/* 0 -unicast , 1 - mcast/unicast */
+	sec_msg->pkt_type = !(key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+
+	sec_msg->security_type = ath11k_nss_cipher_type(ar->ab,
+							key_conf->cipher);
+
+	if (sec_msg->security_type == PEER_SEC_TYPE_TKIP) {
+		mic_key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
+		memcpy(&sec_msg->mic_key[0], mic_key, NSS_WIFILI_MIC_KEY_LEN);
+	}
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num,
+			 NSS_WIFILI_PEER_SECURITY_TYPE_MSG,
+			 sizeof(struct nss_wifili_peer_security_type_msg),
+			 msg_cb, NULL);
+
+	status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "nss peer %d security cfg fail %d\n",
+			    peer->peer_id, status);
+		goto free;
+	}
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer id %d security cfg complete\n",
+		   peer->peer_id);
+free:
+	kfree(wlmsg);
+	return status;
+}
+
+int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id)
+{
+	struct nss_wifili_peer_update_auth_flag *auth_msg;
+	nss_wifili_msg_callback_t msg_cb;
+	struct nss_wifili_msg *wlmsg;
+	nss_tx_status_t status;
+
+	if (!ar->ab->nss.enabled)
+		return 0;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	auth_msg = &wlmsg->msg.peer_auth;
+	auth_msg->peer_id = peer_id;
+	auth_msg->auth_flag = 1;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num,
+			 NSS_WIFILI_PEER_UPDATE_AUTH_FLAG,
+			 sizeof(struct nss_wifili_peer_update_auth_flag),
+			 msg_cb, NULL);
+
+	status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "nss peer %d auth cfg fail %d\n",
+			    peer_id, status);
+		goto free;
+	}
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer id %d auth cfg complete\n",
+		   peer_id);
+free:
+	kfree(wlmsg);
+	return status;
+}
+
+void ath11k_nss_update_sta_stats(struct station_info *sinfo,
+				 struct ieee80211_sta *sta,
+				 struct ath11k_sta *arsta)
+{
+	struct sta_info *stainfo;
+	struct ath11k_peer *peer;
+	int tid_idx;
+	struct ath11k *ar = arsta->arvif->ar;
+	struct ath11k_base *ab = ar->ab;
+
+	if (!ab->nss.enabled)
+		return;
+
+	spin_lock_bh(&ab->base_lock);
+	peer = ath11k_peer_find_by_addr(arsta->arvif->ar->ab, sta->addr);
+	if (!peer) {
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "unable to find peer %pM\n",
+					sta->addr);
+		goto exit;
+	}
+
+	if (!peer->nss.nss_stats)
+		goto exit;
+
+	stainfo = container_of(sta, struct sta_info, sta);
+	if (peer->nss.nss_stats->last_rx &&
+			time_after((unsigned long)peer->nss.nss_stats->last_rx, stainfo->rx_stats.last_rx))
+		stainfo->rx_stats.last_rx = peer->nss.nss_stats->last_rx;
+
+	if (peer->nss.nss_stats->last_ack &&
+			time_after((unsigned long)peer->nss.nss_stats->last_ack, stainfo->status_stats.last_ack))
+		stainfo->status_stats.last_ack = peer->nss.nss_stats->last_ack;
+
+	stainfo->rx_stats.dropped += peer->nss.nss_stats->rx_dropped -
+		peer->nss.nss_stats->last_rxdrop;
+	peer->nss.nss_stats->last_rxdrop = peer->nss.nss_stats->rx_dropped;
+
+	sinfo->tx_packets = 0;
+	/* Add only ac-0 count as mgmt packets uses WME_AC_BE */
+	sinfo->tx_packets += stainfo->tx_stats.packets[WME_AC_BE];
+	sinfo->tx_packets += peer->nss.nss_stats->tx_packets;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+	sinfo->tx_bytes = 0;
+
+	/* Add only ac-0 count as mgmt packets uses WME_AC_BE */
+	sinfo->tx_bytes += stainfo->tx_stats.bytes[WME_AC_BE];
+	sinfo->tx_bytes += peer->nss.nss_stats->tx_bytes;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
+
+	sinfo->tx_failed = stainfo->status_stats.retry_failed +
+		peer->nss.nss_stats->tx_failed;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+
+	sinfo->tx_retries = stainfo->status_stats.retry_count +
+		peer->nss.nss_stats->tx_retries;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+
+	sinfo->rx_packets = stainfo->rx_stats.packets +
+		peer->nss.nss_stats->rx_packets;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+
+	sinfo->rx_bytes = stainfo->rx_stats.bytes +
+		peer->nss.nss_stats->rx_bytes;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
+
+	if (peer->nss.nss_stats->rxrate.legacy || peer->nss.nss_stats->rxrate.nss) {
+		if (peer->nss.nss_stats->rxrate.legacy) {
+			sinfo->rxrate.legacy = peer->nss.nss_stats->rxrate.legacy;
+		} else {
+			sinfo->rxrate.mcs = peer->nss.nss_stats->rxrate.mcs;
+			sinfo->rxrate.nss = peer->nss.nss_stats->rxrate.nss;
+			sinfo->rxrate.bw = peer->nss.nss_stats->rxrate.bw;
+			sinfo->rxrate.he_gi = peer->nss.nss_stats->rxrate.he_gi;
+			sinfo->rxrate.he_dcm = peer->nss.nss_stats->rxrate.he_dcm;
+			sinfo->rxrate.he_ru_alloc = peer->nss.nss_stats->rxrate.he_ru_alloc;
+		}
+		sinfo->rxrate.flags = peer->nss.nss_stats->rxrate.flags;
+		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+	}
+
+exit:
+	spin_unlock_bh(&ab->base_lock);
+}
+
+void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info,
+				  struct ath11k_peer *peer)
+{
+	struct ath11k_sta *arsta = (struct ath11k_sta *)peer->sta->drv_priv;
+	u16 ath11k_hal_rx_legacy_rates[] =
+	{ 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 };
+	u16 rate = 0;
+	u16 num_msdu;
+	struct ath11k *ar = arsta->arvif->ar;
+	struct ath11k_base *ab = ar->ab;
+
+	if (!ab->nss.enabled)
+		return;
+
+	if (!peer->nss.nss_stats)
+		return;
+
+        if ((ppdu_info->preamble_type == WMI_RATE_PREAMBLE_CCK ||
+                ppdu_info->preamble_type == WMI_RATE_PREAMBLE_OFDM) &&
+                (ppdu_info->rate < ATH11K_LEGACY_NUM)) {
+		rate = ath11k_hal_rx_legacy_rates[ppdu_info->rate];
+	}
+
+	memset(&peer->nss.nss_stats->rxrate, 0, sizeof(peer->nss.nss_stats->rxrate));
+
+	switch (ppdu_info->preamble_type) {
+	case WMI_RATE_PREAMBLE_OFDM:
+		peer->nss.nss_stats->rxrate.legacy = rate;
+		break;
+	case WMI_RATE_PREAMBLE_CCK:
+		peer->nss.nss_stats->rxrate.legacy = rate;
+		break;
+	case WMI_RATE_PREAMBLE_HT:
+		if (ppdu_info->mcs > ATH11K_HT_MCS_MAX) {
+			ath11k_dbg(ar->ab, ATH11K_DBG_NSS,
+				    "Received invalid mcs in HT mode %d\n",
+				     ppdu_info->mcs);
+			return;
+		}
+		peer->nss.nss_stats->rxrate.mcs = ppdu_info->mcs + 8 * (ppdu_info->nss - 1);
+		peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_MCS;
+		if (ppdu_info->gi)
+			peer->nss.nss_stats->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+		break;
+	case WMI_RATE_PREAMBLE_VHT:
+		if (ppdu_info->mcs > ATH11K_VHT_MCS_MAX) {
+			ath11k_dbg(ar->ab, ATH11K_DBG_NSS,
+				    "Received invalid mcs in VHT mode %d\n",
+				     ppdu_info->mcs);
+			return;
+		}
+		peer->nss.nss_stats->rxrate.mcs = ppdu_info->mcs;
+		peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+		if (ppdu_info->gi)
+			peer->nss.nss_stats->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+		break;
+	case WMI_RATE_PREAMBLE_HE:
+		if (ppdu_info->mcs > ATH11K_HE_MCS_MAX) {
+			ath11k_dbg(ar->ab, ATH11K_DBG_NSS,
+				    "Received invalid mcs in HE mode %d\n",
+				    ppdu_info->mcs);
+			return;
+		}
+		peer->nss.nss_stats->rxrate.mcs = ppdu_info->mcs;
+		peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
+		peer->nss.nss_stats->rxrate.he_dcm = ppdu_info->dcm;
+		peer->nss.nss_stats->rxrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(ppdu_info->gi);
+		peer->nss.nss_stats->rxrate.he_ru_alloc = ppdu_info->ru_alloc;
+		break;
+	}
+
+	peer->nss.nss_stats->rxrate.nss = ppdu_info->nss;
+	peer->nss.nss_stats->rxrate.bw = ath11k_mac_bw_to_mac80211_bw(ppdu_info->bw);
+
+	num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count +
+				ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count;
+}
+
+int ath11k_nss_peer_delete(struct ath11k_base *ab, u8 *addr)
+{
+	struct nss_wifili_peer_msg *peer_msg;
+	struct nss_wifili_msg *wlmsg = NULL;
+	struct ath11k_peer *peer;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int ret;
+
+	if (!ab->nss.enabled)
+		return 0;
+
+	spin_lock_bh(&ab->base_lock);
+
+	peer = ath11k_peer_find_by_addr(ab, addr);
+	if (!peer) {
+		ath11k_warn(ab, "peer not found for nss peer delete\n");
+		spin_unlock_bh(&ab->base_lock);
+		return -EINVAL;
+	}
+
+	if (!peer->nss.vaddr) {
+		ath11k_warn(ab, "peer already deleted or peer create failed %pM\n",
+			    addr);
+		spin_unlock_bh(&ab->base_lock);
+		return -EINVAL;
+	}
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg) {
+		ath11k_warn(ab, "nss send peer delete msg alloc failure\n");
+		ret = -ENOMEM;
+		goto free_peer;
+	}
+
+	peer_msg = &wlmsg->msg.peermsg;
+
+	peer_msg->vdev_id = peer->vdev_id;
+	peer_msg->peer_id = peer->peer_id;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_PEER_DELETE_MSG,
+			 sizeof(struct nss_wifili_peer_msg),
+			 msg_cb, NULL);
+
+	reinit_completion(&peer->nss.complete);
+
+	status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss send peer (%pM) delete msg tx error %d\n",
+			    addr, status);
+		ret = -EINVAL;
+		kfree(wlmsg);
+		goto free_peer;
+	} else {
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer delete message success : peer_id %d\n",
+			   peer->peer_id);
+		ret = 0;
+	}
+
+	spin_unlock_bh(&ab->base_lock);
+
+	kfree(wlmsg);
+
+	/* No need to return failure or free up here, since the msg was tx succesfully
+	 * the peer delete response would be received from NSS which will free up
+	 * the allocated memory
+	 */
+	ret = wait_for_completion_timeout(&peer->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret)
+		ath11k_warn(ab, "timeout while waiting for nss peer delete msg response\n");
+
+	return 0;
+
+free_peer:
+	dma_unmap_single(ab->dev, peer->nss.paddr,
+		 WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE);
+	kfree(peer->nss.vaddr);
+	if (peer->nss.nss_stats) {
+		kfree(peer->nss.nss_stats);
+		peer->nss.nss_stats = NULL;
+	}
+	spin_unlock_bh(&ab->base_lock);
+	return ret;
+}
+
+int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer)
+{
+	struct nss_wifili_peer_msg *peer_msg;
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int ret;
+
+	if (!ab->nss.enabled)
+		return -ENOTSUPP;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	peer_msg = &wlmsg->msg.peermsg;
+
+	peer_msg->vdev_id = peer->vdev_id;
+	peer_msg->peer_id = peer->peer_id;
+	peer_msg->hw_ast_idx = peer->hw_peer_id;
+	peer_msg->tx_ast_hash = peer->ast_hash;
+	ether_addr_copy(peer_msg->peer_mac_addr, peer->addr);
+
+	peer->nss.vaddr = kzalloc(WIFILI_NSS_PEER_BYTE_SIZE, GFP_ATOMIC);
+
+	/* Initialize completion for verifying Peer NSS message response */
+	init_completion(&peer->nss.complete);
+
+	if (!peer->nss.vaddr) {
+		ath11k_warn(ab, "failed to allocate memory for nss peer info\n");
+		kfree(wlmsg);
+		return -ENOMEM;
+	}
+
+	peer->nss.paddr = dma_map_single(ab->dev, peer->nss.vaddr,
+					 WIFILI_NSS_PEER_BYTE_SIZE, DMA_TO_DEVICE);
+
+	ret = dma_mapping_error(ab->dev, peer->nss.paddr);
+	if (ret) {
+		ath11k_warn(ab, "error during nss peer info  memalloc\n");
+		kfree(peer->nss.vaddr);
+		ret = -ENOMEM;
+		goto msg_free;
+	}
+
+	peer_msg->nss_peer_mem = peer->nss.paddr;
+	peer_msg->psta_vdev_id = peer->vdev_id;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_PEER_CREATE_MSG,
+			 sizeof(struct nss_wifili_peer_msg),
+			 msg_cb, NULL);
+
+	status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ret = -EINVAL;
+		ath11k_warn(ab, "nss send peer (%pM) create msg tx error\n",
+			    peer->addr);
+		goto peer_mem_free;
+	}
+
+	ret = 0;
+	ath11k_dbg(ab, ATH11K_DBG_NSS,
+		   "nss peer_create msg success mac:%pM vdev:%d peer_id:%d hw_ast_idx:%d ast_hash:%d\n",
+		   peer_msg->peer_mac_addr, peer_msg->vdev_id, peer_msg->peer_id,
+		   peer_msg->hw_ast_idx, peer_msg->tx_ast_hash);
+
+	peer->nss.nss_stats = kzalloc(sizeof(*peer->nss.nss_stats), GFP_ATOMIC);
+	if (!peer->nss.nss_stats) {
+		ret = -ENOMEM;
+		ath11k_warn(ab, "Unable to create nss stats memory\n");
+		goto peer_mem_free;
+	}
+
+	goto msg_free;
+
+peer_mem_free:
+	dma_unmap_single(ab->dev, peer->nss.paddr,
+			 WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE);
+	kfree(peer->nss.vaddr);
+msg_free:
+	kfree(wlmsg);
+	return ret;
+}
+
+/*-------------------------------INIT/DEINIT---------------------------------*/
+
+static int ath11k_nss_radio_buf_cfg(struct ath11k *ar, int range, int buf_sz)
+{
+	struct nss_wifili_radio_buf_cfg_msg *buf_cfg;
+	struct nss_wifili_radio_cfg_msg *radio_buf_cfg_msg;
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_tx_status_t status;
+	int ret = 0;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	radio_buf_cfg_msg = &wlmsg->msg.radiocfgmsg;
+
+	radio_buf_cfg_msg->radio_if_num = ar->nss.if_num;
+	buf_cfg = &wlmsg->msg.radiocfgmsg.radiomsg.radiobufcfgmsg;
+	buf_cfg->range = range;
+	buf_cfg->buf_cnt = buf_sz;
+
+	nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num,
+			 NSS_WIFILI_RADIO_BUF_CFG,
+			 sizeof(struct nss_wifili_radio_buf_cfg_msg),
+			 NULL, NULL);
+
+	status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ar->ab, "nss radio buf cfg send failed %d\n", status);
+		ret = -EINVAL;
+	} else {
+		ath11k_dbg(ar->ab, ATH11K_DBG_NSS,
+			   "nss radio cfg message range:%d buf_sz:%d if_num:%d ctx:%p\n",
+			   range, buf_sz, ar->nss.if_num, ar->nss.ctx);
+	}
+
+	kfree(wlmsg);
+	return ret;
+}
+
+static void ath11k_nss_fill_srng_info(struct ath11k_base *ab, int ring_id,
+				      struct nss_wifili_hal_srng_info *hsi)
+{
+	struct ath11k_hal *hal = &ab->hal;
+	struct hal_srng *srng;
+	u32 offset;
+	int i;
+
+	if (ring_id < 0) {
+		ath11k_warn(ab, "Invalid ring id used for nss init\n");
+		WARN_ON(1);
+		return;
+	}
+
+	srng = &hal->srng_list[ring_id];
+
+	hsi->ring_id = srng->ring_id;
+	hsi->ring_dir = srng->ring_dir;
+	hsi->ring_base_paddr = srng->ring_base_paddr;
+	hsi->entry_size = srng->entry_size;
+	hsi->num_entries = srng->num_entries;
+	hsi->flags = srng->flags;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS,
+		   "Ring info to send to nss - ring_id:%d ring_dir:%d ring_paddr:%d entry_size:%d num_entries:%d flags:%d\n",
+		   hsi->ring_id, hsi->ring_dir, hsi->ring_base_paddr,
+		   hsi->entry_size, hsi->num_entries, hsi->flags);
+
+	for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) {
+		offset = srng->hwreg_base[i];
+
+		/* For PCI based devices, get the umac ring base address offset
+		 * based on window register configuration.
+		 */
+		if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING))
+			offset = ath11k_hif_get_window_offset(ab, srng->hwreg_base[i]);
+
+		hsi->hwreg_base[i]  = (u32)ab->mem_pa + offset;
+
+		ath11k_dbg(ab, ATH11K_DBG_NSS, "SRNG Register %d address %d\n",
+			   i, hsi->hwreg_base[i]);
+	}
+}
+
+static void ath11k_nss_tx_desc_mem_free(struct ath11k_base *ab)
+{
+	int i;
+
+	for (i = 0; i < ATH11K_NSS_MAX_NUMBER_OF_PAGE; i++) {
+		if (!ab->nss.tx_desc_paddr[i])
+			continue;
+
+		dma_free_coherent(ab->dev,
+				  ab->nss.tx_desc_size[i],
+				  ab->nss.tx_desc_vaddr[i],
+				  ab->nss.tx_desc_paddr[i]);
+		ab->nss.tx_desc_vaddr[i] = NULL;
+	}
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated tx desc mem freed\n");
+}
+
+static int ath11k_nss_tx_desc_mem_alloc(struct ath11k_base *ab, u32 required_size, u32 *page_idx)
+{
+	int i, alloc_size;
+	int curr_page_idx;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "tx desc mem alloc size: %d\n", required_size);
+
+	curr_page_idx = *page_idx;
+
+	for (i = 0, alloc_size = 0; i < required_size; i += alloc_size) {
+		alloc_size = required_size - i;
+
+		if (alloc_size > WIFILI_NSS_MAX_MEM_PAGE_SIZE)
+			alloc_size = WIFILI_NSS_MAX_MEM_PAGE_SIZE;
+
+		ab->nss.tx_desc_vaddr[curr_page_idx] =
+					dma_alloc_coherent(ab->dev, alloc_size,
+							   &ab->nss.tx_desc_paddr[curr_page_idx],
+							   GFP_KERNEL);
+
+		if (!ab->nss.tx_desc_vaddr[curr_page_idx])
+			return -ENOMEM;
+
+		ab->nss.tx_desc_size[curr_page_idx] = alloc_size;
+		curr_page_idx++;
+
+		ath11k_dbg(ab, ATH11K_DBG_NSS,
+			   "curr page %d, allocated %d, total allocated %d\n",
+			   curr_page_idx, alloc_size, i + alloc_size);
+
+		if (curr_page_idx == ATH11K_NSS_MAX_NUMBER_OF_PAGE) {
+			ath11k_warn(ab, "max page number reached while tx desc mem allocation\n");
+			return -EINVAL;
+		}
+	}
+	*page_idx = curr_page_idx;
+	return 0;
+}
+
+static int ath11k_nss_fill_tx_desc_info(struct ath11k_base *ab,
+					struct nss_wifili_init_msg *wim)
+{
+	struct nss_wifili_tx_desc_addtnl_mem_msg *dam;
+	u32 required_size, required_size_ext;
+	struct nss_wifili_tx_desc_init_msg *dim;
+	u32 tx_desc_limit_0 = 0;
+	u32 tx_desc_limit_1 = 0;
+	u32 tx_desc_limit_2 = 0;
+	u32 dam_page_idx = 0;
+	int page_idx = 0;
+	int i;
+
+	wim->tx_sw_internode_queue_size = ATH11K_WIFIILI_MAX_TX_PROCESSQ;
+
+	dim = &wim->wtdim;
+	dam = &wim->wtdam;
+
+	dim->num_pool = ab->num_radios;
+	dim->num_tx_device_limit = ATH11K_WIFILI_MAX_TX_DESC;
+
+	//TODO Revisit below calc based on platform/mem cfg
+	switch (dim->num_pool) {
+	case 1:
+		tx_desc_limit_0 = ATH11K_WIFILI_DBDC_NUM_TX_DESC;
+		break;
+	case 2:
+		tx_desc_limit_0 = ATH11K_WIFILI_DBDC_NUM_TX_DESC;
+		tx_desc_limit_1 = ATH11K_WIFILI_DBDC_NUM_TX_DESC;
+		break;
+	case 3:
+		tx_desc_limit_0 = ATH11K_WIFILI_DBTC_NUM_TX_DESC;
+		tx_desc_limit_1 = ATH11K_WIFILI_DBTC_NUM_TX_DESC;
+		tx_desc_limit_2 = ATH11K_WIFILI_DBTC_NUM_TX_DESC;
+		break;
+	default:
+		ath11k_warn(ab, "unexpected num radios during tx desc alloc\n");
+		return -EINVAL;
+	}
+
+	dim->num_tx_desc = tx_desc_limit_0;
+	dim->num_tx_desc_ext = tx_desc_limit_0;
+	dim->num_tx_desc_2 = tx_desc_limit_1;
+	dim->num_tx_desc_ext_2 = tx_desc_limit_1;
+	dim->num_tx_desc_3 = tx_desc_limit_2;
+	dim->num_tx_desc_ext_3 = tx_desc_limit_2;
+
+	required_size = (dim->num_tx_desc + dim->num_tx_desc_2 +
+			 dim->num_tx_desc_3 +
+			 dim->num_pool) * WIFILI_NSS_TX_DESC_SIZE;
+
+	required_size_ext = (dim->num_tx_desc_ext + dim->num_tx_desc_ext_2 +
+			     dim->num_tx_desc_ext_3 +
+			     dim->num_pool) * WIFILI_NSS_TX_EXT_DESC_SIZE;
+
+	if (ath11k_nss_tx_desc_mem_alloc(ab, required_size, &page_idx)) {
+		ath11k_warn(ab, "memory allocation for tx desc of size %d failed\n",
+			    required_size);
+		return -ENOMEM;
+	}
+
+	/* Fill the page number from where extension tx descriptor is available */
+	dim->ext_desc_page_num  = page_idx;
+
+	if (ath11k_nss_tx_desc_mem_alloc(ab, required_size_ext, &page_idx)) {
+		ath11k_warn(ab, "memory allocation for extension tx desc of size %d failed\n",
+			    required_size_ext);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < page_idx; i++)	{
+		if (i < NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG) {
+			dim->memory_addr[i] = (u32)ab->nss.tx_desc_paddr[i];
+			dim->memory_size[i] = (u32)ab->nss.tx_desc_size[i];
+			dim->num_memaddr++;
+		} else {
+			dam_page_idx = i - NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG;
+			dam->addtnl_memory_addr[dam_page_idx] = (u32)ab->nss.tx_desc_paddr[i];
+			dam->addtnl_memory_size[dam_page_idx] = (u32)ab->nss.tx_desc_size[i];
+			dam->num_addtnl_addr++;
+		}
+	}
+
+	if (i > NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG)
+		wim->flags |= WIFILI_ADDTL_MEM_SEG_SET;
+
+	return 0;
+}
+
+static int ath11k_nss_get_target_type(struct ath11k_base *ab)
+{
+	switch (ab->hw_rev) {
+	case ATH11K_HW_IPQ8074:
+		return ATH11K_WIFILI_TARGET_TYPE_QCA8074V2;
+	case ATH11K_HW_IPQ6018_HW10:
+		return ATH11K_WIFILI_TARGET_TYPE_QCA6018;
+	case ATH11K_HW_QCN9074_HW10:
+		return ATH11K_WIFILI_TARGET_TYPE_QCN9074;
+	case ATH11K_HW_IPQ5018:
+		return ATH11K_WIFILI_TARGET_TYPE_QCA5018;
+	default:
+		ath11k_warn(ab, "NSS Offload not supported for this HW\n");
+		return ATH11K_WIFILI_TARGET_TYPE_UNKNOWN;
+	}
+}
+
+static int ath11k_nss_get_interface_type(struct ath11k_base *ab)
+{
+	switch (ab->hw_rev) {
+	case ATH11K_HW_IPQ8074:
+	case ATH11K_HW_IPQ6018_HW10:
+	case ATH11K_HW_IPQ5018:
+		return NSS_WIFILI_INTERNAL_INTERFACE;
+	case ATH11K_HW_QCN9074_HW10:
+		return nss_get_available_wifili_external_if();
+	default:
+		/* This can't happen since we validated target type earlier */
+		WARN_ON(1);
+		return NSS_MAX_NET_INTERFACES;
+	}
+}
+
+static int ath11k_nss_get_dynamic_interface_type(struct ath11k_base *ab)
+{
+	switch (ab->nss.if_num) {
+	case NSS_WIFILI_INTERNAL_INTERFACE:
+		return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_INTERNAL;
+	case NSS_WIFILI_EXTERNAL_INTERFACE0:
+		return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_EXTERNAL0;
+	case NSS_WIFILI_EXTERNAL_INTERFACE1:
+		return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_EXTERNAL1;
+	default:
+		ath11k_warn(ab, "NSS Offload invalid interface\n");
+		return NSS_DYNAMIC_INTERFACE_TYPE_NONE;
+	}
+}
+
+static int ath11k_nss_init(struct ath11k_base *ab)
+{
+	struct nss_wifili_init_msg *wim = NULL;
+	struct nss_wifili_msg *wlmsg = NULL;
+	struct nss_ctx_instance *nss_contex;
+	nss_wifili_msg_callback_t msg_cb;
+	u32 target_type;
+	u32 features = 0;
+	nss_tx_status_t status;
+	struct ath11k_dp *dp;
+	int i, ret;
+
+	dp = &ab->dp;
+
+	target_type = ath11k_nss_get_target_type(ab);
+
+	if (target_type == ATH11K_WIFILI_TARGET_TYPE_UNKNOWN)
+		return -ENOTSUPP;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	wim = &wlmsg->msg.init;
+
+	wim->target_type = target_type;
+
+	/* fill rx parameters to initialize rx context */
+	wim->wrip.tlv_size = sizeof(struct hal_rx_desc);
+	wim->wrip.rx_buf_len = DP_RX_BUFFER_SIZE;
+
+	/* fill hal srng message */
+	wim->hssm.dev_base_addr = (u32)ab->mem_pa;
+	wim->hssm.shadow_rdptr_mem_addr = (u32)ab->hal.rdp.paddr;
+	wim->hssm.shadow_wrptr_mem_addr = (u32)ab->hal.wrp.paddr;
+
+	/* fill TCL data/completion ring info */
+	wim->num_tcl_data_rings = DP_TCL_NUM_RING_MAX;
+	for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) {
+		ath11k_nss_fill_srng_info(ab, dp->tx_ring[i].tcl_data_ring.ring_id,
+					  &wim->tcl_ring_info[i]);
+		ath11k_nss_fill_srng_info(ab, dp->tx_ring[i].tcl_comp_ring.ring_id,
+					  &wim->tx_comp_ring[i]);
+	}
+
+	/* allocate tx desc memory for NSS and fill corresponding info */
+	ret = ath11k_nss_fill_tx_desc_info(ab, wim);
+	if (ret)
+		goto free;
+
+	/* fill reo dest ring info */
+	wim->num_reo_dest_rings = DP_REO_DST_RING_MAX;
+	for (i = 0; i < DP_REO_DST_RING_MAX; i++) {
+		ath11k_nss_fill_srng_info(ab, dp->reo_dst_ring[i].ring_id,
+					  &wim->reo_dest_ring[i]);
+	}
+
+	/* fill reo reinject ring info */
+	ath11k_nss_fill_srng_info(ab, dp->reo_reinject_ring.ring_id,
+				  &wim->reo_reinject_ring);
+
+	/* fill reo release ring info */
+	ath11k_nss_fill_srng_info(ab, dp->rx_rel_ring.ring_id,
+				  &wim->rx_rel_ring);
+
+	/* fill reo exception ring info */
+	ath11k_nss_fill_srng_info(ab, dp->reo_except_ring.ring_id,
+				  &wim->reo_exception_ring);
+
+	ab->nss.if_num = ath11k_nss_get_interface_type(ab);
+
+	ath11k_info(ab, "nss init soc nss if_num %d userpd_id %d\n", ab->nss.if_num, ab->userpd_id);
+
+	if (ab->nss.if_num >= NSS_MAX_NET_INTERFACES) {
+		ath11k_warn(ab, "NSS invalid interface\n");
+		goto free;
+	}
+
+	/* register callbacks for events and exceptions with nss */
+	nss_contex = nss_register_wifili_if(ab->nss.if_num, NULL,
+					    (nss_wifili_callback_t)ath11k_nss_wifili_ext_callback_fn,
+					    (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive,
+					    (struct net_device *)ab, features);
+
+	if (!nss_contex) {
+		ath11k_warn(ab, "nss wifili register failure\n");
+		goto free;
+	}
+
+	if (nss_cmn_get_state(nss_contex) != NSS_STATE_INITIALIZED) {
+		ath11k_warn(ab, "nss state in default init state\n");
+		goto free;
+	}
+
+	/* The registered soc context is stored in ab, and will be used for
+	 * all soc related messages with nss
+	 */
+	ab->nss.ctx = nss_contex;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	/* Initialize the common part of the wlmsg */
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_INIT_MSG,
+			 sizeof(struct nss_wifili_init_msg),
+			 msg_cb, NULL);
+
+	reinit_completion(&ab->nss.complete);
+
+	/* Note: response is contention free during init sequence */
+	ab->nss.response = ATH11K_NSS_MSG_ACK;
+
+	status = nss_wifili_tx_msg(nss_contex, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "failure to send nss init msg\n");
+		goto unregister;
+	}
+
+	ret = wait_for_completion_timeout(&ab->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ab, "timeout while waiting for nss init msg response\n");
+		goto unregister;
+	}
+
+	/* Check if the response is success from the callback */
+	if (ab->nss.response != ATH11K_NSS_MSG_ACK)
+		goto unregister;
+
+	kfree(wlmsg);
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Init Message TX Success %p %d\n",
+		   ab->nss.ctx, ab->nss.if_num);
+	return 0;
+
+unregister:
+	nss_unregister_wifili_if(ab->nss.if_num);
+free:
+	ath11k_nss_tx_desc_mem_free(ab);
+	kfree(wlmsg);
+	return -EINVAL;
+}
+
+static int ath11k_nss_stats_cfg(struct ath11k *ar, int nss_msg, int enable)
+{
+	struct nss_wifili_msg *wlmsg = NULL;
+	struct nss_wifili_stats_cfg_msg *stats_cfg;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	struct ath11k_base *ab = ar->ab;
+	int ret = 0;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	stats_cfg = &wlmsg->msg.scm;
+	stats_cfg->cfg = enable;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 nss_msg,
+			 sizeof(struct nss_wifili_stats_cfg_msg),
+			 msg_cb, NULL);
+
+	status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss stats cfg %d msg tx failure\n", nss_msg);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "nss stats %d enable %d\n", nss_msg, enable);
+
+free:
+	kfree(wlmsg);
+	return ret;
+}
+
+static void ath11k_nss_sojourn_stats_disable(struct ath11k *ar)
+{
+	ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_V2_CFG_MSG,
+			     ATH11K_NSS_STATS_DISABLE);
+}
+
+void ath11k_nss_peer_stats_disable(struct ath11k *ar)
+{
+	ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_CFG_MSG,
+			     ATH11K_NSS_STATS_DISABLE);
+}
+
+void ath11k_nss_peer_stats_enable(struct ath11k *ar)
+{
+	ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_CFG_MSG,
+			     ATH11K_NSS_STATS_ENABLE);
+}
+
+int ath11k_nss_pdev_init(struct ath11k_base *ab, int radio_id)
+{
+	struct ath11k *ar = ab->pdevs[radio_id].ar;
+	struct nss_wifili_pdev_init_msg *pdevmsg;
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int radio_if_num = -1;
+	int refill_ring_id;
+	int features = 0;
+	int dyn_if_type;
+	int ret, i;
+
+	dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab);
+
+	/* Allocate a node for dynamic interface */
+	radio_if_num = nss_dynamic_interface_alloc_node(dyn_if_type);
+
+	if (radio_if_num < 0)
+		return -EINVAL;
+
+	/* The ifnum and registered radio context is stored in ar and used
+	 * for messages related to vdev/radio
+	 */
+	ar->nss.if_num = radio_if_num;
+
+	/* No callbacks are registered for radio specific events/data */
+	ar->nss.ctx = nss_register_wifili_radio_if((u32)radio_if_num, NULL,
+						   NULL, NULL, (struct net_device *)ar,
+						   features);
+
+	if (!ar->nss.ctx) {
+		ath11k_warn(ab, "failure during nss pdev register\n");
+		ret = -EINVAL;
+		goto dealloc;
+	}
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "nss pdev init - id:%d init ctxt:%p ifnum:%d\n",
+		   ar->pdev->pdev_id, ar->nss.ctx, ar->nss.if_num);
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg) {
+		ret = -ENOMEM;
+		goto unregister;
+	}
+
+	pdevmsg = &wlmsg->msg.pdevmsg;
+
+	pdevmsg->radio_id = radio_id;
+	pdevmsg->lmac_id = ar->lmac_id;
+	pdevmsg->target_pdev_id = ar->pdev->pdev_id;
+	pdevmsg->num_rx_swdesc = WIFILI_RX_DESC_POOL_WEIGHT * DP_RXDMA_BUF_RING_SIZE;
+
+	/* Store rxdma ring info to the message */
+	refill_ring_id = ar->dp.rx_refill_buf_ring.refill_buf_ring.ring_id;
+	ath11k_nss_fill_srng_info(ab, refill_ring_id, &pdevmsg->rxdma_ring);
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_PDEV_INIT_MSG,
+			 sizeof(struct nss_wifili_pdev_init_msg),
+			 msg_cb, NULL);
+
+	reinit_completion(&ab->nss.complete);
+
+	/* Note: response is contention free during init sequence */
+	ab->nss.response = ATH11K_NSS_MSG_ACK;
+
+	status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
+
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss send pdev msg tx error : %d\n", status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ret = wait_for_completion_timeout(&ab->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ab, "timeout while waiting for pdev init msg response\n");
+		ret = -ETIMEDOUT;
+		goto free;
+	}
+
+	/* Check if the response is success from the callback */
+	if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
+		ret = -EINVAL;
+		goto free;
+	}
+
+	kfree(wlmsg);
+
+	/* Disable nss sojourn stats by default */
+	ath11k_nss_sojourn_stats_disable(ar);
+	/* Enable nss wifili peer stats by default */
+	ath11k_nss_peer_stats_enable(ar);
+
+	/*TODO CFG Tx buffer limit as per no clients range per radio
+	 * this needs to be based on target/mem cfg
+	 * similar to tx desc cfg at soc init per radio
+	 */
+
+	for (i = 0; i < ATH11K_NSS_RADIO_TX_LIMIT_RANGE; i++)
+		ath11k_nss_radio_buf_cfg(ar, i, ATH11K_NSS_RADIO_TX_LIMIT_RANGE3);
+
+	return 0;
+
+free:
+	kfree(wlmsg);
+unregister:
+	nss_unregister_wifili_radio_if(ar->nss.if_num);
+dealloc:
+	nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type);
+	return ret;
+}
+
+/* TODO : Check if start, reset and stop messages can be done using single function as
+ * body is similar, having it now for clarity */
+
+int ath11k_nss_start(struct ath11k_base *ab)
+{
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int ret;
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	/* Empty message for NSS Start message */
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_START_MSG,
+			 0,
+			 msg_cb, NULL);
+
+	reinit_completion(&ab->nss.complete);
+
+	/* Note: response is contention free during init sequence */
+	ab->nss.response = ATH11K_NSS_MSG_ACK;
+
+	status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
+
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss send start msg tx error %d\n", status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ret = wait_for_completion_timeout(&ab->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ab, "timeout while waiting for response for nss start msg\n");
+		ret = -ETIMEDOUT;
+		goto free;
+	}
+
+	/* Check if the response is success from the callback */
+	if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
+		ret = -EINVAL;
+		goto free;
+	}
+
+	/* NSS Start success */
+	ret = 0;
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "nss start success\n");
+
+free:
+	kfree(wlmsg);
+	return ret;
+}
+
+static void ath11k_nss_reset(struct ath11k_base *ab)
+{
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int ret;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS reset\n");
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg) {
+		ath11k_warn(ab, "mem allocation failure during nss reset\n");
+		return;
+	}
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	/* Empty message for NSS Reset message */
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_SOC_RESET_MSG,
+			 0,
+			 msg_cb, NULL);
+
+	reinit_completion(&ab->nss.complete);
+
+	/* Note: response is contention free during deinit sequence */
+	ab->nss.response = ATH11K_NSS_MSG_ACK;
+
+	status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
+
+	/* Add a retry mechanism to reset nss until success */
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss send reset msg tx error %d\n", status);
+		goto free;
+	}
+
+	ret = wait_for_completion_timeout(&ab->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ab, "timeout while waiting for response for nss reset msg\n");
+		goto free;
+	}
+
+	/* Check if the response is success from the callback */
+	if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
+		ath11k_warn(ab, "failure response during nss reset %d\n", ab->nss.response);
+		goto free;
+	}
+
+	/* Unregister wifili interface */
+	nss_unregister_wifili_if(ab->nss.if_num);
+
+free:
+	kfree(wlmsg);
+}
+
+static int ath11k_nss_stop(struct ath11k_base *ab)
+{
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int ret;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS stop\n");
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	/* Empty message for Stop command */
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_STOP_MSG,
+			 0,
+			 msg_cb, NULL);
+
+	reinit_completion(&ab->nss.complete);
+
+	/* Note: response is contention free during deinit sequence */
+	ab->nss.response = ATH11K_NSS_MSG_ACK;
+
+	status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
+
+	/* Add a retry mechanism to stop nss until success */
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss send stop msg tx error %d\n", status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ret = wait_for_completion_timeout(&ab->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ab, "timeout while waiting for response for nss stop msg\n");
+		ret = -ETIMEDOUT;
+		goto free;
+	}
+
+	/* Check if the response is success from the callback */
+	if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
+		ret = -EINVAL;
+		goto free;
+	}
+
+	/* NSS Stop success */
+	ret = 0;
+free:
+	kfree(wlmsg);
+	return ret;
+}
+
+int ath11k_nss_pdev_deinit(struct ath11k_base *ab, int radio_id)
+{
+	struct ath11k *ar = ab->pdevs[radio_id].ar;
+	struct nss_wifili_pdev_deinit_msg *deinit;
+	struct nss_wifili_msg *wlmsg = NULL;
+	nss_wifili_msg_callback_t msg_cb;
+	nss_tx_status_t status;
+	int dyn_if_type;
+	int ret;
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS pdev %d deinit\n", radio_id);
+	dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab);
+
+	/* Disable NSS wifili peer stats before teardown */
+	if (ab->nss.stats_enabled)
+		ath11k_nss_peer_stats_disable(ar);
+
+	wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
+	if (!wlmsg)
+		return -ENOMEM;
+
+	deinit = &wlmsg->msg.pdevdeinit;
+	deinit->ifnum = radio_id;
+
+	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
+
+	nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
+			 NSS_WIFILI_PDEV_DEINIT_MSG,
+			 sizeof(struct nss_wifili_pdev_deinit_msg),
+			 msg_cb, NULL);
+
+	reinit_completion(&ab->nss.complete);
+
+	/* Note: response is contention free during deinit sequence */
+	ab->nss.response = ATH11K_NSS_MSG_ACK;
+
+	status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
+	if (status != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "nss send pdev deinit msg tx error %d\n", status);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	ret = wait_for_completion_timeout(&ab->nss.complete,
+					  msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
+	if (!ret) {
+		ath11k_warn(ab, "timeout while waiting for pdev deinit msg response\n");
+		ret = -ETIMEDOUT;
+		goto free;
+	}
+
+	/* Check if the response is success from the callback */
+	if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
+		ret = -EINVAL;
+		goto free;
+	}
+
+	/* pdev deinit msg success, dealloc, deregister and return */
+	ret = 0;
+
+	nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type);
+	nss_unregister_wifili_radio_if(ar->nss.if_num);
+free:
+	kfree(wlmsg);
+	return ret;
+}
+
+int ath11k_nss_teardown(struct ath11k_base *ab)
+{
+	int i, ret;
+
+	if (!ab->nss.enabled)
+		return 0;
+
+	ath11k_nss_stop(ab);
+
+	for (i = 0; i < ab->num_radios ; i++) {
+		ret = ath11k_nss_pdev_deinit(ab, i);
+		if (ret)
+			ath11k_warn(ab, "failure during pdev%d deinit\n", i);
+	}
+
+	ath11k_nss_reset(ab);
+	ath11k_nss_tx_desc_mem_free(ab);
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Teardown Complete\n");
+
+	return 0;
+}
+
+int ath11k_nss_setup(struct ath11k_base *ab)
+{
+	int i;
+	int ret = 0;
+	u32 target_type;
+
+	if (!ab->nss.enabled)
+		return 0;
+
+	target_type = ath11k_nss_get_target_type(ab);
+
+	if (target_type == ATH11K_WIFILI_TARGET_TYPE_UNKNOWN)
+		return -ENOTSUPP;
+
+	/* Verify NSS support is enabled */
+	if (nss_cmn_get_nss_enabled() == false) {
+		ath11k_warn(ab, "NSS offload support disabled, falling back to default mode\n");
+		return -ENOTSUPP;
+	}
+
+	/* Initialize completion for verifying NSS message response */
+	init_completion(&ab->nss.complete);
+
+	/* Setup common resources for NSS */
+	ret = ath11k_nss_init(ab);
+	if (ret) {
+		ath11k_warn(ab, "NSS SOC Initialization Failed :%d\n", ret);
+		goto fail;
+	}
+
+	/* Setup pdev related resources for NSS */
+	for (i = 0; i < ab->num_radios; i++) {
+		ret = ath11k_nss_pdev_init(ab, i);
+		if (ret) {
+			ath11k_warn(ab, "NSS PDEV %d Initialization Failed :%d\n", i, ret);
+			goto pdev_deinit;
+		}
+	}
+
+	/* Set the NSS statemachine to start */
+	ret = ath11k_nss_start(ab);
+	if (ret) {
+		ath11k_warn(ab, "NSS Start Failed : %d\n", ret);
+		goto pdev_deinit;
+	}
+
+	/* Default nexthop interface is set to ETH RX */
+	ret = nss_wifi_vdev_base_set_next_hop(ab->nss.ctx, NSS_ETH_RX_INTERFACE);
+	if (ret != NSS_TX_SUCCESS) {
+		ath11k_warn(ab, "Failure to set default next hop : %d\n", ret);
+		goto stop;
+	}
+
+	ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Setup Complete\n");
+	return ret;
+
+stop:
+	ath11k_nss_stop(ab);
+
+pdev_deinit:
+	for (i -= 1; i >= 0; i--)
+		ath11k_nss_pdev_deinit(ab, i);
+
+	ath11k_nss_reset(ab);
+	ath11k_nss_tx_desc_mem_free(ab);
+fail:
+	return ret;
+}
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/nss.h
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef ATH11K_NSS_H
+#define ATH11K_NSS_H
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+
+struct ath11k;
+struct ath11k_base;
+struct ath11k_vif;
+struct ath11k_peer;
+struct ath11k_sta;
+struct hal_rx_mon_ppdu_info;
+
+/* NSS DBG macro is not included as part of debug enum to avoid
+ * frequent changes during upgrade*/
+#define ATH11K_DBG_NSS	0x80000000
+
+/* WIFILI Supported Target Types */
+#define ATH11K_WIFILI_TARGET_TYPE_UNKNOWN   0xFF
+#define ATH11K_WIFILI_TARGET_TYPE_QCA8074   20
+#define ATH11K_WIFILI_TARGET_TYPE_QCA8074V2 24
+#define ATH11K_WIFILI_TARGET_TYPE_QCA6018   25
+#define ATH11K_WIFILI_TARGET_TYPE_QCN9074   26
+#define ATH11K_WIFILI_TARGET_TYPE_QCA5018   29
+
+/* Max limit for NSS Queue */
+#define ATH11K_WIFIILI_MAX_TX_PROCESSQ 1024
+
+/* Max TX Desc limit */
+#define ATH11K_WIFILI_MAX_TX_DESC	65536
+
+/* TX Desc related info */
+/*TODO : Check this again during experiments for lowmem or
+ changes for platforms based on num radios supported */
+#define ATH11K_WIFILI_DBDC_NUM_TX_DESC (1024 * 8)
+#define ATH11K_WIFILI_DBTC_NUM_TX_DESC (1024 * 8)
+
+// TODO Revisit these page size calc
+#define WIFILI_NSS_TX_DESC_SIZE 20*4
+#define WIFILI_NSS_TX_EXT_DESC_SIZE 40*4
+/* Number of desc per page(12bit) should be<4096, page limit per 1024 byte is 80*3=240 */
+#define WIFILI_NSS_TX_DESC_PAGE_LIMIT 240
+#define WIFILI_NSS_MAX_MEM_PAGE_SIZE (WIFILI_NSS_TX_DESC_PAGE_LIMIT * 1024)
+#define WIFILI_NSS_MAX_EXT_MEM_PAGE_SIZE  (WIFILI_NSS_TX_DESC_PAGE_LIMIT * 1024)
+#define WIFILI_RX_DESC_POOL_WEIGHT 3
+
+/* Status of the NSS messages sent from driver */
+#define ATH11K_NSS_MSG_ACK	0
+/* Timeout for waiting for response from NSS on TX msg */
+#define ATH11K_NSS_MSG_TIMEOUT_MS 5000
+
+/* Init Flags */
+#define WIFILI_NSS_CCE_DISABLED 0x1
+#define WIFILI_ADDTL_MEM_SEG_SET 0x000000002
+
+/* ATH11K NSS PEER Info */
+/* Host memory allocated for peer info storage in nss */
+#define WIFILI_NSS_PEER_BYTE_SIZE NSS_WIFILI_PEER_SIZE
+
+/* ATH11K NSS Stats */
+#define ATH11K_NSS_STATS_ENABLE 1
+#define ATH11K_NSS_STATS_DISABLE 0
+
+/* TX Buf cfg range */
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE 4
+
+/* TODO : Analysis based on platform */
+/* TX Limit till 64 clients */
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE0 8192
+/* TX Limit till 128 clients */
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE1 8192
+/* TX Limit till 256 clients */
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE2 8192
+/* TX Limit > 256 clients */
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE3 8192
+
+#define ATH11K_NSS_MAX_NUMBER_OF_PAGE 96
+
+#define NSS_TX_TID_MAX 8
+
+#define ATH11K_NSS_TXRX_NETDEV_STATS(txrx, vif, len, pkt_count) \
+do {	\
+	struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);	\
+	struct pcpu_sw_netstats *tstats;	\
+	\
+	if (!wdev)	\
+		break;	\
+	tstats = this_cpu_ptr(netdev_tstats(wdev->netdev));	\
+	u64_stats_update_begin(&tstats->syncp);	\
+	tstats->txrx ## _packets += pkt_count;	\
+	tstats->txrx ## _bytes += len;	\
+	u64_stats_update_end(&tstats->syncp);	\
+} while (0)
+
+enum ath11k_nss_opmode {
+	ATH11K_NSS_OPMODE_UNKNOWN,
+	ATH11K_NSS_OPMODE_AP,
+	ATH11K_NSS_OPMODE_IBSS,
+	ATH11K_NSS_OPMODE_STA,
+	ATH11K_NSS_OPMODE_MONITOR,
+};
+
+struct peer_stats {
+	u64 last_rx;
+	u64 last_ack;
+	u32 tx_packets;
+	u32 tx_bytes;
+	u32 tx_retries;
+	u32 tx_failed;
+	u32 rx_packets;
+	u32 rx_bytes;
+	u32 rx_dropped;
+	u32 last_rxdrop;
+	struct rate_info rxrate;
+};
+
+enum ath11k_nss_peer_sec_type {
+	PEER_SEC_TYPE_NONE,
+	PEER_SEC_TYPE_WEP128,
+	PEER_SEC_TYPE_WEP104,
+	PEER_SEC_TYPE_WEP40,
+	PEER_SEC_TYPE_TKIP,
+	PEER_SEC_TYPE_TKIP_NOMIC,
+	PEER_SEC_TYPE_AES_CCMP,
+	PEER_SEC_TYPE_WAPI,
+	PEER_SEC_TYPE_AES_CCMP_256,
+	PEER_SEC_TYPE_AES_GCMP,
+	PEER_SEC_TYPE_AES_GCMP_256,
+	PEER_SEC_TYPES_MAX
+};
+
+/* this holds the memory allocated for nss managed peer info */
+struct ath11k_nss_peer {
+	uint32_t *vaddr;
+	dma_addr_t paddr;
+	struct peer_stats *nss_stats;
+	struct completion complete;
+};
+
+/* Structure to hold the vif related info for nss offload support */
+struct arvif_nss {
+	/* dynamic ifnum allocated by nss driver for vif */
+	int if_num;
+	/* Used for completion status for vdev config nss messages */
+	struct completion complete;
+	/* Keep the copy of encap type for nss */
+	int encap;
+	/* Keep the copy of decap type for nss */
+	int decap;
+	bool created;
+};
+
+/* Structure to hold the pdev/radio related info for nss offload support */
+struct ath11k_nss {
+	/* dynamic ifnum allocated by nss driver for pdev */
+	int if_num;
+	/* Radio/pdev Context obtained on pdev register */
+	void* ctx;
+};
+
+/* Structure to hold the soc related info for nss offload support */
+struct ath11k_soc_nss {
+	/* turn on/off nss offload support in ath11k */
+	bool enabled;
+	/* turn on/off nss stats support in ath11k */
+	bool stats_enabled;
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+	/* soc nss ctx */
+	void* ctx;
+	/* if_num to be used for soc related nss messages */
+	int if_num;
+	/* Completion to nss message response */
+	struct completion complete;
+	/* Response to nss messages are stored here on msg callback
+	* used only in contention free messages during init */
+	int response;
+	/* Below is used for identifying allocated tx descriptors */
+	dma_addr_t tx_desc_paddr[ATH11K_NSS_MAX_NUMBER_OF_PAGE];
+	uint32_t * tx_desc_vaddr[ATH11K_NSS_MAX_NUMBER_OF_PAGE];
+	uint32_t tx_desc_size[ATH11K_NSS_MAX_NUMBER_OF_PAGE];
+#endif
+};
+
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb);
+int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val);
+int ath11k_nss_vdev_create(struct ath11k_vif *arvif);
+void ath11k_nss_vdev_delete(struct ath11k_vif *arvif);
+int ath11k_nss_vdev_up(struct ath11k_vif *arvif);
+int ath11k_nss_vdev_down(struct ath11k_vif *arvif);
+int ath11k_nss_peer_delete(struct ath11k_base *ab, u8 *addr);
+int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id);
+int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer);
+void ath11k_nss_peer_stats_enable(struct ath11k *ar);
+void ath11k_nss_peer_stats_disable(struct ath11k *ar);
+int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer,
+				 struct ieee80211_key_conf *key_conf);
+void ath11k_nss_update_sta_stats(struct station_info *sinfo,
+				 struct ieee80211_sta *sta,
+				 struct ath11k_sta *arsta);
+void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info,
+				  struct ath11k_peer *peer);
+int ath11k_nss_setup(struct ath11k_base *ab);
+int ath11k_nss_teardown(struct ath11k_base *ab);
+void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter);
+#else
+static inline int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_vdev_create(struct ath11k_vif *arvif)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id)
+{
+	return 0;
+}
+
+static inline void ath11k_nss_vdev_delete(struct ath11k_vif *arvif)
+{
+}
+
+static inline void ath11k_nss_update_sta_stats(struct station_info *sinfo,
+					       struct ieee80211_sta *sta,
+					       struct ath11k_sta *arsta)
+{
+	return;
+}
+
+static void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info,
+					 struct ath11k_peer *peer)
+{
+	return;
+}
+
+static inline int ath11k_nss_vdev_up(struct ath11k_vif *arvif)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_vdev_down(struct ath11k_vif *arvif)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_peer_delete(struct ath11k_base *ab, u8 *addr)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer)
+{
+	return 0;
+}
+
+static inline void ath11k_nss_peer_stats_enable(struct ath11k *ar)
+{
+	return;
+}
+
+static inline void ath11k_nss_peer_stats_disable(struct ath11k *ar)
+{
+	return;
+}
+
+static inline int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer,
+					       struct ieee80211_key_conf *key_conf)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_setup(struct ath11k_base *ab)
+{
+	return 0;
+}
+
+static inline int ath11k_nss_teardown(struct ath11k_base *ab)
+{
+	return 0;
+}
+
+void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter)
+{
+	return;
+}
+#endif /* CPTCFG_ATH11K_NSS_SUPPORT */
+#endif
--- a/drivers/net/wireless/ath/ath11k/hif.h
+++ b/drivers/net/wireless/ath/ath11k/hif.h
@@ -29,6 +29,7 @@ struct ath11k_hif_ops {
 	void (*ce_irq_enable)(struct ath11k_base *ab);
 	void (*ce_irq_disable)(struct ath11k_base *ab);
 	void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
+	u32 (*get_window_offset)(struct ath11k_base *ab, u32 offset);
 };
 
 static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)
@@ -126,6 +127,14 @@ static inline void ath11k_get_msi_addres
 	ab->hif.ops->get_msi_address(ab, msi_addr_lo, msi_addr_hi);
 }
 
+static inline u32 ath11k_hif_get_window_offset(struct ath11k_base *ab, u32 offset)
+{
+	if (ab->hif.ops->get_window_offset)
+		return ab->hif.ops->get_window_offset(ab, offset);
+
+	return offset;
+}
+
 static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id,
 					 u32 *msi_data_idx)
 {
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -174,6 +174,20 @@ static inline u32 ath11k_pci_get_window_
 	return window_start;
 }
 
+static inline u32 ath11k_pci_get_window_offset(struct ath11k_base *ab,
+                                               u32 offset)
+{
+        u32 window_start;
+
+        if (ab->bus_params.static_window_map) {
+                window_start = ath11k_pci_get_window_start(ab, offset);
+
+                if (window_start)
+                        offset = window_start + (offset & WINDOW_RANGE_MASK);
+        }
+        return offset;
+}
+
 void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
 {
 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
@@ -1180,6 +1194,7 @@ static const struct ath11k_hif_ops ath11
 	.map_service_to_pipe = ath11k_pci_map_service_to_pipe,
 	.ce_irq_enable = ath11k_pci_hif_ce_irq_enable,
 	.ce_irq_disable = ath11k_pci_hif_ce_irq_disable,
+	.get_window_offset = ath11k_pci_get_window_offset,
 	.get_ce_msi_idx = ath11k_pci_get_ce_msi_idx,
 };
 
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -955,6 +955,29 @@ unlock_exit:
 	spin_unlock_bh(&ab->base_lock);
 }
 
+/* Sends WMI config to filter packets to route packets to WBM release ring */
+int ath11k_dp_rx_pkt_type_filter(struct ath11k *ar, enum ath11k_routing_pkt_type pkt_type, u32 meta_data)
+{
+	struct ath11k_wmi_pkt_route_param param;
+	int ret;
+
+	/* Routing Eapol packets to CCE is only allowed now */
+	if (pkt_type != ATH11K_PKT_TYPE_EAP)
+		return -EINVAL;
+
+	param.opcode = ATH11K_WMI_PKTROUTE_ADD;
+	param.meta_data = meta_data;
+	param.dst_ring = ATH11K_ROUTE_WBM_RELEASE;
+	param.dst_ring_handler = ATH11K_WMI_PKTROUTE_USE_CCE;
+	param.route_type_bmap = 1 << pkt_type;
+
+	ret = ath11k_wmi_send_pdev_pkt_route(ar, &param);
+	if (ret)
+		ath11k_warn(ar->ab, "failed to configure pkt route %d", ret);
+
+	return ret;
+}
+
 int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
 			     u8 tid, u32 ba_win_sz, u16 ssn,
 			     enum hal_pn_type pn_type)
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -1073,6 +1073,44 @@ int ath11k_wmi_send_pdev_set_regdomain(s
 	return ret;
 }
 
+int ath11k_wmi_send_pdev_pkt_route(struct ath11k *ar, struct ath11k_wmi_pkt_route_param *param)
+{
+	struct ath11k_pdev_wmi *wmi = ar->wmi;
+	struct wmi_pdev_pkt_route_cmd *cmd;
+	struct sk_buff *skb;
+	int ret;
+
+	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_pdev_pkt_route_cmd *)skb->data;
+	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+				     WMI_TAG_PDEV_UPDATE_PKT_ROUTING_CMD) |
+			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+	cmd->pdev_id = ar->pdev->pdev_id;
+	cmd->opcode = param->opcode;
+	cmd->route_type_bmap = param->route_type_bmap;
+	cmd->dst_ring = param->dst_ring;
+	cmd->meta_data = param->meta_data;
+	cmd->dst_ring_handler = param->dst_ring_handler;
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+		   "WMI pdev pkt route opcode %d route_bmap %d dst_ring %d meta_datan %d dst_ringg_handler %d\n",
+		   param->opcode, param->route_type_bmap,
+		   param->dst_ring, param->meta_data, param->dst_ring_handler);
+
+	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_UPDATE_PKT_ROUTING_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab,
+			    "failed to send WMI_PDEV_UPDATE_PKT_ROUTING cmd\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
+}
+
 int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr,
 			      u32 vdev_id, u32 param_id, u32 param_val)
 {
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -2785,6 +2785,27 @@ struct pdev_set_regdomain_params {
 	u32 pdev_id;
 };
 
+ /* Defines various options for routing policy */
+enum wmi_pdev_dest_ring_handler_type {
+	ATH11K_WMI_PKTROUTE_USE_CCE  = 0,
+	ATH11K_WMI_PKTROUTE_USE_ASPT = 1,
+	ATH11K_WMI_PKTROUTE_USE_FSE  = 2,
+	ATH11K_WMI_PKTROUTE_USE_CCE2 = 3,
+};
+
+enum ath11k_wmi_pkt_route_opcode {
+	ATH11K_WMI_PKTROUTE_ADD,
+	ATH11K_WMI_PKTROUTE_DEL,
+};
+
+struct ath11k_wmi_pkt_route_param {
+	enum ath11k_wmi_pkt_route_opcode opcode;
+	u32 route_type_bmap;
+	u32 dst_ring_handler;
+	u32 dst_ring;
+	u32 meta_data;
+};
+
 struct rx_reorder_queue_remove_params {
 	u8 *peer_macaddr;
 	u16 vdev_id;
@@ -3037,6 +3058,16 @@ struct wmi_pdev_set_regdomain_cmd {
 	u32 dfs_domain;
 } __packed;
 
+struct wmi_pdev_pkt_route_cmd {
+	u32 tlv_header;
+	u32 pdev_id;
+	u32 opcode;
+	u32 route_type_bmap;
+	u32 dst_ring;
+	u32 meta_data;
+	u32 dst_ring_handler;
+} __packed;
+
 struct wmi_peer_set_param_cmd {
 	u32 tlv_header;
 	u32 vdev_id;
@@ -5733,6 +5764,8 @@ int ath11k_wmi_send_peer_create_cmd(stru
 int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id,
 				  u32 param_id, u32 param_value);
 
+int ath11k_wmi_send_pdev_pkt_route(struct ath11k *ar,
+				   struct ath11k_wmi_pkt_route_param *param);
 int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id,
 				u32 param, u32 param_value);
 int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms);
--- a/drivers/net/wireless/ath/ath11k/dp_rx.h
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.h
@@ -20,6 +20,34 @@
 #define DP_RX_MPDU_ERR_MPDU_LEN			BIT(6)
 #define DP_RX_MPDU_ERR_UNENCRYPTED_FRAME	BIT(7)
 
+/* different supported pkt types for routing */
+enum ath11k_routing_pkt_type {
+	ATH11K_PKT_TYPE_ARP_IPV4,
+	ATH11K_PKT_TYPE_NS_IPV6,
+	ATH11K_PKT_TYPE_IGMP_IPV4,
+	ATH11K_PKT_TYPE_MLD_IPV6,
+	ATH11K_PKT_TYPE_DHCP_IPV4,
+	ATH11K_PKT_TYPE_DHCP_IPV6,
+	ATH11K_PKT_TYPE_DNS_TCP_IPV4,
+	ATH11K_PKT_TYPE_DNS_TCP_IPV6,
+	ATH11K_PKT_TYPE_DNS_UDP_IPV4,
+	ATH11K_PKT_TYPE_DNS_UDP_IPV6,
+	ATH11K_PKT_TYPE_ICMP_IPV4,
+	ATH11K_PKT_TYPE_ICMP_IPV6,
+	ATH11K_PKT_TYPE_TCP_IPV4,
+	ATH11K_PKT_TYPE_TCP_IPV6,
+	ATH11K_PKT_TYPE_UDP_IPV4,
+	ATH11K_PKT_TYPE_UDP_IPV6,
+	ATH11K_PKT_TYPE_IPV4,
+	ATH11K_PKT_TYPE_IPV6,
+	ATH11K_PKT_TYPE_EAP,
+	ATH11K_PKT_TYPE_MAX
+};
+
+#define ATH11K_RX_PROTOCOL_TAG_START_OFFSET  128
+#define ATH11K_ROUTE_WBM_RELEASE	5
+#define ATH11K_ROUTE_EAP_METADATA	(ATH11K_RX_PROTOCOL_TAG_START_OFFSET + ATH11K_PKT_TYPE_EAP)
+
 enum dp_rx_decap_type {
 	DP_RX_DECAP_TYPE_RAW,
 	DP_RX_DECAP_TYPE_NATIVE_WIFI,
@@ -74,6 +102,9 @@ void ath11k_peer_rx_tid_delete(struct at
 int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
 			     u8 tid, u32 ba_win_sz, u16 ssn,
 			     enum hal_pn_type pn_type);
+int ath11k_dp_rx_pkt_type_filter(struct ath11k *ar,
+				  enum ath11k_routing_pkt_type pkt_type,
+				  u32 meta_data);
 void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab,
 				       struct sk_buff *skb);
 int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab);
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -5217,6 +5217,16 @@ static int ath11k_mac_op_start(struct ie
 		}
 	}
 
+	/* nss offload requires eapol packets to be routed to wbm release ring */
+	if (ab->nss.enabled) {
+		ret = ath11k_dp_rx_pkt_type_filter(ar, ATH11K_PKT_TYPE_EAP,
+						   ATH11K_ROUTE_EAP_METADATA);
+		if (ret) {
+			ath11k_err(ar->ab, "failed to configure EAP pkt route: %d\n", ret);
+			goto err;
+		}
+	}
+
 	__ath11k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask);
 
 	/* TODO: Do we need to enable ANI? */
