From 65c511e3aeb9afb84a3c6c8ac34353af91b880e9 Mon Sep 17 00:00:00 2001
From: Sriram R <srirrama@codeaurora.org>
Date: Fri, 10 Jul 2020 12:50:21 +0530
Subject: [PATCH 3/3] ath11k: add nss support

  Add NSS Offload support for ath11k driver.

Signed-off-by: Sriram R <srirrama@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/ahb.c    | 18 ++++++--
 drivers/net/wireless/ath/ath11k/core.c   | 24 ++++++++++
 drivers/net/wireless/ath/ath11k/core.h   | 14 +++++-
 drivers/net/wireless/ath/ath11k/dp.c     | 21 ++++++---
 drivers/net/wireless/ath/ath11k/dp.h     |  1 +
 drivers/net/wireless/ath/ath11k/dp_rx.c  | 17 +++++--
 drivers/net/wireless/ath/ath11k/dp_rx.h  |  6 +++
 drivers/net/wireless/ath/ath11k/hal.h    |  2 +
 drivers/net/wireless/ath/ath11k/hal_rx.c | 10 +++-
 drivers/net/wireless/ath/ath11k/mac.c    | 78 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath11k/peer.c   |  9 +++-
 drivers/net/wireless/ath/ath11k/peer.h   |  6 ++-
 local-symbols                            |  1 +
 13 files changed, 186 insertions(+), 21 deletions(-)

--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -442,6 +442,12 @@ static int ath11k_ahb_ext_irq_config(str
 	int i, j;
 	int irq;
 	int ret;
+	bool nss_offload;
+
+	/* TCL Completion, REO Dest, ERR, Exception and h2rxdma rings are offloaded
+	 * to nss when its enabled, hence don't enable these interrupts
+	 */
+	nss_offload = ab->nss.enabled;
 
 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
@@ -454,20 +460,20 @@ static int ath11k_ahb_ext_irq_config(str
 			       ath11k_ahb_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
 
 		for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) {
-			if (ab->hw_params.ring_mask->tx[i] & BIT(j)) {
+			if (!nss_offload && ab->hw_params.ring_mask->tx[i] & BIT(j)) {
 				irq_grp->irqs[num_irq++] =
 					wbm2host_tx_completions_ring1 - j;
 			}
 
-			if (ab->hw_params.ring_mask->rx[i] & BIT(j)) {
+			if (!nss_offload && ab->hw_params.ring_mask->rx[i] & BIT(j)) {
 				irq_grp->irqs[num_irq++] =
 					reo2host_destination_ring1 - j;
 			}
 
-			if (ab->hw_params.ring_mask->rx_err[i] & BIT(j))
+			if (!nss_offload && ab->hw_params.ring_mask->rx_err[i] & BIT(j))
 				irq_grp->irqs[num_irq++] = reo2host_exception;
 
-			if (ab->hw_params.ring_mask->rx_wbm_rel[i] & BIT(j))
+			if (!nss_offload && ab->hw_params.ring_mask->rx_wbm_rel[i] & BIT(j))
 				irq_grp->irqs[num_irq++] = wbm2host_rx_release;
 
 			if (ab->hw_params.ring_mask->reo_status[i] & BIT(j))
@@ -480,7 +486,7 @@ static int ath11k_ahb_ext_irq_config(str
 						ath11k_hw_get_mac_from_pdev_id(hw, j);
 				}
 
-				if (ab->hw_params.ring_mask->host2rxdma[i] & BIT(j)) {
+				if (!nss_offload && ab->hw_params.ring_mask->host2rxdma[i] & BIT(j)) {
 					irq_grp->irqs[num_irq++] =
 						host2rxdma_host_buf_ring_mac1 -
 						ath11k_hw_get_mac_from_pdev_id(hw, j);
@@ -673,6 +679,7 @@ static int ath11k_ahb_probe(struct platf
 	ab->hw_rev = (enum ath11k_hw_rev)of_id->data;
 	ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
 	ab->mem = mem;
+	ab->mem_pa = mem_res->start;
 	ab->mem_len = resource_size(mem_res);
 	ab->enable_cold_boot_cal = enable_cold_boot_cal;
 	platform_set_drvdata(pdev, ab);
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -16,6 +16,12 @@
 #include "hif.h"
 #include "wow.h"
 
+unsigned int nss_offload;
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+module_param_named(nss_offload, nss_offload, uint, 0644);
+MODULE_PARM_DESC(nss_offload, "Enable NSS Offload support");
+#endif
+
 unsigned int ath11k_debug_mask;
 EXPORT_SYMBOL(ath11k_debug_mask);
 module_param_named(debug_mask, ath11k_debug_mask, uint, 0644);
@@ -641,23 +647,30 @@ static int ath11k_core_pdev_create(struc
 		return ret;
 	}
 
-	ret = ath11k_mac_register(ab);
+	ret = ath11k_dp_pdev_alloc(ab);
 	if (ret) {
-		ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret);
+		ath11k_err(ab, "failed to attach DP pdev: %d\n", ret);
 		goto err_pdev_debug;
 	}
 
-	ret = ath11k_dp_pdev_alloc(ab);
+	ret = ath11k_nss_setup(ab);
 	if (ret) {
-		ath11k_err(ab, "failed to attach DP pdev: %d\n", ret);
-		goto err_mac_unregister;
+		ath11k_err(ab, "failed to setup nss driver interface%d",
+			   ret);
+		goto err_dp_pdev_free;
+	}
+
+	ret = ath11k_mac_register(ab);
+	if (ret) {
+		ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret);
+		goto err_nss_tear;
 	}
 
 	ret = ath11k_thermal_register(ab);
 	if (ret) {
 		ath11k_err(ab, "could not register thermal device: %d\n",
 			   ret);
-		goto err_dp_pdev_free;
+		goto err_mac_unregister;
 	}
 
 	ret = ath11k_spectral_init(ab);
@@ -670,10 +683,12 @@ static int ath11k_core_pdev_create(struc
 
 err_thermal_unregister:
 	ath11k_thermal_unregister(ab);
-err_dp_pdev_free:
-	ath11k_dp_pdev_free(ab);
 err_mac_unregister:
 	ath11k_mac_unregister(ab);
+err_nss_tear:
+	ath11k_nss_teardown(ab);
+err_dp_pdev_free:
+	ath11k_dp_pdev_free(ab);
 err_pdev_debug:
 	ath11k_debugfs_pdev_destroy(ab);
 
@@ -685,6 +700,10 @@ static void ath11k_core_pdev_destroy(str
 	ath11k_spectral_deinit(ab);
 	ath11k_thermal_unregister(ab);
 	ath11k_mac_unregister(ab);
+
+	ath11k_nss_teardown(ab);
+	ab->nss.enabled = false;
+
 	ath11k_hif_irq_disable(ab);
 	ath11k_dp_pdev_free(ab);
 	ath11k_debugfs_pdev_destroy(ab);
@@ -944,6 +963,10 @@ static int ath11k_core_reconfigure_on_cr
 	int ret;
 
 	mutex_lock(&ab->core_lock);
+
+	ath11k_nss_teardown(ab);
+	ab->nss.enabled = false;
+
 #ifdef CONFIG_QCOM_QMI_HELPERS
 	/* Unregister the ssr notifier as we are not interested
 	 * in receving these notifications after mac is unregistered.
@@ -1165,6 +1188,10 @@ int ath11k_core_pre_init(struct ath11k_b
 		ath11k_err(ab, "failed to get hw params: %d\n", ret);
 		return ret;
 	}
+	ab->nss.enabled = nss_offload;
+
+	if (nss_offload)
+		ab->nss.stats_enabled = 1;
 
 	return 0;
 }
@@ -1179,7 +1206,6 @@ int ath11k_core_init(struct ath11k_base
 		ath11k_err(ab, "failed to create soc core: %d\n", ret);
 		return ret;
 	}
-
 	return 0;
 }
 EXPORT_SYMBOL(ath11k_core_init);
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -26,6 +26,7 @@
 #include "spectral.h"
 #include "vendor.h"
 #include "rx_desc.h"
+#include "nss.h"
 
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
 
@@ -260,6 +261,9 @@ struct ath11k_vif {
 	struct ieee80211_chanctx_conf chanctx;
 	struct dentry *debugfs_twt;
 	struct ath11k_mgmt_frame_stats mgmt_stats;
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+	struct arvif_nss nss;
+#endif
 };
 
 struct ath11k_vif_iter {
@@ -407,6 +411,9 @@ struct ath11k_sta {
 #endif
 
 	bool use_4addr_set;
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+	struct ath11k_nss_sta_stats *nss_stats;
+#endif
 };
 
 #define ATH11K_MIN_5G_FREQ 4150
@@ -524,6 +531,9 @@ struct ath11k {
 	struct ieee80211_hw *hw;
 	struct ieee80211_ops *ops;
 	struct ath11k_pdev_wmi *wmi;
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+	struct ath11k_nss nss;
+#endif
 	struct ath11k_pdev_dp dp;
 	u8 mac_addr[ETH_ALEN];
 	u32 ht_cap_info;
@@ -761,8 +771,10 @@ struct ath11k_base {
 	struct ath11k_htc htc;
 
 	struct ath11k_dp dp;
+	struct ath11k_soc_nss nss;
 
 	void __iomem *mem;
+	dma_addr_t mem_pa;
 	unsigned long mem_len;
 
 	struct {
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -45,12 +45,17 @@ int ath11k_dp_peer_setup(struct ath11k *
 	struct ath11k_peer *peer;
 	u32 reo_dest;
 	int ret = 0, tid;
+	bool rx_hash_enable = DP_RX_HASH_ENABLE;
+
+	/* RX Hash based steering is disabled for NSS Offload */
+	if (ar->ab->nss.enabled)
+		rx_hash_enable = DP_RX_HASH_DISABLE;
 
 	/* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */
 	reo_dest = ar->dp.mac_id + 1;
 	ret = ath11k_wmi_set_peer_param(ar, addr, vdev_id,
 					WMI_PEER_SET_DEFAULT_ROUTING,
-					DP_RX_HASH_ENABLE | (reo_dest << 1));
+					rx_hash_enable | (reo_dest << 1));
 
 	if (ret) {
 		ath11k_warn(ab, "failed to set default routing %d peer :%pM vdev_id :%d\n",
@@ -126,6 +131,18 @@ static int ath11k_dp_srng_calculate_msi_
 {
 	const u8 *grp_mask;
 
+	if (ab->nss.enabled) {
+		switch (type) {
+		case HAL_REO_STATUS:
+		case HAL_RXDMA_MONITOR_STATUS:
+		case HAL_RXDMA_MONITOR_DST:
+		case HAL_RXDMA_MONITOR_BUF:
+			break;
+		default:
+			return -ENOENT;
+		}
+	}
+
 	switch (type) {
 	case HAL_WBM2SW_RELEASE:
 		if (ring_num < 3) {
@@ -755,14 +772,18 @@ int ath11k_dp_service_srng(struct ath11k
 	int work_done = 0;
 	int i = 0, j;
 	int tot_work_done = 0;
+	bool nss_offload;
+
+	/* Processing of offloaded rings are not required */
+	nss_offload = ab->nss.enabled;
 
-	while (ab->hw_params.ring_mask->tx[grp_id] >> i) {
+	while (!nss_offload && ab->hw_params.ring_mask->tx[grp_id] >> i) {
 		if (ab->hw_params.ring_mask->tx[grp_id] & BIT(i))
 			ath11k_dp_tx_completion_handler(ab, i);
 		i++;
 	}
 
-	if (ab->hw_params.ring_mask->rx_err[grp_id]) {
+	if (!nss_offload && ab->hw_params.ring_mask->rx_err[grp_id]) {
 		work_done = ath11k_dp_process_rx_err(ab, napi, budget);
 		budget -= work_done;
 		tot_work_done += work_done;
@@ -770,7 +791,7 @@ int ath11k_dp_service_srng(struct ath11k
 			goto done;
 	}
 
-	if (ab->hw_params.ring_mask->rx_wbm_rel[grp_id]) {
+	if (!nss_offload && ab->hw_params.ring_mask->rx_wbm_rel[grp_id]) {
 		work_done = ath11k_dp_rx_process_wbm_err(ab,
 							 napi,
 							 budget);
@@ -781,7 +802,7 @@ int ath11k_dp_service_srng(struct ath11k
 			goto done;
 	}
 
-	if (ab->hw_params.ring_mask->rx[grp_id]) {
+	if (!nss_offload && ab->hw_params.ring_mask->rx[grp_id]) {
 		i =  fls(ab->hw_params.ring_mask->rx[grp_id]) - 1;
 		work_done = ath11k_dp_process_rx(ab, i, napi,
 						 budget);
@@ -815,7 +836,7 @@ int ath11k_dp_service_srng(struct ath11k
 	if (ab->hw_params.ring_mask->reo_status[grp_id])
 		ath11k_dp_process_reo_status(ab);
 
-	for (i = 0; i < ab->num_radios; i++) {
+	for (i = 0; !nss_offload && i < ab->num_radios; i++) {
 		for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) {
 			int id = i * ab->hw_params.num_rxmda_per_pdev + j;
 
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -166,6 +166,7 @@ struct ath11k_pdev_dp {
 #define DP_AVG_MSDUS_PER_MPDU 4
 
 #define DP_RX_HASH_ENABLE	1 /* Enable hash based Rx steering */
+#define DP_RX_HASH_DISABLE	0 /* Disable hash based Rx steering */
 
 #define DP_BA_WIN_SZ_MAX	256
 
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -17,6 +17,7 @@
 #include "hal_rx.h"
 #include "dp_tx.h"
 #include "peer.h"
+#include "nss.h"
 
 #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ)
 
@@ -190,8 +191,8 @@ static u8 ath11k_dp_rx_h_mpdu_start_tid(
 	return ab->hw_params.hw_ops->rx_desc_get_mpdu_tid(desc);
 }
 
-static u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab,
-					     struct hal_rx_desc *desc)
+u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab,
+				      struct hal_rx_desc *desc)
 {
 	return ab->hw_params.hw_ops->rx_desc_get_mpdu_peer_id(desc);
 }
@@ -202,8 +203,8 @@ static u8 ath11k_dp_rx_h_msdu_end_l3pad(
 	return ab->hw_params.hw_ops->rx_desc_get_l3_pad_bytes(desc);
 }
 
-static bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab,
-					       struct hal_rx_desc *desc)
+bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab,
+					struct hal_rx_desc *desc)
 {
 	return ab->hw_params.hw_ops->rx_desc_get_first_msdu(desc);
 }
@@ -294,7 +295,7 @@ static int ath11k_dp_purge_mon_ring(stru
 	return -ETIMEDOUT;
 }
 
-static bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab,
+bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab,
                    struct hal_rx_desc *desc)
 {
    struct rx_attention *attn = ath11k_dp_rx_get_attention(ab, desc);
@@ -498,7 +499,9 @@ static int ath11k_dp_rxdma_pdev_buf_setu
 	struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
 	int i;
 
-	ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF);
+	/* RXDMA BUF ring is offloaded to NSS */
+	if (!ar->ab->nss.enabled)
+		ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF);
 
 	if (ar->ab->hw_params.rxdma1_enable) {
 		rx_ring = &dp->rxdma_mon_buf_ring;
@@ -1971,7 +1974,7 @@ static void ath11k_dp_rx_h_csum_offload(
 			  CHECKSUM_NONE : CHECKSUM_UNNECESSARY;
 }
 
-static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar,
+int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar,
 				       enum hal_encrypt_type enctype)
 {
 	switch (enctype) {
@@ -1998,7 +2001,7 @@ static int ath11k_dp_rx_crypto_mic_len(s
 	return 0;
 }
 
-static int ath11k_dp_rx_crypto_param_len(struct ath11k *ar,
+int ath11k_dp_rx_crypto_param_len(struct ath11k *ar,
 					 enum hal_encrypt_type enctype)
 {
 	switch (enctype) {
@@ -2026,7 +2029,7 @@ static int ath11k_dp_rx_crypto_param_len
 	return 0;
 }
 
-static int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar,
+int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar,
 				       enum hal_encrypt_type enctype)
 {
 	switch (enctype) {
@@ -3406,7 +3409,7 @@ int ath11k_dp_rx_process_mon_status(stru
 	struct sk_buff_head skb_list;
 	struct hal_rx_mon_ppdu_info *ppdu_info;
 	struct ath11k_peer *peer;
-	struct ath11k_sta *arsta;
+	struct ath11k_sta *arsta = NULL;
 	int num_buffs_reaped = 0;
 	u32 rx_buf_sz;
 	u16 log_type = 0;
@@ -3486,6 +3489,8 @@ int ath11k_dp_rx_process_mon_status(stru
 			ath11k_rx_stats_buf_pktlog_process(ar, skb->data, log_type, rx_buf_sz);
 		}
 
+		ath11k_nss_update_sta_rxrate(ppdu_info, peer);
+
 		spin_unlock_bh(&ab->base_lock);
 		rcu_read_unlock();
 
--- a/drivers/net/wireless/ath/ath11k/dp_rx.h
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.h
@@ -115,4 +115,16 @@ int ath11k_peer_rx_frag_setup(struct ath
 int ath11k_dp_rx_pktlog_start(struct ath11k_base *ab);
 int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer);
 
+int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar,
+				       enum hal_encrypt_type enctype);
+int ath11k_dp_rx_crypto_param_len(struct ath11k *ar,
+					 enum hal_encrypt_type enctype);
+int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar,
+				       enum hal_encrypt_type enctype);
+bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab,
+					struct hal_rx_desc *desc);
+bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab,
+				 struct hal_rx_desc *desc);
+u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab,
+				      struct hal_rx_desc *desc);
 #endif /* ATH11K_DP_RX_H */
--- a/drivers/net/wireless/ath/ath11k/hal.h
+++ b/drivers/net/wireless/ath/ath11k/hal.h
@@ -414,6 +414,8 @@ enum hal_srng_ring_id {
 #define HAL_SRNG_RING_ID_MAX    (HAL_SRNG_RING_ID_UMAC_ID_END + \
 				 HAL_SRNG_NUM_LMAC_RINGS)
 
+#define HAL_SRNG_REO_ALTERNATE_SELECT 0x7
+
 enum hal_ring_type {
 	HAL_REO_DST,
 	HAL_REO_EXCEPTION,
--- a/drivers/net/wireless/ath/ath11k/hal_rx.c
+++ b/drivers/net/wireless/ath/ath11k/hal_rx.c
@@ -803,14 +803,18 @@ void ath11k_hal_reo_init_cmd_ring(struct
 
 void ath11k_hal_reo_hw_setup(struct ath11k_base *ab, u32 ring_hash_map)
 {
+	u8 frag_dest_ring = HAL_SRNG_RING_ID_REO2SW1;
 	u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG;
 	u32 val;
 
+	if (ab->nss.enabled)
+		frag_dest_ring = HAL_SRNG_REO_ALTERNATE_SELECT;
+
 	val = ath11k_hif_read32(ab, reo_base + HAL_REO1_GEN_ENABLE);
 
 	val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING;
 	val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING,
-			  HAL_SRNG_RING_ID_REO2SW1) |
+			  frag_dest_ring) |
 	       FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) |
 	       FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1);
 	ath11k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val);
@@ -824,6 +828,10 @@ void ath11k_hal_reo_hw_setup(struct ath1
 	ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3(ab),
 			   HAL_DEFAULT_REO_TIMEOUT_USEC);
 
+	/* REO Dest ring setup is not required in NSS offload case */
+	if (ab->nss.enabled)
+		return;
+
 	ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0,
 			   FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP,
 				      ring_hash_map));
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -17,6 +17,7 @@
 #include "peer.h"
 #include "debugfs_sta.h"
 #include "vendor.h"
+#include "nss.h"
 
 #define CHAN2G(_channel, _freq, _flags) { \
 	.band                   = NL80211_BAND_2GHZ, \
@@ -1102,6 +1103,11 @@ static void ath11k_control_beaconing(str
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
 	if (!info->enable_beacon) {
+
+		ret = ath11k_nss_vdev_down(arvif);
+		if(ret)
+			ath11k_warn(ar->ab, "failure in nss vdev down %d\r\n",ret);
+
 		ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id);
 		if (ret)
 			ath11k_warn(ar->ab, "failed to down vdev_id %i: %d\n",
@@ -1135,6 +1141,12 @@ static void ath11k_control_beaconing(str
 
 	arvif->is_up = true;
 
+	ret = ath11k_nss_vdev_up(arvif);
+	if(ret) {
+		ath11k_warn(ar->ab, "failure in nss vdev up %d\r\n",ret);
+		return;
+	}
+
 	ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
 }
 
@@ -2306,6 +2318,12 @@ static void ath11k_bss_assoc(struct ieee
 		   "mac vdev %d up (associated) bssid %pM aid %d\n",
 		   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
 
+	ret = ath11k_nss_vdev_up(arvif);
+	if(ret) {
+		ath11k_warn(ar->ab, "failure in nss vdev up %d\r\n",ret);
+		return;
+	}
+
 	/* Authorize BSS Peer */
 	ret = ath11k_wmi_set_peer_param(ar, arvif->bssid,
 					arvif->vdev_id,
@@ -2330,6 +2348,10 @@ static void ath11k_bss_disassoc(struct i
 
 	lockdep_assert_held(&ar->conf_mutex);
 
+	ret = ath11k_nss_vdev_down(arvif);
+	if(ret)
+		ath11k_warn(ar->ab, "failure in nss vdev down %d\r\n",ret);
+
 	ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n",
 		   arvif->vdev_id, arvif->bssid);
 
@@ -2603,6 +2625,28 @@ static int ath11k_mac_config_obss_pd(str
 	return 0;
 }
 
+static void ath11k_mac_op_nss_bss_info_changed(struct ieee80211_hw *hw,
+					   struct ieee80211_vif *vif,
+					   u32 changed)
+{
+	struct ath11k *ar = hw->priv;
+	struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+	int ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Setting ap_isolate %d to NSS\n",
+		   arvif->vif->bss_conf.nss_ap_isolate);
+	if (changed & BSS_CHANGED_NSS_AP_ISOLATE) {
+		ret = ath11k_nss_vdev_set_cmd(arvif, NSS_WIFI_VDEV_CFG_AP_BRIDGE_CMD,
+					      !arvif->vif->bss_conf.nss_ap_isolate);
+		if(ret)
+			ath11k_warn(ar->ab, "failed to set ap_isolate in nss %d\n", ret);
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
 					   struct ieee80211_vif *vif,
 					   struct ieee80211_bss_conf *info,
@@ -3360,6 +3404,26 @@ static int ath11k_mac_op_set_key(struct
 
 	spin_lock_bh(&ab->base_lock);
 	peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr);
+
+	/* TODO: Check if vdev specific security cfg is mandatory */
+	ret = ath11k_nss_vdev_set_cmd(arvif, NSS_WIFI_VDEV_SECURITY_TYPE_CMD, key->cipher);
+	if (ret) {
+		ath11k_warn(ab, "failure to set vdev security type in nss");
+		goto unlock;
+	}
+
+	ret = ath11k_nss_set_peer_sec_type(ar, peer, key);
+	if (ret) {
+		ath11k_warn(ab, "failure to set peer security type in nss");
+		goto unlock;
+	}
+
+	ret = ath11k_nss_set_peer_authorize(ar, peer->peer_id);
+	if (ret) {
+		ath11k_warn(ab, "failure to authorize peer in nss");
+		goto unlock;
+	}
+
 	if (peer && cmd == SET_KEY) {
 		peer->keys[key->keyidx] = key;
 		if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
@@ -3398,9 +3462,8 @@ static int ath11k_mac_op_set_key(struct
 			break;
 		}
 	}
-
+unlock:
 	spin_unlock_bh(&ab->base_lock);
-
 exit:
 	mutex_unlock(&ar->conf_mutex);
 	return ret;
@@ -5089,10 +5152,14 @@ static void ath11k_mac_op_tx(struct ieee
 		return;
 	}
 
-	ret = ath11k_dp_tx(ar, arvif, skb);
+	if (ar->ab->nss.enabled)
+		ret = ath11k_nss_tx(arvif,skb);
+	else
+		ret = ath11k_dp_tx(ar, arvif, skb);
 	if (ret) {
 		ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret);
 		ieee80211_free_txskb(ar->hw, skb);
+		return;
 	}
 }
 
@@ -5114,6 +5181,8 @@ static int ath11k_mac_config_mon_status_
 
 	if (enable) {
 		tlv_filter = ath11k_mac_mon_status_filter_default;
+		ath11k_nss_ext_rx_stats(ar->ab, &tlv_filter);
+
 		if (ath11k_debugfs_rx_filter(ar))
 			tlv_filter.rx_filter = ath11k_debugfs_rx_filter(ar);
 	}
@@ -5339,6 +5408,12 @@ static void ath11k_mac_op_update_vif_off
 	u32 param_id, param_value;
 	int ret;
 
+	ret = ath11k_nss_vdev_create(arvif);
+	if(ret) {
+		ath11k_warn(ab, "failed to create nss vdev %d\n", ret);
+		goto err_vdev_del;
+	}
+
 	param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
 	if (ath11k_frame_mode != ATH11K_HW_TXRX_ETHERNET ||
 	    (vif->type != NL80211_IFTYPE_STATION &&
@@ -5483,7 +5558,28 @@ static int ath11k_mac_op_add_interface(s
 	list_add(&arvif->list, &ar->arvifs);
 	spin_unlock_bh(&ar->data_lock);
 
-	ath11k_mac_op_update_vif_offload(hw, vif);
+	if (ath11k_mac_op_update_vif_offload(hw, vif))
+		goto err_vdev_del;
+
+	if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
+		param_value = ATH11K_HW_TXRX_ETHERNET;
+	else if (test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags))
+		param_value = ATH11K_HW_TXRX_RAW;
+	else
+		param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
+
+	ret = ath11k_nss_vdev_set_cmd(arvif, NSS_WIFI_VDEV_ENCAP_TYPE_CMD, param_value);
+
+	if(ret) {
+		ath11k_warn(ab, "failed to set encap type in nss %d\n", ret);
+		goto err_vdev_del;
+	}
+
+	ret = ath11k_nss_vdev_set_cmd(arvif, NSS_WIFI_VDEV_DECAP_TYPE_CMD, param_value);
+	if(ret) {
+		ath11k_warn(ab, "failed to set decap type in nss %d\n", ret);
+		goto err_vdev_del;
+	}
 
 	nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
 	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
@@ -5611,6 +5707,7 @@ err_peer_del:
 	}
 
 err_vdev_del:
+	ath11k_nss_vdev_delete(arvif);
 	ath11k_wmi_vdev_delete(ar, arvif->vdev_id);
 	ar->num_created_vdevs--;
 	ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
@@ -5660,6 +5757,8 @@ static void ath11k_mac_op_remove_interfa
 
 	reinit_completion(&ar->vdev_delete_done);
 
+	ath11k_nss_vdev_delete(arvif);
+
 	ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id);
 	if (ret) {
 		ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n",
@@ -6077,6 +6176,10 @@ ath11k_mac_update_vif_chan(struct ath11k
 		if (WARN_ON(!arvif->is_up))
 			continue;
 
+		ret = ath11k_nss_vdev_down(arvif);
+		if(ret)
+			ath11k_warn(ar->ab, "failure in nss vdev down %d\r\n",ret);
+
 		ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id);
 		if (ret) {
 			ath11k_warn(ab, "failed to down vdev %d: %d\n",
@@ -6119,6 +6222,10 @@ ath11k_mac_update_vif_chan(struct ath11k
 				    arvif->vdev_id, ret);
 			continue;
 		}
+
+		ret = ath11k_nss_vdev_up(arvif);
+		if(ret)
+			ath11k_warn(ar->ab, "failure in nss vdev up %d\r\n",ret);
 	}
 
 	/* Restart the internal monitor vdev on new channel */
@@ -7150,6 +7257,8 @@ static void ath11k_mac_op_sta_statistics
 	/* TODO: Use real NF instead of default one. */
 	sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR;
 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
+
+	ath11k_nss_update_sta_stats(sinfo, sta, arsta);
 }
 
 #define ATH11K_WLAN_PRIO_MAX	0x63
@@ -7247,6 +7356,7 @@ static const struct ieee80211_ops ath11k
 	.update_vif_offload		= ath11k_mac_op_update_vif_offload,
 	.config                         = ath11k_mac_op_config,
 	.bss_info_changed               = ath11k_mac_op_bss_info_changed,
+	.nss_bss_info_changed           = ath11k_mac_op_nss_bss_info_changed,
 	.configure_filter		= ath11k_mac_op_configure_filter,
 	.hw_scan                        = ath11k_mac_op_hw_scan,
 	.cancel_hw_scan                 = ath11k_mac_op_cancel_hw_scan,
@@ -7645,7 +7755,8 @@ static int __ath11k_mac_register(struct
 		ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW);
 		ieee80211_hw_set(ar->hw, SUPPORTS_REORDERING_BUFFER);
 		ieee80211_hw_set(ar->hw, SUPPORTS_AMSDU_IN_AMPDU);
-		ieee80211_hw_set(ar->hw, USES_RSS);
+		if(!ab->nss.enabled)
+			ieee80211_hw_set(ar->hw, USES_RSS);
 	}
 
 	ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
@@ -7717,6 +7828,9 @@ static int __ath11k_mac_register(struct
 		ieee80211_hw_set(ar->hw, SUPPORT_FAST_XMIT);
 	}
 
+	if (ab->nss.enabled)
+		ieee80211_hw_set(ar->hw, SUPPORTS_NSS_OFFLOAD);
+
 	ret = ieee80211_register_hw(ar->hw);
 	if (ret) {
 		ath11k_err(ar->ab, "ieee80211 registration failed: %d\n", ret);
--- a/drivers/net/wireless/ath/ath11k/peer.c
+++ b/drivers/net/wireless/ath/ath11k/peer.c
@@ -6,6 +6,7 @@
 #include "core.h"
 #include "peer.h"
 #include "debug.h"
+#include "nss.h"
 
 struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,
 				     const u8 *addr)
@@ -150,6 +151,8 @@ void ath11k_peer_map_event(struct ath11k
 		ether_addr_copy(peer->addr, mac_addr);
 		list_add(&peer->list, &ab->peers);
 		wake_up(&ab->peer_mapping_wq);
+		if (ab->nss.enabled)
+			ath11k_nss_peer_create(ab, peer);
 	}
 
 	ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer map vdev %d peer %pM id %d\n",
@@ -239,6 +242,8 @@ int ath11k_peer_delete(struct ath11k *ar
 
 	reinit_completion(&ar->peer_delete_done);
 
+	ath11k_nss_peer_delete(ar->ab, addr);
+
 	ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);
 	if (ret) {
 		ath11k_warn(ar->ab,
--- a/drivers/net/wireless/ath/ath11k/peer.h
+++ b/drivers/net/wireless/ath/ath11k/peer.h
@@ -27,6 +27,9 @@ struct ath11k_peer {
 	u16 ast_hash;
 	u8 pdev_idx;
 	u16 hw_peer_id;
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
+	struct ath11k_nss_peer nss;
+#endif
 
 	/* protected by ab->data_lock */
 	struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -749,6 +749,11 @@ static int ath11k_pci_ext_irq_config(str
 		netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
 			       ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
 
+		/* tcl, reo, rx_err, wbm release, rxdma rings are offloaded to nss. */
+		if (ab->nss.enabled && !(ab->hw_params.ring_mask->reo_status[i] ||
+		    ab->hw_params.ring_mask->rx_mon_status[i]))
+			continue;
+
 		if (ab->hw_params.ring_mask->tx[i] ||
 		    ab->hw_params.ring_mask->rx[i] ||
 		    ab->hw_params.ring_mask->rx_err[i] ||
@@ -974,6 +979,7 @@ static int ath11k_pci_claim(struct ath11
 		goto clear_master;
 	}
 
+	ab->mem_pa = pci_resource_start(pdev, ATH11K_PCI_BAR_NUM);
 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot pci_mem 0x%pK\n", ab->mem);
 	return 0;
 
--- a/drivers/net/wireless/ath/ath11k/debugfs.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs.c
@@ -922,6 +922,7 @@ static ssize_t ath11k_write_extd_rx_stat
 			HTT_RX_FP_DATA_FILTER_FLASG3;
 	} else {
 		tlv_filter = ath11k_mac_mon_status_filter_default;
+		ath11k_nss_ext_rx_stats(ar->ab, &tlv_filter);
 	}
 
 	ar->debug.rx_filter = tlv_filter.rx_filter;
@@ -1837,6 +1838,75 @@ static const struct file_operations fops
 	.open = simple_open
 };
 
+static ssize_t ath11k_write_nss_stats(struct file *file,
+				      const char __user *ubuf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath11k *ar = file->private_data;
+	struct ath11k_base *ab = ar->ab;
+	u32 nss_stats;
+	int ret;
+
+	if (!ab->nss.enabled) {
+		ath11k_warn(ab, "nss offload not enabled\n");
+		return -EINVAL;
+	}
+
+	if (kstrtouint_from_user(ubuf, count, 0, &nss_stats))
+		return -EINVAL;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH11K_STATE_ON) {
+		ret = -ENETDOWN;
+		goto out;
+	}
+
+	if (nss_stats == ab->nss.stats_enabled) {
+		ret = count;
+		goto out;
+	}
+
+	if (nss_stats > 0) {
+		ab->nss.stats_enabled = 1;
+		ath11k_nss_peer_stats_enable(ar);
+	} else {
+		ab->nss.stats_enabled = 0;
+		ath11k_nss_peer_stats_disable(ar);
+	}
+
+	ret = count;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static ssize_t ath11k_read_nss_stats(struct file *file,
+				     char __user *ubuf,
+				     size_t count, loff_t *ppos)
+
+{
+	char buf[32] = {0};
+	struct ath11k *ar = file->private_data;
+	struct ath11k_base *ab = ar->ab;
+	int len = 0;
+
+	mutex_lock(&ar->conf_mutex);
+	len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
+			ab->nss.stats_enabled);
+	mutex_unlock(&ar->conf_mutex);
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_nss_stats = {
+	.read = ath11k_read_nss_stats,
+	.write = ath11k_write_nss_stats,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static int ath11k_get_tpc_ctl_mode(struct wmi_tpc_stats_event *tpc_stats,
 				   u32 pream_idx, int *mode)
 {
@@ -2483,6 +2553,10 @@ int ath11k_debugfs_register(struct ath11
 	debugfs_create_file("tpc_stats_type", 0600, ar->debug.debugfs_pdev,
 			    ar, &fops_tpc_stats_type);
 
+	if (ab->nss.enabled)
+		debugfs_create_file("nss_peer_stats_config", 0644,
+				    ar->debug.debugfs_pdev, ar, &fops_nss_stats);
+
 	return 0;
 }
 
--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
@@ -10,7 +10,7 @@
 #include "hw.h"
 #include "peer.h"
 
-static enum hal_tcl_encap_type
+enum hal_tcl_encap_type
 ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
@@ -935,7 +935,7 @@ int ath11k_dp_tx_htt_h2t_ppdu_stats_req(
 		cmd->msg = FIELD_PREP(HTT_PPDU_STATS_CFG_MSG_TYPE,
 				      HTT_H2T_MSG_TYPE_PPDU_STATS_CFG);
 
-		pdev_mask = 1 << (i + 1);
+		pdev_mask = 1 << (ar->pdev_idx + i);
 		cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_PDEV_ID, pdev_mask);
 		cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK, mask);
 
--- a/drivers/net/wireless/ath/ath11k/dp_tx.h
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.h
@@ -36,5 +36,7 @@ int ath11k_dp_tx_htt_rx_filter_setup(str
 				     int mac_id, enum hal_ring_type ring_type,
 				     int rx_buf_size,
 				     struct htt_rx_ring_tlv_filter *tlv_filter);
+enum hal_tcl_encap_type
+ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb);
 
 #endif
--- a/local-symbols
+++ b/local-symbols
@@ -133,6 +133,7 @@ ATH10K_DFS_CERTIFIED=
 ATH11K=
 ATH11K_AHB=
 ATH11K_PCI=
+ATH11K_NSS_SUPPORT=
 ATH11K_DEBUG=
 ATH11K_DEBUGFS=
 ATH11K_TRACING=
