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

Commit 242952bb authored by Skylar Chang's avatar Skylar Chang
Browse files

msm: ipa: optimizations to enable / disable clocks



Change the way IPA driver maintain the reference count for IPA
clock vote to be based on atomic variables to allow atomic
vote for IPA clocks if clocks are on.

CRs-Fixed: 2079976
Change-Id: If173bd0e21c70a4fb166ea0fb5763d3293c23e8f
Acked-by: default avatarAdy Abraham <adya@qti.qualcomm.com>
Signed-off-by: default avatarSkylar Chang <chiaweic@codeaurora.org>
parent 411a5ecc
Loading
Loading
Loading
Loading
+118 −66
Original line number Diff line number Diff line
@@ -235,6 +235,10 @@ static void ipa_gsi_notify_cb(struct gsi_per_notify *notify);
static void ipa3_post_init_wq(struct work_struct *work);
static DECLARE_WORK(ipa3_post_init_work, ipa3_post_init_wq);

static void ipa_dec_clients_disable_clks_on_wq(struct work_struct *work);
static DECLARE_WORK(ipa_dec_clients_disable_clks_on_wq_work,
	ipa_dec_clients_disable_clks_on_wq);

static struct ipa3_plat_drv_res ipa3_res = {0, };
struct msm_bus_scale_pdata *ipa3_bus_scale_table;

@@ -314,7 +318,7 @@ int ipa3_active_clients_log_print_table(char *buf, int size)
	}
	cnt += scnprintf(buf + cnt, size - cnt,
			"\nTotal active clients count: %d\n",
			ipa3_ctx->ipa3_active_clients.cnt);
			atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));

	return cnt;
}
@@ -322,11 +326,11 @@ int ipa3_active_clients_log_print_table(char *buf, int size)
static int ipa3_active_clients_panic_notifier(struct notifier_block *this,
		unsigned long event, void *ptr)
{
	ipa3_active_clients_lock();
	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	ipa3_active_clients_log_print_table(active_clients_table_buf,
			IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE);
	IPAERR("%s", active_clients_table_buf);
	ipa3_active_clients_unlock();
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);

	return NOTIFY_DONE;
}
@@ -395,11 +399,11 @@ static int ipa3_active_clients_log_init(void)

void ipa3_active_clients_log_clear(void)
{
	ipa3_active_clients_lock();
	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	ipa3_ctx->ipa3_active_clients_logging.log_head = 0;
	ipa3_ctx->ipa3_active_clients_logging.log_tail =
			IPA3_ACTIVE_CLIENTS_LOG_BUFFER_SIZE_LINES - 1;
	ipa3_active_clients_unlock();
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
}

static void ipa3_active_clients_log_destroy(void)
@@ -3267,7 +3271,6 @@ void _ipa_enable_clks_v3_0(void)
	}

	ipa3_uc_notify_clk_state(true);
	ipa3_suspend_apps_pipes(false);
}

static unsigned int ipa3_get_bus_vote(void)
@@ -3466,13 +3469,33 @@ void ipa3_active_clients_log_inc(struct ipa_active_client_logging_info *id,
*/
void ipa3_inc_client_enable_clks(struct ipa_active_client_logging_info *id)
{
	ipa3_active_clients_lock();
	int ret;

	ipa3_active_clients_log_inc(id, false);
	ipa3_ctx->ipa3_active_clients.cnt++;
	if (ipa3_ctx->ipa3_active_clients.cnt == 1)
	ret = atomic_inc_not_zero(&ipa3_ctx->ipa3_active_clients.cnt);
	if (ret) {
		IPADBG_LOW("active clients = %d\n",
			atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
		return;
	}

	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);

	/* somebody might voted to clocks meanwhile */
	ret = atomic_inc_not_zero(&ipa3_ctx->ipa3_active_clients.cnt);
	if (ret) {
		mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
		IPADBG_LOW("active clients = %d\n",
			atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
		return;
	}

	ipa3_enable_clks();
	IPADBG_LOW("active clients = %d\n", ipa3_ctx->ipa3_active_clients.cnt);
	ipa3_active_clients_unlock();
	atomic_inc(&ipa3_ctx->ipa3_active_clients.cnt);
	IPADBG_LOW("active clients = %d\n",
		atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
	ipa3_suspend_apps_pipes(false);
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
}

/**
@@ -3486,23 +3509,57 @@ void ipa3_inc_client_enable_clks(struct ipa_active_client_logging_info *id)
int ipa3_inc_client_enable_clks_no_block(struct ipa_active_client_logging_info
		*id)
{
	int res = 0;
	unsigned long flags;
	int ret;

	ret = atomic_inc_not_zero(&ipa3_ctx->ipa3_active_clients.cnt);
	if (ret) {
		ipa3_active_clients_log_inc(id, true);
		IPADBG_LOW("active clients = %d\n",
			atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
		return 0;
	}

	if (ipa3_active_clients_trylock(&flags) == 0)
	return -EPERM;
}

static void __ipa3_dec_client_disable_clks(void)
{
	int ret;

	if (!atomic_read(&ipa3_ctx->ipa3_active_clients.cnt)) {
		IPAERR("trying to disable clocks with refcnt is 0!\n");
		ipa_assert();
		return;
	}

	if (ipa3_ctx->ipa3_active_clients.cnt == 0) {
		res = -EPERM;
	ret = atomic_add_unless(&ipa3_ctx->ipa3_active_clients.cnt, -1, 1);
	if (ret)
		goto bail;

	/* seems like this is the only client holding the clocks */
	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) == 1 &&
	    ipa3_ctx->tag_process_before_gating) {
		ipa3_ctx->tag_process_before_gating = false;
		/*
		 * When TAG process ends, active clients will be
		 * decreased
		 */
		queue_work(ipa3_ctx->power_mgmt_wq, &ipa3_tag_work);
		goto unlock_mutex;
	}
	ipa3_active_clients_log_inc(id, true);
	ipa3_ctx->ipa3_active_clients.cnt++;
	IPADBG_LOW("active clients = %d\n", ipa3_ctx->ipa3_active_clients.cnt);
bail:
	ipa3_active_clients_trylock_unlock(&flags);

	return res;
	/* a different context might increase the clock reference meanwhile */
	ret = atomic_sub_return(1, &ipa3_ctx->ipa3_active_clients.cnt);
	if (ret > 0)
		goto unlock_mutex;
	ipa3_disable_clks();

unlock_mutex:
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
bail:
	IPADBG_LOW("active clients = %d\n",
		atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
}

/**
@@ -3518,29 +3575,39 @@ int ipa3_inc_client_enable_clks_no_block(struct ipa_active_client_logging_info
 */
void ipa3_dec_client_disable_clks(struct ipa_active_client_logging_info *id)
{
	struct ipa_active_client_logging_info log_info;

	ipa3_active_clients_lock();
	ipa3_active_clients_log_dec(id, false);
	ipa3_ctx->ipa3_active_clients.cnt--;
	IPADBG_LOW("active clients = %d\n", ipa3_ctx->ipa3_active_clients.cnt);
	if (ipa3_ctx->ipa3_active_clients.cnt == 0) {
		if (ipa3_ctx->tag_process_before_gating) {
			ipa3_ctx->tag_process_before_gating = false;
			/*
			 * When TAG process ends, active clients will be
			 * decreased
			 */
			IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info,
					"TAG_PROCESS");
			ipa3_active_clients_log_inc(&log_info, false);
			ipa3_ctx->ipa3_active_clients.cnt = 1;
			queue_work(ipa3_ctx->power_mgmt_wq, &ipa3_tag_work);
		} else {
			ipa3_disable_clks();
	__ipa3_dec_client_disable_clks();
}

static void ipa_dec_clients_disable_clks_on_wq(struct work_struct *work)
{
	__ipa3_dec_client_disable_clks();
}
	ipa3_active_clients_unlock();

/**
 * ipa3_dec_client_disable_clks_no_block() - Decrease active clients counter
 * if possible without blocking. If this is the last client then the desrease
 * will happen from work queue context.
 *
 * Return codes:
 * None
 */
void ipa3_dec_client_disable_clks_no_block(
	struct ipa_active_client_logging_info *id)
{
	int ret;

	ipa3_active_clients_log_dec(id, true);
	ret = atomic_add_unless(&ipa3_ctx->ipa3_active_clients.cnt, -1, 1);
	if (ret) {
		IPADBG_LOW("active clients = %d\n",
			atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
		return;
	}

	/* seems like this is the only client holding the clocks */
	queue_work(ipa3_ctx->power_mgmt_wq,
		&ipa_dec_clients_disable_clks_on_wq_work);
}

/**
@@ -3636,34 +3703,20 @@ int ipa3_set_required_perf_profile(enum ipa_voltage_level floor_voltage,
		return 0;
	}

	ipa3_active_clients_lock();
	/* Hold the mutex to avoid race conditions with ipa3_enable_clocks() */
	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	ipa3_ctx->curr_ipa_clk_rate = clk_rate;
	IPADBG_LOW("setting clock rate to %u\n", ipa3_ctx->curr_ipa_clk_rate);
	if (ipa3_ctx->ipa3_active_clients.cnt > 0) {
		struct ipa_active_client_logging_info log_info;

		/*
		 * clk_set_rate should be called with unlocked lock to allow
		 * clients to get a reference to IPA clock synchronously.
		 * Hold a reference to IPA clock here to make sure clock
		 * state does not change during set_rate.
		 */
		IPA_ACTIVE_CLIENTS_PREP_SIMPLE(log_info);
		ipa3_ctx->ipa3_active_clients.cnt++;
		ipa3_active_clients_log_inc(&log_info, false);
		ipa3_active_clients_unlock();

	if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) > 0) {
		if (ipa3_clk)
			clk_set_rate(ipa3_clk, ipa3_ctx->curr_ipa_clk_rate);
		if (msm_bus_scale_client_update_request(ipa3_ctx->ipa_bus_hdl,
				ipa3_get_bus_vote()))
			WARN_ON(1);
		/* remove the vote added here */
		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
	} else {
		IPADBG_LOW("clocks are gated, not setting rate\n");
		ipa3_active_clients_unlock();
	}
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
	IPADBG_LOW("Done\n");

	return 0;
@@ -4625,10 +4678,9 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
	}

	mutex_init(&ipa3_ctx->ipa3_active_clients.mutex);
	spin_lock_init(&ipa3_ctx->ipa3_active_clients.spinlock);
	IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE");
	ipa3_active_clients_log_inc(&log_info, false);
	ipa3_ctx->ipa3_active_clients.cnt = 1;
	atomic_set(&ipa3_ctx->ipa3_active_clients.cnt, 1);

	/* Create workqueues for power management */
	ipa3_ctx->power_mgmt_wq =
+6 −6
Original line number Diff line number Diff line
@@ -363,14 +363,14 @@ static ssize_t ipa3_read_keep_awake(struct file *file, char __user *ubuf,
{
	int nbytes;

	ipa3_active_clients_lock();
	if (ipa3_ctx->ipa3_active_clients.cnt)
	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt))
		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
				"IPA APPS power state is ON\n");
	else
		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
				"IPA APPS power state is OFF\n");
	ipa3_active_clients_unlock();
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);

	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
}
@@ -1064,7 +1064,7 @@ static ssize_t ipa3_read_stats(struct file *file, char __user *ubuf,
		ipa3_ctx->stats.stat_compl,
		ipa3_ctx->stats.aggr_close,
		ipa3_ctx->stats.wan_aggr_close,
		ipa3_ctx->ipa3_active_clients.cnt,
		atomic_read(&ipa3_ctx->ipa3_active_clients.cnt),
		connect,
		ipa3_ctx->stats.wan_rx_empty,
		ipa3_ctx->stats.wan_repl_rx_empty,
@@ -1785,12 +1785,12 @@ static ssize_t ipa3_print_active_clients_log(struct file *file,
		return 0;
	}
	memset(active_clients_buf, 0, IPA_DBG_ACTIVE_CLIENT_BUF_SIZE);
	ipa3_active_clients_lock();
	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	cnt = ipa3_active_clients_log_print_buffer(active_clients_buf,
			IPA_DBG_ACTIVE_CLIENT_BUF_SIZE - IPA_MAX_MSG_LEN);
	table_size = ipa3_active_clients_log_print_table(active_clients_buf
			+ cnt, IPA_MAX_MSG_LEN);
	ipa3_active_clients_unlock();
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);

	return simple_read_from_buffer(ubuf, count, ppos,
			active_clients_buf, cnt + table_size);
+3 −6
Original line number Diff line number Diff line
@@ -850,9 +850,7 @@ struct ipa3_stats {

struct ipa3_active_clients {
	struct mutex mutex;
	spinlock_t spinlock;
	bool mutex_locked;
	int cnt;
	atomic_t cnt;
};

struct ipa3_wakelock_ref_cnt {
@@ -1851,6 +1849,8 @@ void ipa3_inc_client_enable_clks(struct ipa_active_client_logging_info *id);
int ipa3_inc_client_enable_clks_no_block(struct ipa_active_client_logging_info
		*id);
void ipa3_dec_client_disable_clks(struct ipa_active_client_logging_info *id);
void ipa3_dec_client_disable_clks_no_block(
	struct ipa_active_client_logging_info *id);
void ipa3_active_clients_log_dec(struct ipa_active_client_logging_info *id,
		bool int_ctx);
void ipa3_active_clients_log_inc(struct ipa_active_client_logging_info *id,
@@ -1927,10 +1927,7 @@ int ipa3_resume_resource(enum ipa_rm_resource_name name);
bool ipa3_should_pipe_be_suspended(enum ipa_client_type client);
int ipa3_tag_aggr_force_close(int pipe_num);

void ipa3_active_clients_lock(void);
int ipa3_active_clients_trylock(unsigned long *flags);
void ipa3_active_clients_unlock(void);
void ipa3_active_clients_trylock_unlock(unsigned long *flags);
int ipa3_wdi_init(void);
int ipa3_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id);
int ipa3_tag_process(struct ipa3_desc *desc, int num_descs,
+4 −10
Original line number Diff line number Diff line
@@ -322,27 +322,21 @@ static void ipa3_interrupt_defer(struct work_struct *work)

static irqreturn_t ipa3_isr(int irq, void *ctxt)
{
	unsigned long flags;
	struct ipa_active_client_logging_info log_info;

	IPA_ACTIVE_CLIENTS_PREP_SIMPLE(log_info);
	IPADBG_LOW("Enter\n");
	/* defer interrupt handling in case IPA is not clocked on */
	if (ipa3_active_clients_trylock(&flags) == 0) {
	if (ipa3_inc_client_enable_clks_no_block(&log_info)) {
		IPADBG("defer interrupt processing\n");
		queue_work(ipa3_ctx->power_mgmt_wq, &ipa3_interrupt_defer_work);
		return IRQ_HANDLED;
	}

	if (ipa3_ctx->ipa3_active_clients.cnt == 0) {
		IPADBG("defer interrupt processing\n");
		queue_work(ipa3_ctx->power_mgmt_wq, &ipa3_interrupt_defer_work);
		goto bail;
	}

	ipa3_process_interrupts(true);
	IPADBG_LOW("Exit\n");

bail:
	ipa3_active_clients_trylock_unlock(&flags);
	ipa3_dec_client_disable_clks(&log_info);
	return IRQ_HANDLED;
}
/**
+3 −51
Original line number Diff line number Diff line
@@ -1547,43 +1547,6 @@ static struct msm_bus_scale_pdata ipa_bus_client_pdata_v3_0 = {
	.name = "ipa",
};

void ipa3_active_clients_lock(void)
{
	unsigned long flags;

	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
	spin_lock_irqsave(&ipa3_ctx->ipa3_active_clients.spinlock, flags);
	ipa3_ctx->ipa3_active_clients.mutex_locked = true;
	spin_unlock_irqrestore(&ipa3_ctx->ipa3_active_clients.spinlock, flags);
}

int ipa3_active_clients_trylock(unsigned long *flags)
{
	spin_lock_irqsave(&ipa3_ctx->ipa3_active_clients.spinlock, *flags);
	if (ipa3_ctx->ipa3_active_clients.mutex_locked) {
		spin_unlock_irqrestore(&ipa3_ctx->ipa3_active_clients.spinlock,
					 *flags);
		return 0;
	}

	return 1;
}

void ipa3_active_clients_trylock_unlock(unsigned long *flags)
{
	spin_unlock_irqrestore(&ipa3_ctx->ipa3_active_clients.spinlock, *flags);
}

void ipa3_active_clients_unlock(void)
{
	unsigned long flags;

	spin_lock_irqsave(&ipa3_ctx->ipa3_active_clients.spinlock, flags);
	ipa3_ctx->ipa3_active_clients.mutex_locked = false;
	spin_unlock_irqrestore(&ipa3_ctx->ipa3_active_clients.spinlock, flags);
	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
}

/**
 * ipa3_get_clients_from_rm_resource() - get IPA clients which are related to an
 * IPA_RM resource
@@ -1827,16 +1790,8 @@ int ipa3_suspend_resource_no_block(enum ipa_rm_resource_name resource)
	enum ipa_client_type client;
	struct ipa_ep_cfg_ctrl suspend;
	int ipa_ep_idx;
	unsigned long flags;
	struct ipa_active_client_logging_info log_info;

	if (ipa3_active_clients_trylock(&flags) == 0)
		return -EPERM;
	if (ipa3_ctx->ipa3_active_clients.cnt == 1) {
		res = -EPERM;
		goto bail;
	}

	memset(&clients, 0, sizeof(clients));
	res = ipa3_get_clients_from_rm_resource(resource, &clients);
	if (res) {
@@ -1875,14 +1830,11 @@ int ipa3_suspend_resource_no_block(enum ipa_rm_resource_name resource)
	if (res == 0) {
		IPA_ACTIVE_CLIENTS_PREP_RESOURCE(log_info,
				ipa_rm_resource_str(resource));
		ipa3_active_clients_log_dec(&log_info, true);
		ipa3_ctx->ipa3_active_clients.cnt--;
		IPADBG("active clients = %d\n",
		       ipa3_ctx->ipa3_active_clients.cnt);
		/* before gating IPA clocks do TAG process */
		ipa3_ctx->tag_process_before_gating = true;
		ipa3_dec_client_disable_clks_no_block(&log_info);
	}
bail:
	ipa3_active_clients_trylock_unlock(&flags);

	return res;
}