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

Commit af69b89b authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: ipa: rm: add clock scaling support"

parents 66338e40 c38f6eff
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1604,6 +1604,13 @@ fail:
	return retval;
}

int ipa_set_required_perf_profile(enum ipa_voltage_level floor_voltage,
				  u32 bandwidth_mbps)
{
	IPADBG("Not Implemented yet");
	return 0;
}

static int ipa_init_flt_block(void)
{
	int result = 0;
+3 −0
Original line number Diff line number Diff line
@@ -907,6 +907,9 @@ int ipa_id_alloc(void *ptr);
void *ipa_id_find(u32 id);
void ipa_id_remove(u32 id);

int ipa_set_required_perf_profile(enum ipa_voltage_level floor_voltage,
				  u32 bandwidth_mbps);

int ipa_cfg_ep_status(u32 clnt_hdl, const struct ipa_ep_cfg_status *ipa_ep_cfg);


+200 −3
Original line number Diff line number Diff line
@@ -37,13 +37,28 @@ static const char *resource_name_to_str[IPA_RM_RESOURCE_MAX] = {
	__stringify(IPA_RM_RESOURCE_HSIC_CONS),
};

struct ipa_rm_profile_vote_type {
	enum ipa_voltage_level volt[IPA_RM_RESOURCE_MAX];
	enum ipa_voltage_level curr_volt;
	u32 bw_prods[IPA_RM_RESOURCE_PROD_MAX];
	u32 bw_cons[IPA_RM_RESOURCE_CONS_MAX];
	u32 curr_bw;
};

struct ipa_rm_context_type {
	struct ipa_rm_dep_graph *dep_graph;
	struct workqueue_struct *ipa_rm_wq;
	spinlock_t ipa_rm_lock;
	struct ipa_rm_profile_vote_type prof_vote;
};
static struct ipa_rm_context_type *ipa_rm_ctx;

struct ipa_rm_notify_ipa_work_type {
	struct work_struct		work;
	enum ipa_voltage_level		volt;
	u32				bandwidth_mbps;
};

/**
 * ipa_rm_create_resource() - create resource
 * @create_params: [in] parameters needed
@@ -67,6 +82,13 @@ int ipa_rm_create_resource(struct ipa_rm_create_params *create_params)
	}
	IPA_RM_DBG("%s\n", ipa_rm_resource_str(create_params->name));

	if (create_params->floor_voltage < 0 ||
		create_params->floor_voltage >= IPA_VOLTAGE_MAX) {
		IPA_RM_ERR("invalid voltage %d\n",
			create_params->floor_voltage);
		return -EINVAL;
	}

	spin_lock(&ipa_rm_ctx->ipa_rm_lock);
	if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
					  create_params->name,
@@ -353,6 +375,48 @@ bail:
}
EXPORT_SYMBOL(ipa_rm_deregister);

/**
 * ipa_rm_set_perf_profile() - set performance profile
 * @resource_name: resource name
 * @profile: [in] profile information.
 *
 * Returns: 0 on success, negative on failure
 *
 * Set resource performance profile.
 * Updates IPA driver if performance level changed.
 */
int ipa_rm_set_perf_profile(enum ipa_rm_resource_name resource_name,
			struct ipa_rm_perf_profile *profile)
{
	int result;
	struct ipa_rm_resource *resource;

	IPA_RM_DBG("%s\n", ipa_rm_resource_str(resource_name));

	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_set_perf_profile(resource, profile);
	if (result) {
		IPA_RM_ERR("ipa_rm_resource_set_perf_profile failed %d\n",
			result);
		goto bail;
	}

	result = 0;
bail:
	spin_unlock(&ipa_rm_ctx->ipa_rm_lock);
	IPA_RM_DBG("EXIT with %d\n", result);

	return result;
}
EXPORT_SYMBOL(ipa_rm_set_perf_profile);

/**
 * ipa_rm_notify_completion() -
 *	consumer driver notification for
@@ -563,6 +627,139 @@ const char *ipa_rm_resource_str(enum ipa_rm_resource_name resource_name)
	return resource_name_to_str[resource_name];
};

static void ipa_rm_perf_profile_notify_to_ipa_work(struct work_struct *work)
{
	struct ipa_rm_notify_ipa_work_type *notify_work = container_of(work,
				struct ipa_rm_notify_ipa_work_type,
				work);
	int res;

	IPA_RM_DBG("calling to IPA driver. voltage %d bandwidth %d\n",
		notify_work->volt, notify_work->bandwidth_mbps);

	res = ipa_set_required_perf_profile(notify_work->volt,
		notify_work->bandwidth_mbps);
	if (res) {
		IPA_RM_ERR("ipa_set_required_perf_profile failed %d\n", res);
		goto bail;
	}

	IPA_RM_DBG("IPA driver notified\n");
bail:
	kfree(notify_work);
}

static void ipa_rm_perf_profile_notify_to_ipa(enum ipa_voltage_level volt,
					      u32 bandwidth)
{
	struct ipa_rm_notify_ipa_work_type *work;

	work = kzalloc(sizeof(*work), GFP_ATOMIC);
	if (!work) {
		IPA_RM_ERR("no mem\n");
		return;
	}

	INIT_WORK(&work->work, ipa_rm_perf_profile_notify_to_ipa_work);
	work->volt = volt;
	work->bandwidth_mbps = bandwidth;
	queue_work(ipa_rm_ctx->ipa_rm_wq, &work->work);
}

/**
 * ipa_rm_perf_profile_change() - change performance profile vote for resource
 * @resource_name: [in] resource name
 *
 * change bandwidth and voltage vote based on resource state.
 */
void ipa_rm_perf_profile_change(enum ipa_rm_resource_name resource_name)
{
	enum ipa_voltage_level old_volt;
	u32 *bw_ptr;
	u32 old_bw;
	struct ipa_rm_resource *resource;
	int i;
	u32 sum_bw_prod = 0;
	u32 sum_bw_cons = 0;

	IPA_RM_DBG("%s\n", ipa_rm_resource_str(resource_name));

	if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
					  resource_name,
					  &resource) != 0) {
			IPA_RM_ERR("resource does not exists\n");
			WARN_ON(1);
			return;
	}

	old_volt = ipa_rm_ctx->prof_vote.curr_volt;
	old_bw = ipa_rm_ctx->prof_vote.curr_bw;

	if (IPA_RM_RESORCE_IS_PROD(resource_name))
		bw_ptr = &ipa_rm_ctx->prof_vote.bw_prods[resource_name];
	else
		bw_ptr = &ipa_rm_ctx->prof_vote.bw_cons[
				resource_name - IPA_RM_RESOURCE_PROD_MAX];

	switch (resource->state) {
	case IPA_RM_GRANTED:
	case IPA_RM_REQUEST_IN_PROGRESS:
		IPA_RM_DBG("max_bw = %d, needed_bw = %d\n",
			resource->max_bw, resource->needed_bw);
		*bw_ptr = min(resource->max_bw, resource->needed_bw);
		ipa_rm_ctx->prof_vote.volt[resource_name] =
						resource->floor_voltage;
		break;

	case IPA_RM_RELEASE_IN_PROGRESS:
	case IPA_RM_RELEASED:
		*bw_ptr = 0;
		ipa_rm_ctx->prof_vote.volt[resource_name] = 0;
		break;

	default:
		IPA_RM_ERR("unknown state %d\n", resource->state);
		WARN_ON(1);
		return;
	}
	IPA_RM_DBG("resource bandwidth: %d voltage: %d\n", *bw_ptr,
					resource->floor_voltage);

	ipa_rm_ctx->prof_vote.curr_volt = IPA_VOLTAGE_UNSPECIFIED;
	for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
		if (ipa_rm_ctx->prof_vote.volt[i] >
				ipa_rm_ctx->prof_vote.curr_volt) {
			ipa_rm_ctx->prof_vote.curr_volt =
				ipa_rm_ctx->prof_vote.volt[i];
		}
	}

	for (i = 0; i < IPA_RM_RESOURCE_PROD_MAX; i++)
		sum_bw_prod += ipa_rm_ctx->prof_vote.bw_prods[i];

	for (i = 0; i < IPA_RM_RESOURCE_CONS_MAX; i++)
		sum_bw_cons += ipa_rm_ctx->prof_vote.bw_cons[i];

	IPA_RM_DBG("all prod bandwidth: %d all cons bandwidth: %d\n",
		sum_bw_prod, sum_bw_cons);
	ipa_rm_ctx->prof_vote.curr_bw = min(sum_bw_prod, sum_bw_cons);

	if (ipa_rm_ctx->prof_vote.curr_volt == old_volt &&
		ipa_rm_ctx->prof_vote.curr_bw == old_bw) {
		IPA_RM_DBG("same voting\n");
		return;
	}

	IPA_RM_DBG("new voting: voltage %d bandwidth %d\n",
		ipa_rm_ctx->prof_vote.curr_volt,
		ipa_rm_ctx->prof_vote.curr_bw);

	ipa_rm_perf_profile_notify_to_ipa(ipa_rm_ctx->prof_vote.curr_volt,
			ipa_rm_ctx->prof_vote.curr_bw);

	return;
};

/**
 * ipa_rm_exit() - free all IPA RM resources
 */
+2 −0
Original line number Diff line number Diff line
@@ -74,6 +74,8 @@ int ipa_rm_stat(char *buf, int size);

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);

void ipa_rm_exit(void);

#endif /* _IPA_RM_I_H_ */
+109 −40
Original line number Diff line number Diff line
@@ -76,7 +76,8 @@ int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name)
}

static int ipa_rm_resource_consumer_request(
		struct ipa_rm_resource_cons *consumer)
		struct ipa_rm_resource_cons *consumer,
		u32 prod_needed_bw)
{
	int result = 0;
	int driver_result;
@@ -85,6 +86,7 @@ static int ipa_rm_resource_consumer_request(
			ipa_rm_resource_str(consumer->resource.name),
			consumer->resource.state);

	consumer->resource.needed_bw += prod_needed_bw;
	switch (consumer->resource.state) {
	case IPA_RM_RELEASED:
	case IPA_RM_RELEASE_IN_PROGRESS:
@@ -95,10 +97,12 @@ static int ipa_rm_resource_consumer_request(
		IPA_RM_DBG("calling driver CB\n");
		driver_result = consumer->request_resource();
		IPA_RM_DBG("driver CB returned with %d\n", driver_result);
		if (driver_result == 0)
		if (driver_result == 0) {
			consumer->resource.state = IPA_RM_GRANTED;
		else if (driver_result != -EINPROGRESS) {
			ipa_rm_perf_profile_change(consumer->resource.name);
		} else if (driver_result != -EINPROGRESS) {
			consumer->resource.state = prev_state;
			consumer->resource.needed_bw -= prod_needed_bw;
			result = driver_result;
			goto bail;
		}
@@ -106,11 +110,13 @@ static int ipa_rm_resource_consumer_request(
		break;
	}
	case IPA_RM_GRANTED:
		ipa_rm_perf_profile_change(consumer->resource.name);
		break;
	case IPA_RM_REQUEST_IN_PROGRESS:
		result = -EINPROGRESS;
		break;
	default:
		consumer->resource.needed_bw -= prod_needed_bw;
		result = -EPERM;
		goto bail;
	}
@@ -125,34 +131,46 @@ bail:
}

static int ipa_rm_resource_consumer_release(
		struct ipa_rm_resource_cons *consumer)
		struct ipa_rm_resource_cons *consumer,
		u32 prod_needed_bw)
{
	int result = 0;
	int driver_result;
	enum ipa_rm_resource_state save_state;

	IPA_RM_DBG("%s state: %d\n",
		ipa_rm_resource_str(consumer->resource.name),
		consumer->resource.state);
	consumer->resource.needed_bw -= prod_needed_bw;
	switch (consumer->resource.state) {
	case IPA_RM_RELEASED:
		break;
	case IPA_RM_GRANTED:
	case IPA_RM_REQUEST_IN_PROGRESS:
		if (consumer->usage_count > 0)
		if (consumer->usage_count == 0) {
			IPA_RM_ERR("consumer not used\n");
			result = -EPERM;
			break;
		}
		consumer->usage_count--;
		if (consumer->usage_count == 0) {
			save_state = consumer->resource.state;
			consumer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
			IPA_RM_DBG("calling driver CB\n");
			driver_result = consumer->release_resource();
			result = consumer->release_resource();
			IPA_RM_DBG("driver CB returned with %d\n",
				driver_result);
			if (driver_result == 0)
				consumer->resource.state = IPA_RM_RELEASED;
			else if (driver_result != -EINPROGRESS)
				result);
			if (result != 0 && result != -EINPROGRESS) {
				IPA_RM_ERR("driver CB returned error %d\n",
					result);
				consumer->resource.state = save_state;
			result = driver_result;
				goto bail;
			}
			if (result == 0)
				consumer->resource.state = IPA_RM_RELEASED;
			ipa_rm_perf_profile_change(consumer->resource.name);

		} else if (consumer->resource.state == IPA_RM_GRANTED) {
			ipa_rm_perf_profile_change(consumer->resource.name);
		}
		break;
	case IPA_RM_RELEASE_IN_PROGRESS:
@@ -247,6 +265,7 @@ static void ipa_rm_resource_producer_delete(
	struct ipa_rm_notification_info *reg_info;
	struct list_head *pos, *q;

	ipa_rm_resource_producer_release(producer);
	list_for_each_safe(pos, q, &(producer->event_listeners)) {
		reg_info = list_entry(pos,
				struct ipa_rm_notification_info,
@@ -332,6 +351,7 @@ int ipa_rm_resource_create(
		goto peers_alloc_fail;
	}
	(*resource)->name = create_params->name;
	(*resource)->floor_voltage = create_params->floor_voltage;
	(*resource)->state = IPA_RM_RELEASED;
	goto bail;

@@ -532,6 +552,7 @@ int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
	IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
				resource->state);

	resource->needed_bw += depends_on->max_bw;
	switch (resource->state) {
	case IPA_RM_RELEASED:
	case IPA_RM_RELEASE_IN_PROGRESS:
@@ -544,11 +565,13 @@ int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
		((struct ipa_rm_resource_prod *)
					resource)->pending_request++;
		consumer_result = ipa_rm_resource_consumer_request(
				(struct ipa_rm_resource_cons *)depends_on);
				(struct ipa_rm_resource_cons *)depends_on,
				resource->max_bw);
		if (consumer_result != -EINPROGRESS) {
			resource->state = prev_state;
			((struct ipa_rm_resource_prod *)
					resource)->pending_request--;
			ipa_rm_perf_profile_change(resource->name);
		}
		result = consumer_result;
		break;
@@ -599,10 +622,12 @@ int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
	IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
				resource->state);

	resource->needed_bw -= depends_on->max_bw;
	switch (resource->state) {
	case IPA_RM_RELEASED:
		break;
	case IPA_RM_GRANTED:
		ipa_rm_perf_profile_change(resource->name);
		release_consumer = true;
		break;
	case IPA_RM_RELEASE_IN_PROGRESS:
@@ -616,6 +641,7 @@ int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
			resource->state = IPA_RM_RELEASED;
			state_changed = true;
			evt = IPA_RM_RESOURCE_RELEASED;
			ipa_rm_perf_profile_change(resource->name);
		}
		break;
	case IPA_RM_REQUEST_IN_PROGRESS:
@@ -630,6 +656,7 @@ int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
			resource->state = IPA_RM_GRANTED;
			state_changed = true;
			evt = IPA_RM_RESOURCE_GRANTED;
			ipa_rm_perf_profile_change(resource->name);
		}
		break;
	default:
@@ -652,7 +679,8 @@ int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
			resource->name);
	if (release_consumer)
		(void) ipa_rm_resource_consumer_release(
				(struct ipa_rm_resource_cons *)depends_on);
				(struct ipa_rm_resource_cons *)depends_on,
				resource->max_bw);
bail:
	IPA_RM_DBG("EXIT with %d\n", result);

@@ -673,17 +701,6 @@ int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)
	int consumer_result;
	enum ipa_rm_resource_state state;

	if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
		state = producer->resource.state;
		producer->resource.state = IPA_RM_GRANTED;
		(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
			producer->resource.name,
			IPA_RM_RESOURCE_GRANTED,
			true);
		result = 0;
		goto unlock_and_bail;
	}

	state = producer->resource.state;
	switch (producer->resource.state) {
	case IPA_RM_RELEASED:
@@ -710,7 +727,8 @@ int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)
		if (consumer) {
			producer->pending_request++;
			consumer_result = ipa_rm_resource_consumer_request(
				(struct ipa_rm_resource_cons *)consumer);
				(struct ipa_rm_resource_cons *)consumer,
				producer->resource.max_bw);
			if (consumer_result == -EINPROGRESS) {
				result = -EINPROGRESS;
			} else {
@@ -725,6 +743,7 @@ int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)

	if (producer->pending_request == 0) {
		producer->resource.state = IPA_RM_GRANTED;
		ipa_rm_perf_profile_change(producer->resource.name);
		(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
			producer->resource.name,
			IPA_RM_RESOURCE_GRANTED,
@@ -756,16 +775,6 @@ int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer)
	int consumer_result;
	enum ipa_rm_resource_state state;

	if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
		state = producer->resource.state;
		producer->resource.state = IPA_RM_RELEASED;
		(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
			producer->resource.name,
			IPA_RM_RESOURCE_RELEASED,
			true);
		goto bail;
	}

	state = producer->resource.state;
	switch (producer->resource.state) {
	case IPA_RM_RELEASED:
@@ -792,13 +801,15 @@ int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer)
		if (consumer) {
			producer->pending_release++;
			consumer_result = ipa_rm_resource_consumer_release(
				(struct ipa_rm_resource_cons *)consumer);
				(struct ipa_rm_resource_cons *)consumer,
				producer->resource.max_bw);
			producer->pending_release--;
		}
	}

	if (producer->pending_release == 0) {
		producer->resource.state = IPA_RM_RELEASED;
		ipa_rm_perf_profile_change(producer->resource.name);
		(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
			producer->resource.name,
			IPA_RM_RESOURCE_RELEASED,
@@ -833,6 +844,8 @@ static void ipa_rm_resource_producer_handle_cb(
			if (producer->pending_request == 0) {
				producer->resource.state =
						IPA_RM_GRANTED;
				ipa_rm_perf_profile_change(
					producer->resource.name);
				ipa_rm_resource_producer_notify_clients(
						producer,
						IPA_RM_RESOURCE_GRANTED,
@@ -849,6 +862,8 @@ static void ipa_rm_resource_producer_handle_cb(
			if (producer->pending_release == 0) {
				producer->resource.state =
						IPA_RM_RELEASED;
				ipa_rm_perf_profile_change(
					producer->resource.name);
				ipa_rm_resource_producer_notify_clients(
						producer,
						IPA_RM_RESOURCE_RELEASED,
@@ -896,6 +911,7 @@ void ipa_rm_resource_consumer_handle_cb(struct ipa_rm_resource_cons *consumer,
		if (event == IPA_RM_RESOURCE_RELEASED)
			goto bail;
		consumer->resource.state = IPA_RM_GRANTED;
		ipa_rm_perf_profile_change(consumer->resource.name);
		break;
	case IPA_RM_RELEASE_IN_PROGRESS:
		if (event == IPA_RM_RESOURCE_GRANTED)
@@ -930,6 +946,60 @@ bail:
	return;
}

/*
 * ipa_rm_resource_set_perf_profile() - sets the performance profile to
 *					resource.
 *
 * @resource: [in] resource
 * @profile: [in] profile to be set
 *
 * sets the profile to the given resource, In case the resource is
 * granted, update bandwidth vote of the resource
 */
int ipa_rm_resource_set_perf_profile(struct ipa_rm_resource *resource,
				     struct ipa_rm_perf_profile *profile)
{
	int peers_index;
	struct ipa_rm_resource *peer;

	if (!resource || !profile) {
		IPA_RM_ERR("invalid params\n");
		return -EINVAL;
	}

	if (profile->max_supported_bandwidth_mbps == resource->max_bw) {
		IPA_RM_DBG("same profile\n");
		return 0;
	}

	if ((resource->type == IPA_RM_PRODUCER &&
	    (resource->state == IPA_RM_GRANTED ||
	    resource->state == IPA_RM_REQUEST_IN_PROGRESS)) ||
	    resource->type == IPA_RM_CONSUMER) {
		for (peers_index = 0;
		     peers_index < ipa_rm_peers_list_get_size(
		     resource->peers_list);
		     peers_index++) {
			peer = ipa_rm_peers_list_get_resource(peers_index,
				resource->peers_list);
			if (!peer)
				continue;
			peer->needed_bw -= resource->max_bw;
			peer->needed_bw +=
				profile->max_supported_bandwidth_mbps;
			if (peer->state == IPA_RM_GRANTED)
				ipa_rm_perf_profile_change(peer->name);
		}
	}

	resource->max_bw = profile->max_supported_bandwidth_mbps;
	if (resource->state == IPA_RM_GRANTED)
		ipa_rm_perf_profile_change(resource->name);

	return 0;
}


/*
 * ipa_rm_resource_producer_print_stat() - print the
 * resource status and all his dependencies
@@ -940,7 +1010,6 @@ bail:
 *
 * Returns: number of bytes used on success, negative on failure
 */

int ipa_rm_resource_producer_print_stat(
				struct ipa_rm_resource *resource,
				char *buf,
@@ -992,7 +1061,7 @@ int ipa_rm_resource_producer_print_stat(
			resource->peers_list);
		if (consumer) {
			nbytes = scnprintf(buf + cnt, size - cnt,
				ipa_rm_resource_str(resource->name));
				ipa_rm_resource_str(consumer->name));
			cnt += nbytes;
			nbytes = scnprintf(buf + cnt, size - cnt, "[");
			cnt += nbytes;
Loading