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

Commit a055be83 authored by Nicholas Troast's avatar Nicholas Troast
Browse files

power: pmic-voter: handle scenarios where voter state is invalid



After the voter is initialized and before the first valid vote
is made, the voter state is invalid. Introduce some changes to
accommodate this state:

1. Initialize effective_result and all client states to -EINVAL.
2. If the current vote does not change effective_result, or if
   there are no valid votes in the array, skip the callback and
   maintain the current effective result.
3. Allow the user to provide a default_value, which is returned
   in the scenario where the effective_result is queried before
   making a valid vote.

CRs-Fixed: 927044
Change-Id: Ic00064d95e5a9234bcf44a415ad2bbff6e84a2f3
Signed-off-by: default avatarNicholas Troast <ntroast@codeaurora.org>
parent 41747adc
Loading
Loading
Loading
Loading
+42 −15
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ struct votable {
	int			type;
	int			effective_client_id;
	int			effective_result;
	int			default_result;
	struct mutex		vote_lock;
	int			(*callback)(struct device *dev,
						int effective_result,
@@ -46,7 +47,7 @@ static int vote_set_any(struct votable *votable)
	int i;

	for (i = 0; i < votable->num_clients; i++)
		if (votable->votes[i].state)
		if (votable->votes[i].state == 1)
			return 1;
	return 0;
}
@@ -54,11 +55,11 @@ static int vote_set_any(struct votable *votable)
static int vote_min(struct votable *votable)
{
	int min_vote = INT_MAX;
	int client_index;
	int client_index = -EINVAL;
	int i;

	for (i = 0; i < votable->num_clients; i++) {
		if (votable->votes[i].state &&
		if (votable->votes[i].state == 1 &&
				min_vote > votable->votes[i].value) {
			min_vote = votable->votes[i].value;
			client_index = i;
@@ -71,11 +72,11 @@ static int vote_min(struct votable *votable)
static int vote_max(struct votable *votable)
{
	int max_vote = INT_MIN;
	int client_index;
	int client_index = -EINVAL;
	int i;

	for (i = 0; i < votable->num_clients; i++) {
		if (votable->votes[i].state &&
		if (votable->votes[i].state == 1 &&
				max_vote < votable->votes[i].value) {
			max_vote = votable->votes[i].value;
			client_index = i;
@@ -107,6 +108,9 @@ int get_client_vote(struct votable *votable, int client_id)

int get_client_vote_locked(struct votable *votable, int client_id)
{
	if (votable->votes[client_id].state < 0)
		return votable->default_result;

	return votable->votes[client_id].value;
}

@@ -122,6 +126,9 @@ int get_effective_result(struct votable *votable)

int get_effective_result_locked(struct votable *votable)
{
	if (votable->effective_result < 0)
		return votable->default_result;

	return votable->effective_result;
}

@@ -140,13 +147,19 @@ int get_effective_client_id_locked(struct votable *votable)
	return votable->effective_client_id;
}

int vote(struct votable *votable, int client_id, int state, int val)
int vote(struct votable *votable, int client_id, bool state, int val)
{
	int effective_id, effective_result;
	int rc = 0;

	lock_votable(votable);

	if (votable->votes[client_id].state == state &&
				votable->votes[client_id].value == val) {
		pr_debug("%s: votes unchanged; skipping\n", votable->name);
		goto out;
	}

	votable->votes[client_id].state = state;
	votable->votes[client_id].value = val;

@@ -171,9 +184,15 @@ int vote(struct votable *votable, int client_id, int state, int val)
						state, client_id);
		}
		goto out;
	default:
		rc = -EINVAL;
		goto vote_err;
	}

	/*
	 * If the votable does not have any votes it will maintain the last
	 * known effective_result and effective_client_id
	 */
	if (effective_id < 0) {
		pr_debug("%s: no votes; skipping callback\n", votable->name);
		goto out;
	}

	effective_result = votable->votes[effective_id].value;
@@ -181,15 +200,12 @@ int vote(struct votable *votable, int client_id, int state, int val)
	if (effective_result != votable->effective_result) {
		votable->effective_client_id = effective_id;
		votable->effective_result = effective_result;
		pr_debug("%s: effective vote is now %d voted by %d",
		pr_debug("%s: effective vote is now %d voted by %d\n",
				votable->name, effective_result, effective_id);
		rc = votable->callback(votable->dev, effective_result,
					effective_id, val, client_id);
	}
	goto out;

vote_err:
	pr_err("Invalid votable type specified for voter\n");
out:
	unlock_votable(votable);
	return rc;
@@ -198,6 +214,7 @@ out:
struct votable *create_votable(struct device *dev, const char *name,
					int votable_type,
					int num_clients,
					int default_result,
					int (*callback)(struct device *dev,
							int effective_result,
							int effective_client,
@@ -205,6 +222,7 @@ struct votable *create_votable(struct device *dev, const char *name,
							int last_client)
					)
{
	int i;
	struct votable *votable = devm_kzalloc(dev, sizeof(struct votable),
							GFP_KERNEL);

@@ -231,9 +249,18 @@ struct votable *create_votable(struct device *dev, const char *name,
	votable->num_clients = num_clients;
	votable->callback = callback;
	votable->type = votable_type;

	votable->effective_result = -1;
	votable->default_result = default_result;
	mutex_init(&votable->vote_lock);

	/*
	 * Because effective_result and client states are invalid
	 * before the first vote, initialize them to -EINVAL
	 */
	votable->effective_result = -EINVAL;
	votable->effective_client_id = -EINVAL;

	for (i = 0; i < votable->num_clients; i++)
		votable->votes[i].state = -EINVAL;

	return votable;
}
+8 −7
Original line number Diff line number Diff line
@@ -27,9 +27,10 @@ int get_effective_result(struct votable *votable);
int get_effective_result_locked(struct votable *votable);
int get_effective_client_id(struct votable *votable);
int get_effective_client_id_locked(struct votable *votable);
int vote(struct votable *votable, int client_id, int state, int val);
int vote(struct votable *votable, int client_id, bool state, int val);
struct votable *create_votable(struct device *dev, const char *name,
				int votable_type, int num_clients,
				int default_result,
				int (*callback)(struct device *dev,
						int effective_result,
						int effective_client,
+16 −17
Original line number Diff line number Diff line
@@ -7347,45 +7347,44 @@ static int smbchg_probe(struct spmi_device *spmi)

	chip->fcc_votable = create_votable(&spmi->dev,
			"SMBCHG: fcc",
			VOTE_MIN,
			NUM_FCC_VOTER, set_fastchg_current_vote_cb);
			VOTE_MIN, NUM_FCC_VOTER, 2000,
			set_fastchg_current_vote_cb);
	if (IS_ERR(chip->fcc_votable))
		return PTR_ERR(chip->fcc_votable);

	chip->usb_icl_votable = create_votable(&spmi->dev,
			"SMBCHG: usb_icl",
			VOTE_MIN,
			NUM_ICL_VOTER, set_usb_current_limit_vote_cb);
			VOTE_MIN, NUM_ICL_VOTER, 3000,
			set_usb_current_limit_vote_cb);
	if (IS_ERR(chip->usb_icl_votable))
		return PTR_ERR(chip->usb_icl_votable);

	chip->dc_icl_votable = create_votable(&spmi->dev,
			"SMBCHG: dcl_icl",
			VOTE_MIN,
			NUM_ICL_VOTER, set_dc_current_limit_vote_cb);
			VOTE_MIN, NUM_ICL_VOTER, 3000,
			set_dc_current_limit_vote_cb);
	if (IS_ERR(chip->dc_icl_votable))
		return PTR_ERR(chip->dc_icl_votable);

	chip->usb_suspend_votable = create_votable(&spmi->dev,
			"SMBCHG: usb_suspend",
					VOTE_SET_ANY,
					NUM_EN_VOTERS, usb_suspend_vote_cb);
			VOTE_SET_ANY, NUM_EN_VOTERS, 0,
			usb_suspend_vote_cb);
	if (IS_ERR(chip->usb_suspend_votable))
		return PTR_ERR(chip->usb_suspend_votable);

	chip->dc_suspend_votable = create_votable(&spmi->dev,
			"SMBCHG: dc_suspend",
					VOTE_SET_ANY,
					NUM_EN_VOTERS, dc_suspend_vote_cb);
			VOTE_SET_ANY, NUM_EN_VOTERS, 0,
			dc_suspend_vote_cb);
	if (IS_ERR(chip->dc_suspend_votable))
		return PTR_ERR(chip->dc_suspend_votable);

	chip->battchg_suspend_votable = create_votable(&spmi->dev,
					"SMBCHG: battchg_en",
					VOTE_SET_ANY,
					NUM_BATTCHG_EN_VOTERS,
			"SMBCHG: battchg_suspend",
			VOTE_SET_ANY, NUM_BATTCHG_EN_VOTERS, 0,
			charging_suspend_vote_cb);
	if (IS_ERR(chip->dc_suspend_votable))
	if (IS_ERR(chip->battchg_suspend_votable))
		return PTR_ERR(chip->battchg_suspend_votable);

	INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);