From nobody Tue Apr 7 17:57:19 2026 Received: from mail-244122.protonmail.ch (mail-244122.protonmail.ch [109.224.244.122]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D83442550AF; Thu, 12 Mar 2026 06:17:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.224.244.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773296233; cv=none; b=lKTHLiZ9f9ADeJL3VsxICnnCgXtwzJ11gfdQSQ3WAoD5Ls6KHX3e4j68iqE1itYYCi9702xfUt7CXotzYKOO6xbVMYawinPfHd12Gdp75F1yED/U6YLlq7g02bAtmBGKPuQjEmDTT3lcb4ZDwsbwOfiRVgzX8QYUrorfGOK8ihs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773296233; c=relaxed/simple; bh=RwETkPauWLPu8/cpRZEAFRTB7migRPlSchRkeRn0J1o=; h=Date:To:From:Cc:Subject:Message-ID:MIME-Version:Content-Type; b=FxiD4pKA6s1l+bMFgbitvWDB1tskTJSzwkpgQFdnr153wGfdWSgCpqu/ndjbP4J8VlqYsXmqF5C1afGsigJz4B5xfT5OS+mlQLsa1Zz8yS7lS4G8lDgcyXUkrO+JYRflcILMnJgiE/HFtLK72BNNGAKl8Pyk5ixTQuqLrJh6PK4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=oP7xL2nA; arc=none smtp.client-ip=109.224.244.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="oP7xL2nA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1773296222; x=1773555422; bh=3wBo0UCe9WJRN1U7aQiitye13RIjQxmKywtDbgS4TT8=; h=Date:To:From:Cc:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=oP7xL2nA9DjztUMjAe5dQhSwuSorXZvftpvnowLANdn0k5It5BCi7s0Vbp706FXf6 j/o8gsCJkJ1QMRb8DD5Iy8O651Mn0KBy1tomUH1hDXWug3IABG0XYZsw0fJXl//e1H 1Q5uFb6DudQlO00/DBHWKFunpNO8HoMZVU6A7cao/oUtYKNZKqK6FGEAo3hPjYHzcm PKzHyV0uYCxRCJQv5FelDRp9RdrqydJRJStXGLmJmfmPvUvUBLTRy7sIbRk7LoqLU7 TUdKg8p3iiUhsRlNfg17a/jqOMg8oUJgBoYS3YB3NM9ZkjsbYf+24ppogYne80cIa7 9UJPjZIUa1yew== Date: Thu, 12 Mar 2026 06:16:58 +0000 To: Bryan O'Donoghue , Greg Kroah-Hartman , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heikki Krogerus From: Alexander Koskovich Cc: linux-arm-msm@vger.kernel.org, linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Koskovich Subject: [PATCH v2] usb: typec: qcom: Add support for per port VBUS detection Message-ID: <20260312-qcom-typec-shared-vbus-v2-1-99ed9e500947@pm.me> Feedback-ID: 37836894:user:proton X-Pm-Message-ID: 96e83a83952ba69daff8dce48679cbb1f8b710d6 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This is required for devices (e.g. ASUS ROG Phone 3) where more than one USB port can act as a sink and both share a single USBIN input on the PMIC. Because the PM8150B uses USBIN to determine VBUS presence, a charger connected to one port causes the PMIC to falsely detect VBUS on the other port, preventing it from entering source mode. For example, plugging a charger into one port prevents using the other port for a flash drive. Fix this by adding support for the vbus-gpios connector binding so the driver can use an external GPIO for per-port VBUS presence detection instead of the shared USBIN register. Signed-off-by: Alexander Koskovich Acked-by: Heikki Krogerus Reviewed-by: Bryan O'Donoghue --- Changes in v2: - Dropped RFC prefix - Remove redundant vbus-detect-gpios, instead use existing vbus-gpios from = usb-connector (Dmitry) - Updated cover to better describe scenario where this change is relevant - Update comment for EN_TRY_SRC to make more sense - Skip vSafe5V poll too not just vSafe0V - return gpiod_get_value_cansleep (Konrad) - regmap_update_bits -> regmap_set_bits (Konrad) - Get vbus-gpios per connector (Konrad) - Add bracket to if (IS_ERR(pmic_typec_port->vbus_detect_gpio)) (Bryan) - Link to v1: https://lore.kernel.org/r/20260308-qcom-typec-shared-vbus-v1-= 0-7d574b91052a@pm.me --- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c | 53 ++++++++++++++++++= +++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/u= sb/typec/tcpm/qcom/qcom_pmic_typec_port.c index 8051eaa46991..a8f6687a3522 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -5,6 +5,7 @@ =20 #include #include +#include #include #include #include @@ -176,6 +177,8 @@ struct pmic_typec_port { bool vbus_enabled; struct mutex vbus_lock; /* VBUS state serialization */ =20 + struct gpio_desc *vbus_detect_gpio; + int cc; bool debouncing_cc; struct delayed_work cc_debounce_dwork; @@ -279,6 +282,9 @@ static int qcom_pmic_typec_port_vbus_detect(struct pmic= _typec_port *pmic_typec_p unsigned int misc; int ret; =20 + if (pmic_typec_port->vbus_detect_gpio) + return gpiod_get_value_cansleep(pmic_typec_port->vbus_detect_gpio); + ret =3D regmap_read(pmic_typec_port->regmap, pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc); @@ -310,6 +316,13 @@ static int qcom_pmic_typec_port_vbus_toggle(struct pmi= c_typec_port *pmic_typec_p val =3D TYPEC_SM_VBUS_VSAFE0V; } =20 + /* + * On devices with multiple ports sharing USBIN, VBUS from another + * port makes the USBIN-based vsafe polls unreliable. + */ + if (pmic_typec_port->vbus_detect_gpio) + return 0; + /* Poll waiting for transition to required vSafe5V or vSafe0V */ ret =3D regmap_read_poll_timeout(pmic_typec_port->regmap, pmic_typec_port->base + TYPEC_SM_STATUS_REG, @@ -589,7 +602,15 @@ static int qcom_pmic_typec_port_start_toggling(struct = tcpc_dev *tcpc, mode =3D EN_SNK_ONLY; break; case TYPEC_PORT_DRP: - mode =3D EN_TRY_SNK; + /* + * With VBUS present on USBIN from another port, EN_TRY_SNK + * keeps the port in sink mode. Use EN_TRY_SRC so the port + * tries to source first. + */ + if (pmic_typec_port->vbus_detect_gpio) + mode =3D EN_TRY_SRC; + else + mode =3D EN_TRY_SNK; break; } =20 @@ -677,6 +698,19 @@ static int qcom_pmic_typec_port_start(struct pmic_type= c *tcpm, if (ret) goto done; =20 + /* + * On devices with multiple USB-C ports sharing USBIN, bypass + * VSAFE0V so SRC attachment can complete despite VBUS being + * present on USBIN from another port. + */ + if (pmic_typec_port->vbus_detect_gpio) { + ret =3D regmap_set_bits(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_EXIT_STATE_CFG_REG, + BYPASS_VSAFE0V_DURING_ROLE_SWAP); + if (ret) + goto done; + } + pmic_typec_port->tcpm_port =3D tcpm_port; =20 for (i =3D 0; i < pmic_typec_port->nr_irqs; i++) @@ -704,6 +738,7 @@ int qcom_pmic_typec_port_probe(struct platform_device *= pdev, struct device *dev =3D &pdev->dev; struct pmic_typec_port_irq_data *irq_data; struct pmic_typec_port *pmic_typec_port; + struct fwnode_handle *connector; int i, ret, irq; =20 pmic_typec_port =3D devm_kzalloc(dev, sizeof(*pmic_typec_port), GFP_KERNE= L); @@ -724,6 +759,22 @@ int qcom_pmic_typec_port_probe(struct platform_device = *pdev, if (IS_ERR(pmic_typec_port->vdd_vbus)) return PTR_ERR(pmic_typec_port->vdd_vbus); =20 + connector =3D device_get_named_child_node(dev, "connector"); + if (connector) { + pmic_typec_port->vbus_detect_gpio =3D + devm_fwnode_gpiod_get(dev, connector, "vbus", + GPIOD_IN, NULL); + fwnode_handle_put(connector); + + if (IS_ERR(pmic_typec_port->vbus_detect_gpio)) { + ret =3D PTR_ERR(pmic_typec_port->vbus_detect_gpio); + pmic_typec_port->vbus_detect_gpio =3D NULL; + if (ret !=3D -ENOENT) + return dev_err_probe(dev, ret, + "failed to get vbus GPIO\n"); + } + } + pmic_typec_port->dev =3D dev; pmic_typec_port->base =3D base; pmic_typec_port->regmap =3D regmap; --- base-commit: 1f318b96cc84d7c2ab792fcc0bfd42a7ca890681 change-id: 20260308-qcom-typec-shared-vbus-7d37c6b2d155 Best regards, --=20 Alexander Koskovich