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

Commit 86337a55 authored by Ravit Katzav's avatar Ravit Katzav Committed by Gidon Studinski
Browse files

msm: ipa: rm: add support for power save



Adds support for power save feature in IPA Resource Manager.
Manages IPA clock gating from the resource manager.
Adds a new APPS_CONS entity, PRODs which send data to A7 are
dependent on this APPS_CONS for waking up the IPA.

Change-Id: I702e75c0fdef1a919ad7836fd74890b263744b97
Signed-off-by: default avatarRavit Katzav <rkatzav@codeaurora.org>
Signed-off-by: default avatarGidon Studinski <gidons@codeaurora.org>
parent 68766e4f
Loading
Loading
Loading
Loading
+85 −8
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/rbtree.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include "ipa_i.h"
@@ -1800,6 +1801,65 @@ static int ipa_init_flt_block(void)
	return result;
}

/**
* ipa_suspend_handler() - Handles the suspend interrupt:
* wakes up the suspended peripheral by requesting its consumer
* @interrupt:		Interrupt type
* @private_data:	The client's private data
* @interrupt_data:	Interrupt specific information data
*/
void ipa_suspend_handler(enum ipa_irq_type interrupt,
				void *private_data,
				void *interrupt_data)
{
	enum ipa_rm_resource_name resource;
	u32 suspend_data =
		((struct ipa_tx_suspend_irq_data *)interrupt_data)->endpoints;
	u32 bmsk = 1;
	u32 i = 0;

	IPADBG("interrupt=%d, interrupt_data=%u\n", interrupt, suspend_data);
	for (i = 0; i < IPA_NUM_PIPES; i++) {
		if ((suspend_data & bmsk) && (ipa_ctx->ep[i].valid)) {
			resource = ipa_get_rm_resource_from_ep(i);
			ipa_rm_request_resource_with_timer(resource);
		}
		bmsk = bmsk << 1;
	}
}

static int apps_cons_release_resource(void)
{
	return 0;
}

static int apps_cons_request_resource(void)
{
	return 0;
}

static int ipa_create_apps_resource(void)
{
	struct ipa_rm_create_params apps_cons_create_params;
	struct ipa_rm_perf_profile profile;
	int result = 0;

	memset(&apps_cons_create_params, 0,
				sizeof(apps_cons_create_params));
	apps_cons_create_params.name = IPA_RM_RESOURCE_APPS_CONS;
	apps_cons_create_params.request_resource = apps_cons_request_resource;
	apps_cons_create_params.release_resource = apps_cons_release_resource;
	result = ipa_rm_create_resource(&apps_cons_create_params);
	if (result) {
		IPAERR("ipa_rm_create_resource failed\n");
		return result;
	}

	profile.max_supported_bandwidth_mbps = IPA_APPS_MAX_BW_IN_MBPS;
	ipa_rm_set_perf_profile(IPA_RM_RESOURCE_APPS_CONS, &profile);

	return result;
}
/**
* ipa_init() - Initialize the IPA Driver
* @resource_p:	contain platform specific values from DST file
@@ -2074,7 +2134,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
	spin_lock_init(&ipa_ctx->idr_lock);

	mutex_init(&ipa_ctx->ipa_active_clients_lock);
	ipa_ctx->ipa_active_clients = 0;
	ipa_ctx->ipa_active_clients = 1;

	/* wlan related member */
	spin_lock_init(&ipa_ctx->wlan_spinlock);
@@ -2082,9 +2142,6 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
	ipa_ctx->wlan_comm_cnt = 0;
	INIT_LIST_HEAD(&ipa_ctx->wlan_comm_desc_list);
	memset(&ipa_ctx->wstats, 0, sizeof(struct ipa_wlan_stats));
	/* enable IPA clocks until the end of the initialization */
	ipa_inc_client_enable_clks();

	/*
	 * setup an empty routing table in system memory, this will be used
	 * to delete a routing table cleanly and safely
@@ -2166,13 +2223,29 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
	}
	IPADBG("IPA resource manager initialized");

	result = ipa_create_apps_resource();
	if (result) {
		IPAERR("Failed to create APPS_CONS resource\n");
		result = -ENODEV;
		goto fail_create_apps_resource;
	}

	/*register IPA IRQ handler*/
	result = ipa_interrupts_init(resource_p->ipa_irq, resource_p->ee,
	result = ipa_interrupts_init(resource_p->ipa_irq, 0,
			ipa_dev);
	if (result) {
		IPAERR("ipa interrupts initialization failed\n");
		result = -ENODEV;
		goto fail_ipa_rm_init;
		goto fail_ipa_interrupts_init;
	}

	/*add handler for suspend interrupt*/
	result = ipa_add_interrupt_handler(IPA_TX_SUSPEND_IRQ,
			ipa_suspend_handler, true, NULL);
	if (result) {
		IPAERR("register handler for suspend interrupt failed\n");
		result = -ENODEV;
		goto fail_add_interrupt_handler;
	}

	if (ipa_ctx->use_ipa_teth_bridge) {
@@ -2181,7 +2254,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
		if (result) {
			IPAERR(":teth_bridge init failed (%d)\n", -result);
			result = -ENODEV;
			goto fail_teth_bridge_init;
			goto fail_add_interrupt_handler;
		}
		IPADBG("teth_bridge initialized");
	}
@@ -2193,7 +2266,11 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,

	return 0;

fail_teth_bridge_init:
fail_add_interrupt_handler:
	free_irq(resource_p->ipa_irq, ipa_dev);
fail_ipa_interrupts_init:
	ipa_rm_delete_resource(IPA_RM_RESOURCE_APPS_CONS);
fail_create_apps_resource:
	ipa_rm_exit();
fail_ipa_rm_init:
	cdev_del(&ipa_ctx->cdev);
+2 −0
Original line number Diff line number Diff line
@@ -720,6 +720,7 @@ static ssize_t ipa_read_stats(struct file *file, char __user *ubuf,
			"stat_compl=%u\n"
			"lan_aggr_close=%u\n"
			"wan_aggr_close=%u\n"
			"act_clnt=%u\n"
			"con_clnt_bmap=0x%x\n",
			ipa_ctx->stats.tx_sw_pkts,
			ipa_ctx->stats.tx_hw_pkts,
@@ -728,6 +729,7 @@ static ssize_t ipa_read_stats(struct file *file, char __user *ubuf,
			ipa_ctx->stats.stat_compl,
			ipa_ctx->stats.aggr_close,
			ipa_ctx->stats.wan_aggr_close,
			ipa_ctx->ipa_active_clients,
			connect);
		cnt += nbytes;

+4 −3
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ static void deferred_interrupt_work(struct work_struct *work)
			container_of(work,
			struct ipa_interrupt_work_wrap,
			interrupt_work);
	IPAERR("call handler from workq...\n");
	IPADBG("call handler from workq...\n");
	work_data->handler(work_data->interrupt, work_data->private_data,
			work_data->interrupt_data);
	kfree(work_data->interrupt_data);
@@ -127,10 +127,12 @@ static irqreturn_t ipa_isr(int irq, void *ctxt)
	u32 reg;
	u32 bmsk = 1;
	u32 i = 0;
	u32 en;

	en = ipa_read_reg(ipa_ctx->mmio, IPA_IRQ_EN_EE_n_ADDR(ipa_ee));
	reg = ipa_read_reg(ipa_ctx->mmio, IPA_IRQ_STTS_EE_n_ADDR(ipa_ee));
	for (i = 0; i < IPA_IRQ_MAX; i++) {
		if (reg & bmsk)
		if (en & reg & bmsk)
			handle_interrupt(i);
		bmsk = bmsk << 1;
	}
@@ -138,7 +140,6 @@ static irqreturn_t ipa_isr(int irq, void *ctxt)

	return IRQ_HANDLED;
}

/**
* ipa_add_interrupt_handler() - Adds handler to an interrupt type
* @interrupt:		Interrupt type
+183 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@
#include "ipa_i.h"
#include "ipa_rm_dependency_graph.h"
#include "ipa_rm_i.h"
#include "ipa_rm_resource.h"

static const char *resource_name_to_str[IPA_RM_RESOURCE_MAX] = {
	__stringify(IPA_RM_RESOURCE_Q6_PROD),
@@ -254,6 +253,82 @@ bail:
}
EXPORT_SYMBOL(ipa_rm_request_resource);

void delayed_release_work_func(struct work_struct *work)
{
	struct ipa_rm_resource *resource;
	struct ipa_rm_delayed_release_work_type *rwork = container_of(
			to_delayed_work(work),
			struct ipa_rm_delayed_release_work_type,
			work);

	if (!IPA_RM_RESORCE_IS_CONS(rwork->resource_name)) {
		IPA_RM_ERR("can be called on CONS only\n");
		kfree(rwork);
		return;
	}
	spin_lock(&ipa_rm_ctx->ipa_rm_lock);
	if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
					rwork->resource_name,
					&resource) != 0) {
		IPA_RM_ERR("resource does not exists\n");
		goto bail;
	}

	ipa_rm_resource_consumer_release(
		(struct ipa_rm_resource_cons *)resource, rwork->needed_bw);

bail:
	spin_unlock(&ipa_rm_ctx->ipa_rm_lock);
	kfree(rwork);

}

/**
 * ipa_rm_request_resource_with_timer() - requests the specified consumer
 * resource and releases it after 1 second
 * @resource_name: name of the requested resource
 *
 * Returns: 0 on success, negative on failure
 */
int ipa_rm_request_resource_with_timer(enum ipa_rm_resource_name resource_name)
{
	struct ipa_rm_resource *resource;
	struct ipa_rm_delayed_release_work_type *release_work;
	int result;

	if (!IPA_RM_RESORCE_IS_CONS(resource_name)) {
		IPA_RM_ERR("can be called on CONS only\n");
		return -EINVAL;
	}

	spin_lock(&ipa_rm_ctx->ipa_rm_lock);
	if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
			resource_name,
			&resource) != 0) {
		IPA_RM_ERR("resource does not exists\n");
		result = -EPERM;
		goto bail;
	}
	result = ipa_rm_resource_consumer_request(
			(struct ipa_rm_resource_cons *)resource, 0);
	if (result != 0 && result != -EINPROGRESS) {
		IPA_RM_ERR("consumer request returned error %d\n", result);
		result = -EPERM;
		goto bail;
	}

	release_work = kzalloc(sizeof(*release_work), GFP_ATOMIC);
	release_work->resource_name = resource->name;
	release_work->needed_bw = 0;
	INIT_DELAYED_WORK(&release_work->work, delayed_release_work_func);
	schedule_delayed_work(&release_work->work,
			msecs_to_jiffies(IPA_RM_RELEASE_DELAY_IN_MSEC));
	result = 0;
bail:
	spin_unlock(&ipa_rm_ctx->ipa_rm_lock);

	return result;
}
/**
 * ipa_rm_release_resource() - release resource
 * @resource_name: [in] name of the requested resource
@@ -504,6 +579,71 @@ static void ipa_rm_wq_handler(struct work_struct *work)
	kfree((void *) work);
}

static void ipa_rm_wq_resume_handler(struct work_struct *work)
{
	struct ipa_rm_resource *resource;
	struct ipa_rm_wq_suspend_resume_work_type *ipa_rm_work =
			container_of(work,
			struct ipa_rm_wq_suspend_resume_work_type,
			work);
	IPA_RM_DBG("resume work handler: %s",
		ipa_rm_resource_str(ipa_rm_work->resource_name));

	if (!IPA_RM_RESORCE_IS_CONS(ipa_rm_work->resource_name)) {
		IPA_RM_ERR("resource is not CONS\n");
		return;
	}
	ipa_inc_client_enable_clks();
	spin_lock(&ipa_rm_ctx->ipa_rm_lock);
	if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
					ipa_rm_work->resource_name,
					&resource) != 0){
		IPA_RM_ERR("resource does not exists\n");
		spin_unlock(&ipa_rm_ctx->ipa_rm_lock);
		ipa_dec_client_disable_clks();
		goto bail;
	}
	ipa_rm_resource_consumer_request_work(
			(struct ipa_rm_resource_cons *)resource,
			ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true);
	spin_unlock(&ipa_rm_ctx->ipa_rm_lock);
bail:
	kfree(ipa_rm_work);
}


static void ipa_rm_wq_suspend_handler(struct work_struct *work)
{
	struct ipa_rm_resource *resource;
	struct ipa_rm_wq_suspend_resume_work_type *ipa_rm_work =
			container_of(work,
			struct ipa_rm_wq_suspend_resume_work_type,
			work);
	IPA_RM_DBG("suspend work handler: %s",
		ipa_rm_resource_str(ipa_rm_work->resource_name));

	if (!IPA_RM_RESORCE_IS_CONS(ipa_rm_work->resource_name)) {
		IPA_RM_ERR("resource is not CONS\n");
		return;
	}
	ipa_suspend_resource_sync(ipa_rm_work->resource_name);
	spin_lock(&ipa_rm_ctx->ipa_rm_lock);
	if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
					ipa_rm_work->resource_name,
					&resource) != 0){
		IPA_RM_ERR("resource does not exists\n");
		spin_unlock(&ipa_rm_ctx->ipa_rm_lock);
		return;
	}
	ipa_rm_resource_consumer_release_work(
			(struct ipa_rm_resource_cons *)resource,
			ipa_rm_work->prev_state,
			true);
	spin_unlock(&ipa_rm_ctx->ipa_rm_lock);

	kfree(ipa_rm_work);
}

/**
 * ipa_rm_wq_send_cmd() - send a command for deferred work
 * @wq_cmd: command that should be executed
@@ -535,6 +675,48 @@ int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
	return result;
}

int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name,
		enum ipa_rm_resource_state prev_state,
		u32 needed_bw)
{
	int result = -ENOMEM;
	struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
			GFP_ATOMIC);
	if (work) {
		INIT_WORK((struct work_struct *)work,
				ipa_rm_wq_suspend_handler);
		work->resource_name = resource_name;
		work->prev_state = prev_state;
		work->needed_bw = needed_bw;
		result = queue_work(ipa_rm_ctx->ipa_rm_wq,
				(struct work_struct *)work);
	} else {
		IPA_RM_ERR("no mem\n");
	}

	return result;
}

int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
		enum ipa_rm_resource_state prev_state,
		u32 needed_bw)
{
	int result = -ENOMEM;
	struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
			GFP_ATOMIC);
	if (work) {
		INIT_WORK((struct work_struct *)work, ipa_rm_wq_resume_handler);
		work->resource_name = resource_name;
		work->prev_state = prev_state;
		work->needed_bw = needed_bw;
		result = queue_work(ipa_rm_ctx->ipa_rm_wq,
				(struct work_struct *)work);
	} else {
		IPA_RM_ERR("no mem\n");
	}

	return result;
}
/**
 * ipa_rm_initialize() - initialize IPA RM component
 *
+45 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

#include <linux/workqueue.h>
#include <linux/ipa.h>
#include "ipa_rm_resource.h"

#define IPA_RM_DRV_NAME "ipa_rm"

@@ -30,10 +31,25 @@
#define IPA_RM_RESORCE_IS_CONS(x) \
	(x >= IPA_RM_RESOURCE_PROD_MAX && x < IPA_RM_RESOURCE_MAX)
#define IPA_RM_INDEX_INVALID	(-1)
#define IPA_RM_RELEASE_DELAY_IN_MSEC 1000

int ipa_rm_prod_index(enum ipa_rm_resource_name resource_name);
int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name);

/**
 * struct ipa_rm_delayed_release_work_type - IPA RM delayed resource release
 *				work type
 * @delayed_work: work struct
 * @ipa_rm_resource_name: name of the resource on which this work should be done
 * @needed_bw: bandwidth required for resource in Mbps
 */
struct ipa_rm_delayed_release_work_type {
	struct delayed_work		work;
	enum ipa_rm_resource_name	resource_name;
	u32				needed_bw;

};

/**
 * enum ipa_rm_wq_cmd - workqueue commands
 */
@@ -63,11 +79,36 @@ struct ipa_rm_wq_work_type {
	bool				notify_registered_only;
};

/**
 * struct ipa_rm_wq_suspend_resume_work_type - IPA RM worqueue resume or
 *				suspend work type
 * @work: work struct
 * @resource_name: name of the resource on which this work
 *			should be done
 * @prev_state:
 * @needed_bw:
 */
struct ipa_rm_wq_suspend_resume_work_type {
	struct work_struct		work;
	enum ipa_rm_resource_name	resource_name;
	enum ipa_rm_resource_state	prev_state;
	u32				needed_bw;

};

int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
		enum ipa_rm_resource_name resource_name,
		enum ipa_rm_event event,
		bool notify_registered_only);

int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
		enum ipa_rm_resource_state prev_state,
		u32 needed_bw);

int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name,
		enum ipa_rm_resource_state prev_state,
		u32 needed_bw);

int ipa_rm_initialize(void);

int ipa_rm_stat(char *buf, int size);
@@ -76,6 +117,10 @@ const char *ipa_rm_resource_str(enum ipa_rm_resource_name resource_name);

void ipa_rm_perf_profile_change(enum ipa_rm_resource_name resource_name);

int ipa_rm_request_resource_with_timer(enum ipa_rm_resource_name resource_name);

void delayed_release_work_func(struct work_struct *work);

void ipa_rm_exit(void);

#endif /* _IPA_RM_I_H_ */
Loading