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:
parent
0d08b01bc8
commit
d2d51db4a3
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue