summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAshay Jaiswal <ashayj@codeaurora.org>2017-06-14 16:04:45 +0530
committerAshay Jaiswal <ashayj@codeaurora.org>2017-06-21 19:22:08 +0530
commit7a071fadb175463dcd24cb1de3f4e6078d5dfb2d (patch)
tree57c14492185c1c1a4b827c271d7d181c534274c2 /drivers
parentf11fd649ff6594a4614894ae710784b22e7d1289 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c7
-rw-r--r--drivers/power/supply/qcom/smb-lib.c99
-rw-r--r--drivers/power/supply/qcom/smb-lib.h4
-rw-r--r--drivers/power/supply/qcom/storm-watch.c10
-rw-r--r--drivers/power/supply/qcom/storm-watch.h1
5 files changed, 102 insertions, 19 deletions
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 803851d2b3ef..49cd7aa6fdac 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -266,6 +266,10 @@ module_param_named(
debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR
);
+static int __weak_chg_icl_ua = 500000;
+module_param_named(
+ weak_chg_icl_ua, __weak_chg_icl_ua, int, S_IRUSR | S_IWUSR);
+
#define MICRO_1P5A 1500000
#define MICRO_P1A 100000
#define OTG_DEFAULT_DEGLITCH_TIME_MS 50
@@ -2109,7 +2113,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},
},
};
@@ -2303,6 +2307,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";
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 49a5a17a521b..22944f122c44 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -630,6 +630,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);
@@ -641,8 +643,16 @@ static void smblib_uusb_removal(struct smb_charger *chg)
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);
@@ -3138,6 +3148,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) {
@@ -3151,8 +3163,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);
@@ -3166,6 +3188,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) {
@@ -3202,8 +3226,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");
@@ -3582,6 +3616,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;
@@ -3593,8 +3629,16 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
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);
@@ -3826,10 +3870,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;
@@ -3851,16 +3898,32 @@ 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");
- 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;
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index a428b35f9493..c97b84c34084 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -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;
diff --git a/drivers/power/supply/qcom/storm-watch.c b/drivers/power/supply/qcom/storm-watch.c
index 5275079c53e0..21ac669f2ec9 100644
--- a/drivers/power/supply/qcom/storm-watch.c
+++ b/drivers/power/supply/qcom/storm-watch.c
@@ -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);
+}
diff --git a/drivers/power/supply/qcom/storm-watch.h b/drivers/power/supply/qcom/storm-watch.h
index ff05c4a661c3..5275d73613d4 100644
--- a/drivers/power/supply/qcom/storm-watch.h
+++ b/drivers/power/supply/qcom/storm-watch.h
@@ -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