Эх сурвалжийг харах

Merge "qcacmn: Reduce excessive console logging"

Linux Build Service Account 5 жил өмнө
parent
commit
2105b28e4c

+ 68 - 1
dp/inc/cdp_txrx_misc.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -747,4 +747,71 @@ static inline void cdp_pdev_reset_bundle_require_flag(ol_txrx_soc_handle soc,
 		return soc->ops->misc_ops->pdev_reset_bundle_require_flag(
 								soc, pdev_id);
 }
+
+/**
+ * cdp_txrx_ext_stats_request(): request dp tx and rx extended stats
+ * @soc: soc handle
+ * @pdev_id: pdev id
+ * @req: stats request structure to fill
+ *
+ * return: status
+ */
+static inline QDF_STATUS
+cdp_txrx_ext_stats_request(ol_txrx_soc_handle soc, uint8_t pdev_id,
+			   struct cdp_txrx_ext_stats *req)
+{
+	if (!soc || !soc->ops || !soc->ops->misc_ops || !req) {
+		QDF_TRACE(QDF_MODULE_ID_CDP, QDF_TRACE_LEVEL_DEBUG,
+			  "%s: Invalid Instance:", __func__);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (soc->ops->misc_ops->txrx_ext_stats_request)
+		return soc->ops->misc_ops->txrx_ext_stats_request(soc, pdev_id,
+								  req);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * cdp_request_rx_hw_stats(): request rx hw stats
+ * @soc: soc handle
+ * @vdev_id: vdev id
+ *
+ * return: none
+ */
+static inline void
+cdp_request_rx_hw_stats(ol_txrx_soc_handle soc, uint8_t vdev_id)
+{
+	if (!soc || !soc->ops || !soc->ops->misc_ops) {
+		QDF_TRACE(QDF_MODULE_ID_CDP, QDF_TRACE_LEVEL_DEBUG,
+			  "%s: Invalid Instance:", __func__);
+		return;
+	}
+
+	if (soc->ops->misc_ops->request_rx_hw_stats)
+		soc->ops->misc_ops->request_rx_hw_stats(soc, vdev_id);
+}
+
+/**
+ * cdp_wait_for_ext_rx_stats(): wait for reo command status for stats
+ * @soc: soc handle
+ *
+ * return: status
+ */
+static inline QDF_STATUS
+cdp_wait_for_ext_rx_stats(ol_txrx_soc_handle soc)
+{
+	if (!soc || !soc->ops || !soc->ops->misc_ops) {
+		QDF_TRACE(QDF_MODULE_ID_CDP, QDF_TRACE_LEVEL_DEBUG,
+			  "%s: Invalid Instance:", __func__);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (soc->ops->misc_ops->wait_for_ext_rx_stats)
+		return soc->ops->misc_ops->wait_for_ext_rx_stats(soc);
+
+	return QDF_STATUS_SUCCESS;
+}
+
 #endif /* _CDP_TXRX_MISC_H_ */

+ 19 - 1
dp/inc/cdp_txrx_mob_def.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -471,4 +471,22 @@ struct ol_rx_inv_peer_params {
 	uint8_t ta[QDF_MAC_ADDR_SIZE];
 };
 
+/**
+ * cdp_txrx_ext_stats: dp extended stats
+ * tx_msdu_enqueue: tx msdu queued to hw
+ * tx_msdu_overflow: tx msdu overflow
+ * rx_mpdu_received: rx mpdu processed by hw
+ * rx_mpdu_delivered: rx mpdu received from hw
+ * rx_mpdu_error: rx mpdu error count
+ * rx_mpdu_missed: rx mpdu missed by hw
+ */
+struct cdp_txrx_ext_stats {
+	uint32_t tx_msdu_enqueue;
+	uint32_t tx_msdu_overflow;
+	uint32_t rx_mpdu_received;
+	uint32_t rx_mpdu_delivered;
+	uint32_t rx_mpdu_error;
+	uint32_t rx_mpdu_missed;
+};
+
 #endif /* __CDP_TXRX_MOB_DEF_H */

+ 5 - 0
dp/inc/cdp_txrx_ops.h

@@ -1238,6 +1238,11 @@ struct cdp_misc_ops {
 					     uint32_t low_th);
 	void (*pdev_reset_bundle_require_flag)(struct cdp_soc_t *soc_hdl,
 					       uint8_t pdev_id);
+	QDF_STATUS (*txrx_ext_stats_request)(struct cdp_soc_t *soc_hdl,
+					     uint8_t pdev_id,
+					     struct cdp_txrx_ext_stats *req);
+	void (*request_rx_hw_stats)(struct cdp_soc_t *soc_hdl, uint8_t vdev_id);
+	QDF_STATUS (*wait_for_ext_rx_stats)(struct cdp_soc_t *soc_hdl);
 };
 
 /**

+ 182 - 0
dp/wifi3.0/dp_main.c

@@ -2807,6 +2807,17 @@ static void dp_enable_verbose_debug(struct dp_soc *soc)
 }
 #endif
 
+#ifdef WLAN_FEATURE_STATS_EXT
+static inline void dp_create_ext_stats_event(struct dp_soc *soc)
+{
+	qdf_event_create(&soc->rx_hw_stats_event);
+}
+#else
+static inline void dp_create_ext_stats_event(struct dp_soc *soc)
+{
+}
+#endif
+
 /*
  * dp_soc_cmn_setup() - Common SoC level initializion
  * @soc:		Datapath SOC handle
@@ -3055,6 +3066,7 @@ static int dp_soc_cmn_setup(struct dp_soc *soc)
 		wlan_cfg_get_defrag_timeout_check(soc_cfg_ctx);
 	qdf_spinlock_create(&soc->rx.defrag.defrag_lock);
 
+	dp_create_ext_stats_event(soc);
 out:
 	/*
 	 * set the fragment destination ring
@@ -5302,6 +5314,26 @@ static inline void dp_peer_rx_bufq_resources_init(struct dp_peer *peer)
 }
 #endif
 
+#ifdef WLAN_FEATURE_STATS_EXT
+/*
+ * dp_set_ignore_reo_status_cb() - set ignore reo status cb flag
+ * @soc: dp soc handle
+ * @flag: flag to set or reset
+ *
+ * Return: None
+ */
+static inline void dp_set_ignore_reo_status_cb(struct dp_soc *soc,
+					       bool flag)
+{
+	soc->ignore_reo_status_cb = flag;
+}
+#else
+static inline void dp_set_ignore_reo_status_cb(struct dp_soc *soc,
+					       bool flag)
+{
+}
+#endif
+
 /*
  * dp_peer_create_wifi3() - attach txrx peer
  * @soc_hdl: Datapath soc handle
@@ -5458,6 +5490,12 @@ static void *dp_peer_create_wifi3(struct cdp_soc_t *soc_hdl, uint8_t vdev_id,
 		vdev->vap_self_peer = peer;
 	}
 
+	if (wlan_op_mode_sta == vdev->opmode &&
+	    qdf_mem_cmp(peer->mac_addr.raw, vdev->mac_addr.raw,
+			QDF_MAC_ADDR_SIZE) != 0) {
+		dp_set_ignore_reo_status_cb(soc, false);
+	}
+
 	for (i = 0; i < DP_MAX_TIDS; i++)
 		qdf_spinlock_create(&peer->rx_tid[i].tid_lock);
 
@@ -6244,6 +6282,12 @@ static QDF_STATUS dp_peer_delete_wifi3(struct cdp_soc_t *soc, uint8_t vdev_id,
 	qdf_spinlock_destroy(&peer->peer_info_lock);
 	dp_peer_multipass_list_remove(peer);
 
+	if (wlan_op_mode_sta == peer->vdev->opmode &&
+	    qdf_mem_cmp(peer->mac_addr.raw, peer->vdev->mac_addr.raw,
+			QDF_MAC_ADDR_SIZE) != 0) {
+		dp_set_ignore_reo_status_cb(peer->vdev->pdev->soc, true);
+	}
+
 	/*
 	 * Remove the reference added during peer_attach.
 	 * The peer will still be left allocated until the
@@ -6669,6 +6713,19 @@ QDF_STATUS dp_pdev_configure_monitor_rings(struct dp_pdev *pdev)
 	for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) {
 		int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id,
 						pdev->pdev_id);
+		/*
+		 * If two back to back HTT msg sending happened in
+		 * short time, the second HTT msg source SRNG HP
+		 * writing has chance to fail, this has been confirmed
+		 * by HST HW.
+		 * for monitor mode, here is the last HTT msg for sending.
+		 * if the 2nd HTT msg for monitor status ring sending failed,
+		 * HW won't provide anything into 2nd monitor status ring.
+		 * as a WAR, add some delay before 2nd HTT msg start sending,
+		 * > 2us is required per HST HW, delay 100 us for safe.
+		 */
+		if (mac_id)
+			qdf_udelay(100);
 
 		htt_h2t_rx_ring_cfg(soc->htt_handle, mac_for_pdev,
 			pdev->rxdma_mon_status_ring[mac_id].hal_srng,
@@ -9983,6 +10040,125 @@ dp_txrx_post_data_stall_event(struct cdp_soc_t *soc_hdl,
 }
 #endif /* WLAN_SUPPORT_DATA_STALL */
 
+#ifdef WLAN_FEATURE_STATS_EXT
+/* rx hw stats event wait timeout in ms */
+#define DP_REO_STATUS_STATS_TIMEOUT 1000
+/**
+ * dp_txrx_ext_stats_request - request dp txrx extended stats request
+ * @soc_hdl: soc handle
+ * @pdev_id: pdev id
+ * @req: stats request
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+dp_txrx_ext_stats_request(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
+			  struct cdp_txrx_ext_stats *req)
+{
+	struct dp_soc *soc = (struct dp_soc *)soc_hdl;
+	struct dp_pdev *pdev = dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id);
+
+	if (!pdev) {
+		dp_err("pdev is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	dp_aggregate_pdev_stats(pdev);
+
+	req->tx_msdu_enqueue = pdev->stats.tx_i.processed.num;
+	req->tx_msdu_overflow = pdev->stats.tx_i.dropped.ring_full;
+	req->rx_mpdu_received = soc->ext_stats.rx_mpdu_received;
+	req->rx_mpdu_delivered = soc->ext_stats.rx_mpdu_received;
+	req->rx_mpdu_missed = soc->ext_stats.rx_mpdu_missed;
+	req->rx_mpdu_error = soc->stats.rx.err_ring_pkts -
+				soc->stats.rx.rx_frags;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_rx_hw_stats_cb - request rx hw stats response callback
+ * @soc: soc handle
+ * @cb_ctxt: callback context
+ * @reo_status: reo command response status
+ *
+ * Return: None
+ */
+static void dp_rx_hw_stats_cb(struct dp_soc *soc, void *cb_ctxt,
+			      union hal_reo_status *reo_status)
+{
+	struct dp_rx_tid *rx_tid = (struct dp_rx_tid *)cb_ctxt;
+	struct hal_reo_queue_status *queue_status = &reo_status->queue_status;
+
+	if (soc->ignore_reo_status_cb) {
+		qdf_event_set(&soc->rx_hw_stats_event);
+		return;
+	}
+
+	if (queue_status->header.status != HAL_REO_CMD_SUCCESS) {
+		dp_info("REO stats failure %d for TID %d",
+			queue_status->header.status, rx_tid->tid);
+		return;
+	}
+
+	soc->ext_stats.rx_mpdu_received += queue_status->mpdu_frms_cnt;
+	soc->ext_stats.rx_mpdu_missed += queue_status->late_recv_mpdu_cnt;
+
+	if (rx_tid->tid == (DP_MAX_TIDS - 1))
+		qdf_event_set(&soc->rx_hw_stats_event);
+}
+
+/**
+ * dp_request_rx_hw_stats - request rx hardware stats
+ * @soc_hdl: soc handle
+ * @vdev_id: vdev id
+ *
+ * Return: None
+ */
+static void
+dp_request_rx_hw_stats(struct cdp_soc_t *soc_hdl, uint8_t vdev_id)
+{
+	struct dp_soc *soc = (struct dp_soc *)soc_hdl;
+	struct dp_vdev *vdev = dp_get_vdev_from_soc_vdev_id_wifi3(soc, vdev_id);
+	struct dp_peer *peer;
+
+	if (!vdev) {
+		dp_err("vdev is null");
+		qdf_event_set(&soc->rx_hw_stats_event);
+		return;
+	}
+
+	peer = vdev->vap_bss_peer;
+
+	if (!peer || peer->delete_in_progress) {
+		dp_err("Peer deletion in progress");
+		qdf_event_set(&soc->rx_hw_stats_event);
+		return;
+	}
+
+	qdf_event_reset(&soc->rx_hw_stats_event);
+	dp_peer_rxtid_stats(peer, dp_rx_hw_stats_cb, NULL);
+}
+
+/**
+ * dp_wait_for_ext_rx_stats - wait for rx reo status for rx stats
+ * @soc_hdl: cdp opaque soc handle
+ *
+ * Return: status
+ */
+static QDF_STATUS
+dp_wait_for_ext_rx_stats(struct cdp_soc_t *soc_hdl)
+{
+	struct dp_soc *soc = (struct dp_soc *)soc_hdl;
+	QDF_STATUS status;
+
+	status = qdf_wait_single_event(&soc->rx_hw_stats_event,
+				       DP_REO_STATUS_STATS_TIMEOUT);
+
+	return status;
+}
+#endif /* WLAN_FEATURE_STATS_EXT */
+
 #ifdef DP_PEER_EXTENDED_API
 static struct cdp_misc_ops dp_ops_misc = {
 #ifdef FEATURE_WLAN_TDLS
@@ -10002,6 +10178,12 @@ static struct cdp_misc_ops dp_ops_misc = {
 	.txrx_data_stall_cb_deregister = dp_deregister_data_stall_detect_cb,
 	.txrx_post_data_stall_event = dp_txrx_post_data_stall_event,
 #endif
+
+#ifdef WLAN_FEATURE_STATS_EXT
+	.txrx_ext_stats_request = dp_txrx_ext_stats_request,
+	.request_rx_hw_stats = dp_request_rx_hw_stats,
+	.wait_for_ext_rx_stats = dp_wait_for_ext_rx_stats,
+#endif
 };
 #endif
 

+ 14 - 3
dp/wifi3.0/dp_rx.c

@@ -2237,9 +2237,20 @@ done:
 		dp_peer_unref_del_find_by_id(peer);
 	}
 
-	if (deliver_list_head && peer)
-		dp_rx_deliver_to_stack(vdev, peer, deliver_list_head,
-				       deliver_list_tail);
+	if (qdf_likely(deliver_list_head)) {
+		if (qdf_likely(peer))
+			dp_rx_deliver_to_stack(vdev, peer, deliver_list_head,
+					       deliver_list_tail);
+		else {
+			nbuf = deliver_list_head;
+			while (nbuf) {
+				next = nbuf->next;
+				nbuf->next = NULL;
+				dp_rx_deliver_to_stack_no_peer(soc, nbuf);
+				nbuf = next;
+			}
+		}
+	}
 
 	if (dp_rx_enable_eol_data_check(soc) && rx_bufs_used) {
 		if (quota) {

+ 8 - 8
dp/wifi3.0/dp_rx_err.c

@@ -1103,19 +1103,19 @@ void dp_rx_process_mic_error(struct dp_soc *soc, qdf_nbuf_t nbuf,
 		return;
 
 	if (!peer) {
-		dp_err_rl("peer not found");
+		dp_info_rl("peer not found");
 		goto fail;
 	}
 
 	vdev = peer->vdev;
 	if (!vdev) {
-		dp_err_rl("VDEV not found");
+		dp_info_rl("VDEV not found");
 		goto fail;
 	}
 
 	pdev = vdev->pdev;
 	if (!pdev) {
-		dp_err_rl("PDEV not found");
+		dp_info_rl("PDEV not found");
 		goto fail;
 	}
 
@@ -1519,9 +1519,9 @@ done:
 		peer = dp_peer_find_by_id(soc, peer_id);
 
 		if (!peer)
-			dp_err_rl("peer is null! peer_id %u err_src %u err_rsn %u",
-				  peer_id, wbm_err_info.wbm_err_src,
-				  wbm_err_info.reo_psh_rsn);
+			dp_info_rl("peer is null peer_id%u err_src%u err_rsn%u",
+				   peer_id, wbm_err_info.wbm_err_src,
+				   wbm_err_info.reo_psh_rsn);
 
 		/* Set queue_mapping in nbuf to 0 */
 		dp_set_rx_queue(nbuf, 0);
@@ -1586,8 +1586,8 @@ done:
 					break;
 
 				default:
-					dp_err_rl("Got pkt with REO ERROR: %d",
-						  wbm_err_info.reo_err_code);
+					dp_info_rl("Got pkt with REO ERROR: %d",
+						   wbm_err_info.reo_err_code);
 					break;
 				}
 			}

+ 11 - 0
dp/wifi3.0/dp_types.h

@@ -1163,6 +1163,17 @@ struct dp_soc {
 	qdf_atomic_t ipa_pipes_enabled;
 #endif
 
+#ifdef WLAN_FEATURE_STATS_EXT
+	struct {
+		uint32_t rx_mpdu_received;
+		uint32_t rx_mpdu_missed;
+	} ext_stats;
+	qdf_event_t rx_hw_stats_event;
+
+	/* Ignore reo command queue status during peer delete */
+	bool ignore_reo_status_cb;
+#endif
+
 	/* Smart monitor capability for HKv2 */
 	uint8_t hw_nac_monitor_support;
 	/* Flag to indicate if HTT v2 is enabled*/

+ 2 - 2
hal/wifi3.0/hal_api.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -32,7 +32,7 @@
 #define SHADOW_REGISTER_END_ADDRESS_OFFSET \
 	((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (MAX_SHADOW_REGISTERS)))
 #define SHADOW_REGISTER(x) ((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (x)))
-#elif defined(QCA_WIFI_QCA6290)
+#elif defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCN9000)
 #define SHADOW_REGISTER_START_ADDRESS_OFFSET 0x00003024
 #define SHADOW_REGISTER_END_ADDRESS_OFFSET \
 	((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (MAX_SHADOW_REGISTERS)))

+ 104 - 1
umac/dfs/core/src/dfs.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016-2019 The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2013, 2016-2020 The Linux Foundation.  All rights reserved.
  * Copyright (c) 2005-2006 Atheros Communications, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
@@ -197,6 +197,12 @@
 #define WLAN_DFS_DATA_STRUCT_LOCK_DESTROY(_dfs) \
 	qdf_spinlock_destroy(&(_dfs)->dfs_data_struct_lock)
 
+/* Wrappers to call MLME radar during mode switch lock. */
+#define DFS_RADAR_MODE_SWITCH_LOCK(_dfs) \
+	dfs_mlme_acquire_radar_mode_switch_lock((_dfs)->dfs_pdev_obj)
+#define DFS_RADAR_MODE_SWITCH_UNLOCK(_dfs) \
+	dfs_mlme_release_radar_mode_switch_lock((_dfs)->dfs_pdev_obj)
+
 /* Mask for time stamp from descriptor */
 #define DFS_TSMASK    0xFFFFFFFF
 /* Shift for time stamp from descriptor */
@@ -897,6 +903,20 @@ struct dfs_event_log {
 
 #define FREQ_OFFSET_BOUNDARY_FOR_80MHZ 40
 
+/**
+ * struct dfs_mode_switch_defer_params - Parameters storing DFS information
+ * before defer, as part of HW mode switch.
+ *
+ * @radar_params: Deferred radar parameters.
+ * @is_cac_completed: Boolean representing CAC completion event.
+ * @is_radar_detected: Boolean representing radar event.
+ */
+struct dfs_mode_switch_defer_params {
+	struct radar_found_info *radar_params;
+	bool is_cac_completed;
+	bool is_radar_detected;
+};
+
 /**
  * struct wlan_dfs -                 The main dfs structure.
  * @dfs_debug_mask:                  Current debug bitmask.
@@ -1061,6 +1081,9 @@ struct dfs_event_log {
  *                                   be blocked and this variable should be
  *                                   false so that HW pulses and synthetic
  *                                   pulses do not get mixed up.
+ *                                   defer timer running.
+ * @dfs_defer_params:                DFS deferred event parameters (allocated
+ *                                   only for the duration of defer alone).
  */
 struct wlan_dfs {
 	uint32_t       dfs_debug_mask;
@@ -1224,6 +1247,7 @@ struct wlan_dfs {
 #if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
 	bool           dfs_allow_hw_pulses;
 #endif
+	struct dfs_mode_switch_defer_params dfs_defer_params;
 };
 
 #if defined(QCA_SUPPORT_AGILE_DFS) || defined(ATH_SUPPORT_ZERO_CAC_DFS)
@@ -1254,6 +1278,7 @@ struct wlan_dfs_priv {
  * @dfs_precac_timer: agile precac timer
  * @dfs_precac_timer_running: precac timer running flag
  * @ocac_status: Off channel CAC complete status
+ * @dfs_nol_ctx: dfs NOL data for all radios.
  */
 struct dfs_soc_priv_obj {
 	struct wlan_objmgr_psoc *psoc;
@@ -1268,6 +1293,7 @@ struct dfs_soc_priv_obj {
 	bool precac_state_started;
 	bool ocac_status;
 #endif
+	struct dfsreq_nolinfo *dfs_psoc_nolinfo;
 };
 
 /**
@@ -2725,4 +2751,81 @@ int dfs_reinit_timers(struct wlan_dfs *dfs);
  * Return: None.
  */
 void dfs_reset_dfs_prevchan(struct wlan_dfs *dfs);
+
+/**
+ * dfs_init_tmp_psoc_nol() - Init temporary psoc NOL structure.
+ * @dfs: Pointer to wlan_dfs object.
+ * @num_radios: Num of radios in the PSOC.
+ *
+ * Return: void.
+ */
+void dfs_init_tmp_psoc_nol(struct wlan_dfs *dfs, uint8_t num_radios);
+
+/**
+ * dfs_deinit_tmp_psoc_nol() - De-init temporary psoc NOL structure.
+ * @dfs: Pointer to wlan_dfs object.
+ *
+ * Return: void.
+ */
+void dfs_deinit_tmp_psoc_nol(struct wlan_dfs *dfs);
+
+/**
+ * dfs_save_dfs_nol_in_psoc() - Save NOL data of given pdev.
+ * @dfs: Pointer to wlan_dfs object.
+ * @pdev_id: The pdev ID which will have the NOL data.
+ * @low_5ghz_freq: The low 5GHz frequency value of the target pdev id.
+ * @high_5ghz_freq: The high 5GHz frequency value of the target pdev id.
+ *
+ * Based on the frequency of the NOL channel, copy it to the target pdev_id
+ * structure in psoc.
+ *
+ * Return: void.
+ */
+void dfs_save_dfs_nol_in_psoc(struct wlan_dfs *dfs,
+			      uint8_t pdev_id,
+			      uint16_t low_5ghz_freq,
+			      uint16_t high_5ghz_freq);
+
+/**
+ * dfs_reinit_nol_from_psoc_copy() - Reinit saved NOL data to corresponding
+ * DFS object.
+ * @dfs: Pointer to wlan_dfs object.
+ * @pdev_id: pdev_id of the given dfs object.
+ *
+ * Return: void.
+ */
+void dfs_reinit_nol_from_psoc_copy(struct wlan_dfs *dfs, uint8_t pdev_id);
+
+/**
+ * dfs_is_hw_mode_switch_in_progress() - Check if HW mode switch in progress.
+ * @dfs: Pointer to wlan_dfs object.
+ *
+ * Return: True if mode switch is in progress, else false.
+ */
+bool dfs_is_hw_mode_switch_in_progress(struct wlan_dfs *dfs);
+
+/**
+ * dfs_start_mode_switch_defer_timer() - start mode switch defer timer.
+ * @dfs: Pointer to wlan_dfs object.
+ *
+ * Return: void.
+ */
+void dfs_start_mode_switch_defer_timer(struct wlan_dfs *dfs);
+
+/**
+ * dfs_complete_deferred_tasks() - Process mode switch completion event and
+ * handle deffered tasks.
+ * @dfs: Pointer to wlan_dfs object.
+ *
+ * Return: void.
+ */
+void dfs_complete_deferred_tasks(struct wlan_dfs *dfs);
+
+/**
+ * dfs_process_cac_completion() - Process DFS CAC completion event.
+ * @dfs: Pointer to wlan_dfs object.
+ *
+ * Return: void.
+ */
+void dfs_process_cac_completion(struct wlan_dfs *dfs);
 #endif  /* _DFS_H_ */

+ 27 - 1
umac/dfs/core/src/dfs_zero_cac.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
@@ -823,6 +823,32 @@ dfs_get_precac_chan_state_for_freq(struct wlan_dfs *dfs,
  */
 void dfs_zero_cac_reset(struct wlan_dfs *dfs);
 
+/**
+ * dfs_reinit_precac_lists() - Reinit DFS preCAC lists.
+ * @src_dfs: Source DFS from which the preCAC list is copied.
+ * @dest_dfs: Destination DFS to which the preCAC list is copied.
+ * @low_5g_freq: Low 5G frequency value of the destination DFS.
+ * @high_5g_freq: High 5G frequency value of the destination DFS.
+ *
+ * Copy all the preCAC list entries from the source DFS to the destination DFS
+ * which fall within the frequency range of low_5g_freq and high_5g_freq.
+ *
+ * Return: None (void).
+ */
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && !defined(QCA_MCL_DFS_SUPPORT)
+void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs,
+			     struct wlan_dfs *dest_dfs,
+			     uint16_t low_5g_freq,
+			     uint16_t high_5g_freq);
+#else
+static inline void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs,
+					   struct wlan_dfs *dest_dfs,
+					   uint16_t low_5g_freq,
+					   uint16_t high_5g_freq)
+{
+}
+#endif
+
 /**
  * dfs_is_precac_done_on_ht20_40_80_chan() - Is precac done on a
  *                                           VHT20/40/80 channel.

+ 25 - 1
umac/dfs/core/src/misc/dfs.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  * Copyright (c) 2002-2006, Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
@@ -31,6 +31,7 @@
 #include "../dfs_full_offload.h"
 #include <wlan_objmgr_vdev_obj.h>
 #include "wlan_dfs_utils_api.h"
+#include "../dfs_process_radar_found_ind.h"
 #include "../dfs_partial_offload_radar.h"
 
 /* Disable NOL in FW. */
@@ -917,3 +918,26 @@ void dfs_reset_dfs_prevchan(struct wlan_dfs *dfs)
 {
 	qdf_mem_zero(dfs->dfs_prevchan, sizeof(struct dfs_channel));
 }
+
+bool dfs_is_hw_mode_switch_in_progress(struct wlan_dfs *dfs)
+{
+	return lmac_dfs_is_hw_mode_switch_in_progress(dfs->dfs_pdev_obj);
+}
+
+void dfs_complete_deferred_tasks(struct wlan_dfs *dfs)
+{
+	if (dfs->dfs_defer_params.is_radar_detected) {
+		/* Handle radar event that was deferred and free the temporary
+		 * storage of the radar event parameters.
+		 */
+		dfs_process_radar_ind(dfs, dfs->dfs_defer_params.radar_params);
+		qdf_mem_free(dfs->dfs_defer_params.radar_params);
+		dfs->dfs_defer_params.is_radar_detected = false;
+	} else if (dfs->dfs_defer_params.is_cac_completed) {
+		/* Handle CAC completion event that was deferred for HW mode
+		 * switch.
+		 */
+		dfs_process_cac_completion(dfs);
+		dfs->dfs_defer_params.is_cac_completed = false;
+	}
+}

+ 24 - 13
umac/dfs/core/src/misc/dfs_cac.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
@@ -122,24 +122,16 @@ static void dfs_clear_cac_started_chan(struct wlan_dfs *dfs)
 		     sizeof(dfs->dfs_cac_started_chan));
 }
 
-/**
- * dfs_cac_timeout() - DFS cactimeout function.
- *
- * Sets dfs_cac_timer_running to 0  and dfs_cac_valid_timer.
- */
-#ifdef CONFIG_CHAN_FREQ_API
-static os_timer_func(dfs_cac_timeout)
+void dfs_process_cac_completion(struct wlan_dfs *dfs)
 {
-	struct wlan_dfs *dfs = NULL;
 	enum phy_ch_width ch_width = CH_WIDTH_INVALID;
 	uint16_t primary_chan_freq = 0, secondary_chan_freq = 0;
 	struct dfs_channel *dfs_curchan;
 
-	OS_GET_TIMER_ARG(dfs, struct wlan_dfs *);
 	dfs->dfs_cac_timer_running = 0;
 	dfs_curchan = dfs->dfs_curchan;
 
-	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "cac expired, chan %d curr time %d",
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "cac expired, chan %d cur time %d",
 		 dfs->dfs_curchan->dfs_ch_freq,
 		 (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000));
 
@@ -154,12 +146,13 @@ static os_timer_func(dfs_cac_timeout)
 					   dfs_curchan->dfs_ch_mhz_freq_seg2,
 					   dfs_curchan->dfs_ch_flags);
 		dfs_debug(dfs, WLAN_DEBUG_DFS,
-			  "CAC timer on channel %u (%u MHz) stopped due to radar",
+			  "CAC timer on chan %u (%u MHz) stopped due to radar",
 			  dfs_curchan->dfs_ch_ieee,
 			  dfs_curchan->dfs_ch_freq);
 	} else {
 		dfs_debug(dfs, WLAN_DEBUG_DFS,
-			  "CAC timer on channel %u (%u MHz) expired; no radar detected",
+			  "CAC timer on channel %u (%u MHz) expired;"
+			  "no radar detected",
 			  dfs_curchan->dfs_ch_ieee,
 			  dfs_curchan->dfs_ch_freq);
 
@@ -195,6 +188,24 @@ static os_timer_func(dfs_cac_timeout)
 		dfs->dfs_defer_precac_channel_change = 0;
 	}
 }
+
+/**
+ * dfs_cac_timeout() - DFS cactimeout function.
+ *
+ * Sets dfs_cac_timer_running to 0  and dfs_cac_valid_timer.
+ */
+#ifdef CONFIG_CHAN_FREQ_API
+static os_timer_func(dfs_cac_timeout)
+{
+	struct wlan_dfs *dfs = NULL;
+
+	OS_GET_TIMER_ARG(dfs, struct wlan_dfs *);
+
+	if (dfs_is_hw_mode_switch_in_progress(dfs))
+		dfs->dfs_defer_params.is_cac_completed = true;
+	else
+		dfs_process_cac_completion(dfs);
+}
 #else
 #ifdef CONFIG_CHAN_NUM_API
 static os_timer_func(dfs_cac_timeout)

+ 99 - 1
umac/dfs/core/src/misc/dfs_nol.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  * Copyright (c) 2002-2010, Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
@@ -899,3 +899,101 @@ void dfs_remove_spoof_channel_from_nol(struct wlan_dfs *dfs)
 }
 #endif
 #endif
+
+void dfs_init_tmp_psoc_nol(struct wlan_dfs *dfs, uint8_t num_radios)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj;
+
+	if (WLAN_UMAC_MAX_PDEVS < num_radios) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"num_radios (%u) exceeds limit", num_radios);
+		return;
+	}
+
+	/* Allocate the temporary psoc NOL copy structure for the number
+	 * of radios provided.
+	 */
+	dfs_soc_obj->dfs_psoc_nolinfo =
+		qdf_mem_malloc(sizeof(struct dfsreq_nolinfo) * num_radios);
+}
+
+void dfs_deinit_tmp_psoc_nol(struct wlan_dfs *dfs)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj;
+
+	if (!dfs_soc_obj->dfs_psoc_nolinfo)
+		return;
+
+	qdf_mem_free(dfs_soc_obj->dfs_psoc_nolinfo);
+	dfs_soc_obj->dfs_psoc_nolinfo = NULL;
+}
+
+void dfs_save_dfs_nol_in_psoc(struct wlan_dfs *dfs,
+			      uint8_t pdev_id,
+			      uint16_t low_5ghz_freq,
+			      uint16_t high_5ghz_freq)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj;
+	struct dfsreq_nolinfo tmp_nolinfo, *nolinfo;
+	uint32_t i, num_chans = 0;
+	uint16_t tmp_freq;
+
+	if (!dfs->dfs_nol_count)
+		return;
+
+	if (!dfs_soc_obj->dfs_psoc_nolinfo)
+		return;
+
+	nolinfo = &dfs_soc_obj->dfs_psoc_nolinfo[pdev_id];
+	/* Fetch the NOL entries for the DFS object. */
+	dfs_getnol(dfs, &tmp_nolinfo);
+
+	/* nolinfo might already have some data. Do not overwrite it */
+	num_chans = nolinfo->dfs_ch_nchans;
+	for (i = 0; i < tmp_nolinfo.dfs_ch_nchans; i++) {
+		tmp_freq = tmp_nolinfo.dfs_nol[i].nol_freq;
+
+		/* Add to nolinfo only if within the pdev's frequency range. */
+		if ((low_5ghz_freq < tmp_freq) && (high_5ghz_freq > tmp_freq)) {
+			/* Figure out the completed duration of each NOL. */
+			uint32_t nol_completed_ms =
+				qdf_system_ticks_to_msecs(qdf_system_ticks() -
+				tmp_nolinfo.dfs_nol[i].nol_start_ticks);
+
+			nolinfo->dfs_nol[num_chans] = tmp_nolinfo.dfs_nol[i];
+			/* Remember the remaining NOL time in the timeout
+			 * variable.
+			 */
+			nolinfo->dfs_nol[num_chans++].nol_timeout_ms -=
+				nol_completed_ms;
+		}
+	}
+
+	nolinfo->dfs_ch_nchans = num_chans;
+}
+
+void dfs_reinit_nol_from_psoc_copy(struct wlan_dfs *dfs, uint8_t pdev_id)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj;
+	struct dfsreq_nolinfo *nol;
+	uint8_t i;
+
+	if (!dfs_soc_obj->dfs_psoc_nolinfo)
+		return;
+
+	if (!dfs_soc_obj->dfs_psoc_nolinfo[pdev_id].dfs_ch_nchans)
+		return;
+
+	nol = &dfs_soc_obj->dfs_psoc_nolinfo[pdev_id];
+
+	/* The NOL timeout value in each entry points to the remaining time
+	 * of the NOL. This is to indicate that the NOL entries are paused
+	 * and are not left to continue.
+	 * While adding these NOL, update the start ticks to current time
+	 * to avoid losing entries which might have timed out during
+	 * the pause and resume mechanism.
+	 */
+	for (i = 0; i < nol->dfs_ch_nchans; i++)
+		nol->dfs_nol[i].nol_start_ticks = qdf_system_ticks();
+	dfs_set_nol(dfs, nol->dfs_nol, nol->dfs_ch_nchans);
+}

+ 48 - 11
umac/dfs/core/src/misc/dfs_process_radar_found_ind.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -951,16 +951,50 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	bool wait_for_csa = false;
 	uint16_t freq_list[NUM_CHANNELS_160MHZ];
 	uint8_t num_channels;
-	QDF_STATUS status;
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
 	uint32_t freq_center;
 	uint32_t radarfound_freq;
 	struct dfs_channel *dfs_curchan;
 
+	/* Acquire a lock to avoid initiating mode switch till radar
+	 * processing is completed.
+	 */
+	DFS_RADAR_MODE_SWITCH_LOCK(dfs);
+
+	/* Before processing radar, check if HW mode switch is in progress.
+	 * If in progress, defer the processing of radar event received till
+	 * the mode switch is completed.
+	 */
+	if (dfs_is_hw_mode_switch_in_progress(dfs)) {
+		struct radar_found_info *radar_params = NULL;
+
+		radar_params = qdf_mem_malloc(sizeof(*radar_params));
+		if (!radar_params)
+			goto exit;
+
+		/* If CAC timer is running, cancel it here rather than
+		 * after processing to avoid handling unnecessary CAC timeouts.
+		 */
+		if (dfs->dfs_cac_timer_running)
+			dfs_cac_stop(dfs);
+
+		/* If CAC timer is to be handled after mode switch and then
+		 * we receive radar, no point in handling CAC completion.
+		 */
+		if (dfs->dfs_defer_params.is_cac_completed)
+			dfs->dfs_defer_params.is_cac_completed = false;
+		qdf_mem_copy(radar_params, radar_found, sizeof(*radar_params));
+		dfs->dfs_defer_params.radar_params = radar_params;
+		dfs->dfs_defer_params.is_radar_detected = true;
+		status = QDF_STATUS_SUCCESS;
+		goto exit;
+	}
+
 	dfs_curchan = dfs->dfs_curchan;
 
 	if (!dfs_curchan) {
 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs->dfs_curchan is NULL");
-		return QDF_STATUS_E_FAILURE;
+		goto exit;
 	}
 
 	/* Check if the current channel is a non DFS channel
@@ -972,7 +1006,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	    !(radar_found->detector_id == AGILE_DETECTOR_ID)) {
 		dfs_err(dfs, WLAN_DEBUG_DFS,
 			"radar event on a non-DFS channel");
-		return QDF_STATUS_E_FAILURE;
+		goto exit;
 	}
 
 	/* Sanity checks for radar on Agile detector */
@@ -981,7 +1015,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	{
 		dfs_err(dfs, WLAN_DEBUG_DFS,
 			"radar on Agile detector when ADFS is not running");
-		return QDF_STATUS_E_FAILURE;
+		goto exit;
 	}
 
 	/* For Full Offload, FW sends segment id,freq_offset and chirp
@@ -1019,7 +1053,8 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	if (!dfs->dfs_use_nol) {
 		dfs_reset_bangradar(dfs);
 		dfs_send_csa_to_current_chan(dfs);
-		return QDF_STATUS_SUCCESS;
+		status = QDF_STATUS_SUCCESS;
+		goto exit;
 	}
 
 	if (dfs->dfs_bangradar_type == DFS_BANGRADAR_FOR_ALL_SUBCHANS)
@@ -1049,7 +1084,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	if (QDF_IS_STATUS_ERROR(status)) {
 		dfs_err(dfs, WLAN_DEBUG_DFS,
 			"radar event received on invalid channel");
-		return status;
+		goto exit;
 	}
 
 	dfs->dfs_is_nol_ie_sent = false;
@@ -1105,7 +1140,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	 * channel change is not required.
 	 */
 	if (radar_found->detector_id == AGILE_DETECTOR_ID)
-		return QDF_STATUS_SUCCESS;
+		goto exit;
 	if (!dfs->dfs_is_offload_enabled &&
 	    dfs->is_radar_found_on_secondary_seg) {
 		dfs_second_segment_radar_disable(dfs);
@@ -1113,7 +1148,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 
 		if (dfs->is_radar_during_precac) {
 			dfs->is_radar_during_precac = 0;
-			return QDF_STATUS_SUCCESS;
+			goto exit;
 		}
 	}
 
@@ -1124,7 +1159,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 	 * needs to be fixed. See EV 105776.
 	 */
 	if (wait_for_csa)
-		return QDF_STATUS_SUCCESS;
+		goto exit;
 
 	/*
 	 * EV 129487 : We have detected radar in the channel,
@@ -1144,6 +1179,8 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
 				   dfs->dfs_curchan->dfs_ch_mhz_freq_seg2,
 				   dfs->dfs_curchan->dfs_ch_flags);
 
-	return QDF_STATUS_SUCCESS;
+exit:
+	DFS_RADAR_MODE_SWITCH_UNLOCK(dfs);
+	return status;
 }
 #endif

+ 2 - 2
umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2016-2020 The Linux Foundation. All rights reserved.
  * Copyright (c) 2010, Atheros Communications Inc.
  * All Rights Reserved.
  *
@@ -77,7 +77,7 @@
 #define DFS_LAST_IOCTL 29
 
 #ifndef DFS_CHAN_MAX
-#define DFS_CHAN_MAX 1023
+#define DFS_CHAN_MAX 25
 #endif
 
 /**

+ 10 - 1
umac/dfs/dispatcher/inc/wlan_dfs_lmac_api.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -118,4 +118,13 @@ static inline bool lmac_is_host_dfs_check_support_enabled(
 	return false;
 }
 #endif
+
+/**
+ * lmac_dfs_is_hw_mode_switch_in_progress() - Check if HW mode switch is in
+ * progress.
+ * @pdev: Pointer to PDEV structure.
+ *
+ * Return: true if HW mode switch is in progress, else false.
+ */
+bool lmac_dfs_is_hw_mode_switch_in_progress(struct wlan_objmgr_pdev *pdev);
 #endif /* _WLAN_DFS_LMAC_API_H_ */

+ 19 - 1
umac/dfs/dispatcher/inc/wlan_dfs_mlme_api.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -440,4 +440,22 @@ void dfs_mlme_handle_dfs_scan_violation(struct wlan_objmgr_pdev *pdev)
  * Return: true if pdev opmode is STA, else false.
  */
 bool dfs_mlme_is_opmode_sta(struct wlan_objmgr_pdev *pdev);
+
+/**
+ * dfs_mlme_acquire_radar_mode_switch_lock() - Acquire lock for radar processing
+ * over mode switch handling.
+ * @pdev: Pointer to DFS pdev object.
+ *
+ * Return: void.
+ */
+void dfs_mlme_acquire_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev);
+
+/**
+ * dfs_mlme_release_radar_mode_switch_lock() - Release lock taken for radar
+ * processing over mode switch handling.
+ * @pdev: Pointer to DFS pdev object.
+ *
+ * Return: void.
+ */
+void dfs_mlme_release_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev);
 #endif /* _WLAN_DFS_MLME_API_H_ */

+ 74 - 1
umac/dfs/dispatcher/inc/wlan_dfs_tgt_api.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -604,4 +604,77 @@ void tgt_dfs_set_fw_adfs_support(struct wlan_objmgr_pdev *pdev,
 {
 }
 #endif
+
+/**
+ * tgt_dfs_init_tmp_psoc_nol() - Init temporary psoc NOL structure.
+ * @pdev: Pointer to pdev object.
+ * @num_radios: Number of radios in the psoc.
+ *
+ * Return: void.
+ */
+void tgt_dfs_init_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev,
+			       uint8_t num_radios);
+
+/**
+ * tgt_dfs_deinit_tmp_psoc_nol() - De-init temporary psoc NOL structure.
+ * @pdev: Pointer to pdev object.
+ *
+ * Return: void.
+ */
+void tgt_dfs_deinit_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev);
+
+/**
+ * tgt_dfs_save_dfs_nol_in_psoc() - Save NOL data of given pdev.
+ * @pdev: Pointer to pdev object.
+ * @pdev_id: The pdev ID which will have the NOL data.
+ * @low_5ghz_freq: The low 5GHz frequency value of the target pdev id.
+ * @high_5ghz_freq: The high 5GHz frequency value of the target pdev id.
+ *
+ * Based on the frequency of the NOL channel, copy it to the target pdev_id
+ * structure in psoc.
+ *
+ * Return: void.
+ */
+void tgt_dfs_save_dfs_nol_in_psoc(struct wlan_objmgr_pdev *pdev,
+				  uint8_t pdev_id,
+				  uint16_t low_5ghz_freq,
+				  uint16_t high_5ghz_freq);
+
+/**
+ * tgt_dfs_reinit_nol_from_psoc_copy() - Reinit saved NOL data to corresponding
+ * pdevs.
+ * @pdev: Pointer to pdev object.
+ * @pdev_id: pdev_id of the given pdev.
+ *
+ * Return: void.
+ */
+void tgt_dfs_reinit_nol_from_psoc_copy(struct wlan_objmgr_pdev *pdev,
+				       uint8_t pdev_id);
+
+/**
+ * tgt_dfs_reinit_precac_lists() - Reinit preCAC lists.
+ * @src_pdev: Source pdev object from which the preCAC list is copied.
+ * @dest_pdev: Destination pdev object to which the preCAC list is copied.
+ * @low_5g_freq: Low 5G frequency value of the destination DFS.
+ * @high_5g_freq: High 5G frequency value of the destination DFS.
+ *
+ * Copy all the preCAC list entries from the source pdev object to the
+ * destination pdev object which fall within the frequency range of
+ * low_5g_freq and high_5g_freq.
+ *
+ * Return: None (void).
+ */
+void tgt_dfs_reinit_precac_lists(struct wlan_objmgr_pdev *src_pdev,
+				 struct wlan_objmgr_pdev *dest_pdev,
+				 uint16_t low_5g_freq,
+				 uint16_t high_5g_freq);
+
+/**
+ * tgt_dfs_complete_deferred_tasks() - Process HW mode switch completion and
+ * handle deferred tasks.
+ * @pdev: Pointer to primary pdev object.
+ *
+ * Return: void.
+ */
+void tgt_dfs_complete_deferred_tasks(struct wlan_objmgr_pdev *pdev);
 #endif /* _WLAN_DFS_TGT_API_H_ */

+ 9 - 1
umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -73,6 +73,10 @@
  *                                     list.
  * @mlme_get_cac_timeout_for_freq:     Get CAC timeout for a given channel
  *                                     frequency.
+ * @mlme_acquire_radar_mode_switch_lock: Acquire lock for radar processing over
+ *                                     mode switch.
+ * @mlme_release_radar_mode_switch_lock: Release lock taken for radar processing
+ *                                     over mode switch.
  */
 struct dfs_to_mlme {
 	QDF_STATUS (*pdev_component_obj_attach)(struct wlan_objmgr_pdev *pdev,
@@ -242,6 +246,10 @@ struct dfs_to_mlme {
 			(struct wlan_objmgr_pdev *pdev,
 			 uint16_t freq,
 			 enum WLAN_DFS_EVENTS event);
+	void (*mlme_acquire_radar_mode_switch_lock)
+			(struct wlan_objmgr_pdev *pdev);
+	void (*mlme_release_radar_mode_switch_lock)
+			(struct wlan_objmgr_pdev *pdev);
 };
 
 extern struct dfs_to_mlme global_dfs_to_mlme;

+ 5 - 1
umac/dfs/dispatcher/src/wlan_dfs_init_deinit_api.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -139,6 +139,10 @@ void register_dfs_callbacks(void)
 	tmp_dfs_to_mlme->mlme_dfs_deliver_event =
 		mlme_dfs_deliver_event;
 
+	tmp_dfs_to_mlme->mlme_acquire_radar_mode_switch_lock =
+		mlme_acquire_radar_mode_switch_lock;
+	tmp_dfs_to_mlme->mlme_release_radar_mode_switch_lock =
+		mlme_release_radar_mode_switch_lock;
 	/*
 	 * Register precac auto channel switch feature related callbacks
 	 */

+ 18 - 1
umac/dfs/dispatcher/src/wlan_dfs_lmac_api.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -197,3 +197,20 @@ bool lmac_is_host_dfs_check_support_enabled(struct wlan_objmgr_pdev *pdev)
 	return enabled;
 }
 #endif
+
+bool lmac_dfs_is_hw_mode_switch_in_progress(struct wlan_objmgr_pdev *pdev)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops;
+	bool is_hw_mode_switch_in_progress = false;
+
+	psoc = wlan_pdev_get_psoc(pdev);
+	dfs_tx_ops = &psoc->soc_cb.tx_ops.dfs_tx_ops;
+
+	if (dfs_tx_ops->dfs_check_mode_switch_state)
+		dfs_tx_ops->dfs_check_mode_switch_state(
+			pdev,
+			&is_hw_mode_switch_in_progress);
+
+	return is_hw_mode_switch_in_progress;
+}

+ 17 - 1
umac/dfs/dispatcher/src/wlan_dfs_mlme_api.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -567,3 +567,19 @@ bool dfs_mlme_is_opmode_sta(struct wlan_objmgr_pdev *pdev)
 
 	return global_dfs_to_mlme.mlme_is_opmode_sta(pdev);
 }
+
+void dfs_mlme_acquire_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev)
+{
+	if (!global_dfs_to_mlme.mlme_acquire_radar_mode_switch_lock)
+		return;
+
+	global_dfs_to_mlme.mlme_acquire_radar_mode_switch_lock(pdev);
+}
+
+void dfs_mlme_release_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev)
+{
+	if (!global_dfs_to_mlme.mlme_release_radar_mode_switch_lock)
+		return;
+
+	global_dfs_to_mlme.mlme_release_radar_mode_switch_lock(pdev);
+}

+ 100 - 1
umac/dfs/dispatcher/src/wlan_dfs_tgt_api.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -944,3 +944,102 @@ void tgt_dfs_set_fw_adfs_support(struct wlan_objmgr_pdev *pdev,
 
 qdf_export_symbol(tgt_dfs_set_fw_adfs_support);
 #endif
+
+void tgt_dfs_init_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev,
+			       uint8_t num_radios)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = wlan_pdev_get_dfs_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+
+	dfs_init_tmp_psoc_nol(dfs, num_radios);
+}
+
+qdf_export_symbol(tgt_dfs_init_tmp_psoc_nol);
+
+void tgt_dfs_deinit_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = wlan_pdev_get_dfs_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+
+	dfs_deinit_tmp_psoc_nol(dfs);
+}
+
+qdf_export_symbol(tgt_dfs_deinit_tmp_psoc_nol);
+
+void tgt_dfs_save_dfs_nol_in_psoc(struct wlan_objmgr_pdev *pdev,
+				  uint8_t pdev_id,
+				  uint16_t low_5ghz_freq,
+				  uint16_t high_5ghz_freq)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = wlan_pdev_get_dfs_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+
+	dfs_save_dfs_nol_in_psoc(dfs, pdev_id, low_5ghz_freq, high_5ghz_freq);
+}
+
+qdf_export_symbol(tgt_dfs_save_dfs_nol_in_psoc);
+
+void tgt_dfs_reinit_nol_from_psoc_copy(struct wlan_objmgr_pdev *pdev,
+				       uint8_t pdev_id)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = wlan_pdev_get_dfs_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+
+	dfs_reinit_nol_from_psoc_copy(dfs, pdev_id);
+}
+
+qdf_export_symbol(tgt_dfs_reinit_nol_from_psoc_copy);
+
+void tgt_dfs_reinit_precac_lists(struct wlan_objmgr_pdev *src_pdev,
+				 struct wlan_objmgr_pdev *dest_pdev,
+				 uint16_t low_5g_freq,
+				 uint16_t high_5g_freq)
+{
+	struct wlan_dfs *src_dfs, *dest_dfs;
+
+	src_dfs = wlan_pdev_get_dfs_obj(src_pdev);
+	if (!src_dfs) {
+		dfs_err(src_dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+	dest_dfs = wlan_pdev_get_dfs_obj(dest_pdev);
+	if (!dest_dfs) {
+		dfs_err(dest_dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+
+	dfs_reinit_precac_lists(src_dfs, dest_dfs, low_5g_freq, high_5g_freq);
+}
+
+void tgt_dfs_complete_deferred_tasks(struct wlan_objmgr_pdev *pdev)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = wlan_pdev_get_dfs_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+		return;
+	}
+
+	dfs_complete_deferred_tasks(dfs);
+}

+ 24 - 0
umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h

@@ -802,6 +802,7 @@ struct wlan_lmac_if_reg_tx_ops {
  * @dfs_send_avg_radar_params_to_fw:    Send average radar parameters to FW.
  * @dfs_send_usenol_pdev_param:         Send usenol pdev param to FW.
  * @dfs_send_subchan_marking_pdev_param: Send subchan marking pdev param to FW.
+ * @dfs_check_mode_switch_state:        Find if HW mode switch is in progress.
  */
 
 struct wlan_lmac_if_dfs_tx_ops {
@@ -855,6 +856,9 @@ struct wlan_lmac_if_dfs_tx_ops {
 	QDF_STATUS (*dfs_send_subchan_marking_pdev_param)(
 			struct wlan_objmgr_pdev *pdev,
 			bool subchanmark);
+	QDF_STATUS (*dfs_check_mode_switch_state)(
+			struct wlan_objmgr_pdev *pdev,
+			bool *is_hw_mode_switch_in_progress);
 };
 
 /**
@@ -1374,6 +1378,12 @@ struct wlan_lmac_if_wifi_pos_rx_ops {
  * @dfs_is_hw_pulses_allowed:         Check if HW pulses are allowed or not.
  * @dfs_set_fw_adfs_support:          Set the agile DFS FW support in DFS.
  * @dfs_reset_dfs_prevchan:           Reset DFS previous channel structure.
+ * @dfs_init_tmp_psoc_nol:            Init temporary PSOC NOL structure.
+ * @dfs_deinit_tmp_psoc_nol:          Deinit temporary PSOC NOL structure.
+ * @dfs_save_dfs_nol_in_psoc:         Copy DFS NOL data to the PSOC copy.
+ * @dfs_reinit_nol_from_psoc_copy:    Reinit DFS NOL from the PSOC NOL copy.
+ * @dfs_reinit_precac_lists:          Reinit precac lists from other pdev.
+ * @dfs_complete_deferred_tasks:      Process mode switch completion in DFS.
  */
 struct wlan_lmac_if_dfs_rx_ops {
 	QDF_STATUS (*dfs_get_radars)(struct wlan_objmgr_pdev *pdev);
@@ -1541,6 +1551,20 @@ struct wlan_lmac_if_dfs_rx_ops {
 					bool fw_adfs_support_160,
 					bool fw_adfs_support_non_160);
 	void (*dfs_reset_dfs_prevchan)(struct wlan_objmgr_pdev *pdev);
+	void (*dfs_init_tmp_psoc_nol)(struct wlan_objmgr_pdev *pdev,
+				      uint8_t num_radios);
+	void (*dfs_deinit_tmp_psoc_nol)(struct wlan_objmgr_pdev *pdev);
+	void (*dfs_save_dfs_nol_in_psoc)(struct wlan_objmgr_pdev *pdev,
+					 uint8_t pdev_id,
+					 uint16_t low_5ghz_freq,
+					 uint16_t high_5ghz_freq);
+	void (*dfs_reinit_nol_from_psoc_copy)(struct wlan_objmgr_pdev *pdev,
+					      uint8_t pdev_id);
+	void (*dfs_reinit_precac_lists)(struct wlan_objmgr_pdev *src_pdev,
+					struct wlan_objmgr_pdev *dest_pdev,
+					uint16_t low_5g_freq,
+					uint16_t high_5g_freq);
+	void (*dfs_complete_deferred_tasks)(struct wlan_objmgr_pdev *pdev);
 };
 
 /**

+ 13 - 2
umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
@@ -523,7 +523,18 @@ wlan_lmac_if_umac_dfs_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
 		tgt_dfs_set_fw_adfs_support;
 	dfs_rx_ops->dfs_reset_dfs_prevchan =
 		utils_dfs_reset_dfs_prevchan;
-
+	dfs_rx_ops->dfs_init_tmp_psoc_nol =
+		tgt_dfs_init_tmp_psoc_nol;
+	dfs_rx_ops->dfs_deinit_tmp_psoc_nol =
+		tgt_dfs_deinit_tmp_psoc_nol;
+	dfs_rx_ops->dfs_save_dfs_nol_in_psoc =
+		tgt_dfs_save_dfs_nol_in_psoc;
+	dfs_rx_ops->dfs_reinit_nol_from_psoc_copy =
+		tgt_dfs_reinit_nol_from_psoc_copy;
+	dfs_rx_ops->dfs_reinit_precac_lists =
+		tgt_dfs_reinit_precac_lists;
+	dfs_rx_ops->dfs_complete_deferred_tasks =
+		tgt_dfs_complete_deferred_tasks;
 	register_precac_auto_chan_rx_ops(dfs_rx_ops);
 	register_precac_auto_chan_rx_ops_ieee(dfs_rx_ops);
 	register_precac_auto_chan_rx_ops_freq(dfs_rx_ops);

+ 6 - 3
umac/regulatory/core/src/reg_services_common.c

@@ -34,6 +34,7 @@
 #include "reg_db_parser.h"
 #include "reg_build_chan_list.h"
 #include <wlan_objmgr_pdev_obj.h>
+#include <target_if.h>
 
 const struct chan_map *channel_map;
 #ifdef CONFIG_CHAN_NUM_API
@@ -2656,11 +2657,13 @@ QDF_STATUS reg_modify_pdev_chan_range(struct wlan_objmgr_pdev *pdev)
 	struct wlan_lmac_if_reg_tx_ops *reg_tx_ops;
 	struct wlan_psoc_host_hal_reg_capabilities_ext *reg_cap_ptr;
 	uint32_t cnt;
-	uint32_t pdev_id;
+	uint32_t phy_id;
 	enum direction dir;
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct target_pdev_info *tgt_pdev;
 
-	pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
+	tgt_pdev = wlan_pdev_get_tgt_if_handle(pdev);
+	phy_id = (uint32_t)target_pdev_get_phy_idx(tgt_pdev);
 	pdev_priv_obj = reg_get_pdev_obj(pdev);
 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
 		reg_err("pdev reg component is NULL");
@@ -2688,7 +2691,7 @@ QDF_STATUS reg_modify_pdev_chan_range(struct wlan_objmgr_pdev *pdev)
 			return QDF_STATUS_E_FAULT;
 		}
 
-		if (reg_cap_ptr->phy_id == pdev_id)
+		if (reg_cap_ptr->phy_id == phy_id)
 			break;
 		reg_cap_ptr++;
 	}