[PATCH v6 5/5] usb: dwc3: qcom: Modify interrupt handling for eUSB2 Phy targets

From: Krishna Kurapati

Date: Wed Aug 26 2026 - 13:06:13 EST


eUSB2 targets handle wakeup interrupts differently depending on device
speed when operating in host mode.

According to the eUSB2 specification, remote wakeup signaling in host
mode is detected via different data-line assertions based on the
connected device speed.

When a low-speed device is connected, the host repeater drives eD+ to
logic '1' upon detecting a K-state on the USB lines during remote wakeup
(eUSB2 specification, Section 5.5.14).

When a full-speed or high-speed device is connected, the host repeater
drives eD- to logic '1' upon detecting a K-state on the USB line during
remote wakeup (eUSB2 specification, Sections 5.5.15 and 5.5.18).

Since the eUSB2 PHY's "DP" and "DM" interrupt lines monitor the eD+ and
eD- line states, query the phy type and configure the wakeup interrupts
accordingly.

Signed-off-by: Krishna Kurapati <krishna.kurapati@xxxxxxxxxxxxxxxx>
---
drivers/usb/dwc3/dwc3-qcom.c | 63 +++++++++++++++++++++++++++++++++-----------
1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index f43f73ac36ff..9ba7ea31b524 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -85,6 +85,7 @@ struct dwc3_qcom {
struct icc_path *icc_path_apps;

enum usb_role current_role;
+ bool has_eusb2_phy;
};

#define to_dwc3_qcom(d) container_of((d), struct dwc3_qcom, dwc)
@@ -272,15 +273,23 @@ static void dwc3_qcom_disable_wakeup_irq(int irq)
disable_irq_nosync(irq);
}

-static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port)
+static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom *qcom, int port_index)
{
+ struct dwc3_qcom_port *port = &qcom->ports[port_index];
+
dwc3_qcom_disable_wakeup_irq(port->qusb2_phy_irq);

if (port->usb2_speed == USB_SPEED_LOW) {
- dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
+ if (qcom->has_eusb2_phy)
+ dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
+ else
+ dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
} else if ((port->usb2_speed == USB_SPEED_HIGH) ||
(port->usb2_speed == USB_SPEED_FULL)) {
- dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
+ if (qcom->has_eusb2_phy)
+ dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
+ else
+ dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
} else {
dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
@@ -289,26 +298,38 @@ static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port)
dwc3_qcom_disable_wakeup_irq(port->ss_phy_irq);
}

-static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom_port *port)
+static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom *qcom, int port_index)
{
+ struct dwc3_qcom_port *port = &qcom->ports[port_index];
+
dwc3_qcom_enable_wakeup_irq(port->qusb2_phy_irq, 0);

/*
* Configure DP/DM line interrupts based on the USB2 device attached to
- * the root hub port. When HS/FS device is connected, configure the DP line
- * as falling edge to detect both disconnect and remote wakeup scenarios. When
- * LS device is connected, configure DM line as falling edge to detect both
- * disconnect and remote wakeup. When no device is connected, configure both
- * DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
+ * the root hub port. For non-eUSB2 targets, when HS/FS device is connected,
+ * configure the DP line as falling edge to detect disconnect and remote
+ * wakeup. When LS device is connected, configure the DM line as falling edge
+ * for the same. For eUSB2 targets, the lines are swapped and rising edge is
+ * used: DP line for LS and DM line for HS/FS device detection. When no
+ * device is connected, configure both DP and DM lines as rising edge to
+ * detect HS/FS/LS device connect scenario.
*/

if (port->usb2_speed == USB_SPEED_LOW) {
- dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
- IRQ_TYPE_EDGE_FALLING);
+ if (qcom->has_eusb2_phy)
+ dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
+ IRQ_TYPE_EDGE_RISING);
+ else
+ dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
+ IRQ_TYPE_EDGE_FALLING);
} else if ((port->usb2_speed == USB_SPEED_HIGH) ||
(port->usb2_speed == USB_SPEED_FULL)) {
- dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
- IRQ_TYPE_EDGE_FALLING);
+ if (qcom->has_eusb2_phy)
+ dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
+ IRQ_TYPE_EDGE_RISING);
+ else
+ dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
+ IRQ_TYPE_EDGE_FALLING);
} else {
dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
IRQ_TYPE_EDGE_RISING);
@@ -324,7 +345,7 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
int i;

for (i = 0; i < qcom->num_ports; i++)
- dwc3_qcom_disable_port_interrupts(&qcom->ports[i]);
+ dwc3_qcom_disable_port_interrupts(qcom, i);
}

static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
@@ -332,7 +353,7 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
int i;

for (i = 0; i < qcom->num_ports; i++)
- dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
+ dwc3_qcom_enable_port_interrupts(qcom, i);
}

static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
@@ -617,6 +638,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
int ret;
bool ignore_pipe_clk;
bool wakeup_source;
+ struct phy *phy;

qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL);
if (!qcom)
@@ -683,6 +705,17 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
if (ignore_pipe_clk)
dwc3_qcom_select_utmi_clk(qcom);

+ phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+ if (IS_ERR(phy)) {
+ if (PTR_ERR(phy) != -ENODEV) {
+ ret = dev_err_probe(dev, PTR_ERR(phy),
+ "failed to get usb2 phy\n");
+ goto clk_disable;
+ }
+ phy = NULL;
+ }
+ qcom->has_eusb2_phy = phy && (phy_get_type(phy) == PHY_TYPE_EUSB2);
+
qcom->mode = usb_get_dr_mode(dev);

if (qcom->mode == USB_DR_MODE_HOST) {

--
2.34.1