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

Commit 31661d13 authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar
Browse files

power: qpnp-smbcharger: fix restart with HVDCP



Restarting the phone with HVDCP plugged in causes the charger to
suspend. This is likely because D+/D- grounding by USB PHY during
restart puts the charger in a suspended state.

A workaround for this problem is to do the following when shutting down

if HVDCP is present
	disable parallel charging
	switch to 5V hvdcp
	wait 500ms
	disable hvdcp
	fake a removal by setting the uv threshold to 9V
	fake an insertion by setting the uv threshold to 5-9V
	wait 1 second
	continue with the shutdown

Upon restart when HLOS comes up it does this
if DCP
	reduce ICL to 300mA
	fake removal
	wait 500ms
	fake insertion
	if (SDP is detected)
		wait 500ms
		fake removal
		wait 500ms
		fake insertion
	restore ICL

It is observed that the HVDCP EN bit gets set by CSIR at power up.
However, HVDCP isn't detected, only DCP is detected. Hence HLOS reruns APSD
to initiate HVDCP mode.

CRs-Fixed: 917683
Change-Id: I442150a6549b8e13a7fdea286cb457582459562a
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent 46d4e4a5
Loading
Loading
Loading
Loading
+154 −3
Original line number Diff line number Diff line
@@ -123,7 +123,6 @@ struct smbchg_chip {
	int				prechg_safety_time;
	int				bmd_pin_src;
	int				jeita_temp_hard_limit;
	int				sw_esr_pulse_current_ma;
	int				aicl_rerun_period_s;
	bool				use_vfloat_adjustments;
	bool				iterm_disabled;
@@ -293,6 +292,7 @@ enum smbchg_wa {
	SMBCHG_BATT_OV_WA = BIT(3),
	SMBCHG_CC_ESR_WA = BIT(4),
	SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5),
	SMBCHG_RESTART_WA = BIT(6),
};

enum print_reason {
@@ -326,6 +326,7 @@ enum icl_voters {
	USER_ICL_VOTER,
	WEAK_CHARGER_ICL_VOTER,
	SW_AICL_ICL_VOTER,
	CHG_SUSPEND_WORKAROUND_ICL_VOTER,
	NUM_ICL_VOTER,
};

@@ -6378,6 +6379,18 @@ static int smbchg_hw_init(struct smbchg_chip *chip)
			chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
			chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);

	/* Setup 9V HVDCP */
	if (!chip->hvdcp_not_supported) {
		rc = smbchg_sec_masked_write(chip,
				chip->usb_chgpth_base + CHGPTH_CFG,
				HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
		if (rc < 0) {
			pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n",
					rc);
			return rc;
		}
	}

	if (chip->aicl_rerun_period_s > 0) {
		rc = smbchg_set_aicl_rerun_period_s(chip,
				chip->aicl_rerun_period_s);
@@ -7394,7 +7407,8 @@ static int smbchg_check_chg_version(struct smbchg_chip *chip)
	case PMI8994:
		chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA
				| SMBCHG_BATT_OV_WA
				| SMBCHG_CC_ESR_WA;
				| SMBCHG_CC_ESR_WA
				| SMBCHG_RESTART_WA;
		use_pmi8994_tables(chip);
		chip->schg_version = QPNP_SCHG;
		break;
@@ -7414,7 +7428,8 @@ static int smbchg_check_chg_version(struct smbchg_chip *chip)
		break;
	case PMI8996:
		chip->wa_flags |= SMBCHG_CC_ESR_WA
				| SMBCHG_FLASH_ICL_DISABLE_WA;
				| SMBCHG_FLASH_ICL_DISABLE_WA
				| SMBCHG_RESTART_WA;
		use_pmi8996_tables(chip);
		chip->schg_version = QPNP_SCHG;
		break;
@@ -7430,6 +7445,50 @@ static int smbchg_check_chg_version(struct smbchg_chip *chip)
	return 0;
}

static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip)
{
	enum power_supply_type usb_supply_type;
	char *usb_type_name;
	int rc;

	if (!(chip->wa_flags & SMBCHG_RESTART_WA))
		return;

	read_usb_type(chip, &usb_type_name, &usb_supply_type);
	if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP
		&& !is_hvdcp_present(chip)) {
		pr_smb(PR_STATUS, "DCP found rerunning APSD\n");
		rc = vote(chip->usb_icl_votable,
				CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300);
		if (rc < 0)
			pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n",
					rc);

		pr_smb(PR_STATUS, "Faking Removal\n");
		fake_insertion_removal(chip, false);
		msleep(500);
		pr_smb(PR_STATUS, "Faking Insertion\n");
		fake_insertion_removal(chip, true);

		read_usb_type(chip, &usb_type_name, &usb_supply_type);

		if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) {
			msleep(500);
			pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n");
			fake_insertion_removal(chip, false);
			msleep(500);
			pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n");
			fake_insertion_removal(chip, true);
		}

		rc = vote(chip->usb_icl_votable,
				CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0);
		if (rc < 0)
			pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
					rc);
	}
}

static int smbchg_probe(struct spmi_device *spmi)
{
	int rc;
@@ -7635,6 +7694,8 @@ static int smbchg_probe(struct spmi_device *spmi)
		power_supply_set_present(chip->usb_psy, chip->usb_present);
	}

	rerun_hvdcp_det_if_necessary(chip);

	dump_regs(chip);
	create_debugfs_entries(chip);
	dev_info(chip->dev,
@@ -7672,6 +7733,95 @@ static int smbchg_remove(struct spmi_device *spmi)
	return 0;
}

static void smbchg_shutdown(struct spmi_device *spmi)
{
	struct smbchg_chip *chip = dev_get_drvdata(&spmi->dev);
	int rc;

	if (!(chip->wa_flags & SMBCHG_RESTART_WA))
		return;

	if (!is_hvdcp_present(chip))
		return;

	pr_smb(PR_MISC, "Disable Parallel\n");
	mutex_lock(&chip->parallel.lock);
	smbchg_parallel_en = 0;
	smbchg_parallel_usb_disable(chip);
	mutex_unlock(&chip->parallel.lock);

	pr_smb(PR_MISC, "Disable all interrupts\n");
	disable_irq(chip->aicl_done_irq);
	disable_irq(chip->batt_cold_irq);
	disable_irq(chip->batt_cool_irq);
	disable_irq(chip->batt_hot_irq);
	disable_irq(chip->batt_missing_irq);
	disable_irq(chip->batt_warm_irq);
	disable_irq(chip->chg_error_irq);
	disable_irq(chip->chg_hot_irq);
	disable_irq(chip->chg_term_irq);
	disable_irq(chip->dcin_uv_irq);
	disable_irq(chip->fastchg_irq);
	disable_irq(chip->otg_fail_irq);
	disable_irq(chip->otg_oc_irq);
	disable_irq(chip->power_ok_irq);
	disable_irq(chip->recharge_irq);
	disable_irq(chip->src_detect_irq);
	disable_irq(chip->taper_irq);
	disable_irq(chip->usbid_change_irq);
	disable_irq(chip->usbin_ov_irq);
	disable_irq(chip->usbin_uv_irq);
	disable_irq(chip->vbat_low_irq);
	disable_irq(chip->wdog_timeout_irq);

	/* switch to 5V HVDCP */
	pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
				HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
	if (rc < 0) {
		pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
		return;
	}

	pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n");
	/* wait for HVDCP to lower to 5V */
	msleep(500);
	/*
	 * Check if the same hvdcp session is in progress. src_det should be
	 * high and that we are still in 5V hvdcp
	 */
	if (!is_src_detect_high(chip)) {
		pr_smb(PR_MISC, "src det low after 500mS sleep\n");
		return;
	}

	/* disable HVDCP */
	pr_smb(PR_MISC, "Disable HVDCP\n");
	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
			HVDCP_EN_BIT, 0);
	if (rc < 0)
		pr_err("Couldn't disable HVDCP rc=%d\n", rc);

	chip->hvdcp_3_det_ignore_uv = true;
	/* fake a removal */
	pr_smb(PR_MISC, "Faking Removal\n");
	rc = fake_insertion_removal(chip, false);
	if (rc < 0)
		pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);

	/* fake an insertion */
	pr_smb(PR_MISC, "Faking Insertion\n");
	rc = fake_insertion_removal(chip, true);
	if (rc < 0)
		pr_err("Couldn't fake insertion rc=%d\n", rc);

	pr_smb(PR_MISC, "Wait 1S to settle\n");
	msleep(1000);
	chip->hvdcp_3_det_ignore_uv = false;

	pr_smb(PR_STATUS, "wrote power off configurations\n");
}

static const struct dev_pm_ops smbchg_pm_ops = {
};

@@ -7686,6 +7836,7 @@ static struct spmi_driver smbchg_driver = {
	},
	.probe		= smbchg_probe,
	.remove		= smbchg_remove,
	.shutdown	= smbchg_shutdown,
};

static int __init smbchg_init(void)