From 2dad6f26dc9039672ba600d237cb7172c426dc94 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Fri, 16 Sep 2016 11:07:45 -0700 Subject: [PATCH] qpnp-smb2: fix reverse boost when input is removed When any input is removed it is likely that reverse boost can happen. Detect reverse boost by checking if the switcher-power-ok interrupt triggers 3 times within 1 second. If detected then suspend all input. Once VBUS falls the input can be resumed for the next insertion. Change-Id: I3dbe4fe426111023b60eefd968c426be7d6057b9 Signed-off-by: Nicholas Troast --- drivers/power/qcom-charger/qpnp-smb2.c | 4 +- drivers/power/qcom-charger/smb-lib.c | 59 ++++++++++++++++++++++---- drivers/power/qcom-charger/smb-lib.h | 3 ++ 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index f9d76c56aa2e..0e2f5afb9416 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -1241,6 +1241,7 @@ static int smb2_setup_wa_flags(struct smb2 *chip) switch (pmic_rev_id->pmic_subtype) { case PMICOBALT_SUBTYPE: + chip->chg.wa_flags |= BOOST_BACK_WA; if (pmic_rev_id->rev4 == PMICOBALT_V1P1_REV4) /* PMI rev 1.1 */ chg->wa_flags |= QC_CHARGER_DETECTION_WA_BIT; break; @@ -1451,7 +1452,8 @@ static struct smb2_irq_info smb2_irqs[] = { }, { .name = "switcher-power-ok", - .handler = smblib_handle_debug, + .handler = smblib_handle_switcher_power_ok, + .storm_data = {true, 1000, 3}, }, }; diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 198e77469bbe..7a542c59f496 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -596,7 +596,11 @@ static int smblib_usb_suspend_vote_callback(struct votable *votable, void *data, { struct smb_charger *chg = data; - return smblib_set_usb_suspend(chg, suspend); + /* 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, @@ -604,10 +608,11 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, { struct smb_charger *chg = data; + /* resume input if suspend is invalid */ if (suspend < 0) - suspend = false; + suspend = 0; - return smblib_set_dc_suspend(chg, suspend); + return smblib_set_dc_suspend(chg, (bool)suspend); } static int smblib_fcc_max_vote_callback(struct votable *votable, void *data, @@ -2230,11 +2235,8 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } } - if (!chg->dpdm_reg) - goto skip_dpdm_float; - if (chg->vbus_present) { - if (!regulator_is_enabled(chg->dpdm_reg)) { + if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) { smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); rc = regulator_enable(chg->dpdm_reg); if (rc < 0) @@ -2242,7 +2244,14 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) rc); } } else { - if (regulator_is_enabled(chg->dpdm_reg)) { + if (chg->wa_flags & BOOST_BACK_WA) { + vote(chg->usb_suspend_votable, + BOOST_BACK_VOTER, false, 0); + vote(chg->dc_suspend_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"); rc = regulator_disable(chg->dpdm_reg); if (rc < 0) @@ -2251,7 +2260,6 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } } -skip_dpdm_float: power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", irq_data->name, chg->vbus_present ? "attached" : "detached"); @@ -2683,6 +2691,39 @@ irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data) return IRQ_HANDLED; } +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; + int rc; + u8 stat; + + if (!(chg->wa_flags & BOOST_BACK_WA)) + return IRQ_HANDLED; + + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", rc); + return IRQ_HANDLED; + } + + if ((stat & USE_USBIN_BIT) && + get_effective_result(chg->usb_suspend_votable)) + return IRQ_HANDLED; + + if ((stat & USE_DCIN_BIT) && + get_effective_result(chg->dc_suspend_votable)) + return IRQ_HANDLED; + + if (is_storming(&irq_data->storm_data)) { + smblib_dbg(chg, PR_MISC, "reverse boost detected; suspending input\n"); + vote(chg->usb_suspend_votable, BOOST_BACK_VOTER, true, 0); + vote(chg->dc_suspend_votable, BOOST_BACK_VOTER, true, 0); + } + + return IRQ_HANDLED; +} + /*************** * Work Queues * ***************/ diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 4be06ffcfb25..61168f7f71e6 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -46,6 +46,7 @@ enum print_reason { #define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER" #define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER" #define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER" +#define BOOST_BACK_VOTER "BOOST_BACK_VOTER" enum smb_mode { PARALLEL_MASTER = 0, @@ -55,6 +56,7 @@ enum smb_mode { enum { QC_CHARGER_DETECTION_WA_BIT = BIT(0), + BOOST_BACK_WA = BIT(1), }; struct smb_regulator { @@ -240,6 +242,7 @@ irqreturn_t smblib_handle_icl_change(int irq, void *data); irqreturn_t smblib_handle_usb_typec_change(int irq, void *data); irqreturn_t smblib_handle_dc_plugin(int irq, void *data); irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data); +irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data); int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val);