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: Ashay Jaiswal <ashayj@codeaurora.org>
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 06429e8..84f1e55 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -629,6 +629,8 @@
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 @@
rc);
}
- 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);
+ }
+ }
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
@@ -3144,6 +3154,8 @@
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 @@
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 @@
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 @@
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 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 @@
rc);
}
- 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);
+ }
+ }
/* reset APSD voters */
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
@@ -3837,10 +3881,13 @@
}
#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,16 +3909,32 @@
return IRQ_HANDLED;
if (is_storming(&irq_data->storm_data)) {
- 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.
- */
- schedule_delayed_work(&chg->bb_removal_work,
- msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS));
+ /* 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.
+ */
+ schedule_delayed_work(&chg->bb_removal_work,
+ msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS));
+ }
}
return IRQ_HANDLED;