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

Commit 2f3b0c11 authored by Ashay Jaiswal's avatar Ashay Jaiswal Committed by Abhijeet Dharmapurikar
Browse files

power: smb-lib: Add support to detect weak charger



A weak charger might trigger switcher_power_ok interrupt storm
which gets incorrectly detected as a boost_back condition and
input gets suspended. Add a logic to distinguish a weak charger
and the boost_back condition by first reducing the ICL current
to a lower value (500mA by default) on detecting a
switcher_power_ok storm. If the switcher_ok storm disappears
then its indeed a weak charger and if the storm continues its
possibly a boost_back condition.

Change-Id: I46b406e403aa16a502e6da149b180545848fc906
Signed-off-by: default avatarAshay Jaiswal <ashayj@codeaurora.org>
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent 36feefef
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -266,6 +266,10 @@ module_param_named(
	debug_mask, __debug_mask, int, 0600
);

static int __weak_chg_icl_ua = 500000;
module_param_named(
	weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600);

#define MICRO_1P5A		1500000
#define MICRO_P1A		100000
#define OTG_DEFAULT_DEGLITCH_TIME_MS	50
@@ -2113,7 +2117,7 @@ static struct smb_irq_info smb2_irqs[] = {
	[SWITCH_POWER_OK_IRQ] = {
		.name		= "switcher-power-ok",
		.handler	= smblib_handle_switcher_power_ok,
		.storm_data	= {true, 1000, 3},
		.storm_data	= {true, 1000, 8},
	},
};

@@ -2307,6 +2311,7 @@ static int smb2_probe(struct platform_device *pdev)
	chg->dev = &pdev->dev;
	chg->param = v1_params;
	chg->debug_mask = &__debug_mask;
	chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
	chg->mode = PARALLEL_MASTER;
	chg->irq_info = smb2_irqs;
	chg->name = "PMI";
+81 −18
Original line number Diff line number Diff line
@@ -629,6 +629,8 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param,
static void smblib_uusb_removal(struct smb_charger *chg)
{
	int rc;
	struct smb_irq_data *data;
	struct storm_watch *wdata;

	cancel_delayed_work_sync(&chg->pl_enable_work);

@@ -640,8 +642,16 @@ static void smblib_uusb_removal(struct smb_charger *chg)
				rc);
	}

	if (chg->wa_flags & BOOST_BACK_WA)
	if (chg->wa_flags & BOOST_BACK_WA) {
		data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
		if (data) {
			wdata = &data->storm_data;
			update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
			vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
			vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
					false, 0);
		}
	}
	vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
	vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);

@@ -3144,6 +3154,8 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg)
	int rc;
	u8 stat;
	bool vbus_rising;
	struct smb_irq_data *data;
	struct storm_watch *wdata;

	rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
	if (rc < 0) {
@@ -3157,8 +3169,18 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg)
		smblib_cc2_sink_removal_exit(chg);
	} else {
		smblib_cc2_sink_removal_enter(chg);
		if (chg->wa_flags & BOOST_BACK_WA)
			vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
		if (chg->wa_flags & BOOST_BACK_WA) {
			data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
			if (data) {
				wdata = &data->storm_data;
				update_storm_count(wdata,
						WEAK_CHG_STORM_COUNT);
				vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
						false, 0);
				vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
						false, 0);
			}
		}
	}

	power_supply_changed(chg->usb_psy);
@@ -3172,6 +3194,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
	int rc;
	u8 stat;
	bool vbus_rising;
	struct smb_irq_data *data;
	struct storm_watch *wdata;

	rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
	if (rc < 0) {
@@ -3208,8 +3232,18 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
		schedule_delayed_work(&chg->pl_enable_work,
					msecs_to_jiffies(PL_DELAY_MS));
	} else {
		if (chg->wa_flags & BOOST_BACK_WA)
			vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
		if (chg->wa_flags & BOOST_BACK_WA) {
			data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
			if (data) {
				wdata = &data->storm_data;
				update_storm_count(wdata,
						WEAK_CHG_STORM_COUNT);
				vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
						false, 0);
				vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
						false, 0);
			}
		}

		if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
			smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
@@ -3593,6 +3627,8 @@ static void typec_sink_removal(struct smb_charger *chg)
static void smblib_handle_typec_removal(struct smb_charger *chg)
{
	int rc;
	struct smb_irq_data *data;
	struct storm_watch *wdata;

	chg->cc2_detach_wa_active = false;

@@ -3604,8 +3640,16 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
				rc);
	}

	if (chg->wa_flags & BOOST_BACK_WA)
	if (chg->wa_flags & BOOST_BACK_WA) {
		data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
		if (data) {
			wdata = &data->storm_data;
			update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
			vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
			vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
					false, 0);
		}
	}

	/* reset APSD voters */
	vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
@@ -3837,10 +3881,13 @@ static void smblib_bb_removal_work(struct work_struct *work)
}

#define BOOST_BACK_UNVOTE_DELAY_MS		750
#define BOOST_BACK_STORM_COUNT			3
#define WEAK_CHG_STORM_COUNT			8
irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
{
	struct smb_irq_data *irq_data = data;
	struct smb_charger *chg = irq_data->parent_data;
	struct storm_watch *wdata = &irq_data->storm_data;
	int rc, usb_icl;
	u8 stat;

@@ -3862,17 +3909,33 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
		return IRQ_HANDLED;

	if (is_storming(&irq_data->storm_data)) {
		smblib_err(chg, "Reverse boost detected: voting 0mA to suspend input\n");
		/* This could be a weak charger reduce ICL */
		if (!is_client_vote_enabled(chg->usb_icl_votable,
						WEAK_CHARGER_VOTER)) {
			smblib_err(chg,
				"Weak charger detected: voting %dmA ICL\n",
				*chg->weak_chg_icl_ua / 1000);
			vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
					true, *chg->weak_chg_icl_ua);
			/*
			 * reset storm data and set the storm threshold
			 * to 3 for reverse boost detection.
			 */
			update_storm_count(wdata, BOOST_BACK_STORM_COUNT);
		} else {
			smblib_err(chg,
				"Reverse boost detected: voting 0mA to suspend input\n");
			vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
			vote(chg->awake_votable, BOOST_BACK_VOTER, true, 0);
			/*
			 * Remove the boost-back vote after a delay, to avoid
		 * permanently suspending the input if the boost-back condition
		 * is unintentionally hit.
			 * permanently suspending the input if the boost-back
			 * condition is unintentionally hit.
			 */
			schedule_delayed_work(&chg->bb_removal_work,
				msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS));
		}
	}

	return IRQ_HANDLED;
}
+4 −0
Original line number Diff line number Diff line
@@ -64,9 +64,12 @@ enum print_reason {
#define BATT_PROFILE_VOTER		"BATT_PROFILE_VOTER"
#define OTG_DELAY_VOTER			"OTG_DELAY_VOTER"
#define USBIN_I_VOTER			"USBIN_I_VOTER"
#define WEAK_CHARGER_VOTER		"WEAK_CHARGER_VOTER"

#define VCONN_MAX_ATTEMPTS	3
#define OTG_MAX_ATTEMPTS	3
#define BOOST_BACK_STORM_COUNT	3
#define WEAK_CHG_STORM_COUNT	8

enum smb_mode {
	PARALLEL_MASTER = 0,
@@ -230,6 +233,7 @@ struct smb_charger {
	struct smb_chg_freq	chg_freq;
	int			smb_version;
	int			otg_delay_ms;
	int			*weak_chg_icl_ua;

	/* locks */
	struct mutex		lock;
+10 −0
Original line number Diff line number Diff line
@@ -64,3 +64,13 @@ void reset_storm_count(struct storm_watch *data)
	data->storm_count = 0;
	mutex_unlock(&data->storm_lock);
}

void update_storm_count(struct storm_watch *data, int max_count)
{
	if (!data)
		return;

	mutex_lock(&data->storm_lock);
	data->max_storm_count = max_count;
	mutex_unlock(&data->storm_lock);
}
+1 −0
Original line number Diff line number Diff line
@@ -37,4 +37,5 @@ struct storm_watch {

bool is_storming(struct storm_watch *data);
void reset_storm_count(struct storm_watch *data);
void update_storm_count(struct storm_watch *data, int max_count);
#endif