[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260116232106.2234978-7-elson.serrao@oss.qualcomm.com>
Date: Fri, 16 Jan 2026 15:21:03 -0800
From: Elson Serrao <elson.serrao@....qualcomm.com>
To: Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Bjorn Andersson <andersson@...nel.org>,
Konrad Dybcio <konradybcio@...nel.org>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Souradeep Chowdhury <quic_schowdhu@...cinc.com>
Cc: linux-arm-msm@...r.kernel.org, devicetree@...r.kernel.org,
linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH 6/9] usb: misc: qcom_eud: add host mode coordination
EUD functions by presenting itself as a USB device to the host PC for
debugging, making it incompatible in USB host mode configurations.
Enabling EUD, when in host mode can also cause the USB controller to
misbehave as the EUD hub can only have one upstream facing port.
Handle below two scenarios to prevent these conflicts:
1. Prevent user from enabling EUD via sysfs when the USB port is
in host mode.
2. Automatically disable EUD when USB port switches to host mode
and re-enable it when exiting host mode.
This ensures consistent state management without creating conflicts
between the EUD debug hub and the USB controller.
Signed-off-by: Elson Serrao <elson.serrao@....qualcomm.com>
---
drivers/usb/misc/qcom_eud.c | 79 ++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 0ea6491f963c..3f1cc7ea2a6a 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -49,12 +49,15 @@ 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;
bool enabled;
bool usb_attached;
bool phy_enabled;
+ bool eud_disabled_for_host;
u8 port_idx;
};
@@ -162,32 +165,66 @@ 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;
+ mutex_lock(&chip->state_lock);
+
/* Skip operation if already in desired state */
- if (chip->enabled == enable)
+ if (chip->enabled == enable) {
+ mutex_unlock(&chip->state_lock);
return count;
+ }
+
+ /*
+ * Handle double-disable scenario: User is disabling EUD that was already
+ * disabled due to host mode. Since the hardware is already disabled, we
+ * only need to clear the host-disabled flag to prevent unwanted re-enabling
+ * when exiting host mode. This respects the user's explicit disable request.
+ */
+ if (!enable && chip->eud_disabled_for_host) {
+ chip->eud_disabled_for_host = false;
+ chip->enabled = false;
+ mutex_unlock(&chip->state_lock);
+ return count;
+ }
if (enable) {
+ /*
+ * EUD functions by presenting itself as a USB device to the host PC for
+ * debugging, making it incompatible in USB host mode configuration.
+ * Prevent enabling EUD in this configuration to avoid hardware conflicts.
+ */
+ path = chip->paths[chip->port_idx];
+ if (path && path->curr_role == USB_ROLE_HOST) {
+ dev_err(chip->dev, "EUD not usable in host mode configuration\n");
+ mutex_unlock(&chip->state_lock);
+ return -EBUSY;
+ }
+
ret = enable_eud(chip);
if (ret) {
dev_err(chip->dev, "failed to enable eud\n");
+ mutex_unlock(&chip->state_lock);
return ret;
}
} else {
ret = disable_eud(chip);
if (ret) {
dev_err(chip->dev, "failed to disable eud\n");
+ mutex_unlock(&chip->state_lock);
return ret;
}
}
chip->enabled = enable;
+ mutex_unlock(&chip->state_lock);
+
return count;
}
@@ -324,18 +361,56 @@ 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);
+ struct eud_chip *chip = path->chip;
int ret;
+ mutex_lock(&chip->state_lock);
+
+ /*
+ * EUD must be disabled when USB operates in host mode. EUD functions by
+ * presenting itself as a USB device to the host PC for debugging, making
+ * it incompatible in host mode configuration.
+ *
+ * chip->enabled preserves user's sysfs configuration and is not modified
+ * during host mode transitions to maintain user intent.
+ */
+
+ /* Only act if EUD is enabled and this is the active path */
+ if (chip->enabled && path->num == chip->port_idx) {
+ if (role == USB_ROLE_HOST && !chip->eud_disabled_for_host) {
+ ret = disable_eud(chip);
+ if (ret) {
+ dev_err(chip->dev, "Failed to disable EUD for host mode: %d\n",
+ ret);
+ mutex_unlock(&chip->state_lock);
+ return ret;
+ }
+ chip->eud_disabled_for_host = true;
+ } else if (role != USB_ROLE_HOST && chip->eud_disabled_for_host) {
+ ret = enable_eud(chip);
+ if (ret) {
+ dev_err(chip->dev, "Failed to re-enable EUD after host mode: %d\n",
+ ret);
+ mutex_unlock(&chip->state_lock);
+ return ret;
+ }
+ chip->eud_disabled_for_host = false;
+ }
+ }
+
/* 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);
+ mutex_unlock(&chip->state_lock);
return ret;
}
path->curr_role = role;
+ mutex_unlock(&chip->state_lock);
+
return 0;
}
@@ -433,6 +508,8 @@ static int eud_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
+ mutex_init(&chip->state_lock);
+
ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip);
if (ret)
return ret;
--
2.34.1
Powered by blists - more mailing lists