ipa: use common ring event ring for WLAN MCC pipes in auto config

In IPA4.0 Auto configuration, all event rings are allocated for
different use cases and if we use different event rings for WLAN
MCC pipes event ring allocation fails resulting in WLAN enablement
failure. Make changes to use common event ring for WLAN MCC pipes.

Change-Id: I46a462a4ad1a9135b1fd28fbc627eb49017a11e3
Signed-off-by: Chaitanya Pratapa <cpratapa@codeaurora.org>
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index c38f1be..03e20005 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 2020, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -944,6 +944,9 @@
 	__gsi_config_gen_irq(props->ee, ~0,
 		~GSI_EE_n_CNTXT_GSI_IRQ_CLR_GSI_BREAK_POINT_BMSK);
 
+	gsi_ctx->shared_ch_info.ch_id = gsi_ctx->max_ch;
+	gsi_ctx->shared_ch_info.evchid = gsi_ctx->max_ev;
+
 	gsi_writel(props->intr, gsi_ctx->base +
 			GSI_EE_n_CNTXT_INTSET_OFFS(gsi_ctx->per.ee));
 
@@ -1801,7 +1804,8 @@
 
 		if (atomic_read(
 			&gsi_ctx->evtr[props->evt_ring_hdl].chan_ref_cnt) &&
-			gsi_ctx->evtr[props->evt_ring_hdl].props.exclusive) {
+			gsi_ctx->evtr[props->evt_ring_hdl].props.exclusive &&
+			!props->common_evt_ring) {
 			GSIERR("evt ring=%lu exclusively used by chan_hdl=%p\n",
 				props->evt_ring_hdl, chan_hdl);
 			return -GSI_STATUS_UNSUPPORTED_OP;
@@ -1870,6 +1874,10 @@
 	ctx->user_data = user_data;
 	*chan_hdl = props->ch_id;
 	ctx->allocated = true;
+	if ((props->evt_ring_hdl != ~0) && props->common_evt_ring) {
+		gsi_ctx->shared_ch_info.ch_id = props->ch_id;
+		gsi_ctx->shared_ch_info.evchid = props->evt_ring_hdl;
+	}
 	ctx->stats.dp.last_timestamp = jiffies_to_msecs(jiffies);
 	atomic_inc(&gsi_ctx->num_chan);
 
@@ -2375,6 +2383,11 @@
 		atomic_dec(&ctx->evtr->chan_ref_cnt);
 	atomic_dec(&gsi_ctx->num_chan);
 
+	if (gsi_ctx->shared_ch_info.ch_id == chan_hdl) {
+		gsi_ctx->shared_ch_info.ch_id = gsi_ctx->max_ch;
+		gsi_ctx->shared_ch_info.evchid = gsi_ctx->max_ev;
+	}
+
 	return GSI_STATUS_SUCCESS;
 }
 EXPORT_SYMBOL(gsi_dealloc_channel);
@@ -2751,6 +2764,7 @@
 int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode)
 {
 	struct gsi_chan_ctx *ctx;
+	struct gsi_chan_ctx *shared_ch_ctx;
 	enum gsi_chan_mode curr;
 	unsigned long flags;
 
@@ -2793,13 +2807,39 @@
 			mode == GSI_CHAN_MODE_POLL) {
 		__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, 0);
 		atomic_set(&ctx->poll_mode, mode);
+		if ((ctx->props.common_evt_ring) &&
+			(gsi_ctx->shared_ch_info.ch_id == chan_hdl)) {
+			atomic_set(&ctx->evtr->chan->poll_mode, mode);
+		} else if ((ctx->props.common_evt_ring) &&
+				gsi_ctx->shared_ch_info.evchid ==
+					ctx->evtr->id) {
+			shared_ch_ctx =
+				&gsi_ctx->chan[gsi_ctx->shared_ch_info.ch_id];
+			if (shared_ch_ctx != NULL)
+				atomic_set(&shared_ch_ctx->poll_mode, mode);
+		}
+		GSIDBG("set gsi_ctx evtr_id %d to %d mode\n",
+			ctx->evtr->id, mode);
 		ctx->stats.callback_to_poll++;
 	}
 
 	if (curr == GSI_CHAN_MODE_POLL &&
 			mode == GSI_CHAN_MODE_CALLBACK) {
 		atomic_set(&ctx->poll_mode, mode);
+		if ((ctx->props.common_evt_ring) &&
+			(gsi_ctx->shared_ch_info.ch_id == chan_hdl)) {
+			atomic_set(&ctx->evtr->chan->poll_mode, mode);
+		} else if ((ctx->props.common_evt_ring) &&
+				gsi_ctx->shared_ch_info.evchid ==
+				ctx->evtr->id) {
+			shared_ch_ctx =
+				&gsi_ctx->chan[gsi_ctx->shared_ch_info.ch_id];
+			if (shared_ch_ctx != NULL)
+				atomic_set(&shared_ch_ctx->poll_mode, mode);
+		}
 		__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, ~0);
+		GSIDBG("set gsi_ctx evtr_id %d to %d mode\n",
+			ctx->evtr->id, mode);
 		ctx->stats.poll_to_callback++;
 	}
 	spin_unlock_irqrestore(&gsi_ctx->slock, flags);
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index 4a3f4ec..9bc7be9 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 2020, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -190,6 +190,11 @@
 	unsigned long halt_channel;
 };
 
+struct gsi_shared_chan_info {
+	uint8_t ch_id;
+	uint8_t evchid;
+};
+
 struct gsi_ctx {
 	void __iomem *base;
 	struct device *dev;
@@ -213,6 +218,8 @@
 	struct completion gen_ee_cmd_compl;
 	void *ipc_logbuf;
 	void *ipc_logbuf_low;
+	struct gsi_shared_chan_info shared_ch_info;
+
 	/*
 	 * The following used only on emulation systems.
 	 */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index c9c6281..3a7b25d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -102,11 +102,14 @@
 		struct ipa3_tx_pkt_wrapper *tx_pkt,
 		struct ipahal_imm_cmd_pyld **tag_pyld_ret);
 static int ipa_poll_gsi_pkt(struct ipa3_sys_context *sys,
-	struct ipa_mem_buffer *mem_info);
+	struct ipa_mem_buffer *mem_info,
+	struct gsi_chan_xfer_notify *xfer_notify);
 static unsigned long tag_to_pointer_wa(uint64_t tag);
 static uint64_t pointer_to_tag_wa(struct ipa3_tx_pkt_wrapper *tx_pkt);
 
 static u32 ipa_adjust_ra_buff_base_sz(u32 aggr_byte_limit);
+static bool ipa_update_common_evt_ring(enum ipa_client_type src,
+	enum ipa_client_type dst);
 
 static void ipa3_wq_write_done_common(struct ipa3_sys_context *sys,
 				struct ipa3_tx_pkt_wrapper *tx_pkt)
@@ -710,20 +713,24 @@
 	int ret;
 	int cnt = 0;
 	struct ipa_mem_buffer mem_info = { 0 };
+	struct gsi_chan_xfer_notify notify;
 
+	memset(&notify, 0, sizeof(struct gsi_chan_xfer_notify));
 	while ((in_poll_state ? atomic_read(&sys->curr_polling_state) :
 		!atomic_read(&sys->curr_polling_state))) {
 		if (cnt && !process_all)
 			break;
 
-		ret = ipa_poll_gsi_pkt(sys, &mem_info);
+		ret = ipa_poll_gsi_pkt(sys, &mem_info, &notify);
 		if (ret)
 			break;
 
 		if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(sys->ep->client))
 			ipa3_dma_memcpy_notify(sys, &mem_info);
 		else if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
-			ipa3_wlan_wq_rx_common(sys, mem_info.size);
+			ipa3_wlan_wq_rx_common(
+			(struct ipa3_sys_context *)(notify.chan_user_data),
+			mem_info.size);
 		else
 			ipa3_wq_rx_common(sys, mem_info.size);
 
@@ -738,6 +745,8 @@
 static void ipa3_rx_switch_to_intr_mode(struct ipa3_sys_context *sys)
 {
 	int ret;
+	u32 wlan_ep_idx;
+	struct ipa3_ep_context *wlan_ep;
 
 	if (!atomic_read(&sys->curr_polling_state)) {
 		IPAERR("already in intr mode\n");
@@ -751,6 +760,16 @@
 		IPAERR("Failed to switch to intr mode.\n");
 		goto fail;
 	}
+	if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client)) {
+		wlan_ep_idx = ipa3_get_ep_mapping(
+			((sys->ep->client == IPA_CLIENT_WLAN2_CONS) ?
+			IPA_CLIENT_WLAN3_CONS : IPA_CLIENT_WLAN2_CONS));
+		if (wlan_ep_idx != IPA_EP_NOT_ALLOCATED &&
+			ipa3_ctx->ep[wlan_ep_idx].valid == 1) {
+			wlan_ep = &ipa3_ctx->ep[wlan_ep_idx];
+			atomic_set(&wlan_ep->sys->curr_polling_state, 0);
+		}
+	}
 	return;
 
 fail:
@@ -1182,6 +1201,12 @@
 		ipa3_ctx->gsi_evt_comm_ring_rem +=
 			ep->gsi_mem_info.chan_ring_len;
 	} else if (ep->gsi_evt_ring_hdl != ~0) {
+		/* common event ring is used for WLAN2/WLAN3 pipes */
+		if (IPA_IS_4_0_AUTO_CONFIG() &&
+			ep->client == IPA_CLIENT_WLAN2_CONS) {
+			/* Skip resetting the channel. */
+			goto teardown;
+		}
 		result = gsi_reset_evt_ring(ep->gsi_evt_ring_hdl);
 		if (result != GSI_STATUS_SUCCESS) {
 			IPAERR("Failed to reset evt ring: %d.\n",
@@ -1201,6 +1226,8 @@
 			return result;
 		}
 	}
+
+teardown:
 	if (ep->sys->repl_wq)
 		flush_workqueue(ep->sys->repl_wq);
 	if (IPA_CLIENT_IS_CONS(ep->client))
@@ -3412,8 +3439,21 @@
 void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys)
 {
 	bool clk_off;
+	u32 wlan_ep_idx;
+	struct ipa3_ep_context *wlan_ep;
 
 	atomic_set(&sys->curr_polling_state, 1);
+	if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client)) {
+		wlan_ep_idx = ipa3_get_ep_mapping(
+			((sys->ep->client == IPA_CLIENT_WLAN2_CONS) ?
+			IPA_CLIENT_WLAN3_CONS : IPA_CLIENT_WLAN2_CONS));
+		if (wlan_ep_idx != IPA_EP_NOT_ALLOCATED &&
+			ipa3_ctx->ep[wlan_ep_idx].valid == 1) {
+			wlan_ep = &ipa3_ctx->ep[wlan_ep_idx];
+			/* Set the polling state. */
+			atomic_set(&wlan_ep->sys->curr_polling_state, 1);
+		}
+	}
 	ipa3_inc_acquire_wakelock();
 
 	/*
@@ -3463,11 +3503,7 @@
 					   struct ipa3_rx_pkt_wrapper, link);
 	rx_pkt_rcvd = (struct ipa3_rx_pkt_wrapper *)notify->xfer_user_data;
 
-	if (rx_pkt_expected != rx_pkt_rcvd) {
-		IPAERR("Pkt was not filled in head of rx buffer.\n");
-		WARN_ON(1);
-		return;
-	}
+	sys->ep->xfer_notify = *notify;
 	sys->ep->bytes_xfered_valid = true;
 	sys->ep->bytes_xfered = notify->bytes_xfered;
 	sys->ep->phys_base = rx_pkt_rcvd->data.dma_addr;
@@ -3534,6 +3570,26 @@
 	}
 }
 
+static bool ipa_update_common_evt_ring(enum ipa_client_type src,
+	enum ipa_client_type dst)
+{
+	u32 src_ep_idx, dst_ep_idx;
+
+	src_ep_idx = ipa3_get_ep_mapping(src);
+	dst_ep_idx = ipa3_get_ep_mapping(dst);
+	if (src_ep_idx != IPA_EP_NOT_ALLOCATED &&
+		ipa3_ctx->ep[src_ep_idx].valid == 1 &&
+		dst_ep_idx != IPA_EP_NOT_ALLOCATED &&
+		ipa3_ctx->ep[dst_ep_idx].valid == 1) {
+		/* copy event ring handle */
+		ipa3_ctx->ep[dst_ep_idx].gsi_evt_ring_hdl =
+			ipa3_ctx->ep[src_ep_idx].gsi_evt_ring_hdl;
+		return true;
+	}
+	return false;
+}
+
+
 int ipa3_alloc_common_event_ring(void)
 {
 	struct gsi_evt_ring_props gsi_evt_ring_props;
@@ -3611,6 +3667,13 @@
 		ep->gsi_evt_ring_hdl = ipa3_ctx->gsi_evt_comm_hdl;
 	} else if (ep->sys->policy != IPA_POLICY_NOINTR_MODE ||
 	     IPA_CLIENT_IS_CONS(ep->client)) {
+		/* Use common event ring in auto config for WLAN2/WLAN3 pipes */
+		if (IPA_IS_4_0_AUTO_CONFIG() &&
+			(ep->client == IPA_CLIENT_WLAN3_CONS) &&
+			ipa_update_common_evt_ring(IPA_CLIENT_WLAN2_CONS,
+				ep->client)) {
+			goto setup_channel;
+		}
 		gsi_evt_ring_props.intf = GSI_EVT_CHTYPE_GPI_EV;
 		gsi_evt_ring_props.intr = GSI_INTR_IRQ;
 		gsi_evt_ring_props.re_size =
@@ -3623,6 +3686,14 @@
 		 */
 		gsi_evt_ring_props.ring_len = 2 * in->desc_fifo_sz;
 
+		/* In Auto config, common event ring is used for WLAN sys pipes.
+		 * Double the event ring size.
+		 */
+		if (IPA_IS_4_0_AUTO_CONFIG() &&
+		    IPA_CLIENT_IS_WLAN_CONS(ep->client))
+			gsi_evt_ring_props.ring_len =
+				2 * gsi_evt_ring_props.ring_len;
+
 		gsi_evt_ring_props.ring_base_vaddr =
 			dma_alloc_coherent(ipa3_ctx->pdev,
 			gsi_evt_ring_props.ring_len,
@@ -3659,6 +3730,7 @@
 			goto fail_alloc_evt_ring;
 	}
 
+setup_channel:
 	memset(&gsi_channel_props, 0, sizeof(gsi_channel_props));
 	gsi_channel_props.prot = GSI_CHAN_PROT_GPI;
 	if (IPA_CLIENT_IS_PROD(ep->client)) {
@@ -3731,6 +3803,12 @@
 		gsi_channel_props.xfer_cb = ipa_gsi_irq_rx_notify_cb;
 	if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(ep->client))
 		gsi_channel_props.xfer_cb = ipa_dma_gsi_irq_rx_notify_cb;
+
+	/* In Auto config, common event ring is used for WLAN sys pipes.*/
+	if (IPA_IS_4_0_AUTO_CONFIG() &&
+	    IPA_CLIENT_IS_WLAN_CONS(ep->client))
+		gsi_channel_props.common_evt_ring = true;
+
 	result = gsi_alloc_channel(&gsi_channel_props, ipa3_ctx->gsi_dev_hdl,
 		&ep->gsi_chan_hdl);
 	if (result != GSI_STATUS_SUCCESS)
@@ -3814,21 +3892,22 @@
 }
 
 static int ipa_poll_gsi_pkt(struct ipa3_sys_context *sys,
-		struct ipa_mem_buffer *mem_info)
+		struct ipa_mem_buffer *mem_info,
+		struct gsi_chan_xfer_notify *xfer_notify)
 {
 	int ret;
-	struct gsi_chan_xfer_notify xfer_notify;
 	struct ipa3_rx_pkt_wrapper *rx_pkt;
 
 	if (sys->ep->bytes_xfered_valid) {
 		mem_info->phys_base = sys->ep->phys_base;
 		mem_info->size = (u32)sys->ep->bytes_xfered;
 		sys->ep->bytes_xfered_valid = false;
+		*xfer_notify = sys->ep->xfer_notify;
 		return GSI_STATUS_SUCCESS;
 	}
 
 	ret = gsi_poll_channel(sys->ep->gsi_chan_hdl,
-		&xfer_notify);
+		xfer_notify);
 	if (ret == GSI_STATUS_POLL_EMPTY)
 		return ret;
 	else if (ret != GSI_STATUS_SUCCESS) {
@@ -3837,9 +3916,9 @@
 	}
 
 	rx_pkt = (struct ipa3_rx_pkt_wrapper *)
-		xfer_notify.xfer_user_data;
+		(xfer_notify->xfer_user_data);
 	mem_info->phys_base = rx_pkt->data.dma_addr;
-	mem_info->size = xfer_notify.bytes_xfered;
+	mem_info->size = xfer_notify->bytes_xfered;
 
 	return ret;
 }
@@ -3861,6 +3940,7 @@
 	struct ipa_mem_buffer mem_info = {0};
 	static int total_cnt;
 	struct ipa_active_client_logging_info log;
+	struct gsi_chan_xfer_notify notify;
 
 	IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log, "NAPI");
 
@@ -3869,14 +3949,14 @@
 		IPAERR("bad parm 0x%x\n", clnt_hdl);
 		return cnt;
 	}
-
+	memset(&notify, 0, sizeof(struct gsi_chan_xfer_notify));
 	ep = &ipa3_ctx->ep[clnt_hdl];
 
 	while (cnt < weight &&
 		   atomic_read(&ep->sys->curr_polling_state)) {
 
 		atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1);
-		ret = ipa_poll_gsi_pkt(ep->sys, &mem_info);
+		ret = ipa_poll_gsi_pkt(ep->sys, &mem_info, &notify);
 		if (ret)
 			break;
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index c876938..277b637 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -385,6 +385,10 @@
 #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
 #define TZ_MEM_PROTECT_REGION_ID 0x10
 
+#define IPA_IS_4_0_AUTO_CONFIG() \
+	((ipa3_ctx->ipa_hw_type == IPA_HW_v4_0) && \
+	(ipa3_ctx->ipa_config_is_auto))
+
 struct ipa3_active_client_htable_entry {
 	struct hlist_node list;
 	char id_string[IPA3_ACTIVE_CLIENTS_LOG_NAME_LEN];
@@ -728,6 +732,7 @@
  * @gsi_evt_ring_hdl: EP's GSI channel event ring handle
  * @gsi_mem_info: EP's GSI channel rings info
  * @chan_scratch: EP's GSI channel scratch info
+ * @xfer_notify: transfer element
  * @cfg: EP cionfiguration
  * @dst_pipe_index: destination pipe index
  * @rt_tbl_idx: routing table index
@@ -752,6 +757,7 @@
 	unsigned long gsi_evt_ring_hdl;
 	struct ipa_gsi_ep_mem_info gsi_mem_info;
 	union __packed gsi_channel_scratch chan_scratch;
+	struct gsi_chan_xfer_notify xfer_notify;
 	bool bytes_xfered_valid;
 	u16 bytes_xfered;
 	dma_addr_t phys_base;
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index 79e7daa..0b85d3b 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 2020, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -352,7 +352,7 @@
  *
  * @err_cb:          error notification callback
  * @chan_user_data:  cookie used for notifications
- *
+ * @common_evt_ring: Boolean indicating common event ring.
  * All the callbacks are in interrupt context
  *
  */
@@ -373,6 +373,7 @@
 	void (*xfer_cb)(struct gsi_chan_xfer_notify *notify);
 	void (*err_cb)(struct gsi_chan_err_notify *notify);
 	void *chan_user_data;
+	bool common_evt_ring;
 };
 
 enum gsi_xfer_flag {