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

Commit 6bd558aa authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar Committed by Ankit Sharma
Browse files

pmic-voter: remove default value



The default value was introduced to prevent crashes on the clients
where it couldn't handle situations when there were no voters.
The default value led to many complicated scenarios in the votable
implementation.

Remove the concept of default value and return error when
there are no votes. Update all the clients to handle error values
from votables. Also invoke the callback to inform about no active
voters.

CRs-Fixed: 1018090
Change-Id: I81f5974f81a697f3698bc58df1d3ed59fa2579a7
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent 878c6205
Loading
Loading
Loading
Loading
+132 −20
Original line number Diff line number Diff line
@@ -40,26 +40,55 @@ 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,
						const char *effective_client);
	char			*client_strs[NUM_MAX_CLIENTS];
	bool			voted_on;
	struct dentry		*ent;
};

/**
 * vote_set_any()
 * @votable:	votable object
 * @client_id:	client number of the latest voter
 * @eff_res:	sets 0 or 1 based on the voting
 * @eff_id:	Always returns the client_id argument
 *
 * Note that for SET_ANY voter, the value is always same as enabled. There is
 * no idea of a voter abstaining from the election. Hence there is never a
 * situation when the effective_id will be invalid, during election.
 *
 * Context:
 *	Must be called with the votable->lock held
 */
static void vote_set_any(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int i;

	*eff_res = 0;

	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++)
		*eff_res |= votable->votes[i].enabled;

	*eff_id = client_id;
}

/**
 * vote_min() -
 * @votable:	votable object
 * @client_id:	client number of the latest voter
 * @eff_res:	sets this to the min. of all the values amongst enabled voters.
 *		If there is no enabled client, this is set to INT_MAX
 * @eff_id:	sets this to the client id that has the min value amongst all
 *		the enabled clients. If there is no enabled client, sets this
 *		to -EINVAL
 *
 * Context:
 *	Must be called with the votable->lock held
 */
static void vote_min(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
@@ -74,8 +103,23 @@ static void vote_min(struct votable *votable, int client_id,
			*eff_id = i;
		}
	}
	if (*eff_id == -EINVAL)
		*eff_res = -EINVAL;
}

/**
 * vote_max() -
 * @votable:	votable object
 * @client_id:	client number of the latest voter
 * @eff_res:	sets this to the max. of all the values amongst enabled voters.
 *		If there is no enabled client, this is set to -EINVAL
 * @eff_id:	sets this to the client id that has the max value amongst all
 *		the enabled clients. If there is no enabled client, sets this to
 *		-EINVAL
 *
 * Context:
 *	Must be called with the votable->lock held
 */
static void vote_max(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
@@ -90,6 +134,8 @@ static void vote_max(struct votable *votable, int client_id,
			*eff_id = i;
		}
	}
	if (*eff_id == -EINVAL)
		*eff_res = -EINVAL;
}

static int get_client_id(struct votable *votable, const char *client_str)
@@ -134,16 +180,28 @@ void unlock_votable(struct votable *votable)
	mutex_unlock(&votable->vote_lock);
}

/**
 * get_client_vote() -
 * get_client_vote_locked() -
 *		The unlocked and locked variants of getting a client's voted
 *		value.
 * @votable:	the votable object
 * @client_str: client of interest
 *
 * Returns:
 *	The value the client voted for. -EINVAL is returned if the client
 *	is not enabled or the client is not found.
 */
int get_client_vote_locked(struct votable *votable, const char *client_str)
{
	int client_id = get_client_id(votable, client_str);

	if (client_id < 0)
		return votable->default_result;
		return -EINVAL;

	if ((votable->type != VOTE_SET_ANY)
		&& !votable->votes[client_id].enabled)
		return votable->default_result;
		return -EINVAL;

	return votable->votes[client_id].value;
}
@@ -158,11 +216,25 @@ int get_client_vote(struct votable *votable, const char *client_str)
	return value;
}

/**
 * get_effective_result() -
 * get_effective_result_locked() -
 *		The unlocked and locked variants of getting the effective value
 *		amongst all the enabled voters.
 *
 * @votable:	the votable object
 *
 * Returns:
 *	The effective result.
 *	For MIN and MAX votable, returns -EINVAL when the votable
 *	object has been created but no clients have casted their votes or
 *	the last enabled client disables its vote.
 *	For SET_ANY votable it returns 0 when no clients have casted their votes
 *	because for SET_ANY there is no concept of abstaining from election. The
 *	votes for all the clients of SET_ANY votable is defaulted to false.
 */
int get_effective_result_locked(struct votable *votable)
{
	if (votable->effective_result < 0)
		return votable->default_result;

	return votable->effective_result;
}

@@ -191,12 +263,36 @@ const char *get_effective_client(struct votable *votable)
	return client_str;
}

/**
 * vote() -
 *
 * @votable:	the votable object
 * @client_str: the voting client
 * @enabled:	This provides a means for the client to exclude himself from
 *		election. This clients val (the next argument) will be
 *		considered only when he has enabled his participation.
 *		Note that this takes a differnt meaning for SET_ANY type, as
 *		there is no concept of abstaining from participation.
 *		Enabled is treated as the boolean value the client is voting.
 * @val:	The vote value. This is ignored for SET_ANY votable types.
 *		For MIN, MAX votable types this value is used as the
 *		clients vote value when the enabled is true, this value is
 *		ignored if enabled is false.
 *
 * The callback is called only when there is a change in the election results or
 * if it is the first time someone is voting.
 *
 * Returns:
 *	The return from the callback when present and needs to be called
 *	or zero.
 */
int vote(struct votable *votable, const char *client_str, bool enabled, int val)
{
	int effective_id = -EINVAL;
	int effective_result;
	int client_id;
	int rc = 0;
	bool similar_vote = false;

	lock_votable(votable);

@@ -206,19 +302,34 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val)
		goto out;
	}

	if (votable->votes[client_id].enabled == enabled &&
				votable->votes[client_id].value == val) {
	/*
	 * for SET_ANY the val is to be ignored, set it
	 * to enabled so that the election still works based on
	 * value regardless of the type
	 */
	if (votable->type == VOTE_SET_ANY)
		val = enabled;

	if ((votable->votes[client_id].enabled == enabled) &&
		(votable->votes[client_id].value == val)) {
		pr_debug("%s: %s,%d same vote %s of %d\n",
				votable->name,
				client_str, client_id,
				enabled ? "on" : "off",
				val);
		goto out;
		similar_vote = true;
	}

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

	if (similar_vote && votable->voted_on) {
		pr_debug("%s: %s,%d Similar vote %s of %d\n",
			votable->name,
			client_str, client_id, enabled ? "on" : "off", val);
		goto out;
	}

	pr_debug("%s: %s,%d voting %s of %d\n",
		votable->name,
		client_str, client_id, enabled ? "on" : "off", val);
@@ -233,18 +344,16 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val)
		vote_set_any(votable, client_id,
				&effective_result, &effective_id);
		break;
	default:
		return -EINVAL;
	}

	/*
	 * If the votable does not have any votes it will maintain the last
	 * known effective_result and effective_client_id
	 * Note that the callback is called with a NULL string and -EINVAL
	 * result when there are no enabled votes
	 */
	if (effective_id < 0) {
		pr_debug("%s: no votes; skipping callback\n", votable->name);
		goto out;
	}

	if (effective_result != votable->effective_result) {
	if (!votable->voted_on
			|| (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 %s,%d\n",
@@ -256,6 +365,7 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val)
					get_client_str(votable, effective_id));
	}

	votable->voted_on = true;
out:
	unlock_votable(votable);
	return rc;
@@ -304,6 +414,7 @@ static int show_votable_clients(struct seq_file *m, void *data)
	struct votable *votable = m->private;
	int i;
	char *type_str = "Unkonwn";
	const char *effective_client_str;

	lock_votable(votable);

@@ -331,8 +442,9 @@ static int show_votable_clients(struct seq_file *m, void *data)

	seq_printf(m, "Type: %s\n", type_str);
	seq_puts(m, "Effective:\n");
	effective_client_str = get_effective_client_locked(votable);
	seq_printf(m, "%-15s:\t\tv=%d\n",
			get_effective_client_locked(votable),
			effective_client_str ? effective_client_str : "none",
			get_effective_result_locked(votable));
	unlock_votable(votable);

@@ -356,7 +468,6 @@ static const struct file_operations votable_debugfs_ops = {

struct votable *create_votable(struct device *dev, const char *name,
					int votable_type,
					int default_result,
					int (*callback)(struct device *dev,
						int effective_result,
						const char *effective_client)
@@ -391,7 +502,6 @@ struct votable *create_votable(struct device *dev, const char *name,
	votable->num_clients = NUM_MAX_CLIENTS;
	votable->callback = callback;
	votable->type = votable_type;
	votable->default_result = default_result;
	mutex_init(&votable->vote_lock);

	/*
@@ -399,6 +509,8 @@ struct votable *create_votable(struct device *dev, const char *name,
	 * before the first vote, initialize them to -EINVAL
	 */
	votable->effective_result = -EINVAL;
	if (votable->type == VOTE_SET_ANY)
		votable->effective_result = 0;
	votable->effective_client_id = -EINVAL;

	spin_lock_irqsave(&votable_list_slock, flags);
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ int vote(struct votable *votable, const char *client_str, bool state, int val);
int rerun_election(struct votable *votable);
struct votable *find_votable(const char *name);
struct votable *create_votable(struct device *dev, const char *name,
				int votable_type, int default_result,
				int votable_type,
				int (*callback)(struct device *dev,
						int effective_result,
						const char *effective_client)
+98 −21
Original line number Diff line number Diff line
@@ -2159,6 +2159,7 @@ restore_icl:
static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
{
	struct power_supply *parallel_psy = get_parallel_psy(chip);
	int fcc_ma, usb_icl_ma;

	if (!parallel_psy || !chip->parallel_charger_detected)
		return;
@@ -2171,10 +2172,19 @@ static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
	power_supply_set_current_limit(parallel_psy,
				SUSPEND_CURRENT_MA * 1000);
	power_supply_set_present(parallel_psy, false);
	smbchg_set_fastchg_current_raw(chip,
			get_effective_result_locked(chip->fcc_votable));
	smbchg_set_usb_current_max(chip,
			get_effective_result_locked(chip->usb_icl_votable));

	fcc_ma = get_effective_result_locked(chip->fcc_votable);
	usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
	if (fcc_ma < 0)
		pr_err("no voters for fcc, skip it\n");
	else
		smbchg_set_fastchg_current_raw(chip, fcc_ma);

	if (usb_icl_ma < 0)
		pr_err("no voters for usb_icl, skip it\n");
	else
		smbchg_set_usb_current_max(chip, usb_icl_ma);

	smbchg_rerun_aicl(chip);
}

@@ -2267,6 +2277,10 @@ static void smbchg_parallel_usb_enable(struct smbchg_chip *chip,
	}
	/* Set USB ICL */
	target_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
	if (target_icl_ma < 0) {
		pr_err("no voters for usb_icl, skip it\n");
		return;
	}

	pmi_icl_ma = total_current_ma * smbchg_main_chg_icl_percent / 100;
	pmi_icl_ma = max(chip->parallel.min_main_icl_ma, pmi_icl_ma);
@@ -2291,10 +2305,12 @@ static void smbchg_parallel_usb_enable(struct smbchg_chip *chip,

	/* begin splitting the fast charge current */
	fcc_ma = get_effective_result_locked(chip->fcc_votable);
	parallel_chg_fcc_percent =
		100 - smbchg_main_chg_fcc_percent;
	target_parallel_fcc_ma =
		(fcc_ma * parallel_chg_fcc_percent) / 100;
	if (fcc_ma < 0) {
		pr_err("no voters for fcc, skip it\n");
		return;
	}
	parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent;
	target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100;
	pval.intval = target_parallel_fcc_ma * 1000;
	parallel_psy->set_property(parallel_psy,
			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
@@ -2340,6 +2356,15 @@ static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip,
		return false;
	}

	if (fcc_ma < 0) {
		pr_err("no voters for fcc! Can't enable parallel\n");
		return false;
	}
	if (usb_icl_ma < 0) {
		pr_err("no voters for usb_icl, Can't enable parallel\n");
		return false;
	}

	kt_since_last_disable = ktime_sub(ktime_get_boottime(),
					chip->parallel.last_disabled);
	if (chip->parallel.current_max_ma == 0
@@ -2579,6 +2604,11 @@ static int charging_suspend_vote_cb(struct device *dev, int suspend,
	int rc;
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (suspend < 0) {
		pr_err("No voters\n");
		suspend = false;
	}

	rc = smbchg_charging_en(chip, !suspend);
	if (rc < 0) {
		dev_err(chip->dev,
@@ -2595,6 +2625,11 @@ static int usb_suspend_vote_cb(struct device *dev, int suspend,
	int rc;
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (suspend < 0) {
		pr_err("No voters\n");
		suspend = false;
	}

	rc = smbchg_usb_suspend(chip, suspend);
	if (rc < 0)
		return rc;
@@ -2614,6 +2649,11 @@ static int dc_suspend_vote_cb(struct device *dev, int suspend,
	int rc;
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (suspend < 0) {
		pr_err("No voters\n");
		suspend = false;
	}

	rc = smbchg_dc_suspend(chip, suspend);
	if (rc < 0)
		return rc;
@@ -2649,6 +2689,11 @@ static int set_fastchg_current_vote_cb(struct device *dev,
	struct smbchg_chip *chip = dev_get_drvdata(dev);
	int rc;

	if (fcc_ma < 0) {
		pr_err("No voters\n");
		return 0;
	}

	if (chip->parallel.current_max_ma == 0) {
		rc = smbchg_set_fastchg_current_raw(chip, fcc_ma);
		if (rc < 0) {
@@ -2934,6 +2979,11 @@ static int set_dc_current_limit_vote_cb(struct device *dev,
{
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (icl_ma < 0) {
		pr_err("No voters\n");
		return 0;
	}

	return smbchg_set_dc_current_max(chip, icl_ma);
}

@@ -2949,6 +2999,11 @@ static int set_usb_current_limit_vote_cb(struct device *dev,
	int rc, aicl_ma;
	const char *effective_client;

	if (icl_ma < 0) {
		pr_err("No voters\n");
		return 0;
	}

	effective_client = get_effective_client_locked(chip->usb_icl_votable);

	/* disable parallel charging if HVDCP is voting for 300mA */
@@ -3567,6 +3622,10 @@ static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct device *dev,
	int rc = 0;
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (enable < 0) {
		pr_err("No voters\n");
		enable = 0;
	}
	/*
	 * If the indirect voting result of all the clients is to enable hw aicl
	 * rerun, then remove our vote to disable hw aicl rerun
@@ -3587,6 +3646,11 @@ static int smbchg_hw_aicl_rerun_disable_cb(struct device *dev, int disable,
	int rc = 0;
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (disable < 0) {
		pr_err("No voters\n");
		disable = 0;
	}

	rc = smbchg_sec_masked_write(chip,
		chip->misc_base + MISC_TRIM_OPT_15_8,
		AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON);
@@ -3602,6 +3666,11 @@ static int smbchg_aicl_deglitch_config_cb(struct device *dev, int shorter,
	int rc = 0;
	struct smbchg_chip *chip = dev_get_drvdata(dev);

	if (shorter < 0) {
		pr_err("No voters\n");
		shorter = 0;
	}

	rc = smbchg_sec_masked_write(chip,
		chip->usb_chgpth_base + USB_AICL_CFG,
		USB_AICL_DEGLITCH_MASK,
@@ -6166,8 +6235,12 @@ static int smbchg_battery_get_property(struct power_supply *psy,
		val->intval = get_prop_batt_present(chip);
		break;
	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
		val->intval =
			!get_effective_result(chip->battchg_suspend_votable);
		val->intval
			= get_effective_result(chip->battchg_suspend_votable);
		if (val->intval < 0) /* no votes */
			val->intval = 1;
		else
			val->intval = !val->intval;
		break;
	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
		val->intval = chip->chg_enabled;
@@ -6304,7 +6377,11 @@ static int smbchg_dc_get_property(struct power_supply *psy,
		val->intval = is_dc_present(chip);
		break;
	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
		val->intval = !get_effective_result(chip->dc_suspend_votable);
		val->intval = get_effective_result(chip->dc_suspend_votable);
		if (val->intval < 0) /* no votes */
			val->intval = 1;
		else
			val->intval = !val->intval;
		break;
	case POWER_SUPPLY_PROP_ONLINE:
		/* return if dc is charging the battery */
@@ -8341,70 +8418,70 @@ static int smbchg_probe(struct spmi_device *spmi)

	chip->fcc_votable = create_votable(&spmi->dev,
			"BATT_FCC",
			VOTE_MIN, 2000,
			VOTE_MIN,
			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,
			"USB_ICL",
			VOTE_MIN, 3000,
			VOTE_MIN,
			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,
			"DCIN_ICL",
			VOTE_MIN, 3000,
			VOTE_MIN,
			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,
			"USB_SUSPEND",
			VOTE_SET_ANY, 0,
			VOTE_SET_ANY,
			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,
			"DC_SUSPEND",
			VOTE_SET_ANY, 0,
			VOTE_SET_ANY,
			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,
			"BATTCHG_SUSPEND",
			VOTE_SET_ANY, 0,
			VOTE_SET_ANY,
			charging_suspend_vote_cb);
	if (IS_ERR(chip->battchg_suspend_votable))
		return PTR_ERR(chip->battchg_suspend_votable);

	chip->hw_aicl_rerun_disable_votable = create_votable(&spmi->dev,
			"HWAICL_DISABLE",
			VOTE_SET_ANY, 0,
			VOTE_SET_ANY,
			smbchg_hw_aicl_rerun_disable_cb);
	if (IS_ERR(chip->hw_aicl_rerun_disable_votable))
		return PTR_ERR(chip->hw_aicl_rerun_disable_votable);

	chip->hw_aicl_rerun_enable_indirect_votable = create_votable(&spmi->dev,
			"HWAICL_ENABLE_INDIRECT",
			VOTE_SET_ANY, 0,
			VOTE_SET_ANY,
			smbchg_hw_aicl_rerun_enable_indirect_cb);
	if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable))
		return PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);

	chip->aicl_deglitch_short_votable = create_votable(&spmi->dev,
			"HWAICL_SHORT_DEGLITCH",
			VOTE_SET_ANY, 0,
			VOTE_SET_ANY,
			smbchg_aicl_deglitch_config_cb);
	if (IS_ERR(chip->aicl_deglitch_short_votable))
		return PTR_ERR(chip->aicl_deglitch_short_votable);

	chip->hvdcp_enable_votable = create_votable(&spmi->dev,
			"HVDCP_ENABLE",
			VOTE_MIN, 1,
			VOTE_MIN,
			smbchg_hvdcp_enable_cb);
	if (IS_ERR(chip->hvdcp_enable_votable))
		return PTR_ERR(chip->hvdcp_enable_votable);