Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68766e4f authored by Gidon Studinski's avatar Gidon Studinski
Browse files

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: default avatarGidon Studinski <gidons@codeaurora.org>
parent 879cdf53
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -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
+41 −71
Original line number Diff line number Diff line
@@ -44,10 +44,15 @@ int ipa_enable_data_path(u32 clnt_hdl)
	}

	/* Enable the pipe */
	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 */
	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.
* 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_resume(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 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.
*
* Returns:	0 on success, negative on failure
*
* Note:	Should not be called from atomic context
*/
int ipa_suspend(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;
	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;
		}

	ep = &ipa_ctx->ep[clnt_hdl];

	if (ep->suspended) {
		IPAERR("EP already suspended. clnt_hdl %d\n", clnt_hdl);
		return -EPERM;
	}

	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);
+0 −2
Original line number Diff line number Diff line
@@ -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 ",
+21 −4
Original line number Diff line number Diff line
@@ -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;
+15 −4
Original line number Diff line number Diff line
@@ -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_ */
Loading