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

Commit 37092142 authored by Umang Agrawal's avatar Umang Agrawal
Browse files

power: smb5: Implement charge termination WA



Currently post charge termination, if the charger switches to BSM mode,
then there exist a possibility of battery being overcharged. This is
due to finite delay in opening of BATFET post its requirement to stay
in BSM mode, allowing charge current to leak into the battery, resulting
in over charge.

Add a SW WA to suspend input, once cc_soc increases beyond a certain
threshold post termination.

Change-Id: I6c1966bb69445c89bc29b099ab9adf2d5f3e2334
Signed-off-by: default avatarUmang Agrawal <uagrawal@codeaurora.org>
parent 2abf96e5
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -282,7 +282,8 @@ static int smb5_chg_config_init(struct smb5 *chip)
		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;
+165 −0
Original line number Diff line number Diff line
@@ -773,6 +773,7 @@ static void smblib_uusb_removal(struct smb_charger *chg)
	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 @@ static void smblib_uusb_removal(struct smb_charger *chg)
	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 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
		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 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
		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 @@ irqreturn_t default_irq_handler(int irq, void *data)
	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 @@ irqreturn_t chg_state_change_irq_handler(int irq, void *data)
	}

	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 @@ static void typec_src_removal(struct smb_charger *chg)
	}

	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 @@ static void typec_src_removal(struct smb_charger *chg)
	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);
@@ -3995,6 +4051,12 @@ static void smblib_moisture_protection_work(struct work_struct *work)
	bool usb_plugged_in;
	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 @@ static void smblib_moisture_protection_work(struct work_struct *work)
	}

out:
	vote(chg->awake_votable, MOISTURE_VOTER, false, 0);
	pm_relax(chg->dev);
}

@@ -4076,6 +4139,93 @@ static enum alarmtimer_restart moisture_protection_alarm_cb(struct alarm *alarm,
	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 @@ int smblib_init(struct smb_charger *chg)
	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 @@ int smblib_deinit(struct smb_charger *chg)
			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);
+6 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ enum print_reason {
#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 @@ enum {
	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 smb_charger {
	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 @@ struct smb_charger {

	/* alarm */
	struct alarm		moisture_protection_alarm;
	struct alarm		chg_termination_alarm;

	/* pd */
	int			voltage_min_uv;
@@ -407,6 +411,8 @@ struct smb_charger {
	bool			moisture_present;
	bool			moisture_protection_enabled;
	bool			fcc_stepper_enable;
	int			charge_full_cc;
	int			cc_soc_ref;

	/* workaround flag */
	u32			wa_flags;