Linux ARM-MSM sub-architecture
 help / color / mirror / Atom feed
From: Elson Serrao <elson.serrao@oss.qualcomm.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Bjorn Andersson <andersson@kernel.org>,
	Konrad Dybcio <konradybcio@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Souradeep Chowdhury <quic_schowdhu@quicinc.com>
Cc: linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH RFC v3 03/10] usb: misc: qcom_eud: add per-port High-Speed PHY control
Date: Mon,  9 Mar 2026 13:33:30 -0700	[thread overview]
Message-ID: <20260309203337.803986-4-elson.serrao@oss.qualcomm.com> (raw)
In-Reply-To: <20260309203337.803986-1-elson.serrao@oss.qualcomm.com>

EUD hardware can support multiple High-Speed USB ports, each routed
through its own PHY. The active port is selected in hardware via the
EUD_PORT_SEL register. As a High-Speed hub, EUD requires access to
the High-Speed PHY associated with the active port. To support this
multi-port capability, the driver must manage PHY resources on a
per-port basis, ensuring that the PHY for the currently selected
port is properly initialized and powered.

This patch adds per-port PHY management to the driver. The driver
now powers the appropriate PHY based on the selected and enabled
port, ensuring correct operation when EUD is enabled.

Historically, EUD appeared to work on single-port systems because
the USB controller kept the PHY initialized. However, EUD is
designed to operate independently of the USB controller and
therefore requires explicit PHY control for proper operation.

Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
 drivers/usb/misc/Kconfig    |   1 +
 drivers/usb/misc/qcom_eud.c | 103 +++++++++++++++++++++++++++++++++++-
 2 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 0b56b773dbdf..2d9190c756f9 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -147,6 +147,7 @@ config USB_APPLEDISPLAY
 config USB_QCOM_EUD
 	tristate "QCOM Embedded USB Debugger(EUD) Driver"
 	depends on ARCH_QCOM || COMPILE_TEST
+	depends on OF
 	select QCOM_SCM
 	select USB_ROLE_SWITCH
 	help
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 1a136f8f1ae5..b042e01c6ca2 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -11,6 +11,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
@@ -37,23 +39,75 @@
 struct eud_chip {
 	struct device			*dev;
 	struct usb_role_switch		*role_sw;
+	struct phy			*phy[EUD_MAX_PORTS];
 	void __iomem			*base;
 	phys_addr_t			mode_mgr;
 	unsigned int			int_status;
 	int				irq;
 	bool				enabled;
 	bool				usb_attached;
+	bool				phy_enabled;
 	u8				port_idx;
 };
 
+static int eud_phy_enable(struct eud_chip *chip)
+{
+	struct phy *phy;
+	int ret;
+
+	if (chip->phy_enabled)
+		return 0;
+
+	phy = chip->phy[chip->port_idx];
+
+	ret = phy_init(phy);
+	if (ret) {
+		dev_err(chip->dev, "Failed to initialize USB2 PHY for port %u: %d\n",
+			chip->port_idx, ret);
+		return ret;
+	}
+
+	ret = phy_power_on(phy);
+	if (ret) {
+		dev_err(chip->dev, "Failed to power on USB2 PHY for port %u: %d\n",
+			chip->port_idx, ret);
+		phy_exit(phy);
+		return ret;
+	}
+
+	chip->phy_enabled = true;
+
+	return 0;
+}
+
+static void eud_phy_disable(struct eud_chip *chip)
+{
+	struct phy *phy;
+
+	if (!chip->phy_enabled)
+		return;
+
+	phy = chip->phy[chip->port_idx];
+
+	phy_power_off(phy);
+	phy_exit(phy);
+	chip->phy_enabled = false;
+}
+
 static int enable_eud(struct eud_chip *priv)
 {
 	int ret;
 
-	ret = qcom_scm_io_writel(priv->mode_mgr + EUD_REG_EUD_EN2, 1);
+	ret = eud_phy_enable(priv);
 	if (ret)
 		return ret;
 
+	ret = qcom_scm_io_writel(priv->mode_mgr + EUD_REG_EUD_EN2, 1);
+	if (ret) {
+		eud_phy_disable(priv);
+		return ret;
+	}
+
 	writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN);
 	writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE,
 			priv->base + EUD_REG_INT1_EN_MASK);
@@ -70,6 +124,8 @@ static int disable_eud(struct eud_chip *priv)
 		return ret;
 
 	writel(0, priv->base + EUD_REG_CSR_EUD_EN);
+	eud_phy_disable(priv);
+
 	return 0;
 }
 
@@ -132,6 +188,11 @@ static ssize_t port_store(struct device *dev,
 	if (port >= EUD_MAX_PORTS)
 		return -EINVAL;
 
+	if (!chip->phy[port]) {
+		dev_err(chip->dev, "EUD not supported on selected port\n");
+		return -EOPNOTSUPP;
+	}
+
 	/* Port selection must be done before enabling EUD */
 	if (chip->enabled) {
 		dev_err(chip->dev, "Cannot change port while EUD is enabled\n");
@@ -224,6 +285,35 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int eud_parse_dt_port(struct eud_chip *chip, u8 port_id)
+{
+	struct device_node *controller_node;
+	struct phy *phy;
+
+	/*
+	 * Multiply port_id by 2 to get controller port number:
+	 * port_id 0 -> port@0 (primary USB controller)
+	 * port_id 1 -> port@2 (secondary USB controller)
+	 */
+	controller_node = of_graph_get_remote_node(chip->dev->of_node,
+						   port_id * 2, -1);
+	if (!controller_node)
+		return dev_err_probe(chip->dev, -ENODEV,
+				     "failed to get controller node for port %u\n", port_id);
+
+	phy = devm_of_phy_get_by_index(chip->dev, controller_node, 0);
+	if (IS_ERR(phy)) {
+		of_node_put(controller_node);
+		return dev_err_probe(chip->dev, PTR_ERR(phy),
+				     "failed to get HS PHY for port %u\n", port_id);
+	}
+	chip->phy[port_id] = phy;
+
+	of_node_put(controller_node);
+
+	return 0;
+}
+
 static void eud_role_switch_release(void *data)
 {
 	struct eud_chip *chip = data;
@@ -243,6 +333,17 @@ static int eud_probe(struct platform_device *pdev)
 
 	chip->dev = &pdev->dev;
 
+	/*
+	 * Parse the DT resources for primary port.
+	 * This is the default EUD port and is mandatory.
+	 */
+	ret = eud_parse_dt_port(chip, 0);
+	if (ret)
+		return ret;
+
+	/* Secondary port is optional */
+	eud_parse_dt_port(chip, 1);
+
 	chip->role_sw = usb_role_switch_get(&pdev->dev);
 	if (IS_ERR(chip->role_sw))
 		return dev_err_probe(chip->dev, PTR_ERR(chip->role_sw),
-- 
2.34.1


  parent reply	other threads:[~2026-03-09 20:33 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-09 20:33 [PATCH RFC v3 00/10] Improve Qualcomm EUD driver and platform support Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 01/10] dt-bindings: soc: qcom: eud: Add support for dual-port configuration Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 02/10] usb: misc: qcom_eud: add sysfs attribute for port selection Elson Serrao
2026-03-11 13:16   ` Greg Kroah-Hartman
2026-03-11 22:57     ` Elson Serrao
2026-03-13 12:10     ` Konrad Dybcio
2026-03-13 12:45       ` Greg Kroah-Hartman
2026-03-13 12:50         ` Konrad Dybcio
2026-03-09 20:33 ` Elson Serrao [this message]
2026-03-09 20:33 ` [PATCH RFC v3 04/10] usb: misc: qcom_eud: add per-port role switch support Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 05/10] usb: misc: qcom_eud: improve enable_store API Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 06/10] usb: misc: qcom_eud: fix virtual attach/detach event handling Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 07/10] usb: misc: qcom_eud: add host mode coordination Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 08/10] usb: dwc3: qcom: notify EUD driver of role changes Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 09/10] arm64: dts: qcom: kodiak: Fix EUD USB controller connection Elson Serrao
2026-03-09 20:33 ` [PATCH RFC v3 10/10] arm64: dts: qcom: Map USB connector to EUD for kodiak boards Elson Serrao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260309203337.803986-4-elson.serrao@oss.qualcomm.com \
    --to=elson.serrao@oss.qualcomm.com \
    --cc=andersson@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=konradybcio@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=quic_schowdhu@quicinc.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox