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: Nicholas Troast <ntroast@codeaurora.org>
This commit is contained in:
Nicholas Troast 2015-10-19 17:13:42 -07:00 committed by Gerrit - the friendly Code Review server
parent 0d08b01bc8
commit d2d51db4a3
3 changed files with 66 additions and 39 deletions

View File

@ -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;
}

View File

@ -27,14 +27,15 @@ 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 (*callback)(struct device *dev,
int effective_result,
int effective_client,
int last_result,
int last_client)
int votable_type, int num_clients,
int default_result,
int (*callback)(struct device *dev,
int effective_result,
int effective_client,
int last_result,
int last_client)
);
void lock_votable(struct votable *votable);
void unlock_votable(struct votable *votable);

View File

@ -7410,45 +7410,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);
"SMBCHG: usb_suspend",
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);
"SMBCHG: dc_suspend",
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,
charging_suspend_vote_cb);
if (IS_ERR(chip->dc_suspend_votable))
"SMBCHG: battchg_suspend",
VOTE_SET_ANY, NUM_BATTCHG_EN_VOTERS, 0,
charging_suspend_vote_cb);
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);