* [PATCH v8 1/8] dt-bindings: soc: qcom: eud: Add per-path child nodes for UTMI routing
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
@ 2026-09-12 22:52 ` Elson Serrao
2026-09-12 22:52 ` [PATCH v8 2/8] usb: misc: qcom_eud: add sysfs attribute for port selection Elson Serrao
` (6 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:52 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel,
Krzysztof Kozlowski
The Qualcomm Embedded USB Debugger (EUD) can intercept one or two
High-Speed UTMI paths depending on the SoC. For example, SC7280 supports
EUD on the primary UTMI path only, while SM8350 supports EUD on both
primary and secondary UTMI paths.
Each UTMI path connects an independent USB controller/connector pair and
forms its own role-switch domain. EUD routes traffic based on USB role.
In device role the debug hub is inserted, while in host role it is
bypassed.
The existing binding models EUD using a flattened ports representation.
While sufficient for fixed device-role configurations, it cannot naturally
associate a controller, connector, and role switch with a specific
EUD-routable path. This is problematic for role-switch capable ports and
becomes more pronounced on SoCs with multiple paths. In such cases, each
path forms an independent role-switch domain and requires per-path role
awareness.
Model each UTMI path as a separate child node with its own OF graph. This
representation is used for role-aware EUD routing regardless of whether a
SoC exposes one or two UTMI paths. Add an SM8350 compatible for
dual-path EUD topologies. Retain support for the legacy flat ports
representation on SC7280 for backward compatibility.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Acked-by: Bjorn Andersson <andersson@kernel.org>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
.../bindings/soc/qcom/qcom,eud.yaml | 138 +++++++++++++++++-
1 file changed, 137 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml
index 84218636c0d8..90dc104e4475 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml
@@ -18,6 +18,7 @@ properties:
items:
- enum:
- qcom,sc7280-eud
+ - qcom,sm8350-eud
- const: qcom,eud
reg:
@@ -45,10 +46,92 @@ properties:
$ref: /schemas/graph.yaml#/properties/port
description: This port is to be attached to the type C connector.
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+patternProperties:
+ "^eud-path@[0-1]$":
+ type: object
+ description:
+ Represents one High-Speed UTMI path that EUD intercepts. Use eud-path nodes
+ to associate role-switching behavior with specific port connections, allowing
+ EUD to manage role transitions independently for each UTMI path.
+
+ properties:
+ reg:
+ maxItems: 1
+ description: Path number
+
+ usb-role-switch:
+ type: boolean
+ description:
+ Indicates that EUD relays USB role-switch events for this path and
+ performs role-dependent UTMI routing accordingly. In device role,
+ the EUD hub is inserted into this path to enable debug
+ functionality. In host role, the EUD hub is bypassed and UTMI
+ traffic flows directly between the PHY and the USB controller.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ description:
+ These ports are to be attached to the endpoint of the USB controller node
+ and USB connector node.
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: This port is to be attached to the USB controller.
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: This port is to be attached to the USB connector.
+
+ required:
+ - reg
+ - ports
+
+ additionalProperties: false
+
required:
- compatible
- reg
- - ports
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,sc7280-eud
+ then:
+ oneOf:
+ - required:
+ - ports
+ properties:
+ eud-path@0: false
+ eud-path@1: false
+ - required:
+ - eud-path@0
+ - '#address-cells'
+ - '#size-cells'
+ properties:
+ ports: false
+ eud-path@1: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,sm8350-eud
+ then:
+ required:
+ - eud-path@0
+ - eud-path@1
+ - '#address-cells'
+ - '#size-cells'
+ properties:
+ ports: false
additionalProperties: false
@@ -77,3 +160,56 @@ examples:
};
};
};
+ - |
+ eud@88e0000 {
+ compatible = "qcom,sm8350-eud", "qcom,eud";
+ reg = <0x88e0000 0x2000>,
+ <0x88e2000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eud-path@0 {
+ reg = <0>;
+ usb-role-switch;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ eud_usb0: endpoint {
+ remote-endpoint = <&usb_1_dwc3_hs>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ eud_con0: endpoint {
+ remote-endpoint = <&pmic_glink_hs_in>;
+ };
+ };
+ };
+ };
+
+ eud-path@1 {
+ reg = <1>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ eud_usb1: endpoint {
+ remote-endpoint = <&usb_2_dwc3_hs>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ eud_con1: endpoint {
+ remote-endpoint = <&conn_2_hs_in>;
+ };
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v8 2/8] usb: misc: qcom_eud: add sysfs attribute for port selection
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
2026-09-12 22:52 ` [PATCH v8 1/8] dt-bindings: soc: qcom: eud: Add per-path child nodes for UTMI routing Elson Serrao
@ 2026-09-12 22:52 ` Elson Serrao
2026-09-12 23:02 ` sashiko-bot
2026-09-12 22:52 ` [PATCH v8 3/8] usb: misc: qcom_eud: add per-path High-Speed PHY control Elson Serrao
` (5 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:52 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel, Peter Chen,
Konrad Dybcio
EUD can be mapped to either the primary USB port or the secondary USB port
depending on the value of the EUD_PORT_SEL register. Add a 'port' sysfs
attribute to allow userspace to select which port EUD should operate on
and update the ABI documentation. This is needed for systems with dual
USB ports where EUD needs to be accessible on either port depending on the
system configuration and use case.
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
Documentation/ABI/testing/sysfs-driver-eud | 18 ++++++++++
drivers/usb/misc/qcom_eud.c | 39 ++++++++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-driver-eud b/Documentation/ABI/testing/sysfs-driver-eud
index 2bab0db2d2f0..fb927a003596 100644
--- a/Documentation/ABI/testing/sysfs-driver-eud
+++ b/Documentation/ABI/testing/sysfs-driver-eud
@@ -7,3 +7,21 @@ Description:
EUD based on a 1 or a 0 value. By enabling EUD,
the user is able to activate the mini-usb hub of
EUD for debug and trace capabilities.
+
+What: /sys/bus/platform/drivers/qcom_eud/.../port
+Date: January 2026
+Contact: Elson Serrao <elson.serrao@oss.qualcomm.com>
+Description:
+ Selects which USB port the Embedded USB Debugger (EUD)
+ is mapped to on platforms providing multiple High-Speed
+ USB ports.
+
+ Valid values:
+ primary - Primary USB port
+ secondary - Secondary USB port
+
+ Writing any other value returns -EINVAL.
+
+ The attribute is writable only while EUD is disabled.
+ Writing it while EUD is enabled returns -EBUSY. Reading
+ the attribute returns the currently selected USB port.
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index a7b9ed4ac071..d32d382a311e 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -23,14 +23,22 @@
#define EUD_REG_VBUS_INT_CLR 0x0080
#define EUD_REG_CSR_EUD_EN 0x1014
#define EUD_REG_SW_ATTACH_DET 0x1018
+#define EUD_REG_PORT_SEL 0x1028
#define EUD_REG_EUD_EN2 0x0000
+#define EUD_MAX_PORTS 2
+
#define EUD_ENABLE BIT(0)
#define EUD_INT_PET_EUD BIT(0)
#define EUD_INT_VBUS BIT(2)
#define EUD_INT_SAFE_MODE BIT(4)
#define EUD_INT_ALL (EUD_INT_VBUS | EUD_INT_SAFE_MODE)
+static const char * const eud_port_names[] = {
+ "primary",
+ "secondary",
+};
+
struct eud_chip {
struct device *dev;
struct usb_role_switch *role_sw;
@@ -40,6 +48,7 @@ struct eud_chip {
int irq;
bool enabled;
bool usb_attached;
+ u8 port_idx;
};
static int enable_eud(struct eud_chip *priv)
@@ -104,8 +113,38 @@ static ssize_t enable_store(struct device *dev,
static DEVICE_ATTR_RW(enable);
+static ssize_t port_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct eud_chip *chip = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", eud_port_names[chip->port_idx]);
+}
+
+static ssize_t port_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct eud_chip *chip = dev_get_drvdata(dev);
+ int port;
+
+ port = sysfs_match_string(eud_port_names, buf);
+ if (port < 0)
+ return port;
+
+ /* Port selection must be done before enabling EUD */
+ if (chip->enabled)
+ return -EBUSY;
+
+ writel(port, chip->base + EUD_REG_PORT_SEL);
+ chip->port_idx = port;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(port);
+
static struct attribute *eud_attrs[] = {
&dev_attr_enable.attr,
+ &dev_attr_port.attr,
NULL,
};
ATTRIBUTE_GROUPS(eud);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v8 2/8] usb: misc: qcom_eud: add sysfs attribute for port selection
2026-09-12 22:52 ` [PATCH v8 2/8] usb: misc: qcom_eud: add sysfs attribute for port selection Elson Serrao
@ 2026-09-12 23:02 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-12 23:02 UTC (permalink / raw)
To: Elson Serrao; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `chip->port_idx` state variable is not initialized from the hardware register on probe, potentially desynchronizing from the actual hardware state.
--
commit ff68b261929b924c0854c25143a07864641e767b
Author: Elson Serrao <elson.serrao@oss.qualcomm.com>
usb: misc: qcom_eud: add sysfs attribute for port selection
EUD can be mapped to either the primary USB port or the secondary USB port
depending on the value of the EUD_PORT_SEL register. Add a 'port' sysfs
attribute to allow userspace to select which port EUD should operate on
and update the ABI documentation.
> diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
> index 926419ca560fc..25a6a8f110ec8 100644
> --- a/drivers/usb/misc/qcom_eud.c
> +++ b/drivers/usb/misc/qcom_eud.c
[ ... ]
> @@ -104,8 +113,38 @@ static ssize_t enable_store(struct device *dev,
>
> static DEVICE_ATTR_RW(enable);
>
> +static ssize_t port_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> + struct eud_chip *chip = dev_get_drvdata(dev);
> +
> + return sysfs_emit(buf, "%s\n", eud_port_names[chip->port_idx]);
[Severity: Medium]
Will this report a stale default value ("primary") instead of the true hardware
state if the EUD_REG_PORT_SEL register was modified prior to driver probe?
Because chip->port_idx defaults to 0 and isn't synchronized with the hardware
register in eud_probe() (either by reading it or resetting it to 0), the driver
could report the primary port while the hardware is actually routing EUD to the
secondary port.
This can happen if the user switches to the secondary port and the driver module
is unloaded and reloaded, or if the bootloader left the hardware operating on
the secondary port.
When EUD is subsequently enabled, the driver would then power on the PHY for
the primary port instead of the secondary port, breaking the connection.
> +}
> +
> +static ssize_t port_store(struct device *dev, struct device_attribute *attr,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912225301.3396556-1-elson.serrao@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 3/8] usb: misc: qcom_eud: add per-path High-Speed PHY control
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
2026-09-12 22:52 ` [PATCH v8 1/8] dt-bindings: soc: qcom: eud: Add per-path child nodes for UTMI routing Elson Serrao
2026-09-12 22:52 ` [PATCH v8 2/8] usb: misc: qcom_eud: add sysfs attribute for port selection Elson Serrao
@ 2026-09-12 22:52 ` Elson Serrao
2026-09-12 23:16 ` sashiko-bot
2026-09-12 22:52 ` [PATCH v8 4/8] usb: misc: qcom_eud: add per-path role switch support Elson Serrao
` (4 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:52 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel, Peter Chen,
Konrad Dybcio
EUD hardware can support multiple High-Speed USB paths, each routed
through its own PHY. The active path 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 path. To support this
multi-path capability, the driver must manage PHY resources on a
per-path basis, ensuring that the PHY for the currently selected
path is properly initialized and powered.
This patch restructures the driver to implement per-path PHY management.
The driver now powers the appropriate PHY based on the selected and
enabled UTMI path, ensuring correct operation when EUD is enabled.
Historically, EUD appeared to work on single-path 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.
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
Documentation/ABI/testing/sysfs-driver-eud | 3 +-
drivers/usb/misc/qcom_eud.c | 124 ++++++++++++++++++++-
2 files changed, 125 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-driver-eud b/Documentation/ABI/testing/sysfs-driver-eud
index fb927a003596..f20b82b5a38a 100644
--- a/Documentation/ABI/testing/sysfs-driver-eud
+++ b/Documentation/ABI/testing/sysfs-driver-eud
@@ -20,7 +20,8 @@ Description:
primary - Primary USB port
secondary - Secondary USB port
- Writing any other value returns -EINVAL.
+ Writing any other value returns -EINVAL. Writing a port
+ not supported on the current platform returns -EOPNOTSUPP.
The attribute is writable only while EUD is disabled.
Writing it while EUD is enabled returns -EBUSY. Reading
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index d32d382a311e..70915af40ebe 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>
@@ -39,26 +41,84 @@ static const char * const eud_port_names[] = {
"secondary",
};
+struct eud_path {
+ struct eud_chip *chip;
+ struct phy *phy;
+ u8 num;
+};
+
struct eud_chip {
struct device *dev;
struct usb_role_switch *role_sw;
void __iomem *base;
+ struct eud_path *paths[EUD_MAX_PORTS];
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->paths[chip->port_idx]->phy;
+
+ 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->paths[chip->port_idx]->phy;
+
+ 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);
@@ -75,6 +135,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;
}
@@ -130,6 +192,10 @@ static ssize_t port_store(struct device *dev, struct device_attribute *attr,
if (port < 0)
return port;
+ /* Check if the corresponding path is available */
+ if (!chip->paths[port])
+ return -EOPNOTSUPP;
+
/* Port selection must be done before enabling EUD */
if (chip->enabled)
return -EBUSY;
@@ -227,8 +293,54 @@ static void eud_role_switch_release(void *data)
usb_role_switch_put(chip->role_sw);
}
+static int eud_init_path(struct eud_chip *chip, struct device_node *np)
+{
+ struct device_node *controller_node;
+ struct eud_path *path;
+ u32 path_num;
+ int ret;
+
+ ret = of_property_read_u32(np, "reg", &path_num);
+ if (ret) {
+ /* Legacy DT uses 'ports' node without 'reg' property; treat as path 0 */
+ if (of_node_name_eq(np, "ports"))
+ path_num = 0;
+ else
+ return dev_err_probe(chip->dev, ret, "unexpected child node '%s'\n",
+ np->name);
+ }
+
+ if (path_num >= EUD_MAX_PORTS)
+ return dev_err_probe(chip->dev, -EINVAL, "invalid path number: %u (max %d)\n",
+ path_num, EUD_MAX_PORTS - 1);
+
+ path = devm_kzalloc(chip->dev, sizeof(*path), GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+
+ path->chip = chip;
+ path->num = path_num;
+
+ controller_node = of_graph_get_remote_node(np, 0, -1);
+ if (!controller_node)
+ return dev_err_probe(chip->dev, -ENODEV,
+ "failed to get controller node for path %u\n", path_num);
+
+ path->phy = devm_of_phy_get_by_index(chip->dev, controller_node, 0);
+ of_node_put(controller_node);
+
+ if (IS_ERR(path->phy))
+ return dev_err_probe(chip->dev, PTR_ERR(path->phy),
+ "failed to get PHY for path %d\n", path_num);
+
+ chip->paths[path_num] = path;
+
+ return 0;
+}
+
static int eud_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct eud_chip *chip;
struct resource *res;
int ret;
@@ -257,6 +369,16 @@ static int eud_probe(struct platform_device *pdev)
return -ENODEV;
chip->mode_mgr = res->start;
+ for_each_child_of_node_scoped(np, child) {
+ ret = eud_init_path(chip, child);
+ if (ret)
+ return ret;
+ }
+
+ /* Primary path is mandatory. Secondary is optional */
+ if (!chip->paths[0])
+ return dev_err_probe(chip->dev, -ENODEV, "primary path not found\n");
+
chip->irq = platform_get_irq(pdev, 0);
if (chip->irq < 0)
return chip->irq;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v8 3/8] usb: misc: qcom_eud: add per-path High-Speed PHY control
2026-09-12 22:52 ` [PATCH v8 3/8] usb: misc: qcom_eud: add per-path High-Speed PHY control Elson Serrao
@ 2026-09-12 23:16 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-12 23:16 UTC (permalink / raw)
To: Elson Serrao; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Early return in disable_eud skips PHY disablement, leaking High-Speed PHY resources.
Pre-existing issues:
- [High] Calling readl_poll_timeout from a hardirq handler sleeps in atomic context.
--
commit 895071c1614ba92a198085df0882e77e4e133a74
Author: Elson Serrao <elson.serrao@oss.qualcomm.com>
usb: misc: qcom_eud: add per-path High-Speed PHY control
EUD hardware can support multiple High-Speed USB paths, each routed
through its own PHY. This patch restructures the driver to implement
per-path PHY management, ensuring that the PHY for the currently selected
path is properly initialized and powered.
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
> diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
> index 25a6a8f110ec8..868725ad35e09 100644
> --- a/drivers/usb/misc/qcom_eud.c
> +++ b/drivers/usb/misc/qcom_eud.c
[ ... ]
> @@ -75,6 +135,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;
> }
[Severity: Medium]
Does this early return bypass the newly introduced PHY disablement on error?
If qcom_scm_io_writel() fails, the function returns early and skips
eud_phy_disable(priv). Will this leave the High-Speed PHY resources
initialized and powered on, leaking the hardware state?
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but is it safe to call
readl_poll_timeout() from a hardirq handler?
When the EUD_INT_SAFE_MODE hardware interrupt fires, the primary hardirq
handler directly calls pet_eud():
drivers/usb/misc/qcom_eud.c:handle_eud_irq() {
...
case EUD_INT_SAFE_MODE:
pet_eud(chip);
return IRQ_HANDLED;
}
Which then calls readl_poll_timeout() with a non-zero delay:
drivers/usb/misc/qcom_eud.c:pet_eud() {
...
ret = readl_poll_timeout(chip->base + EUD_REG_SW_ATTACH_DET,
reg, (reg == 0), 1, 100);
...
}
Since readl_poll_timeout() internally uses usleep_range() which sleeps,
can this cause a panic or deadlock by sleeping in an atomic context?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912225301.3396556-1-elson.serrao@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 4/8] usb: misc: qcom_eud: add per-path role switch support
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
` (2 preceding siblings ...)
2026-09-12 22:52 ` [PATCH v8 3/8] usb: misc: qcom_eud: add per-path High-Speed PHY control Elson Serrao
@ 2026-09-12 22:52 ` Elson Serrao
2026-09-12 23:08 ` sashiko-bot
2026-09-12 22:52 ` [PATCH v8 5/8] usb: misc: qcom_eud: improve enable_store API Elson Serrao
` (3 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:52 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel, Peter Chen
The EUD hardware can support multiple High-Speed USB paths, each connected
to different USB controllers. The current implementation uses a single
chip-level role switch, which cannot properly handle multi-path
configurations where each path needs independent role management. Since
EUD is physically present between the USB connector and the controller,
it should also relay the role change requests from the connector.
Restructure the driver to support per-path role switches and remove the
chip-level role switch. Additionally, as EUD need not modify the USB
role upon enabling, remove the unnecessary role switch call from
enable_eud().
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
drivers/usb/misc/qcom_eud.c | 103 +++++++++++++++++++++++++++++-------
1 file changed, 84 insertions(+), 19 deletions(-)
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 70915af40ebe..e2694c6109ba 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -44,12 +44,15 @@ static const char * const eud_port_names[] = {
struct eud_path {
struct eud_chip *chip;
struct phy *phy;
+ struct usb_role_switch *controller_sw;
+ struct usb_role_switch *eud_sw;
+ enum usb_role curr_role;
+ char name[16];
u8 num;
};
struct eud_chip {
struct device *dev;
- struct usb_role_switch *role_sw;
void __iomem *base;
struct eud_path *paths[EUD_MAX_PORTS];
phys_addr_t mode_mgr;
@@ -123,7 +126,7 @@ static int enable_eud(struct eud_chip *priv)
writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE,
priv->base + EUD_REG_INT1_EN_MASK);
- return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE);
+ return 0;
}
static int disable_eud(struct eud_chip *priv)
@@ -270,12 +273,15 @@ static irqreturn_t handle_eud_irq(int irq, void *data)
static irqreturn_t handle_eud_irq_thread(int irq, void *data)
{
struct eud_chip *chip = data;
+ struct eud_path *path;
int ret;
+ path = chip->paths[chip->port_idx];
+
if (chip->usb_attached)
- ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_DEVICE);
+ ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_DEVICE);
else
- ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_HOST);
+ ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_HOST);
if (ret)
dev_err(chip->dev, "failed to set role switch\n");
@@ -286,17 +292,40 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
return IRQ_HANDLED;
}
-static void eud_role_switch_release(void *data)
+static int eud_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
{
- struct eud_chip *chip = data;
+ struct eud_path *path = usb_role_switch_get_drvdata(sw);
+ int ret;
+
+ /* curr_role tracks the role from EUD's point of view */
+ path->curr_role = role;
+
+ /* Forward the role request to the USB controller */
+ ret = usb_role_switch_set_role(path->controller_sw, role);
+ if (ret) {
+ dev_err(path->chip->dev, "failed to set role %s for port %u: %d\n",
+ usb_role_string(role), path->num, ret);
+ return ret;
+ }
- usb_role_switch_put(chip->role_sw);
+ return 0;
+}
+
+static void eud_path_role_switch_release(void *data)
+{
+ struct eud_path *path = data;
+
+ usb_role_switch_unregister(path->eud_sw);
+ usb_role_switch_put(path->controller_sw);
}
static int eud_init_path(struct eud_chip *chip, struct device_node *np)
{
+ struct usb_role_switch_desc role_sw_desc = {NULL};
+ struct usb_role_switch *sw;
struct device_node *controller_node;
struct eud_path *path;
+ const char *dr_mode = "";
u32 path_num;
int ret;
@@ -327,14 +356,59 @@ static int eud_init_path(struct eud_chip *chip, struct device_node *np)
"failed to get controller node for path %u\n", path_num);
path->phy = devm_of_phy_get_by_index(chip->dev, controller_node, 0);
- of_node_put(controller_node);
-
- if (IS_ERR(path->phy))
+ if (IS_ERR(path->phy)) {
+ of_node_put(controller_node);
return dev_err_probe(chip->dev, PTR_ERR(path->phy),
"failed to get PHY for path %d\n", path_num);
+ }
chip->paths[path_num] = path;
+ of_property_read_string(controller_node, "dr_mode", &dr_mode);
+
+ /* No role switch needed for fixed role controllers */
+ if (!strcmp(dr_mode, "host")) {
+ path->curr_role = USB_ROLE_HOST;
+ of_node_put(controller_node);
+ return 0;
+ }
+
+ if (!strcmp(dr_mode, "peripheral")) {
+ path->curr_role = USB_ROLE_DEVICE;
+ of_node_put(controller_node);
+ return 0;
+ }
+
+ path->curr_role = USB_ROLE_NONE;
+
+ sw = usb_role_switch_find_by_fwnode(of_fwnode_handle(controller_node));
+ if (!sw) {
+ of_node_put(controller_node);
+ return dev_err_probe(chip->dev, -EPROBE_DEFER,
+ "Failed to get controller role switch for path %d\n",
+ path_num);
+ }
+ path->controller_sw = sw;
+
+ of_node_put(controller_node);
+
+ role_sw_desc.fwnode = of_fwnode_handle(np);
+ role_sw_desc.set = eud_role_switch_set;
+ role_sw_desc.driver_data = path;
+ snprintf(path->name, sizeof(path->name), "eud-path%u", path_num);
+ role_sw_desc.name = path->name;
+
+ path->eud_sw = usb_role_switch_register(chip->dev, &role_sw_desc);
+ if (IS_ERR(path->eud_sw)) {
+ usb_role_switch_put(path->controller_sw);
+ return dev_err_probe(chip->dev, PTR_ERR(path->eud_sw),
+ "Failed to register EUD role switch for path %d\n", path_num);
+ }
+
+ ret = devm_add_action_or_reset(chip->dev, eud_path_role_switch_release, path);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -351,15 +425,6 @@ static int eud_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
- 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),
- "failed to get role switch\n");
-
- ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip);
- if (ret)
- return ret;
-
chip->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v8 4/8] usb: misc: qcom_eud: add per-path role switch support
2026-09-12 22:52 ` [PATCH v8 4/8] usb: misc: qcom_eud: add per-path role switch support Elson Serrao
@ 2026-09-12 23:08 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-12 23:08 UTC (permalink / raw)
To: Elson Serrao; +Cc: devicetree, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Medium] The driver registers its USB role switch with the incorrect fwnode for legacy device trees, preventing USB connectors from finding the switch.
- [Medium] Dangling fwnode pointer in the registered USB role switch due to missing device node refcount increment.
Pre-existing issues:
- [High] The hardirq handler calls a function that sleeps, leading to a kernel panic.
--
commit 9414ff0b2c9f7c78a66f0bce1fe95af108a567fa
Author: Elson Serrao <elson.serrao@oss.qualcomm.com>
usb: misc: qcom_eud: add per-path role switch support
The EUD hardware can support multiple High-Speed USB paths, each connected
to different USB controllers. Restructure the driver to support per-path
role switches and remove the chip-level role switch. Additionally, remove
the unnecessary role switch call from enable_eud().
> diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
> index 868725ad35e09..5e2034fb11ab5 100644
> --- a/drivers/usb/misc/qcom_eud.c
> +++ b/drivers/usb/misc/qcom_eud.c
[ ... ]
> @@ -270,12 +273,15 @@ static irqreturn_t handle_eud_irq(int irq, void *data)
[Severity: High]
This is a pre-existing issue and was not introduced by the patch under
review, but does handle_eud_irq() end up sleeping in a hardirq context?
Looking at the driver's interrupt handling, handle_eud_irq() is the
primary hardirq handler (top-half). When it receives an EUD_INT_SAFE_MODE
interrupt, it calls pet_eud():
drivers/usb/misc/qcom_eud.c:handle_eud_irq() {
...
case EUD_INT_SAFE_MODE:
pet_eud(chip);
return IRQ_HANDLED;
...
}
However, pet_eud() invokes readl_poll_timeout() which internally calls
usleep_range() and attempts to sleep:
drivers/usb/misc/qcom_eud.c:pet_eud() {
...
ret = readl_poll_timeout(chip->base + EUD_REG_SW_ATTACH_DET,
reg, (reg == 0), 1, 100);
...
}
Can this lead to a kernel panic due to scheduling while atomic when the
hardware generates a safe mode interrupt?
> static irqreturn_t handle_eud_irq_thread(int irq, void *data)
> {
[ ... ]
> @@ -327,14 +356,59 @@ static int eud_init_path(struct eud_chip *chip, struct device_node *np)
[ ... ]
> + role_sw_desc.fwnode = of_fwnode_handle(np);
[Severity: Medium]
When processing a legacy device tree, does this assign the wrong fwnode
to the role switch?
In a legacy DT, np points to the ports node instead of the main eud
device node. By setting the fwnode to the ports node here, the role
switch is registered with that specific fwnode.
However, when a connector driver searches for its remote role switch,
of_fwnode_graph_get_port_parent() explicitly skips nodes named "ports" and
returns the parent device node instead. Does this mismatch cause the
connector framework to fail to find the EUD's role switch, breaking the
role-relay functionality on platforms using legacy DT bindings?
> + role_sw_desc.set = eud_role_switch_set;
> + role_sw_desc.driver_data = path;
> + snprintf(path->name, sizeof(path->name), "eud-path%u", path_num);
> + role_sw_desc.name = path->name;
> +
> + path->eud_sw = usb_role_switch_register(chip->dev, &role_sw_desc);
[Severity: Medium]
Does this leave a dangling fwnode pointer in the registered USB role switch?
Looking at eud_probe(), it iterates over child nodes using:
for_each_child_of_node_scoped(np, child) {
ret = eud_init_path(chip, child);
...
}
The child device node pointer is passed to eud_init_path() as np, where
its fwnode is directly passed to usb_role_switch_register().
usb_role_switch_register() stores the fwnode but does not increment its
reference count.
Since for_each_child_of_node_scoped() automatically drops the reference on
the child node when the loop advances or exits, does the registered
role switch retain a pointer to a device node with a decremented reference
count? For dynamic DT overlays, could this lead to a Use-After-Free upon
role switch removal or sysfs access?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912225301.3396556-1-elson.serrao@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 5/8] usb: misc: qcom_eud: improve enable_store API
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
` (3 preceding siblings ...)
2026-09-12 22:52 ` [PATCH v8 4/8] usb: misc: qcom_eud: add per-path role switch support Elson Serrao
@ 2026-09-12 22:52 ` Elson Serrao
2026-09-12 22:52 ` [PATCH v8 6/8] usb: misc: qcom_eud: add role-based EUD control Elson Serrao
` (2 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:52 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel, Konrad Dybcio,
Peter Chen
Currently enable_store() allows operations irrespective of the EUD state,
which can result in redundant operations. Avoid this by adding duplicate
state checks to skip requests when EUD is already in the desired state.
Additionally, improve error handling with explicit logging to provide
better feedback.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
drivers/usb/misc/qcom_eud.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index e2694c6109ba..d280628370e4 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -162,18 +162,27 @@ static ssize_t enable_store(struct device *dev,
if (kstrtobool(buf, &enable))
return -EINVAL;
+ /* Skip operation if already in desired state */
+ if (chip->enabled == enable)
+ return count;
+
if (enable) {
ret = enable_eud(chip);
- if (!ret)
- chip->enabled = enable;
- else
- disable_eud(chip);
-
+ if (ret) {
+ dev_err(chip->dev, "failed to enable eud\n");
+ return ret;
+ }
} else {
ret = disable_eud(chip);
+ if (ret) {
+ dev_err(chip->dev, "failed to disable eud\n");
+ return ret;
+ }
}
- return ret < 0 ? ret : count;
+ chip->enabled = enable;
+
+ return count;
}
static DEVICE_ATTR_RW(enable);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v8 6/8] usb: misc: qcom_eud: add role-based EUD control
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
` (4 preceding siblings ...)
2026-09-12 22:52 ` [PATCH v8 5/8] usb: misc: qcom_eud: improve enable_store API Elson Serrao
@ 2026-09-12 22:52 ` Elson Serrao
2026-09-12 23:03 ` sashiko-bot
2026-09-12 22:53 ` [PATCH v8 7/8] usb: misc: qcom_eud: fix virtual attach/detach event handling Elson Serrao
2026-09-12 22:53 ` [PATCH v8 8/8] arm64: dts: qcom: kodiak: Desribe EUD UTMI routing and connector mapping Elson Serrao
7 siblings, 1 reply; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:52 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel, Peter Chen,
Konrad Dybcio
EUD functions by presenting itself as a USB device to the host PC and is
therefore only applicable when the port operates in device role. Keeping
EUD enabled in other roles provides no benefit and unnecessarily keeps
the associated PHY resources enabled. Furthermore, enabling EUD in host
role can cause the USB controller to misbehave because the EUD hub
supports only a single upstream-facing port.
Address this by treating EUD as usable only in device role. The
role-switch callback enables or disables EUD as the port enters or
leaves device role. A sysfs enable request made outside device role is
recorded and honored when the port subsequently switches to device role.
Additionally, synchronize EUD state management across different paths to
avoid races and maintain consistent hardware state.
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
drivers/usb/misc/qcom_eud.c | 60 +++++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index d280628370e4..353c45dfd328 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -15,6 +15,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/sysfs.h>
#include <linux/usb/role.h>
#include <linux/firmware/qcom/qcom_scm.h>
@@ -55,6 +56,8 @@ struct eud_chip {
struct device *dev;
void __iomem *base;
struct eud_path *paths[EUD_MAX_PORTS];
+ /* serializes EUD control operations */
+ struct mutex state_lock;
phys_addr_t mode_mgr;
unsigned int int_status;
int irq;
@@ -156,17 +159,33 @@ static ssize_t enable_store(struct device *dev,
const char *buf, size_t count)
{
struct eud_chip *chip = dev_get_drvdata(dev);
+ struct eud_path *path;
bool enable;
int ret;
if (kstrtobool(buf, &enable))
return -EINVAL;
+ guard(mutex)(&chip->state_lock);
+
/* Skip operation if already in desired state */
if (chip->enabled == enable)
return count;
if (enable) {
+ path = chip->paths[chip->port_idx];
+
+ /*
+ * If not yet in device role, honor the userspace request and defer
+ * EUD enablement until the port transitions to device role in the
+ * set_role callback.
+ */
+ if (path->curr_role != USB_ROLE_DEVICE) {
+ dev_info(chip->dev, "Deferring EUD enable until port enters device mode\n");
+ chip->enabled = enable;
+ return count;
+ }
+
ret = enable_eud(chip);
if (ret) {
dev_err(chip->dev, "failed to enable eud\n");
@@ -204,6 +223,8 @@ static ssize_t port_store(struct device *dev, struct device_attribute *attr,
if (port < 0)
return port;
+ guard(mutex)(&chip->state_lock);
+
/* Check if the corresponding path is available */
if (!chip->paths[port])
return -EOPNOTSUPP;
@@ -285,8 +306,13 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
struct eud_path *path;
int ret;
+ guard(mutex)(&chip->state_lock);
+
path = chip->paths[chip->port_idx];
+ if (!chip->enabled || path->curr_role != USB_ROLE_DEVICE)
+ goto out;
+
if (chip->usb_attached)
ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_DEVICE);
else
@@ -294,6 +320,7 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
if (ret)
dev_err(chip->dev, "failed to set role switch\n");
+out:
/* set and clear vbus_int_clr[0] to clear interrupt */
writel(BIT(0), chip->base + EUD_REG_VBUS_INT_CLR);
writel(0, chip->base + EUD_REG_VBUS_INT_CLR);
@@ -304,7 +331,30 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
static int eud_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
{
struct eud_path *path = usb_role_switch_get_drvdata(sw);
- int ret;
+ struct eud_chip *chip = path->chip;
+ int ret = 0;
+
+ guard(mutex)(&chip->state_lock);
+
+ /*
+ * EUD is usable only in device role. Power it down for every other
+ * role to avoid keeping an unusable module 'ON'. chip->enabled
+ * preserves user's sysfs configuration and is not modified across
+ * role transitions.
+ */
+ if (chip->enabled && path->num == chip->port_idx && role != path->curr_role) {
+ if (role == USB_ROLE_DEVICE)
+ ret = enable_eud(chip);
+ else if (path->curr_role == USB_ROLE_DEVICE)
+ ret = disable_eud(chip);
+
+ if (ret) {
+ dev_err(chip->dev, "failed to %s EUD for role %s: %d\n",
+ str_enable_disable(role == USB_ROLE_DEVICE),
+ usb_role_string(role), ret);
+ return ret;
+ }
+ }
/* curr_role tracks the role from EUD's point of view */
path->curr_role = role;
@@ -434,6 +484,8 @@ static int eud_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
+ mutex_init(&chip->state_lock);
+
chip->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
@@ -473,8 +525,12 @@ static void eud_remove(struct platform_device *pdev)
{
struct eud_chip *chip = platform_get_drvdata(pdev);
- if (chip->enabled)
+ mutex_lock(&chip->state_lock);
+ if (chip->enabled) {
disable_eud(chip);
+ chip->enabled = false;
+ }
+ mutex_unlock(&chip->state_lock);
device_init_wakeup(&pdev->dev, false);
disable_irq_wake(chip->irq);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v8 6/8] usb: misc: qcom_eud: add role-based EUD control
2026-09-12 22:52 ` [PATCH v8 6/8] usb: misc: qcom_eud: add role-based EUD control Elson Serrao
@ 2026-09-12 23:03 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-12 23:03 UTC (permalink / raw)
To: Elson Serrao; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Mixing goto-based exit logic with scope-based guard() cleanup in `handle_eud_irq_thread`.
--
commit 5bca652d5a5a406c9aee75922062a73bc36dc72b
Author: Elson Serrao <elson.serrao@oss.qualcomm.com>
usb: misc: qcom_eud: add role-based EUD control
This commit updates the QCOM EUD driver to only operate when the port is in
device role, preserving hardware resources and preventing controller misbehavior
in host role. It uses a mutex to serialize state management across sysfs, IRQ,
and role switch paths.
> diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
> --- a/drivers/usb/misc/qcom_eud.c
> +++ b/drivers/usb/misc/qcom_eud.c
[ ... ]
> @@ -285,8 +306,13 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
> struct eud_path *path;
> int ret;
>
> + guard(mutex)(&chip->state_lock);
> +
> path = chip->paths[chip->port_idx];
>
> + if (!chip->enabled || path->curr_role != USB_ROLE_DEVICE)
> + goto out;
> +
[Severity: Low]
Does this code mix a goto-based exit label (out) with a scope-based
guard(mutex) in handle_eud_irq_thread()?
The Cleanup and Guard Subsystem architectural guidelines explicitly ban mixing
these patterns in the same function to prevent confusing ownership semantics.
While this might not lead to an immediate runtime failure here since the goto
just skips to clearing an interrupt and returning, the subsystem documentation
states that for a given routine, all resources must either be converted to
scope-based cleanup, or none of them should be.
> if (chip->usb_attached)
> ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_DEVICE);
> else
> @@ -294,6 +320,7 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
> if (ret)
> dev_err(chip->dev, "failed to set role switch\n");
>
> +out:
> /* set and clear vbus_int_clr[0] to clear interrupt */
> writel(BIT(0), chip->base + EUD_REG_VBUS_INT_CLR);
> writel(0, chip->base + EUD_REG_VBUS_INT_CLR);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912225301.3396556-1-elson.serrao@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 7/8] usb: misc: qcom_eud: fix virtual attach/detach event handling
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
` (5 preceding siblings ...)
2026-09-12 22:52 ` [PATCH v8 6/8] usb: misc: qcom_eud: add role-based EUD control Elson Serrao
@ 2026-09-12 22:53 ` Elson Serrao
2026-09-12 23:12 ` sashiko-bot
2026-09-12 22:53 ` [PATCH v8 8/8] arm64: dts: qcom: kodiak: Desribe EUD UTMI routing and connector mapping Elson Serrao
7 siblings, 1 reply; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:53 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel, Konrad Dybcio,
Peter Chen
EUD provides virtual USB attach/detach events to simulate cable
plug/unplug while maintaining the physical debug connection. However,
the current implementation incorrectly sets the USB role to HOST on
virtual detach, which doesn't represent the disconnected state.
Fix the virtual detach handling by setting the USB role to NONE
instead of HOST, correctly representing the disconnected state.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
drivers/usb/misc/qcom_eud.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 353c45dfd328..669e2e00dd7b 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -313,10 +313,26 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
if (!chip->enabled || path->curr_role != USB_ROLE_DEVICE)
goto out;
+ /*
+ * EUD virtual attach/detach event handling for low power debugging:
+ *
+ * When EUD is enabled in debug mode, the device remains physically
+ * connected to the PC throughout the debug session, keeping the USB
+ * controller active. This prevents testing of low power scenarios that
+ * require USB disconnection.
+ *
+ * EUD solves this by providing virtual USB attach/detach events while
+ * maintaining the physical connection. These events are triggered from
+ * the Host PC via the enumerated EUD control interface and delivered
+ * to the EUD driver as interrupts.
+ *
+ * These notifications are forwarded to the USB controller through role
+ * switch framework.
+ */
if (chip->usb_attached)
ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_DEVICE);
else
- ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_HOST);
+ ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_NONE);
if (ret)
dev_err(chip->dev, "failed to set role switch\n");
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v8 7/8] usb: misc: qcom_eud: fix virtual attach/detach event handling
2026-09-12 22:53 ` [PATCH v8 7/8] usb: misc: qcom_eud: fix virtual attach/detach event handling Elson Serrao
@ 2026-09-12 23:12 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-12 23:12 UTC (permalink / raw)
To: Elson Serrao; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Medium] Disabling EUD while virtually detached leaves the USB controller stuck in a disconnected state.
--
commit 29002e5f224d237570095828a981e93ede8860c1
Author: Elson Serrao <elson.serrao@oss.qualcomm.com>
usb: misc: qcom_eud: fix virtual attach/detach event handling
EUD provides virtual USB attach/detach events to simulate cable plug/unplug
while maintaining the physical debug connection. This commit fixes virtual
detach handling by setting the USB role to NONE instead of HOST.
> diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
> index 1ed81e0c7dc8e..423320cdc704a 100644
> --- a/drivers/usb/misc/qcom_eud.c
> +++ b/drivers/usb/misc/qcom_eud.c
> @@ -313,10 +313,26 @@ static irqreturn_t handle_eud_irq_thread(int irq, void *data)
> if (!chip->enabled || path->curr_role != USB_ROLE_DEVICE)
> goto out;
>
> [ ... ]
> if (chip->usb_attached)
> ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_DEVICE);
> else
> - ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_HOST);
> + ret = usb_role_switch_set_role(path->controller_sw, USB_ROLE_NONE);
[Severity: Medium]
This is a pre-existing issue, but does disabling EUD while virtually detached
leave the USB controller stuck in a disconnected state?
If EUD is enabled via sysfs, a physical connection is made, EUD triggers a
virtual detach (setting the controller role to NONE), and a user disables
EUD via the enable sysfs node.
The driver disables EUD, but no role restoration is performed for
path->controller_sw to reflect the physical path->curr_role:
drivers/usb/misc/qcom_eud.c:enable_store() {
...
} else {
ret = disable_eud(chip);
if (ret) {
dev_err(chip->dev, "failed to disable eud\n");
return ret;
}
}
...
}
Does the controller remain functionally disconnected (USB_ROLE_NONE) despite
being physically connected (USB_ROLE_DEVICE) until a physical unplug and
replug occurs?
> if (ret)
> dev_err(chip->dev, "failed to set role switch\n");
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912225301.3396556-1-elson.serrao@oss.qualcomm.com?part=7
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 8/8] arm64: dts: qcom: kodiak: Desribe EUD UTMI routing and connector mapping
2026-09-12 22:52 [PATCH v8 0/8] Improve Qualcomm EUD driver and platform support Elson Serrao
` (6 preceding siblings ...)
2026-09-12 22:53 ` [PATCH v8 7/8] usb: misc: qcom_eud: fix virtual attach/detach event handling Elson Serrao
@ 2026-09-12 22:53 ` Elson Serrao
2026-09-12 23:11 ` sashiko-bot
7 siblings, 1 reply; 15+ messages in thread
From: Elson Serrao @ 2026-09-12 22:53 UTC (permalink / raw)
To: Bjorn Andersson, Conor Dooley, Greg Kroah-Hartman, Konrad Dybcio,
Krzysztof Kozlowski, Rob Herring
Cc: linux-arm-msm, devicetree, linux-usb, linux-kernel
The existing EUD description uses a legacy single-path representation
that assumes uniform UTMI routing. This was sufficient for minimal
configurations where the USB port operated only in device mode and role
switching was not considered.
Update the description to explicitly model the topology using eud path
child node and add a role-switch declaration to reflect role-dependent
UTMI routing. Also on this SoC, the EUD hardware intercepts only the
primary UTMI path. So remove the USB endpoint mapping to the secondary
controller and associate it with the primary controller.
With the intermediate EUD node now in place, update the Kodiak board
descriptions to map the USB connector endpoint to the EUD instead of
directly to the USB controller, reflecting the intended hardware
topology. Enable the EUD by default now that the complete topology is
described.
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/kodiak.dtsi | 37 ++++++++++++-------
.../boot/dts/qcom/qcm6490-fairphone-fp5.dts | 10 ++---
.../dts/qcom/qcm6490-particle-tachyon.dts | 10 ++---
.../boot/dts/qcom/qcm6490-shift-otter.dts | 10 ++---
arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts | 10 ++---
.../qcom/qcs6490-thundercomm-minipc-g1iot.dts | 4 +-
.../dts/qcom/qcs6490-thundercomm-rubikpi3.dts | 10 ++---
.../qcom/sm7325-motorola-dubai-common.dtsi | 4 +-
.../boot/dts/qcom/sm7325-motorola-dubai.dts | 4 +-
.../boot/dts/qcom/sm7325-nothing-spacewar.dts | 10 ++---
.../boot/dts/qcom/sm7325-xiaomi-taoyao.dts | 4 +-
11 files changed, 61 insertions(+), 52 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
index f2da3706d5c8..6c64dc9902e4 100644
--- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
+++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
@@ -4421,12 +4421,6 @@ usb_2: usb@8c00000 {
phy-names = "usb2-phy";
maximum-speed = "high-speed";
usb-role-switch;
-
- port {
- usb2_role_switch: endpoint {
- remote-endpoint = <&eud_ep>;
- };
- };
};
qspi: spi@88dc000 {
@@ -4753,16 +4747,30 @@ eud: eud@88e0000 {
<0 0x88e2000 0 0x1000>;
interrupts-extended = <&pdc 11 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
+ eud0: eud-path@0 {
+ reg = <0>;
+ usb-role-switch;
- port@0 {
- reg = <0>;
- eud_ep: endpoint {
- remote-endpoint = <&usb2_role_switch>;
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ eud_usb: endpoint {
+ remote-endpoint = <&usb_1_dwc3_hs>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ eud_con: endpoint {
+ };
};
};
};
@@ -4989,6 +4997,7 @@ port@0 {
reg = <0>;
usb_1_dwc3_hs: endpoint {
+ remote-endpoint = <&eud_usb>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
index f41ad446a39e..b1f1ff6b99e6 100644
--- a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
+++ b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
@@ -98,7 +98,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1530,10 +1530,6 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
- remote-endpoint = <&pmic_glink_hs_in>;
-};
-
&usb_1_hsphy {
vdda-pll-supply = <&vreg_l10c>;
vdda18-supply = <&vreg_l1c>;
@@ -1573,3 +1569,7 @@ &wifi {
qcom,calibration-variant = "Fairphone_5";
status = "okay";
};
+
+&eud_con {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcm6490-particle-tachyon.dts b/arch/arm64/boot/dts/qcom/qcm6490-particle-tachyon.dts
index 5bb33a8a9830..41836afd3acd 100644
--- a/arch/arm64/boot/dts/qcom/qcm6490-particle-tachyon.dts
+++ b/arch/arm64/boot/dts/qcom/qcm6490-particle-tachyon.dts
@@ -74,7 +74,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -828,10 +828,6 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
- remote-endpoint = <&pmic_glink_hs_in>;
-};
-
&usb_1_hsphy {
vdda-pll-supply = <&vreg_l10c_0p88>;
vdda33-supply = <&vreg_l2b_3p072>;
@@ -864,3 +860,7 @@ &usb_2_hsphy {
&usb_dp_qmpphy_out {
remote-endpoint = <&pmic_glink_ss_in>;
};
+
+&eud_con {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcm6490-shift-otter.dts b/arch/arm64/boot/dts/qcom/qcm6490-shift-otter.dts
index cf74b3664cf5..45b9318ab54e 100644
--- a/arch/arm64/boot/dts/qcom/qcm6490-shift-otter.dts
+++ b/arch/arm64/boot/dts/qcom/qcm6490-shift-otter.dts
@@ -84,7 +84,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -951,10 +951,6 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
- remote-endpoint = <&pmic_glink_hs_in>;
-};
-
&usb_1_hsphy {
vdda-pll-supply = <&vreg_l10c>;
vdda18-supply = <&vreg_l1c>;
@@ -985,6 +981,10 @@ &usb_dp_qmpphy_out {
remote-endpoint = <&pmic_glink_ss_in>;
};
+&eud_con {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
+
&venus {
firmware-name = "qcom/qcm6490/SHIFT/otter/venus.mbn";
diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
index a13315bf0fb0..6520e920a844 100644
--- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
+++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
@@ -211,7 +211,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1402,14 +1402,14 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
- remote-endpoint = <&pmic_glink_hs_in>;
-};
-
&usb_1_dwc3_ss {
remote-endpoint = <&usb_dp_qmpphy_usb_ss_in>;
};
+&eud_con {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
+
&usb_1_hsphy {
vdda-pll-supply = <&vreg_l10c_0p88>;
vdda33-supply = <&vreg_l2b_3p072>;
diff --git a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts
index 31bafa2cf51d..4c4afe691d64 100644
--- a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts
+++ b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts
@@ -77,7 +77,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1052,7 +1052,7 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
+&eud_con {
remote-endpoint = <&pmic_glink_hs_in>;
};
diff --git a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts
index 13e8e62f6205..b9f92bcf84ab 100644
--- a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts
+++ b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts
@@ -93,7 +93,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1098,10 +1098,6 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
- remote-endpoint = <&pmic_glink_hs_in>;
-};
-
&usb_1_hsphy {
vdda-pll-supply = <&vreg_l10c_0p88>;
vdda33-supply = <&vreg_l2b_3p072>;
@@ -1135,6 +1131,10 @@ &usb_dp_qmpphy_out {
remote-endpoint = <&pmic_glink_ss_in>;
};
+&eud_con {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
+
&ufs_mem_hc {
reset-gpios = <&tlmm 175 GPIO_ACTIVE_LOW>;
vcc-supply = <&vreg_l7b_2p952>;
diff --git a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi
index 3860ab8508a0..2a90de989dad 100644
--- a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi
@@ -96,7 +96,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1429,7 +1429,7 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
+&eud_con {
remote-endpoint = <&pmic_glink_hs_in>;
};
diff --git a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts
index d94571c05a59..7a97a3eedd5f 100644
--- a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts
+++ b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts
@@ -98,7 +98,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1428,7 +1428,7 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
+&eud_con {
remote-endpoint = <&pmic_glink_hs_in>;
};
diff --git a/arch/arm64/boot/dts/qcom/sm7325-nothing-spacewar.dts b/arch/arm64/boot/dts/qcom/sm7325-nothing-spacewar.dts
index 7db9ae1c3cef..540dbd247c38 100644
--- a/arch/arm64/boot/dts/qcom/sm7325-nothing-spacewar.dts
+++ b/arch/arm64/boot/dts/qcom/sm7325-nothing-spacewar.dts
@@ -99,7 +99,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -1438,10 +1438,6 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
- remote-endpoint = <&pmic_glink_hs_in>;
-};
-
&usb_1_hsphy {
vdda-pll-supply = <&vdd_a_usbhs_core>;
vdda18-supply = <&vdd_a_usbhs_1p8>;
@@ -1449,6 +1445,10 @@ &usb_1_hsphy {
status = "okay";
};
+&eud_con {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
+
&venus {
firmware-name = "qcom/sm7325/nothing/spacewar/vpu20_1v.mbn";
status = "okay";
diff --git a/arch/arm64/boot/dts/qcom/sm7325-xiaomi-taoyao.dts b/arch/arm64/boot/dts/qcom/sm7325-xiaomi-taoyao.dts
index 87f267aaeec0..b0bdbcd47503 100644
--- a/arch/arm64/boot/dts/qcom/sm7325-xiaomi-taoyao.dts
+++ b/arch/arm64/boot/dts/qcom/sm7325-xiaomi-taoyao.dts
@@ -94,7 +94,7 @@ port@0 {
reg = <0>;
pmic_glink_hs_in: endpoint {
- remote-endpoint = <&usb_1_dwc3_hs>;
+ remote-endpoint = <&eud_con>;
};
};
@@ -882,7 +882,7 @@ &usb_1 {
status = "okay";
};
-&usb_1_dwc3_hs {
+&eud_con {
remote-endpoint = <&pmic_glink_hs_in>;
};
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v8 8/8] arm64: dts: qcom: kodiak: Desribe EUD UTMI routing and connector mapping
2026-09-12 22:53 ` [PATCH v8 8/8] arm64: dts: qcom: kodiak: Desribe EUD UTMI routing and connector mapping Elson Serrao
@ 2026-09-12 23:11 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-12 23:11 UTC (permalink / raw)
To: Elson Serrao; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Enabling EUD by default causes endless probe deferral on boards where usb_1 is disabled
--
commit f996caeceb74900c278431f664f8ba097d1904a7
Author: Elson Serrao <elson.serrao@oss.qualcomm.com>
arm64: dts: qcom: kodiak: Desribe EUD UTMI routing and connector mapping
This commit updates the EUD description in Kodiak boards to explicitly model
the topology using an EUD path child node and adds a role-switch declaration.
It enables the EUD node by default and maps the USB connector endpoint to
the EUD instead of directly to the USB controller.
> diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> index f2da3706d5c88..6c64dc9902e47 100644
> --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
> +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
[ ... ]
> @@ -4753,16 +4747,30 @@ eud: eud@88e0000 {
> <0 0x88e2000 0 0x1000>;
> interrupts-extended = <&pdc 11 IRQ_TYPE_LEVEL_HIGH>;
>
> - status = "disabled";
> + #address-cells = <1>;
> + #size-cells = <0>;
[Severity: Medium]
Does removing status = "disabled" here cause endless probe deferral on boards
where usb_1 is left disabled?
By enabling the eud node by default for all kodiak-based boards, the EUD
driver will attempt to probe and find the role switch for the downstream
usb_1 controller. On boards that do not enable usb_1 (such as
qcs6490-vicharak-axon-mini.dts and qcs6490-radxa-dragon-q6a.dts), the
controller driver never probes and does not register a role switch.
This causes the EUD driver to explicitly return -EPROBE_DEFER. Since the
dependency is disabled, the EUD driver will defer forever and linger in the
deferred probe list.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260912225301.3396556-1-elson.serrao@oss.qualcomm.com?part=8
^ permalink raw reply [flat|nested] 15+ messages in thread