Merge "ARM: dts: msm: add device tree overlay for msm8940 CDP"
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 0a677ec..7994741 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -282,7 +282,8 @@
break;
case PMI632_SUBTYPE:
chip->chg.smb_version = PMI632_SUBTYPE;
- chg->wa_flags |= WEAK_ADAPTER_WA | USBIN_OV_WA;
+ chg->wa_flags |= WEAK_ADAPTER_WA | USBIN_OV_WA |
+ CHG_TERMINATION_WA;
if (pmic_rev_id->rev4 >= 2)
chg->wa_flags |= MOISTURE_PROTECTION_WA;
chg->param = smb5_pmi632_params;
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 6f13635..fa59ebf 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -773,6 +773,7 @@
struct storm_watch *wdata;
cancel_delayed_work_sync(&chg->pl_enable_work);
+ alarm_cancel(&chg->chg_termination_alarm);
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
@@ -793,6 +794,7 @@
vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA);
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
+ vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
/* reconfigure allowed voltage for HVDCP */
rc = smblib_set_adapter_allowance(chg,
@@ -1482,6 +1484,16 @@
break;
}
+ /*
+ * If charge termination WA is active and has suspended charging, then
+ * continue reporting charging status as FULL.
+ */
+ if (is_client_vote_enabled(chg->usb_icl_votable,
+ CHG_TERMINATION_VOTER)) {
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ return 0;
+ }
+
if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
return 0;
@@ -1991,6 +2003,12 @@
return rc;
}
+ if (is_client_vote_enabled(chg->usb_icl_votable,
+ CHG_TERMINATION_VOTER)) {
+ rc = smblib_get_prop_usb_present(chg, val);
+ return rc;
+ }
+
rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n",
@@ -2760,6 +2778,38 @@
return IRQ_HANDLED;
}
+#define CHG_TERM_WA_ENTRY_DELAY_MS 300000 /* 5 min */
+#define CHG_TERM_WA_EXIT_DELAY_MS 60000 /* 1 min */
+static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status)
+{
+ union power_supply_propval pval = {0, };
+ int rc = 0;
+
+ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CAPACITY, &pval);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read SOC value, rc=%d\n", rc);
+ return;
+ }
+
+ /*
+ * Post charge termination, switch to BSM mode triggers the risk of
+ * over charging as BATFET opening may take some time post the necessity
+ * of staying in supplemental mode, leading to unintended charging of
+ * battery. Trigger the charge termination WA once charging is completed
+ * to prevent overcharing.
+ */
+ if ((batt_status == TERMINATE_CHARGE) && (pval.intval == 100)) {
+ alarm_start_relative(&chg->chg_termination_alarm,
+ ms_to_ktime(CHG_TERM_WA_ENTRY_DELAY_MS));
+ } else if (pval.intval < 100) {
+ /*
+ * Reset CC_SOC reference value for charge termination WA once
+ * we exit the TERMINATE_CHARGE state and soc drops below 100%
+ */
+ chg->cc_soc_ref = 0;
+ }
+}
+
irqreturn_t chg_state_change_irq_handler(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
@@ -2777,6 +2827,10 @@
}
stat = stat & BATTERY_CHARGER_STATUS_MASK;
+
+ if (chg->wa_flags & CHG_TERMINATION_WA)
+ smblib_eval_chg_termination(chg, stat);
+
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
}
@@ -3468,6 +3522,7 @@
}
cancel_delayed_work_sync(&chg->pl_enable_work);
+ alarm_cancel(&chg->chg_termination_alarm);
/* reset input current limit voters */
vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
@@ -3479,6 +3534,7 @@
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
+ vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
/* reset usb irq voters */
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
@@ -3996,6 +4052,12 @@
u8 stat;
/*
+ * Hold awake votable to prevent pm_relax being called prior to
+ * completion of this work.
+ */
+ vote(chg->awake_votable, MOISTURE_VOTER, true, 0);
+
+ /*
* Disable 1% duty cycle on CC_ID pin and enable uUSB factory mode
* detection to track any change on RID, as interrupts are disable.
*/
@@ -4057,6 +4119,7 @@
}
out:
+ vote(chg->awake_votable, MOISTURE_VOTER, false, 0);
pm_relax(chg->dev);
}
@@ -4076,6 +4139,93 @@
return ALARMTIMER_NORESTART;
}
+static void smblib_chg_termination_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ chg_termination_work);
+ union power_supply_propval pval;
+ int rc, delay = CHG_TERM_WA_ENTRY_DELAY_MS;
+
+ /*
+ * Hold awake votable to prevent pm_relax being called prior to
+ * completion of this work.
+ */
+ vote(chg->awake_votable, CHG_TERMINATION_VOTER, true, 0);
+
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0 || !pval.intval)
+ goto out;
+
+ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CAPACITY, &pval);
+ if (rc < 0 || (pval.intval < 100)) {
+ vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
+ goto out;
+ }
+
+ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CHARGE_FULL,
+ &pval);
+ if (rc < 0)
+ goto out;
+
+ /*
+ * On change in the value of learned capacity, re-initialize the
+ * reference cc_soc value due to change in cc_soc characteristic value
+ * at full capacity. Also, in case cc_soc_ref value is reset,
+ * re-initialize it.
+ */
+ if ((pval.intval != chg->charge_full_cc) || !chg->cc_soc_ref) {
+ chg->charge_full_cc = pval.intval;
+ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CC_SOC,
+ &pval);
+ if (rc < 0)
+ goto out;
+
+ chg->cc_soc_ref = pval.intval;
+ } else {
+ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CC_SOC,
+ &pval);
+ if (rc < 0)
+ goto out;
+ }
+
+ /*
+ * Suspend/Unsuspend USB input to keep cc_soc within the 0.5% to 0.75%
+ * overshoot range of the cc_soc value at termination, to prevent
+ * overcharging.
+ */
+ if (pval.intval < DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10050, 10000)) {
+ vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
+ delay = CHG_TERM_WA_ENTRY_DELAY_MS;
+ } else if (pval.intval > DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10075,
+ 10000)) {
+ vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, true, 0);
+ delay = CHG_TERM_WA_EXIT_DELAY_MS;
+ }
+
+ smblib_dbg(chg, PR_MISC, "Chg Term WA readings: cc_soc: %d, cc_soc_ref: %d, delay: %d\n",
+ pval.intval, chg->cc_soc_ref, delay);
+ alarm_start_relative(&chg->chg_termination_alarm, ms_to_ktime(delay));
+out:
+ vote(chg->awake_votable, CHG_TERMINATION_VOTER, false, 0);
+ pm_relax(chg->dev);
+}
+
+static enum alarmtimer_restart chg_termination_alarm_cb(struct alarm *alarm,
+ ktime_t now)
+{
+ struct smb_charger *chg = container_of(alarm, struct smb_charger,
+ chg_termination_alarm);
+
+ smblib_dbg(chg, PR_MISC, "Charge termination WA alarm triggered %lld\n",
+ ktime_to_ms(now));
+
+ /* Atomic context, cannot use voter */
+ pm_stay_awake(chg->dev);
+ schedule_work(&chg->chg_termination_work);
+
+ return ALARMTIMER_NORESTART;
+}
+
#define JEITA_SOFT 0
#define JEITA_HARD 1
static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds,
@@ -4288,6 +4438,19 @@
INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work);
INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work);
+ if (chg->wa_flags & CHG_TERMINATION_WA) {
+ INIT_WORK(&chg->chg_termination_work,
+ smblib_chg_termination_work);
+
+ if (alarmtimer_get_rtcdev()) {
+ alarm_init(&chg->chg_termination_alarm, ALARM_BOOTTIME,
+ chg_termination_alarm_cb);
+ } else {
+ smblib_err(chg, "Couldn't get rtc device\n");
+ return -ENODEV;
+ }
+ }
+
if (chg->moisture_protection_enabled &&
(chg->wa_flags & MOISTURE_PROTECTION_WA)) {
INIT_WORK(&chg->moisture_protection_work,
@@ -4368,9 +4531,11 @@
alarm_cancel(&chg->moisture_protection_alarm);
cancel_work_sync(&chg->moisture_protection_work);
}
+ alarm_cancel(&chg->chg_termination_alarm);
cancel_work_sync(&chg->bms_update_work);
cancel_work_sync(&chg->jeita_update_work);
cancel_work_sync(&chg->pl_update_work);
+ cancel_work_sync(&chg->chg_termination_work);
cancel_delayed_work_sync(&chg->clear_hdc_work);
cancel_delayed_work_sync(&chg->icl_change_work);
cancel_delayed_work_sync(&chg->pl_enable_work);
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 1bba206..64b640f 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -72,6 +72,7 @@
#define MOISTURE_VOTER "MOISTURE_VOTER"
#define USBOV_DBC_VOTER "USBOV_DBC_VOTER"
#define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER"
+#define CHG_TERMINATION_VOTER "CHG_TERMINATION_VOTER"
#define BOOST_BACK_STORM_COUNT 3
#define WEAK_CHG_STORM_COUNT 8
@@ -104,6 +105,7 @@
WEAK_ADAPTER_WA = BIT(1),
MOISTURE_PROTECTION_WA = BIT(2),
USBIN_OV_WA = BIT(3),
+ CHG_TERMINATION_WA = BIT(4),
};
enum {
@@ -345,6 +347,7 @@
struct work_struct pl_update_work;
struct work_struct jeita_update_work;
struct work_struct moisture_protection_work;
+ struct work_struct chg_termination_work;
struct delayed_work ps_change_timeout_work;
struct delayed_work clear_hdc_work;
struct delayed_work icl_change_work;
@@ -355,6 +358,7 @@
/* alarm */
struct alarm moisture_protection_alarm;
+ struct alarm chg_termination_alarm;
/* pd */
int voltage_min_uv;
@@ -407,6 +411,8 @@
bool moisture_present;
bool moisture_protection_enabled;
bool fcc_stepper_enable;
+ int charge_full_cc;
+ int cc_soc_ref;
/* workaround flag */
u32 wa_flags;