From d890c6d602307c9297df12c7d0287f9ffd26208b Mon Sep 17 00:00:00 2001
From: Sriram R <srirrama@codeaurora.org>
Date: Wed, 12 May 2021 19:21:09 +0530
Subject: [PATCH] ath11k: poll reo status ring for IPQ5018

Currently reo status interrupts are not received
due to wrong mapping of the reo status interrupt
line in IPQ5018.

Hence, until the mapping is resolved in HW, use
polling to reap the reo status ring. Rather than
a period timer to reap the ring, the timer is
triggered only on sending a reo command with
status request.

Without proper reaping of the ring, backpressure
and ring full issues are seen in multi client test
setups which leads to flooding the console with
error messages reporting failure to send reo cmds.

Can be reverted once HW solution is available.

Signed-off-by: Sriram R <srirrama@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/core.c  |  8 ++++-
 drivers/net/wireless/ath/ath11k/dp.c    | 59 +++++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath11k/dp.h    |  8 +++++
 drivers/net/wireless/ath/ath11k/dp_tx.c |  4 +++
 drivers/net/wireless/ath/ath11k/hw.c    | 31 +++++++++++++++++
 drivers/net/wireless/ath/ath11k/hw.h    |  2 ++
 6 files changed, 111 insertions(+), 1 deletion(-)

--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -111,6 +111,7 @@ static const struct ath11k_hw_params ath
 		/* In addition to TCL ring use TCL_CMD ring also for tx */
 		.max_tx_ring = DP_TCL_NUM_RING_MAX + 1,
 		.wakeup_mhi = false,
+		.reo_status_poll = false,
 	},
 	{
 		.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -163,6 +164,7 @@ static const struct ath11k_hw_params ath
 		/* In addition to TCL ring use TCL_CMD ring also for tx */
 		.max_tx_ring = DP_TCL_NUM_RING_MAX + 1,
 		.wakeup_mhi = false,
+		.reo_status_poll = false,
 	},
 	{
 		.name = "qca6390 hw2.0",
@@ -209,6 +211,7 @@ static const struct ath11k_hw_params ath
  		.is_qdss_support = false,
 		.max_tx_ring = 1,
 		.wakeup_mhi = true,
+		.reo_status_poll = false,
 	},
 	{
 		.name = "qcn9074 hw1.0",
@@ -267,6 +270,7 @@ static const struct ath11k_hw_params ath
 		/* In addition to TCL ring use TCL_CMD ring also for tx */
 		.max_tx_ring = DP_TCL_NUM_RING_MAX + 1,
 		.wakeup_mhi = false,
+		.reo_status_poll = false,
 	},
 	{
 		.hw_rev = ATH11K_HW_IPQ5018,
@@ -282,7 +286,7 @@ static const struct ath11k_hw_params ath
 		.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
 		.hw_ops = &ipq5018_ops,
 		.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074,
-		.ring_mask = &ath11k_hw_ring_mask_ipq8074,
+		.ring_mask = &ath11k_hw_ring_mask_ipq5018,
 		.regs = &ipq5018_regs,
 		.m3_addr = ATH11K_QMI_IPQ5018_M3_DUMP_ADDRESS,
 		.spectral_fft_sz = 2,
@@ -318,6 +322,7 @@ static const struct ath11k_hw_params ath
 		.is_qdss_support = false,
 		.max_tx_ring = DP_TCL_NUM_RING_MAX,
 		.wakeup_mhi = false,
+		.reo_status_poll = true,
 	},
 	{
 		.hw_rev = ATH11K_HW_QCN6122,
@@ -333,7 +338,7 @@ static const struct ath11k_hw_params ath
 		.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
 		.hw_ops = &qcn6122_ops,
 		.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122,
-		.ring_mask = &ath11k_hw_ring_mask_ipq8074,
+		.ring_mask = &ath11k_hw_ring_mask_ipq5018,
 		.regs = &qcn6122_regs,
 		.m3_addr = ATH11K_QMI_QCN6122_M3_DUMP_ADDRESS,
 		.spectral_fft_sz = 2,
@@ -370,6 +375,7 @@ static const struct ath11k_hw_params ath
  		.is_qdss_support = true,
 		.max_tx_ring = DP_TCL_NUM_RING_MAX,
 		.wakeup_mhi = false,
+		.reo_status_poll = true,
  	},
 };
 
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -367,12 +367,66 @@ void ath11k_dp_stop_shadow_timers(struct
 	ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer);
 }
 
+static void ath11k_dp_handle_reo_status_timer(struct timer_list *timer)
+{
+	struct ath11k_dp *dp = from_timer(dp, timer, reo_status_timer);
+	struct ath11k_base *ab = dp->ab;
+
+	spin_lock_bh(&dp->reo_cmd_lock);
+	dp->reo_status_timer_running = false;
+	spin_unlock_bh(&dp->reo_cmd_lock);
+
+	ath11k_dp_process_reo_status(ab);
+}
+
+void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab)
+{
+	struct ath11k_dp *dp = &ab->dp;
+
+	if (!ab->hw_params.reo_status_poll)
+		return;
+
+	spin_lock_bh(&dp->reo_cmd_lock);
+	if (dp->reo_status_timer_running) {
+		spin_unlock_bh(&dp->reo_cmd_lock);
+		return;
+	}
+	dp->reo_status_timer_running = true;
+	spin_unlock_bh(&dp->reo_cmd_lock);
+
+	mod_timer(&dp->reo_status_timer, jiffies +
+		  msecs_to_jiffies(ATH11K_REO_STATUS_POLL_TIMEOUT_MS));
+}
+
+static void ath11k_dp_stop_reo_status_timer(struct ath11k_base *ab)
+{
+	struct ath11k_dp *dp = &ab->dp;
+
+	if (!ab->hw_params.reo_status_poll)
+		return;
+
+	del_timer_sync(&dp->reo_status_timer);
+	dp->reo_status_timer_running = false;
+}
+
+static void ath11k_dp_init_reo_status_timer(struct ath11k_base *ab)
+{
+	struct ath11k_dp *dp = &ab->dp;
+
+	if (!ab->hw_params.reo_status_poll)
+		return;
+
+	timer_setup(&dp->reo_status_timer,
+		    ath11k_dp_handle_reo_status_timer, 0);
+}
+
 static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab)
 {
 	struct ath11k_dp *dp = &ab->dp;
 	int i;
 
 	ath11k_dp_stop_shadow_timers(ab);
+	ath11k_dp_stop_reo_status_timer(ab);
 	ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring);
 	ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring);
 	ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring);
@@ -394,6 +448,8 @@ static int ath11k_dp_srng_common_setup(s
 	int i, ret;
 	u32 ring_hash_map;
 
+	ath11k_dp_init_reo_status_timer(ab);
+
 	ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring,
 				   HAL_SW2WBM_RELEASE, 0, 0,
 				   DP_WBM_RELEASE_RING_SIZE);
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -44,6 +44,8 @@ struct dp_rx_tid {
 #define DP_MON_PURGE_TIMEOUT_MS     100
 #define DP_MON_SERVICE_BUDGET       128
 
+#define ATH11K_REO_STATUS_POLL_TIMEOUT_MS 10
+
 struct dp_reo_cache_flush_elem {
 	struct list_head list;
 	struct dp_rx_tid data;
@@ -293,6 +295,10 @@ struct ath11k_dp {
 	spinlock_t reo_cmd_lock;
 	struct ath11k_hp_update_timer reo_cmd_timer;
 	struct ath11k_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX];
+
+	/* reo status timer and flags */
+	struct timer_list reo_status_timer;
+	bool reo_status_timer_running;
 };
 
 /* HTT definitions */
@@ -1925,5 +1931,6 @@ void ath11k_dp_shadow_init_timer(struct
 				 struct ath11k_hp_update_timer *update_timer,
 				 u32 interval, u32 ring_id);
 void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab);
+void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab);
 
 #endif
--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
@@ -807,6 +807,10 @@ int ath11k_dp_tx_send_reo_cmd(struct ath
 	if (cmd_num == 0)
 		return -EINVAL;
 
+	/* Trigger reo status polling if required */
+	if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS)
+		ath11k_dp_start_reo_status_timer(ab);
+
 	if (!cb)
 		return 0;
 
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -1321,6 +1321,37 @@ const struct ath11k_hw_ring_mask ath11k_
 	},
 };
 
+const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq5018 = {
+	.tx  = {
+		ATH11K_TX_RING_MASK_0,
+		ATH11K_TX_RING_MASK_1,
+		ATH11K_TX_RING_MASK_2,
+	},
+	.rx_mon_status = {
+		0, 0, 0,
+		ATH11K_RX_MON_STATUS_RING_MASK_0,
+	},
+	.rx = {
+		0, 0, 0, 0,
+		ATH11K_RX_RING_MASK_0,
+		ATH11K_RX_RING_MASK_1,
+		ATH11K_RX_RING_MASK_2,
+		ATH11K_RX_RING_MASK_3,
+	},
+	.rx_err = {
+		ATH11K_RX_ERR_RING_MASK_0,
+	},
+	.rx_wbm_rel = {
+		ATH11K_RX_WBM_REL_RING_MASK_0,
+	},
+	.rxdma2host = {
+		ATH11K_RXDMA2HOST_RING_MASK_0,
+	},
+	.host2rxdma = {
+		ATH11K_HOST2RXDMA_RING_MASK_0,
+	},
+};
+
 /* Target firmware's Copy Engine configuration. */
 const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq8074[] = {
 	/* CE0: host->target HTC control and raw streams */
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -213,6 +213,7 @@ struct ath11k_hw_params {
 	u32 cfr_num_stream_bufs;
 	u32 cfr_stream_buf_size;
 	u8 max_tx_ring;
+	bool reo_status_poll;
 };
 
 struct ath11k_hw_ops {
@@ -283,6 +284,7 @@ extern const struct ath11k_hw_ops qcn612
 extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074;
 extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390;
 extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074;
+extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq5018;
 
 static inline
 int ath11k_hw_get_mac_from_pdev_id(struct ath11k_hw_params *hw,
