Merge "power: smb5: update USB source detection sequence" into msm-4.9
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index d3aaf26..5264254 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -177,6 +177,11 @@
 	debug_mask, __debug_mask, int, 0600
 );
 
+static int __pd_disabled;
+module_param_named(
+	pd_disabled, __pd_disabled, int, 0600
+);
+
 static int __weak_chg_icl_ua = 500000;
 module_param_named(
 	weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600
@@ -434,7 +439,6 @@
 	POWER_SUPPLY_PROP_TYPEC_MODE,
 	POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
 	POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION,
-	POWER_SUPPLY_PROP_PD_ALLOWED,
 	POWER_SUPPLY_PROP_PD_ACTIVE,
 	POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
 	POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
@@ -515,9 +519,6 @@
 		else
 			rc = smblib_get_prop_typec_cc_orientation(chg, val);
 		break;
-	case POWER_SUPPLY_PROP_PD_ALLOWED:
-		rc = smblib_get_prop_pd_allowed(chg, val);
-		break;
 	case POWER_SUPPLY_PROP_PD_ACTIVE:
 		val->intval = chg->pd_active;
 		break;
@@ -605,12 +606,6 @@
 	struct smb_charger *chg = &chip->chg;
 	int rc = 0;
 
-	mutex_lock(&chg->lock);
-	if (!chg->typec_present) {
-		rc = -EINVAL;
-		goto unlock;
-	}
-
 	switch (psp) {
 	case POWER_SUPPLY_PROP_PD_CURRENT_MAX:
 		rc = smblib_set_prop_pd_current_max(chg, val);
@@ -652,8 +647,6 @@
 		break;
 	}
 
-unlock:
-	mutex_unlock(&chg->lock);
 	return rc;
 }
 
@@ -1425,6 +1418,15 @@
 {
 	int rc;
 
+	/* disable apsd */
+	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+				0);
+	if (rc < 0) {
+		dev_err(chg->dev, "Couldn't disable APSD rc=%d\n", rc);
+		return rc;
+	}
+
 	rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_1_REG,
 				TYPEC_CCOUT_DETACH_INT_EN_BIT |
 				TYPEC_CCOUT_ATTACH_INT_EN_BIT);
@@ -1459,14 +1461,6 @@
 {
 	int rc;
 
-	/* configure micro USB mode */
-	rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
-			EN_MICRO_USB_MODE_BIT, EN_MICRO_USB_MODE_BIT);
-	if (rc < 0) {
-		dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
-		return rc;
-	}
-
 	rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
 					MICRO_USB_STATE_CHANGE_INT_EN_BIT,
 					MICRO_USB_STATE_CHANGE_INT_EN_BIT);
@@ -1500,7 +1494,6 @@
 				&chg->default_icl_ua);
 
 	/* Use SW based VBUS control, disable HW autonomous mode */
-	/* TODO: auth can be enabled through vote based on APSD flow */
 	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
 		HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT,
 		HVDCP_AUTH_ALG_EN_CFG_BIT);
@@ -1537,10 +1530,22 @@
 		type = !!(val & EN_MICRO_USB_MODE_BIT);
 	}
 
-	chg->connector_type = type ? POWER_SUPPLY_CONNECTOR_MICRO_USB
-					: POWER_SUPPLY_CONNECTOR_TYPEC;
 	pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC");
 
+	if (type) {
+		chg->connector_type = POWER_SUPPLY_CONNECTOR_MICRO_USB;
+		smblib_rerun_apsd_if_required(chg);
+		rc = smb5_configure_micro_usb(chg);
+	} else {
+		chg->connector_type = POWER_SUPPLY_CONNECTOR_TYPEC;
+		rc = smb5_configure_typec(chg);
+	}
+	if (rc < 0) {
+		dev_err(chg->dev,
+			"Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
+		return rc;
+	}
+
 	/*
 	 * PMI632 based hw init:
 	 * - Initialize flash module for PMI632
@@ -1548,8 +1553,6 @@
 	if (chg->smb_version == PMI632_SUBTYPE)
 		schgm_flash_init(chg);
 
-	smblib_rerun_apsd_if_required(chg);
-
 	/* vote 0mA on usb_icl for non battery platforms */
 	vote(chg->usb_icl_votable,
 		DEFAULT_VOTER, chip->dt.no_battery, 0);
@@ -1565,12 +1568,6 @@
 	vote(chg->fv_votable,
 		BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0,
 		chg->batt_profile_fv_uv);
-	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
-			true, 0);
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-			true, 0);
-	vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER,
-		chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB, 0);
 
 	/* Some h/w limit maximum supported ICL */
 	vote(chg->usb_icl_votable, HW_LIMIT_VOTER,
@@ -1594,16 +1591,6 @@
 		return rc;
 	}
 
-	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
-		rc = smb5_configure_micro_usb(chg);
-	else
-		rc = smb5_configure_typec(chg);
-	if (rc < 0) {
-		dev_err(chg->dev,
-			"Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
-		return rc;
-	}
-
 	/* configure VBUS for software control */
 	rc = smblib_masked_write(chg, DCDC_OTG_CFG_REG, OTG_EN_SRC_CFG_BIT, 0);
 	if (rc < 0) {
@@ -1812,11 +1799,21 @@
 {
 	struct smb_irq_data irq_data = {chip, "determine-initial-status"};
 	struct smb_charger *chg = &chip->chg;
+	union power_supply_propval val;
+	int rc;
+
+	rc = smblib_get_prop_usb_present(chg, &val);
+	if (rc < 0) {
+		pr_err("Couldn't get usb present rc=%d\n", rc);
+		return rc;
+	}
+	chg->early_usb_attach = val.intval;
 
 	if (chg->bms_psy)
 		smblib_suspend_on_debug_battery(chg);
 
 	usb_plugin_irq_handler(0, &irq_data);
+	typec_attach_detach_irq_handler(0, &irq_data);
 	typec_state_change_irq_handler(0, &irq_data);
 	usb_source_change_irq_handler(0, &irq_data);
 	chg_state_change_irq_handler(0, &irq_data);
@@ -2008,6 +2005,7 @@
 	},
 	[TYPEC_ATTACH_DETACH_IRQ] = {
 		.name		= "typec-attach-detach",
+		.handler	= typec_attach_detach_irq_handler,
 	},
 	[TYPEC_LEGACY_CABLE_DETECT_IRQ] = {
 		.name		= "typec-legacy-cable-detect",
@@ -2303,6 +2301,7 @@
 	chg = &chip->chg;
 	chg->dev = &pdev->dev;
 	chg->debug_mask = &__debug_mask;
+	chg->pd_disabled = &__pd_disabled;
 	chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
 	chg->mode = PARALLEL_MASTER;
 	chg->irq_info = smb5_irqs;
@@ -2452,6 +2451,10 @@
 	struct smb5 *chip = platform_get_drvdata(pdev);
 	struct smb_charger *chg = &chip->chg;
 
+	/* force enable APSD */
+	smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				BC1P2_SRC_DETECT_BIT, BC1P2_SRC_DETECT_BIT);
+
 	smb5_free_interrupts(chg);
 	smblib_deinit(chg);
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 5345674..1c93bf6 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -663,10 +663,6 @@
 
 	cancel_delayed_work_sync(&chg->pl_enable_work);
 
-	rc = smblib_request_dpdm(chg, false);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't to disable DPDM rc=%d\n", rc);
-
 	if (chg->wa_flags & BOOST_BACK_WA) {
 		data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
 		if (data) {
@@ -954,18 +950,6 @@
 	return smblib_set_dc_suspend(chg, (bool)suspend);
 }
 
-static int smblib_pd_disallowed_votable_indirect_callback(
-	struct votable *votable, void *data, int disallowed, const char *client)
-{
-	struct smb_charger *chg = data;
-	int rc;
-
-	rc = vote(chg->pd_allowed_votable, PD_DISALLOWED_INDIRECT_VOTER,
-		!disallowed, 0);
-
-	return rc;
-}
-
 static int smblib_awake_vote_callback(struct votable *votable, void *data,
 			int awake, const char *client)
 {
@@ -1989,13 +1973,6 @@
 	return rc;
 }
 
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
-			       union power_supply_propval *val)
-{
-	val->intval = get_effective_result(chg->pd_allowed_votable);
-	return 0;
-}
-
 int smblib_get_prop_input_current_settled(struct smb_charger *chg,
 					  union power_supply_propval *val)
 {
@@ -2039,13 +2016,7 @@
 int smblib_get_pe_start(struct smb_charger *chg,
 			       union power_supply_propval *val)
 {
-	/*
-	 * hvdcp timeout voter is the last one to allow pd. Use its vote
-	 * to indicate start of pe engine
-	 */
-	val->intval
-		= !get_client_vote_locked(chg->pd_disallowed_votable_indirect,
-			APSD_VOTER);
+	val->intval = chg->ok_to_pd;
 	return 0;
 }
 
@@ -2298,14 +2269,14 @@
 	return rc;
 }
 
-static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active)
+int smblib_set_prop_pd_active(struct smb_charger *chg,
+				const union power_supply_propval *val)
 {
 	int rc = 0;
 
-	chg->pd_active = pd_active;
+	chg->pd_active = val->intval;
 
 	if (chg->pd_active) {
-		vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
 		vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
 
 		/*
@@ -2313,24 +2284,24 @@
 		 * It is guaranteed that pd_active is set prior to
 		 * pd_current_max
 		 */
-		rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
-									rc);
-
-		/* clear USB ICL vote for DCP_VOTER */
-		rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
-									rc);
-
-		/* remove USB_PSY_VOTER */
-		rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
+		vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
 	} else {
-		vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+		vote(chg->usb_icl_votable, PD_VOTER, false, 0);
 		vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+
+		/* PD hard resets failed, rerun apsd */
+		if (chg->ok_to_pd) {
+			chg->ok_to_pd = false;
+			rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+					HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+					HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT);
+			if (rc < 0) {
+				dev_err(chg->dev,
+					"Couldn't disable APSD rc=%d\n", rc);
+				return rc;
+			}
+			smblib_rerun_apsd(chg);
+		}
 	}
 
 	smblib_update_usb_type(chg);
@@ -2338,15 +2309,6 @@
 	return rc;
 }
 
-int smblib_set_prop_pd_active(struct smb_charger *chg,
-			      const union power_supply_propval *val)
-{
-	if (!get_effective_result(chg->pd_allowed_votable))
-		return -EINVAL;
-
-	return __smblib_set_prop_pd_active(chg, val->intval);
-}
-
 int smblib_set_prop_ship_mode(struct smb_charger *chg,
 				const union power_supply_propval *val)
 {
@@ -2657,11 +2619,7 @@
 
 static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
 {
-	if (vbus_rising) {
-		/* use the typec flag even though its not typec */
-		chg->typec_present = 1;
-	} else {
-		chg->typec_present = 0;
+	if (!vbus_rising) {
 		smblib_update_usb_type(chg);
 		smblib_notify_device_mode(chg, false);
 		smblib_uusb_removal(chg);
@@ -2764,12 +2722,10 @@
 	struct smb_irq_data *irq_data = data;
 	struct smb_charger *chg = irq_data->parent_data;
 
-	mutex_lock(&chg->lock);
 	if (chg->pd_hard_reset)
 		smblib_usb_plugin_hard_reset_locked(chg);
 	else
 		smblib_usb_plugin_locked(chg);
-	mutex_unlock(&chg->lock);
 
 	return IRQ_HANDLED;
 }
@@ -2858,8 +2814,6 @@
 	if (!rising)
 		return;
 
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
-
 	if (chg->mode == PARALLEL_MASTER)
 		vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
 
@@ -2872,33 +2826,19 @@
 static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
 					      bool rising, bool qc_charger)
 {
-	const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
-
-	/* Hold off PD only until hvdcp 2.0 detection timeout */
 	if (rising) {
 
 		/* enable HDC and ICL irq for QC2/3 charger */
 		if (qc_charger)
 			vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
 		else
-			vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-							false, 0);
-
 		/*
 		 * HVDCP detection timeout done
 		 * If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
+		 * enforce DCP ICL if specified
 		 */
-		if (!qc_charger && (apsd_result->bit & DCP_CHARGER_BIT))
-			/* enforce DCP ICL if specified */
 			vote(chg->usb_icl_votable, DCP_VOTER,
 				chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
-
-		/*
-		 * if pd is not allowed, then set pd_active = false right here,
-		 * so that it starts the hvdcp engine
-		 */
-		if (!get_effective_result(chg->pd_allowed_votable))
-			__smblib_set_prop_pd_active(chg, 0);
 	}
 
 	smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__,
@@ -2926,19 +2866,11 @@
 	case SDP_CHARGER_BIT:
 	case CDP_CHARGER_BIT:
 	case FLOAT_CHARGER_BIT:
-		/* if not DCP then no hvdcp timeout happens. Enable pd here */
-		vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-				false, 0);
 		if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 				|| chg->use_extcon)
 			smblib_notify_device_mode(chg, true);
 		break;
 	case OCP_CHARGER_BIT:
-		vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
-		/* if not DCP then no hvdcp timeout happens, Enable pd here. */
-		vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-				false, 0);
-		break;
 	case DCP_CHARGER_BIT:
 		vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
 		break;
@@ -2957,6 +2889,14 @@
 	int rc = 0;
 	u8 stat;
 
+	/*
+	 * Prepared to run PD or PD is active. At this moment, APSD is disabled,
+	 * but there still can be irq on apsd_done from previously unfinished
+	 * APSD run, skip it.
+	 */
+	if (chg->ok_to_pd)
+		return IRQ_HANDLED;
+
 	rc = smblib_read(chg, APSD_STATUS_REG, &stat);
 	if (rc < 0) {
 		smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
@@ -3011,32 +2951,68 @@
 
 static void typec_sink_insertion(struct smb_charger *chg)
 {
-	/* when a sink is inserted we should not wait on hvdcp timeout to
-	 * enable pd
-	 */
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
+	vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
+
 	if (chg->use_extcon) {
 		smblib_notify_usb_host(chg, true);
 		chg->otg_present = true;
 	}
+
+	if (!chg->pr_swap_in_progress)
+		chg->ok_to_pd = !(*chg->pd_disabled) || chg->early_usb_attach;
+}
+
+static void typec_src_insertion(struct smb_charger *chg)
+{
+	int rc = 0;
+	u8 stat;
+
+	if (chg->pr_swap_in_progress)
+		return;
+
+	rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+			rc);
+		return;
+	}
+
+	chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
+	chg->ok_to_pd = !(chg->typec_legacy || *chg->pd_disabled)
+						|| chg->early_usb_attach;
+	if (!chg->ok_to_pd) {
+		rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT);
+		if (rc < 0) {
+			dev_err(chg->dev,
+				"Couldn't disable APSD rc=%d\n", rc);
+			return;
+		}
+		smblib_rerun_apsd(chg);
+	}
 }
 
 static void typec_sink_removal(struct smb_charger *chg)
 {
-	smblib_set_charge_param(chg, &chg->param.freq_switcher,
-			chg->chg_freq.freq_above_otg_threshold);
-	chg->boost_current_ua = 0;
+	vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
 }
 
-static void smblib_handle_typec_removal(struct smb_charger *chg)
+static void typec_src_removal(struct smb_charger *chg)
 {
 	int rc;
 	struct smb_irq_data *data;
 	struct storm_watch *wdata;
 
-	rc = smblib_request_dpdm(chg, false);
+	/* disable apsd */
+	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+				0);
 	if (rc < 0)
-		smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
+		smblib_err(chg,
+			"Couldn't disable APSD rc=%d\n", rc);
+
+	smblib_update_usb_type(chg);
 
 	if (chg->wa_flags & BOOST_BACK_WA) {
 		data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
@@ -3062,11 +3038,6 @@
 	vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
 	vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, false, 0);
 
-	/* reset power delivery voters */
-	vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
-	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, true, 0);
-
 	/* reset usb irq voters */
 	vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
 	vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
@@ -3078,14 +3049,10 @@
 	vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
 	vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
 
-	chg->vconn_attempts = 0;
-	chg->otg_attempts = 0;
 	chg->pulse_cnt = 0;
 	chg->usb_icl_delta_ua = 0;
 	chg->voltage_min_uv = MICRO_5V;
 	chg->voltage_max_uv = MICRO_5V;
-	chg->pd_active = 0;
-	chg->pd_hard_reset = 0;
 
 	/* write back the default FLOAT charger configuration */
 	rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
@@ -3094,12 +3061,6 @@
 		smblib_err(chg, "Couldn't write float charger options rc=%d\n",
 			rc);
 
-	/* reset back to 103mS tCC debounce */
-	rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
-					REDUCE_TCCDEBOUNCE_TO_2MS_BIT, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc);
-
 	/* reconfigure allowed voltage for HVDCP */
 	rc = smblib_set_adapter_allowance(chg,
 			USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
@@ -3107,58 +3068,15 @@
 		smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
 			rc);
 
-	/* enable DRP */
-	rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
-				TYPEC_POWER_ROLE_CMD_MASK, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
-
-	/* HW controlled CC_OUT */
-	rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
-				TYPEC_CCOUT_SRC_BIT, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
-
-	/* clear exit sink based on cc */
-	rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
-						EXIT_SNK_BASED_ON_CC_BIT, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't clear exit_sink_based_on_cc rc=%d\n",
-				rc);
-
-	typec_sink_removal(chg);
-	smblib_update_usb_type(chg);
-
 	if (chg->use_extcon) {
 		if (chg->otg_present)
 			smblib_notify_usb_host(chg, false);
 		else
 			smblib_notify_device_mode(chg, false);
 	}
+
 	chg->otg_present = false;
-}
-
-static void smblib_handle_typec_insertion(struct smb_charger *chg)
-{
-	int rc;
-	u8 stat;
-
-	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
-
-	rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
-		return;
-	}
-
-	if (stat & SNK_SRC_MODE_BIT) {
-		typec_sink_insertion(chg);
-	} else {
-		rc = smblib_request_dpdm(chg, true);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
-		typec_sink_removal(chg);
-	}
+	chg->typec_legacy = false;
 }
 
 static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
@@ -3200,66 +3118,6 @@
 	vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, true, rp_ua);
 }
 
-static void smblib_handle_typec_cc_state_change(struct smb_charger *chg)
-{
-	u8 stat;
-	int typec_mode, rc;
-
-	if (chg->pr_swap_in_progress)
-		return;
-
-	typec_mode = smblib_get_prop_typec_mode(chg);
-	if (chg->typec_present && (typec_mode != chg->typec_mode))
-		smblib_handle_rp_change(chg, typec_mode);
-
-	chg->typec_mode = typec_mode;
-
-	if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
-		chg->typec_present = true;
-		smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
-			smblib_typec_mode_name[chg->typec_mode]);
-		smblib_handle_typec_insertion(chg);
-	} else if (chg->typec_present &&
-				chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
-		chg->typec_present = false;
-		smblib_dbg(chg, PR_MISC, "TypeC removal\n");
-		smblib_handle_typec_removal(chg);
-	}
-
-	rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
-		return;
-	}
-	/* suspend usb if sink */
-	if ((stat & SNK_SRC_MODE_BIT) && chg->typec_present)
-		vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
-	else
-		vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
-
-	smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
-				smblib_typec_mode_name[chg->typec_mode]);
-}
-
-static void smblib_usb_typec_change(struct smb_charger *chg)
-{
-	int rc;
-	u8 stat;
-
-	smblib_handle_typec_cc_state_change(chg);
-
-	rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
-		return;
-	}
-
-	if (stat & TYPEC_VBUS_ERROR_STATUS_BIT)
-		smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
-
-	power_supply_changed(chg->usb_psy);
-}
-
 irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data)
 {
 	struct smb_irq_data *irq_data = data;
@@ -3281,17 +3139,81 @@
 {
 	struct smb_irq_data *irq_data = data;
 	struct smb_charger *chg = irq_data->parent_data;
+	int typec_mode;
 
-	if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
-			|| chg->pr_swap_in_progress) {
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
 		smblib_dbg(chg, PR_INTERRUPT,
-				"Ignoring since pr_swap_in_progress\n");
+				"Ignoring for micro USB\n");
 		return IRQ_HANDLED;
 	}
 
-	mutex_lock(&chg->lock);
-	smblib_usb_typec_change(chg);
-	mutex_unlock(&chg->lock);
+	typec_mode = smblib_get_prop_typec_mode(chg);
+	if (chg->sink_src_mode != UNATTACHED_MODE
+			&& (typec_mode != chg->typec_mode))
+		smblib_handle_rp_change(chg, typec_mode);
+	chg->typec_mode = typec_mode;
+
+	smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
+				smblib_typec_mode_name[chg->typec_mode]);
+
+	power_supply_changed(chg->usb_psy);
+
+	return IRQ_HANDLED;
+}
+
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
+{
+	struct smb_irq_data *irq_data = data;
+	struct smb_charger *chg = irq_data->parent_data;
+	u8 stat;
+	int rc;
+
+	smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+	rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+			rc);
+		return IRQ_HANDLED;
+	}
+
+	if (stat & TYPEC_ATTACH_DETACH_STATE_BIT) {
+		rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+		if (rc < 0) {
+			smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
+				rc);
+			return IRQ_HANDLED;
+		}
+
+		if (stat & SNK_SRC_MODE_BIT) {
+			chg->sink_src_mode = SRC_MODE;
+			typec_sink_insertion(chg);
+		} else {
+			chg->sink_src_mode = SINK_MODE;
+			typec_src_insertion(chg);
+		}
+
+	} else {
+		switch (chg->sink_src_mode) {
+		case SRC_MODE:
+			typec_sink_removal(chg);
+			break;
+		case SINK_MODE:
+			typec_src_removal(chg);
+			break;
+		default:
+			break;
+		}
+
+		if (!chg->pr_swap_in_progress) {
+			chg->ok_to_pd = false;
+			chg->sink_src_mode = UNATTACHED_MODE;
+			chg->early_usb_attach = false;
+		}
+	}
+
+	power_supply_changed(chg->usb_psy);
+
 	return IRQ_HANDLED;
 }
 
@@ -3717,23 +3639,6 @@
 		return rc;
 	}
 
-	chg->pd_disallowed_votable_indirect
-		= create_votable("PD_DISALLOWED_INDIRECT", VOTE_SET_ANY,
-			smblib_pd_disallowed_votable_indirect_callback, chg);
-	if (IS_ERR(chg->pd_disallowed_votable_indirect)) {
-		rc = PTR_ERR(chg->pd_disallowed_votable_indirect);
-		chg->pd_disallowed_votable_indirect = NULL;
-		return rc;
-	}
-
-	chg->pd_allowed_votable = create_votable("PD_ALLOWED",
-					VOTE_SET_ANY, NULL, NULL);
-	if (IS_ERR(chg->pd_allowed_votable)) {
-		rc = PTR_ERR(chg->pd_allowed_votable);
-		chg->pd_allowed_votable = NULL;
-		return rc;
-	}
-
 	chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY,
 					smblib_awake_vote_callback,
 					chg);
@@ -3771,10 +3676,6 @@
 		destroy_votable(chg->dc_suspend_votable);
 	if (chg->usb_icl_votable)
 		destroy_votable(chg->usb_icl_votable);
-	if (chg->pd_disallowed_votable_indirect)
-		destroy_votable(chg->pd_disallowed_votable_indirect);
-	if (chg->pd_allowed_votable)
-		destroy_votable(chg->pd_allowed_votable);
 	if (chg->awake_votable)
 		destroy_votable(chg->awake_votable);
 	if (chg->chg_disable_votable)
@@ -3786,7 +3687,6 @@
 	int rc = 0;
 
 	mutex_init(&chg->lock);
-	mutex_init(&chg->otg_oc_lock);
 	INIT_WORK(&chg->bms_update_work, bms_update_work);
 	INIT_WORK(&chg->pl_update_work, pl_update_work);
 	INIT_WORK(&chg->jeita_update_work, jeita_update_work);
@@ -3799,6 +3699,7 @@
 	chg->fake_input_current_limited = -EINVAL;
 	chg->fake_batt_status = -EINVAL;
 	chg->jeita_configured = false;
+	chg->sink_src_mode = UNATTACHED_MODE;
 
 	switch (chg->mode) {
 	case PARALLEL_MASTER:
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 5d6d3a6..96bd550 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -80,6 +80,12 @@
 	NUM_MODES,
 };
 
+enum sink_src_mode {
+	SINK_MODE,
+	SRC_MODE,
+	UNATTACHED_MODE,
+};
+
 enum {
 	BOOST_BACK_WA			= BIT(0),
 };
@@ -259,6 +265,7 @@
 	struct smb_params	param;
 	struct smb_iio		iio;
 	int			*debug_mask;
+	int			*pd_disabled;
 	enum smb_mode		mode;
 	struct smb_chg_freq	chg_freq;
 	int			smb_version;
@@ -269,7 +276,6 @@
 	/* locks */
 	struct mutex		lock;
 	struct mutex		ps_change_lock;
-	struct mutex		otg_oc_lock;
 
 	/* power supplies */
 	struct power_supply		*batt_psy;
@@ -296,8 +302,6 @@
 	struct votable		*fcc_votable;
 	struct votable		*fv_votable;
 	struct votable		*usb_icl_votable;
-	struct votable		*pd_disallowed_votable_indirect;
-	struct votable		*pd_allowed_votable;
 	struct votable		*awake_votable;
 	struct votable		*pl_disable_votable;
 	struct votable		*chg_disable_votable;
@@ -315,10 +319,17 @@
 	struct delayed_work	uusb_otg_work;
 	struct delayed_work	bb_removal_work;
 
-	/* cached status */
+	/* pd */
 	int			voltage_min_uv;
 	int			voltage_max_uv;
 	int			pd_active;
+	bool			pd_hard_reset;
+	bool			pr_swap_in_progress;
+	bool			early_usb_attach;
+	bool			ok_to_pd;
+	bool			typec_legacy;
+
+	/* cached status */
 	bool			system_suspend_supported;
 	int			boost_threshold_ua;
 	int			system_temp_level;
@@ -334,15 +345,10 @@
 	int			connector_type;
 	bool			otg_en;
 	bool			suspend_input_on_debug_batt;
-	int			otg_attempts;
-	int			vconn_attempts;
 	int			default_icl_ua;
 	int			otg_cl_ua;
 	bool			uusb_apsd_rerun_done;
-	bool			pd_hard_reset;
-	bool			typec_present;
 	int			fake_input_current_limited;
-	bool			pr_swap_in_progress;
 	int			typec_mode;
 	int			usb_icl_change_irq_enabled;
 	u32			jeita_status;
@@ -352,6 +358,7 @@
 	int			hw_max_icl_ua;
 	int			auto_recharge_soc;
 	bool			jeita_configured;
+	enum sink_src_mode	sink_src_mode;
 
 	/* workaround flag */
 	u32			wa_flags;
@@ -420,6 +427,7 @@
 irqreturn_t usb_source_change_irq_handler(int irq, void *data);
 irqreturn_t icl_change_irq_handler(int irq, void *data);
 irqreturn_t typec_state_change_irq_handler(int irq, void *data);
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data);
 irqreturn_t dc_plugin_irq_handler(int irq, void *data);
 irqreturn_t high_duty_cycle_irq_handler(int irq, void *data);
 irqreturn_t switcher_power_ok_irq_handler(int irq, void *data);
@@ -485,8 +493,6 @@
 				union power_supply_propval *val);
 int smblib_get_prop_typec_power_role(struct smb_charger *chg,
 				union power_supply_propval *val);
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
-				union power_supply_propval *val);
 int smblib_get_prop_input_current_settled(struct smb_charger *chg,
 				union power_supply_propval *val);
 int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h
index 4d399aa..7a886b0 100644
--- a/drivers/power/supply/qcom/smb5-reg.h
+++ b/drivers/power/supply/qcom/smb5-reg.h
@@ -215,6 +215,7 @@
 #define HVDCP_AUTH_ALG_EN_CFG_BIT		BIT(6)
 #define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT	BIT(5)
 #define BC1P2_SRC_DETECT_BIT			BIT(3)
+#define HVDCP_EN_BIT				BIT(2)
 
 #define USBIN_OPTIONS_2_CFG_REG			(USBIN_BASE + 0x63)
 #define FLOAT_OPTIONS_MASK			GENMASK(2, 0)
@@ -262,6 +263,9 @@
 #define SRC_RA_OPEN_BIT				BIT(1)
 #define AUDIO_ACCESS_RA_RA_BIT			BIT(0)
 
+#define TYPE_C_STATE_MACHINE_STATUS_REG		(TYPEC_BASE + 0x09)
+#define TYPEC_ATTACH_DETACH_STATE_BIT		BIT(5)
+
 #define TYPE_C_MISC_STATUS_REG			(TYPEC_BASE + 0x0B)
 #define SNK_SRC_MODE_BIT			BIT(6)
 #define TYPEC_VBUS_ERROR_STATUS_BIT		BIT(4)
@@ -269,6 +273,7 @@
 #define CC_ATTACHED_BIT				BIT(0)
 
 #define LEGACY_CABLE_STATUS_REG			(TYPEC_BASE + 0x0D)
+#define TYPEC_LEGACY_CABLE_STATUS_BIT		BIT(1)
 #define TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT	BIT(0)
 
 #define TYPEC_U_USB_STATUS_REG			(TYPEC_BASE + 0x0F)