From 68766e4fe57bbf54240b6d3b8c90890cc6e330ff Mon Sep 17 00:00:00 2001
From: Gidon Studinski <gidons@codeaurora.org>
Date: Fri, 14 Mar 2014 08:24:30 +0200
Subject: [PATCH] msm: ipa: add support for power save

Adds support for power save feature in IPA driver. IPA clocks will be gated
when there are no clients connected to IPA.

Change-Id: I401cb84185c8c91e79f18a5baea340b644186298
Signed-off-by: Gidon Studinski <gidons@codeaurora.org>
---
 drivers/platform/msm/ipa/ipa.c         |  27 +++
 drivers/platform/msm/ipa/ipa_client.c  | 112 ++++------
 drivers/platform/msm/ipa/ipa_debugfs.c |   2 -
 drivers/platform/msm/ipa/ipa_dp.c      |  25 ++-
 drivers/platform/msm/ipa/ipa_i.h       |  19 +-
 drivers/platform/msm/ipa/ipa_utils.c   | 295 ++++++++++++++++++++++++-
 include/linux/ipa.h                    |  15 +-
 7 files changed, 395 insertions(+), 100 deletions(-)

diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 5639447ea335..5c185c8d181d 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -1597,6 +1597,33 @@ void ipa_inc_client_enable_clks(void)
 	mutex_unlock(&ipa_ctx->ipa_active_clients_lock);
 }
 
+/**
+* ipa_inc_client_enable_clks_no_block() - Only increment the number of active
+* clients if no asynchronous actions should be done. Asynchronous actions are
+* locking a mutex and waking up IPA HW.
+*
+* Return codes: 0 for success
+*		-EPERM if an asynchronous action should have been done
+*/
+int ipa_inc_client_enable_clks_no_block(void)
+{
+	int res = 0;
+
+	if (mutex_trylock(&ipa_ctx->ipa_active_clients_lock) == 0)
+		return -EPERM;
+	if (ipa_ctx->ipa_active_clients == 0) {
+		res = -EPERM;
+		goto bail;
+	}
+
+	ipa_ctx->ipa_active_clients++;
+	IPADBG("active clients = %d\n", ipa_ctx->ipa_active_clients);
+bail:
+	mutex_unlock(&ipa_ctx->ipa_active_clients_lock);
+
+	return res;
+}
+
 /**
 * ipa_dec_client_disable_clks() - Decrease active clients counter, and
 * disable ipa clocks if necessary
diff --git a/drivers/platform/msm/ipa/ipa_client.c b/drivers/platform/msm/ipa/ipa_client.c
index d6b531392175..031cf421bfbe 100644
--- a/drivers/platform/msm/ipa/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_client.c
@@ -44,10 +44,15 @@ int ipa_enable_data_path(u32 clnt_hdl)
 	}
 
 	/* Enable the pipe */
-	memset(&ep_cfg_ctrl, 0 , sizeof(ep_cfg_ctrl));
-	ep_cfg_ctrl.ipa_ep_suspend = false;
-
-	ipa_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+	if (IPA_CLIENT_IS_CONS(ep->client) &&
+	    (ep->keep_ipa_awake ||
+	     ep->resume_on_connect ||
+	     !ipa_should_pipe_be_suspended(ep->client))) {
+		memset(&ep_cfg_ctrl, 0 , sizeof(ep_cfg_ctrl));
+		ep_cfg_ctrl.ipa_ep_suspend = false;
+		ipa_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+		ep->resume_on_connect = false;
+	}
 
 	return res;
 }
@@ -74,10 +79,11 @@ int ipa_disable_data_path(u32 clnt_hdl)
 	}
 
 	/* Suspend the pipe */
-	memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl));
-	ep_cfg_ctrl.ipa_ep_suspend = true;
-
-	ipa_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+	if (IPA_CLIENT_IS_CONS(ep->client)) {
+		memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl));
+		ep_cfg_ctrl.ipa_ep_suspend = true;
+		ipa_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+	}
 
 	udelay(IPA_PKT_FLUSH_TO_US);
 	if (IPA_CLIENT_IS_CONS(ep->client) &&
@@ -222,6 +228,7 @@ int ipa_connect(const struct ipa_connect_params *in, struct ipa_sps_params *sps,
 	ep->client = in->client;
 	ep->client_notify = in->notify;
 	ep->priv = in->priv;
+	ep->keep_ipa_awake = in->keep_ipa_awake;
 
 	result = ipa_enable_data_path(ipa_ep_idx);
 	if (result) {
@@ -302,7 +309,11 @@ int ipa_connect(const struct ipa_connect_params *in, struct ipa_sps_params *sps,
 	if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->client))
 		ipa_install_dflt_flt_rules(ipa_ep_idx);
 
+	if (!ep->keep_ipa_awake)
+		ipa_dec_client_disable_clks();
+
 	ipa_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg;
+
 	IPADBG("client %d (ep: %d) connected\n", in->client, ipa_ep_idx);
 
 	return 0;
@@ -361,10 +372,8 @@ int ipa_disconnect(u32 clnt_hdl)
 
 	ep = &ipa_ctx->ep[clnt_hdl];
 
-	if (ep->suspended) {
+	if (!ep->keep_ipa_awake)
 		ipa_inc_client_enable_clks();
-		ep->suspended = false;
-	}
 
 	result = ipa_disable_data_path(clnt_hdl);
 	if (result) {
@@ -422,79 +431,40 @@ int ipa_disconnect(u32 clnt_hdl)
 EXPORT_SYMBOL(ipa_disconnect);
 
 /**
- * ipa_resume() - low-level IPA client resume
- * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
- *
- * Should be called by the driver of the peripheral that wants to resume IPA
- * connection. Resume IPA connection results in turning on IPA clocks in
- * case they were off as a result of suspend.
- * this api can be called only if a call to ipa_suspend() was
- * made.
- *
- * Returns:	0 on success, negative on failure
- *
- * Note:	Should not be called from atomic context
- */
-int ipa_resume(u32 clnt_hdl)
-{
-	struct ipa_ep_context *ep;
-
-	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
-		IPAERR("bad parm. clnt_hdl %d\n", clnt_hdl);
-		return -EINVAL;
-	}
-
-	ep = &ipa_ctx->ep[clnt_hdl];
-
-	if (!ep->suspended) {
-		IPAERR("EP not suspended. clnt_hdl %d\n", clnt_hdl);
-		return -EPERM;
-	}
-
-	ipa_inc_client_enable_clks();
-	ep->suspended = false;
-
-	return 0;
-}
-EXPORT_SYMBOL(ipa_resume);
-
-/**
-* ipa_suspend() - low-level IPA client suspend
-* @clnt_hdl:	[in] opaque client handle assigned by IPA to client
-*
-* Should be called by the driver of the peripheral that wants to suspend IPA
-* connection. Suspend IPA connection results in turning off IPA clocks in
-* case that there is no active clients using IPA. Pipes remains connected in
-* case of suspend.
+* ipa_reset_endpoint() - reset an endpoint from BAM perspective
+* @clnt_hdl: [in] IPA client handle
 *
 * Returns:	0 on success, negative on failure
 *
 * Note:	Should not be called from atomic context
 */
-int ipa_suspend(u32 clnt_hdl)
+int ipa_reset_endpoint(u32 clnt_hdl)
 {
+	int res;
 	struct ipa_ep_context *ep;
 
-	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
-		IPAERR("bad parm. clnt_hdl %d\n", clnt_hdl);
-		return -EINVAL;
+	if (clnt_hdl < 0 || clnt_hdl >= IPA_CLIENT_MAX) {
+		IPAERR("Bad parameters.\n");
+		return -EFAULT;
 	}
-
 	ep = &ipa_ctx->ep[clnt_hdl];
 
-	if (ep->suspended) {
-		IPAERR("EP already suspended. clnt_hdl %d\n", clnt_hdl);
-		return -EPERM;
+	ipa_inc_client_enable_clks();
+	res = sps_disconnect(ep->ep_hdl);
+	if (res) {
+		IPAERR("sps_disconnect() failed, res=%d.\n", res);
+		goto bail;
+	} else {
+		res = sps_connect(ep->ep_hdl, &ep->connect);
+		if (res) {
+			IPAERR("sps_connect() failed, res=%d.\n", res);
+			goto bail;
+		}
 	}
 
-	if (IPA_CLIENT_IS_CONS(ep->client) &&
-				ep->cfg.aggr.aggr_en == IPA_ENABLE_AGGR &&
-				ep->cfg.aggr.aggr_time_limit)
-		msleep(ep->cfg.aggr.aggr_time_limit);
-
+bail:
 	ipa_dec_client_disable_clks();
-	ep->suspended = true;
 
-	return 0;
+	return res;
 }
-EXPORT_SYMBOL(ipa_suspend);
+EXPORT_SYMBOL(ipa_reset_endpoint);
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
index 7381a32db226..37aa78156898 100644
--- a/drivers/platform/msm/ipa/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -624,7 +624,6 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count,
 {
 	int i;
 	int j;
-	int k;
 	struct ipa_flt_tbl *tbl;
 	struct ipa_flt_entry *entry;
 	enum ipa_ip_type ip = (enum ipa_ip_type)file->private_data;
@@ -681,7 +680,6 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count,
 				bitmap = entry->rule.attrib.attrib_mask;
 				eq = false;
 			}
-			k = ipa_get_client_mapping(j);
 			pr_info(
 				"ep_idx:%d rule_idx:%d act:%d rt_tbl_idx:%d "
 				"attrib_mask:%08x to_uc:%d, retain_hdr:%d eq:%d ",
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index c557ca28e113..29a408b9b315 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -922,26 +922,33 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
 
 	ep = &ipa_ctx->ep[ipa_ep_idx];
 
+	ipa_inc_client_enable_clks();
+
 	if (ep->valid == 1) {
 		if (sys_in->client != IPA_CLIENT_APPS_LAN_WAN_PROD) {
 			IPAERR("EP already allocated.\n");
-			goto fail_gen;
+			goto fail_and_disable_clocks;
 		} else {
 			if (ipa_cfg_ep_hdr(ipa_ep_idx,
 						&sys_in->ipa_ep_cfg.hdr)) {
 				IPAERR("fail to configure hdr prop of EP.\n");
-				return -EFAULT;
+				result = -EFAULT;
+				goto fail_and_disable_clocks;
 			}
 			if (ipa_cfg_ep_cfg(ipa_ep_idx,
 						&sys_in->ipa_ep_cfg.cfg)) {
 				IPAERR("fail to configure cfg prop of EP.\n");
-				return -EFAULT;
+				result = -EFAULT;
+				goto fail_and_disable_clocks;
 			}
 			IPADBG("client %d (ep: %d) overlay ok sys=%p\n",
 					sys_in->client, ipa_ep_idx, ep->sys);
 			ep->client_notify = sys_in->notify;
 			ep->priv = sys_in->priv;
 			*clnt_hdl = ipa_ep_idx;
+			if (!ep->keep_ipa_awake)
+				ipa_dec_client_disable_clks();
+
 			return 0;
 		}
 	}
@@ -952,7 +959,7 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
 	if (!ep->sys) {
 		IPAERR("failed to sys ctx for client %d\n", sys_in->client);
 		result = -ENOMEM;
-		goto fail_gen;
+		goto fail_and_disable_clocks;
 	}
 
 	snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipawq%d", sys_in->client);
@@ -975,6 +982,7 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
 	ep->client = sys_in->client;
 	ep->client_notify = sys_in->notify;
 	ep->priv = sys_in->priv;
+	ep->keep_ipa_awake = sys_in->keep_ipa_awake;
 	ep->avail_fifo_desc =
 		((sys_in->desc_fifo_sz/sizeof(struct sps_iovec))-1);
 	INIT_LIST_HEAD(&ep->sys->head_desc_list);
@@ -1081,6 +1089,9 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
 	if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(sys_in->client))
 		ipa_install_dflt_flt_rules(ipa_ep_idx);
 
+	if (!ep->keep_ipa_awake)
+		ipa_dec_client_disable_clks();
+
 	ipa_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg;
 	IPADBG("client %d (ep: %d) connected sys=%p\n", sys_in->client,
 			ipa_ep_idx, ep->sys);
@@ -1100,6 +1111,8 @@ fail_gen2:
 fail_wq:
 	kfree(ep->sys);
 	memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
+fail_and_disable_clocks:
+	ipa_dec_client_disable_clks();
 fail_gen:
 	return result;
 }
@@ -1120,6 +1133,8 @@ int ipa_teardown_sys_pipe(u32 clnt_hdl)
 		return -EINVAL;
 	}
 
+	ipa_inc_client_enable_clks();
+
 	ep = &ipa_ctx->ep[clnt_hdl];
 
 	if (IPA_CLIENT_IS_CONS(ep->client))
@@ -1136,6 +1151,8 @@ int ipa_teardown_sys_pipe(u32 clnt_hdl)
 	ipa_delete_dflt_flt_rules(clnt_hdl);
 	memset(ep, 0, sizeof(struct ipa_ep_context));
 
+	ipa_dec_client_disable_clks();
+
 	IPADBG("client (ep: %d) disconnected\n", clnt_hdl);
 
 	return 0;
diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h
index 024a9e890339..0e5e23d6ef97 100644
--- a/drivers/platform/msm/ipa/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_i.h
@@ -124,6 +124,12 @@
 	(((start_ofst) + 127) & ~127)
 #define IPA_RT_FLT_HW_RULE_BUF_SIZE	(128)
 
+#define MAX_RESOURCE_TO_CLIENTS (5)
+struct ipa_client_names {
+	enum ipa_client_type names[MAX_RESOURCE_TO_CLIENTS];
+	int length;
+};
+
 /**
  * struct ipa_mem_buffer - IPA memory buffer
  * @base: base
@@ -322,7 +328,6 @@ struct ipa_ep_cfg_status {
  * @data_fifo_pipe_mem_ofst: data FIFO pipe memory offset
  * @desc_fifo_client_allocated: if descriptors FIFO was allocated by a client
  * @data_fifo_client_allocated: if data FIFO was allocated by a client
- * @suspended: valid for B2B pipes, whether IPA EP is suspended
  * @skip_ep_cfg: boolean field that determines if EP should be configured
  *  by IPA driver
  * @keep_ipa_awake: when true, IPA will not be clock gated
@@ -346,13 +351,13 @@ struct ipa_ep_context {
 	u32 data_fifo_pipe_mem_ofst;
 	bool desc_fifo_client_allocated;
 	bool data_fifo_client_allocated;
-	bool suspended;
 	struct ipa_sys_context *sys;
 	u32 avail_fifo_desc;
 	u32 dflt_flt4_rule_hdl;
 	u32 dflt_flt6_rule_hdl;
 	bool skip_ep_cfg;
 	bool keep_ipa_awake;
+	bool resume_on_connect;
 };
 
 enum ipa_sys_pipe_policy {
@@ -818,7 +823,9 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
 int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
 		bool in_atomic);
 int ipa_get_ep_mapping(enum ipa_client_type client);
-int ipa_get_client_mapping(int pipe_idx);
+enum ipa_client_type ipa_get_client_mapping(int pipe_idx);
+enum ipa_rm_resource_name ipa_get_rm_resource_from_ep(int pipe_idx);
+
 int ipa_generate_hw_rule(enum ipa_ip_type ip,
 			 const struct ipa_rule_attrib *attrib,
 			 u8 **buf,
@@ -854,6 +861,7 @@ struct ipa_context *ipa_get_ctx(void);
 void ipa_enable_clks(void);
 void ipa_disable_clks(void);
 void ipa_inc_client_enable_clks(void);
+int ipa_inc_client_enable_clks_no_block(void);
 void ipa_dec_client_disable_clks(void);
 int ipa_interrupts_init(u32 ipa_irq, u32 ee, struct device *ipa_dev);
 int __ipa_del_rt_rule(u32 rule_hdl);
@@ -930,6 +938,9 @@ int ipa_set_required_perf_profile(enum ipa_voltage_level floor_voltage,
 
 int ipa_cfg_ep_status(u32 clnt_hdl, const struct ipa_ep_cfg_status *ipa_ep_cfg);
 
-
+int ipa_suspend_resource_no_block(enum ipa_rm_resource_name name);
+int ipa_suspend_resource_sync(enum ipa_rm_resource_name name);
+int ipa_resume_resource(enum ipa_rm_resource_name name);
+bool ipa_should_pipe_be_suspended(enum ipa_client_type client);
 
 #endif /* _IPA_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_utils.c b/drivers/platform/msm/ipa/ipa_utils.c
index e2b426bf9eeb..f01cf0566ede 100644
--- a/drivers/platform/msm/ipa/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_utils.c
@@ -223,6 +223,247 @@ static struct msm_bus_scale_pdata ipa_bus_client_pdata_v2_0 = {
 	.name = "ipa",
 };
 
+/**
+ * ipa_get_clients_from_rm_resource() - get IPA clients which are related to an
+ * IPA_RM resource
+ *
+ * @resource: [IN] IPA Resource Manager resource
+ * @clients: [OUT] Empty array which will contain the list of clients. The
+ *         caller must initialize this array.
+ *
+ * Return codes: 0 on success, negative on failure.
+ */
+int ipa_get_clients_from_rm_resource(
+	enum ipa_rm_resource_name resource,
+	struct ipa_client_names *clients)
+{
+	int i = 0;
+
+	if (resource < 0 ||
+	    resource >= IPA_RM_RESOURCE_MAX ||
+	    !clients) {
+		IPAERR("Bad parameters\n");
+		return -EINVAL;
+	}
+
+	switch (resource) {
+	case IPA_RM_RESOURCE_USB_CONS:
+		clients->names[i++] = IPA_CLIENT_USB_CONS;
+		clients->names[i++] = IPA_CLIENT_USB2_CONS;
+		clients->names[i++] = IPA_CLIENT_USB3_CONS;
+		clients->names[i++] = IPA_CLIENT_USB4_CONS;
+		clients->length = i;
+		break;
+	case IPA_RM_RESOURCE_WLAN_CONS:
+		clients->names[i++] = IPA_CLIENT_WLAN1_CONS;
+		clients->names[i++] = IPA_CLIENT_WLAN2_CONS;
+		clients->names[i++] = IPA_CLIENT_WLAN3_CONS;
+		clients->names[i++] = IPA_CLIENT_WLAN4_CONS;
+		clients->length = i;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * ipa_should_pipe_be_suspended() - returns true when the client's pipe should
+ * be suspended during a power save scenario. False otherwise.
+ *
+ * @client: [IN] IPA client
+ */
+bool ipa_should_pipe_be_suspended(enum ipa_client_type client)
+{
+	struct ipa_ep_context *ep;
+	int ipa_ep_idx;
+
+	ipa_ep_idx = ipa_get_ep_mapping(client);
+	if (ipa_ep_idx == -1) {
+		IPAERR("Invalid client.\n");
+		WARN_ON(1);
+		return false;
+	}
+
+	ep = &ipa_ctx->ep[ipa_ep_idx];
+
+	if (ep->keep_ipa_awake)
+		return false;
+
+	if (client == IPA_CLIENT_USB_CONS   ||
+	    client == IPA_CLIENT_USB2_CONS  ||
+	    client == IPA_CLIENT_USB3_CONS  ||
+	    client == IPA_CLIENT_USB4_CONS  ||
+	    client == IPA_CLIENT_WLAN1_CONS ||
+	    client == IPA_CLIENT_WLAN2_CONS ||
+	    client == IPA_CLIENT_WLAN3_CONS ||
+	    client == IPA_CLIENT_WLAN4_CONS)
+		return true;
+
+	return false;
+}
+
+/**
+ * ipa_suspend_resource_sync() - suspend client endpoints related to the IPA_RM
+ * resource and decrement active clients counter, which may result in clock
+ * gating of IPA clocks.
+ *
+ * @resource: [IN] IPA Resource Manager resource
+ *
+ * Return codes: 0 on success, negative on failure.
+ */
+int ipa_suspend_resource_sync(enum ipa_rm_resource_name resource)
+{
+	struct ipa_client_names clients;
+	int res;
+	int index;
+	struct ipa_ep_cfg_ctrl suspend;
+	enum ipa_client_type client;
+	int ipa_ep_idx;
+	bool pipe_suspended = false;
+
+	memset(&clients, 0, sizeof(clients));
+	res = ipa_get_clients_from_rm_resource(resource, &clients);
+	if (res) {
+		IPAERR("Bad params.\n");
+		return res;
+	}
+
+	for (index = 0; index < clients.length; index++) {
+		client = clients.names[index];
+		ipa_ep_idx = ipa_get_ep_mapping(client);
+		if (ipa_ep_idx == -1) {
+			IPAERR("Invalid client.\n");
+			res = -EINVAL;
+			continue;
+		}
+		if (ipa_should_pipe_be_suspended(client) &&
+		    ipa_ctx->ep[ipa_ep_idx].valid) {
+			/* suspend endpoint */
+			memset(&suspend, 0, sizeof(suspend));
+			suspend.ipa_ep_suspend = true;
+			ipa_cfg_ep_ctrl(ipa_ep_idx, &suspend);
+			pipe_suspended = true;
+		}
+	}
+	/* Sleep ~1 msec */
+	if (pipe_suspended)
+		usleep_range(1000, 2000);
+
+	ipa_dec_client_disable_clks();
+
+	return 0;
+}
+
+/**
+ * ipa_suspend_resource_no_block() - suspend client endpoints related to the
+ * IPA_RM resource and decrement active clients counter. This function is
+ * guaranteed to avoid sleeping.
+ *
+ * @resource: [IN] IPA Resource Manager resource
+ *
+ * Return codes: 0 on success, negative on failure.
+ */
+int ipa_suspend_resource_no_block(enum ipa_rm_resource_name resource)
+{
+	int res;
+	struct ipa_client_names clients;
+	int index;
+	enum ipa_client_type client;
+	struct ipa_ep_cfg_ctrl suspend;
+	int ipa_ep_idx;
+
+	if (mutex_trylock(&ipa_ctx->ipa_active_clients_lock) == 0)
+		return -EPERM;
+	if (ipa_ctx->ipa_active_clients == 1) {
+		res = -EPERM;
+		goto bail;
+	}
+
+	memset(&clients, 0, sizeof(clients));
+	res = ipa_get_clients_from_rm_resource(resource, &clients);
+	if (res) {
+		IPAERR("ipa_get_clients_from_rm_resource() failed, name = %d.\n"
+		       , resource);
+		goto bail;
+	}
+
+	for (index = 0; index < clients.length; index++) {
+		client = clients.names[index];
+		ipa_ep_idx = ipa_get_ep_mapping(client);
+		if (ipa_ep_idx == -1) {
+			IPAERR("Invalid client.\n");
+			res = -EINVAL;
+			continue;
+		}
+		if (ipa_should_pipe_be_suspended(client) &&
+		    ipa_ctx->ep[ipa_ep_idx].valid) {
+			/* suspend endpoint */
+			memset(&suspend, 0, sizeof(suspend));
+			suspend.ipa_ep_suspend = true;
+			ipa_cfg_ep_ctrl(ipa_ep_idx, &suspend);
+		}
+	}
+
+	if (res == 0) {
+		ipa_ctx->ipa_active_clients--;
+		IPADBG("active clients = %d\n", ipa_ctx->ipa_active_clients);
+	}
+bail:
+	mutex_unlock(&ipa_ctx->ipa_active_clients_lock);
+
+	return res;
+}
+
+/**
+ * ipa_resume_resource() - resume client endpoints related to the IPA_RM
+ * resource.
+ *
+ * @resource: [IN] IPA Resource Manager resource
+ *
+ * Return codes: 0 on success, negative on failure.
+ */
+int ipa_resume_resource(enum ipa_rm_resource_name resource)
+{
+
+	struct ipa_client_names clients;
+	int res;
+	int index;
+	struct ipa_ep_cfg_ctrl suspend;
+	enum ipa_client_type client;
+	int ipa_ep_idx;
+
+	memset(&clients, 0, sizeof(clients));
+	res = ipa_get_clients_from_rm_resource(resource, &clients);
+	if (res) {
+		IPAERR("ipa_get_clients_from_rm_resource() failed.\n");
+		return res;
+	}
+
+	for (index = 0; index < clients.length; index++) {
+		client = clients.names[index];
+		ipa_ep_idx = ipa_get_ep_mapping(client);
+		if (ipa_ep_idx == -1) {
+			IPAERR("Invalid client.\n");
+			res = -EINVAL;
+			continue;
+		}
+		if (ipa_should_pipe_be_suspended(client)) {
+			if (ipa_ctx->ep[ipa_ep_idx].valid) {
+				memset(&suspend, 0, sizeof(suspend));
+				suspend.ipa_ep_suspend = false;
+				ipa_cfg_ep_ctrl(ipa_ep_idx, &suspend);
+			} else {
+				ipa_ctx->ep[ipa_ep_idx].resume_on_connect =
+					true;
+			}
+		}
+	}
+
+	return res;
+}
+
 /* read how much SRAM is available for SW use
  * In case of IPAv2.0 this will also supply an offset from
  * which we can start write
@@ -430,7 +671,7 @@ int ipa_get_ep_mapping(enum ipa_client_type client)
 	u8 hw_type_index = IPA_1_1;
 
 	if (client >= IPA_CLIENT_MAX || client < 0) {
-		IPAERR("Bad client number!\n");
+		IPAERR("Bad client number! client =%d\n", client);
 		return -EINVAL;
 	}
 
@@ -442,30 +683,64 @@ int ipa_get_ep_mapping(enum ipa_client_type client)
 EXPORT_SYMBOL(ipa_get_ep_mapping);
 
 /**
- * ipa_get_client_mapping() - provide client mapping
- * @pipe_idx: IPA end-point number
+ * ipa_get_rm_resource_from_ep() - get the IPA_RM resource which is related to
+ * the supplied pipe index.
  *
- * Return value: client mapping
+ * @pipe_idx:
+ *
+ * Return value: IPA_RM resource related to the pipe, -1 if a resource was not
+ * found.
  */
-int ipa_get_client_mapping(int pipe_idx)
+enum ipa_rm_resource_name ipa_get_rm_resource_from_ep(int pipe_idx)
 {
 	int i;
-	u8 hw_type_index = IPA_1_1;
+	int j;
+	enum ipa_client_type client;
+	struct ipa_client_names clients;
+	bool found = false;
 
 	if (pipe_idx >= IPA_CLIENT_MAX || pipe_idx < 0) {
 		IPAERR("Bad pipe index!\n");
 		return -EINVAL;
 	}
 
-	if (ipa_ctx->ipa_hw_type == IPA_HW_v2_0)
-		hw_type_index = IPA_2_0;
+	client = ipa_ctx->ep[pipe_idx].client;
 
-	for (i = 0; i < IPA_CLIENT_MAX; i++)
-		if (ep_mapping[hw_type_index][i] == pipe_idx)
+	for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
+		memset(&clients, 0, sizeof(clients));
+		ipa_get_clients_from_rm_resource(i, &clients);
+		for (j = 0; j < clients.length; j++) {
+			if (clients.names[j] == client) {
+				found = true;
+				break;
+			}
+		}
+		if (found)
 			break;
+	}
+
+	if (!found)
+		return -EFAULT;
+
 	return i;
 }
 
+/**
+ * ipa_get_client_mapping() - provide client mapping
+ * @pipe_idx: IPA end-point number
+ *
+ * Return value: client mapping
+ */
+enum ipa_client_type ipa_get_client_mapping(int pipe_idx)
+{
+	if (pipe_idx >= IPA_CLIENT_MAX || pipe_idx < 0) {
+		IPAERR("Bad pipe index!\n");
+		return -EINVAL;
+	}
+
+	return ipa_ctx->ep[pipe_idx].client;
+}
+
 /**
  * ipa_write_32() - convert 32 bit value to byte array
  * @w: 32 bit integer
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index 326e04223ce2..2a06f46cb5f5 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -403,6 +403,7 @@ typedef void (*ipa_notify_cb)(void *priv, enum ipa_dp_evt_type evt,
  * @data:	data FIFO meta-data when client has allocated it
  * @skip_ep_cfg: boolean field that determines if EP should be configured
  *  by IPA driver
+ * @keep_ipa_awake: when true, IPA will not be clock gated
  */
 struct ipa_connect_params {
 	struct ipa_ep_cfg ipa_ep_cfg;
@@ -417,6 +418,7 @@ struct ipa_connect_params {
 	struct sps_mem_buffer desc;
 	struct sps_mem_buffer data;
 	bool skip_ep_cfg;
+	bool keep_ipa_awake;
 };
 
 /**
@@ -482,6 +484,7 @@ struct ipa_ext_intf {
  *		enum for valid cases.
  * @skip_ep_cfg: boolean field that determines if EP should be configured
  *  by IPA driver
+ * @keep_ipa_awake: when true, IPA will not be clock gated
  */
 struct ipa_sys_connect_params {
 	struct ipa_ep_cfg ipa_ep_cfg;
@@ -490,6 +493,7 @@ struct ipa_sys_connect_params {
 	void *priv;
 	ipa_notify_cb notify;
 	bool skip_ep_cfg;
+	bool keep_ipa_awake;
 };
 
 /**
@@ -739,9 +743,7 @@ int ipa_disconnect(u32 clnt_hdl);
 /*
  * Resume / Suspend
  */
-int ipa_resume(u32 clnt_hdl);
-
-int ipa_suspend(u32 clnt_hdl);
+int ipa_reset_endpoint(u32 clnt_hdl);
 
 /*
  * Configuration
@@ -968,12 +970,7 @@ static inline int ipa_disconnect(u32 clnt_hdl)
 /*
  * Resume / Suspend
  */
-static inline int ipa_resume(u32 clnt_hdl)
-{
-	return -EPERM;
-}
-
-static inline int ipa_suspend(u32 clnt_hdl)
+static inline int ipa_reset_endpoint(u32 clnt_hdl)
 {
 	return -EPERM;
 }
-- 
GitLab