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

Commit 5e0bfbe5 authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar Committed by Harry Yang
Browse files

smb-lib: fix icl changed interrupt storm



Current driver has two votables for controlling the usb current
and the usb suspend bit. This could lead to a situation where
the icl votable changes current setting while its suspended.
Changing current setting while suspended causes icl changed
interrupt storm.

Fix it by removing usb_suspend_votable and make all the suspend
voters call usb_icl_votable with 0mA to cause suspend.
Update usb_icl_votable to update current settings only when
not suspended. Moreover disable the interrupt while changing
the current settings and enable the interrupt only after the
unsuspend.

Change-Id: Iafc8ee3c934952b8131d374d0529e865424016d5
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent e38fe7e4
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1382,7 +1382,8 @@ static int smb2_init_hw(struct smb2 *chip)
	}

	/* votes must be cast before configuring software control */
	vote(chg->usb_suspend_votable,
	/* vote 0mA on usb_icl for non battery platforms */
	vote(chg->usb_icl_votable,
		DEFAULT_VOTER, chip->dt.no_battery, 0);
	vote(chg->dc_suspend_votable,
		DEFAULT_VOTER, chip->dt.no_battery, 0);
+100 −84
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#include <linux/power_supply.h>
#include <linux/regulator/driver.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/input/qpnp-power-on.h>
#include <linux/irq.h>
#include "smb-lib.h"
#include "smb-reg.h"
@@ -698,7 +697,7 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg)
		return;
	}

	vote(chg->usb_suspend_votable, DEBUG_BOARD_VOTER, val.intval, 0);
	vote(chg->usb_icl_votable, DEBUG_BOARD_VOTER, val.intval, 0);
	vote(chg->dc_suspend_votable, DEBUG_BOARD_VOTER, val.intval, 0);
	if (val.intval)
		pr_info("Input suspended: Fake battery\n");
@@ -741,18 +740,6 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
 * VOTABLE CALLBACKS *
 *********************/

static int smblib_usb_suspend_vote_callback(struct votable *votable, void *data,
			int suspend, const char *client)
{
	struct smb_charger *chg = data;

	/* resume input if suspend is invalid */
	if (suspend < 0)
		suspend = 0;

	return smblib_set_usb_suspend(chg, (bool)suspend);
}

static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
			int suspend, const char *client)
{
@@ -770,44 +757,12 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
#define USBIN_150MA	150000
#define USBIN_500MA	500000
#define USBIN_900MA	900000
static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
			int icl_ua, const char *client)
{
	struct smb_charger *chg = data;
	int rc = 0;
	bool suspend, override;
	u8 icl_options = 0;

	override = true;
	/* remove override if no voters or type = SDP or CDP */
	if (client == NULL
		|| chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB
		|| chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP)
		override = false;

	suspend = false;
	if (client && (icl_ua < USBIN_25MA))
		suspend = true;

	if (suspend)
		goto out;

	if (chg->usb_psy_desc.type != POWER_SUPPLY_TYPE_USB) {
		if (client) {
			rc = smblib_set_charge_param(chg, &chg->param.usb_icl,
					icl_ua - chg->icl_reduction_ua);
			if (rc < 0) {
				smblib_err(chg, "Couldn't set HC ICL rc=%d\n",
					rc);
				return rc;
			}
		}

		smblib_dbg(chg, PR_PARALLEL,
				"icl_ua=%d icl_reduction=%d\n",
				icl_ua, chg->icl_reduction_ua);
		goto out;
	}
static int set_sdp_current(struct smb_charger *chg, int icl_ua)
{
	int rc;
	u8 icl_options;

	/* power source is SDP */
	switch (icl_ua) {
@@ -829,35 +784,97 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
		break;
	default:
		smblib_err(chg, "ICL %duA isn't supported for SDP\n", icl_ua);
		icl_options = 0;
		break;
		return -EINVAL;
	}

out:
	if (override)
		icl_options |= USBIN_MODE_CHG_BIT;

	rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
		CFG_USB3P0_SEL_BIT | USB51_MODE_BIT | USBIN_MODE_CHG_BIT,
		icl_options);
		CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options);
	if (rc < 0) {
		smblib_err(chg, "Couldn't set ICL options rc=%d\n", rc);
		return rc;
	}

	rc = vote(chg->usb_suspend_votable, PD_VOTER, suspend, 0);
	if (rc < 0) {
		smblib_err(chg, "Couldn't %s input rc=%d\n",
			suspend ? "suspend" : "resume", rc);
	return rc;
}

static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
			int icl_ua, const char *client)
{
	struct smb_charger *chg = data;
	int rc = 0;
	bool override;
	union power_supply_propval pval;

	/* suspend and return if 25mA or less is requested */
	if (client && (icl_ua < USBIN_25MA))
		return smblib_set_usb_suspend(chg, true);

	disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);
	if (!client)
		goto override_suspend_config;

	rc = smblib_get_prop_typec_mode(chg, &pval);
	if (rc < 0) {
		smblib_err(chg, "Couldn't get typeC mode rc = %d\n", rc);
		goto enable_icl_changed_interrupt;
	}

	/* configure current */
	if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
		&& (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) {
		rc = set_sdp_current(chg, icl_ua);
		if (rc < 0) {
			smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc);
			goto enable_icl_changed_interrupt;
		}
	} else {
		rc = smblib_set_charge_param(chg, &chg->param.usb_icl,
				icl_ua - chg->icl_reduction_ua);
		if (rc < 0) {
			smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc);
			goto enable_icl_changed_interrupt;
		}
	}

override_suspend_config:
	/* determine if override needs to be enforced */
	override = true;
	if (client == NULL) {
		/* remove override if no voters - hw defaults is desired */
		override = false;
	} else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
		if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)
			/* For std cable with type = SDP never override */
			override = false;
		else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP
			&& icl_ua - chg->icl_reduction_ua == 1500000)
			/*
			 * For std cable with type = CDP override only if
			 * current is not 1500mA
			 */
			override = false;
	}

	/* enforce override */
	rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
		USBIN_MODE_CHG_BIT, override ? USBIN_MODE_CHG_BIT : 0);

	rc = smblib_icl_override(chg, override);
	if (rc < 0) {
		smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc);
		return rc;
		goto enable_icl_changed_interrupt;
	}

	/* unsuspend after configuring current and override */
	rc = smblib_set_usb_suspend(chg, false);
	if (rc < 0) {
		smblib_err(chg, "Couldn't resume input rc=%d\n", rc);
		goto enable_icl_changed_interrupt;
	}

enable_icl_changed_interrupt:
	enable_irq(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);

	return rc;
}

@@ -1269,8 +1286,9 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev)
int smblib_get_prop_input_suspend(struct smb_charger *chg,
				  union power_supply_propval *val)
{
	val->intval = get_client_vote(chg->usb_suspend_votable, USER_VOTER) &&
			get_client_vote(chg->dc_suspend_votable, USER_VOTER);
	val->intval
		= (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0)
		 && get_client_vote(chg->dc_suspend_votable, USER_VOTER);
	return 0;
}

@@ -1567,7 +1585,8 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg,
{
	int rc;

	rc = vote(chg->usb_suspend_votable, USER_VOTER, (bool)val->intval, 0);
	/* vote 0mA when suspended */
	rc = vote(chg->usb_icl_votable, USER_VOTER, (bool)val->intval, 0);
	if (rc < 0) {
		smblib_err(chg, "Couldn't vote to %s USB rc=%d\n",
			(bool)val->intval ? "suspend" : "resume", rc);
@@ -1713,7 +1732,7 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
	int rc = 0;
	u8 stat;

	if (get_client_vote(chg->usb_suspend_votable, USER_VOTER)) {
	if (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) {
		val->intval = false;
		return rc;
	}
@@ -2866,8 +2885,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
		}
	} else {
		if (chg->wa_flags & BOOST_BACK_WA)
			vote(chg->usb_suspend_votable,
						BOOST_BACK_VOTER, false, 0);
			vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);

		if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
			smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
@@ -3149,6 +3167,13 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)

	power_supply_changed(chg->usb_psy);

	rc = smblib_read(chg, APSD_STATUS_REG, &stat);
	if (rc < 0) {
		smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
		return IRQ_HANDLED;
	}
	smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat);

	return IRQ_HANDLED;
}

@@ -3244,12 +3269,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)

	chg->vconn_attempts = 0;
	chg->otg_attempts = 0;
	typec_source_removal(chg);
	typec_sink_removal(chg);

	chg->usb_ever_removed = true;

	smblib_update_usb_type(chg);

	typec_source_removal(chg);
	typec_sink_removal(chg);
}

static void smblib_handle_typec_insertion(struct smb_charger *chg,
@@ -3432,15 +3458,15 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
	}

	if ((stat & USE_USBIN_BIT) &&
				get_effective_result(chg->usb_suspend_votable))
			get_effective_result(chg->usb_icl_votable) < USBIN_25MA)
		return IRQ_HANDLED;

	if (stat & USE_DCIN_BIT)
		return IRQ_HANDLED;

	if (is_storming(&irq_data->storm_data)) {
		smblib_err(chg, "Reverse boost detected: suspending input\n");
		vote(chg->usb_suspend_votable, BOOST_BACK_VOTER, true, 0);
		smblib_err(chg, "Reverse boost detected: voting 0mA to suspend input\n");
		vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
	}

	return IRQ_HANDLED;
@@ -3789,14 +3815,6 @@ static int smblib_create_votables(struct smb_charger *chg)
	vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
	vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0);

	chg->usb_suspend_votable = create_votable("USB_SUSPEND", VOTE_SET_ANY,
					smblib_usb_suspend_vote_callback,
					chg);
	if (IS_ERR(chg->usb_suspend_votable)) {
		rc = PTR_ERR(chg->usb_suspend_votable);
		return rc;
	}

	chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY,
					smblib_dc_suspend_vote_callback,
					chg);
@@ -3894,8 +3912,6 @@ static int smblib_create_votables(struct smb_charger *chg)

static void smblib_destroy_votables(struct smb_charger *chg)
{
	if (chg->usb_suspend_votable)
		destroy_votable(chg->usb_suspend_votable);
	if (chg->dc_suspend_votable)
		destroy_votable(chg->dc_suspend_votable);
	if (chg->usb_icl_votable)
+0 −1
Original line number Diff line number Diff line
@@ -252,7 +252,6 @@ struct smb_charger {
	struct regulator	*dpdm_reg;

	/* votables */
	struct votable		*usb_suspend_votable;
	struct votable		*dc_suspend_votable;
	struct votable		*fcc_votable;
	struct votable		*fv_votable;
+0 −2
Original line number Diff line number Diff line
@@ -827,8 +827,6 @@ static int smb138x_init_hw(struct smb138x *chip)
	int rc = 0;

	/* votes must be cast before configuring software control */
	vote(chg->usb_suspend_votable,
		DEFAULT_VOTER, chip->dt.suspend_input, 0);
	vote(chg->dc_suspend_votable,
		DEFAULT_VOTER, chip->dt.suspend_input, 0);
	vote(chg->fcc_votable,