* [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus
@ 2026-10-13 10:29 Qianhui Xu
2026-10-13 10:29 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name Qianhui Xu
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Qianhui Xu @ 2026-10-13 10:29 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-arm-msm, linux-kernel, Xu Qianhui
From: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
Some boards wire the High Speed data lines of a single DWC3 controller to
two receptacles and select between them with a GPIO, while the SuperSpeed
lanes stay per connector. This series makes such a design usable:
- patch 1 documents "usb-psy-name", a property the dwc3 glue drivers
already parse but which the bindings never described;
- patch 2 describes the two-connector HS data path: "port-select-gpios"
plus a "ports" node whose connector endpoints carry "mux-state" (the
logical port-select level of that connector) and "usb-role-switch";
- patch 3 adds the driver side: one role switch per connector, mutual
arbitration between them, and a data-path switch performed after the
previous host/gadget instance has been torn down.
Single-connector designs are unaffected: the mux path is only taken when
"port-select-gpios" is present.
This work was developed on the Qualcomm SM8750 (Lenovo TB322FC) tablet;
the board device tree is not upstream yet, so no in-tree user is switched
over by this series.
Xu Qianhui (3):
dt-bindings: usb: snps,dwc3: document usb-psy-name
dt-bindings: usb: snps,dwc3: describe the two-connector HS data path
usb: dwc3: support two connectors sharing one HS data bus
.../bindings/usb/snps,dwc3-common.yaml | 65 ++++++-
drivers/usb/dwc3/core.c | 30 ++-
drivers/usb/dwc3/core.h | 22 +++
drivers/usb/dwc3/debugfs.c | 4 +
drivers/usb/dwc3/drd.c | 177 +++++++++++++++++-
5 files changed, 294 insertions(+), 4 deletions(-)
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name
2026-10-13 10:29 [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
@ 2026-10-13 10:29 ` Qianhui Xu
2026-10-14 11:12 ` Krzysztof Kozlowski
2026-10-13 10:29 ` [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path Qianhui Xu
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Qianhui Xu @ 2026-10-13 10:29 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-arm-msm, linux-kernel, Xu Qianhui
From: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
The dwc3 driver looks up "usb-psy-name" to find the power supply which
accepts the input current limit requested by the gadget after USB
configuration, and while the bus is suspended or resumed
(dwc3_vbus_draw_work()), but the property is not described by any
binding:
$ git grep -n usb-psy-name -- Documentation/
(no match)
As a result, any DTS using it fails dtbs_check. The property is read
from the DWC3 device node, which for the single-node flavours (e.g.
qcom,snps-dwc3.yaml) is the node that $refs this file, so document it
here.
Signed-off-by: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
---
.../devicetree/bindings/usb/snps,dwc3-common.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
index 6c0b8b6538..6609a709c1 100644
--- a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
+++ b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
@@ -51,6 +51,13 @@ properties:
- items:
pattern: "^usb(2-([0-9]|1[0-4])|3-[0-3])$"
+ usb-psy-name:
+ description:
+ Name of the USB power supply accepting the input current limit
+ requested by the gadget after USB configuration, and during bus
+ suspend and resume.
+ $ref: /schemas/types.yaml#/definitions/string
+
snps,usb2-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM for host
mode.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path
2026-10-13 10:29 [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-13 10:29 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name Qianhui Xu
@ 2026-10-13 10:29 ` Qianhui Xu
2026-10-14 11:13 ` Krzysztof Kozlowski
2026-10-13 10:29 ` [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-14 11:04 ` [PATCH 0/3] " Konrad Dybcio
3 siblings, 1 reply; 8+ messages in thread
From: Qianhui Xu @ 2026-10-13 10:29 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-arm-msm, linux-kernel, Xu Qianhui
From: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
Some designs wire the High Speed data lines of a single DWC3 controller to
two receptacles and select between them with a GPIO, while the Super Speed
lanes stay per connector:
+-----------+ HS +----------+
DWC3 ---- port@0 -----| port-select|-----| connector|
| | GPIO | +----------+
+-- port@2 --------------------...
+-----------+
Describe the hardware in snps,dwc3-common.yaml:
- port-select-gpios selects which connector drives the shared HS bus;
- each endpoint of "ports" carries "mux-state", the logical port-select
level of that connector, and marks itself as a role-switch input with
"usb-role-switch".
The connector endpoints are restricted to the port-base/endpoint-base
schemas so that endpoint subnodes cannot carry unrelated properties.
Signed-off-by: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
---
.../bindings/usb/snps,dwc3-common.yaml | 72 ++++++++++++++++---
1 file changed, 63 insertions(+), 9 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
index 6609a709c1..a18bb08afb 100644
--- a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
+++ b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
@@ -13,6 +13,28 @@ description:
Defines the properties of the DWC3 core as being embedded in either an
vendor-specific implementation or as a standalone component.
+$defs:
+ hs-port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ properties:
+ endpoint:
+ $ref: /schemas/graph.yaml#/$defs/endpoint-base
+ unevaluatedProperties: false
+ properties:
+ usb-role-switch:
+ type: boolean
+ description: Separate role-switch input for this connector.
+ mux-state:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description:
+ Logical port-select GPIO level for this data path. State 0
+ takes precedence when both connectors request USB data.
+ dependentRequired:
+ usb-role-switch: [mux-state]
+ mux-state: [usb-role-switch]
+
allOf:
- $ref: usb-drd.yaml#
- if:
@@ -26,8 +48,44 @@ allOf:
$ref: usb.yaml#
else:
$ref: usb-xhci.yaml#
+ - if:
+ required: [port-select-gpios]
+ then:
+ required: [usb-role-switch, ports, dr_mode]
+ properties:
+ dr_mode:
+ const: otg
+ ports:
+ required: [port@0, port@2]
+ properties:
+ port@0:
+ required: [endpoint]
+ properties:
+ endpoint:
+ required: [usb-role-switch, mux-state]
+ port@2:
+ required: [endpoint]
+ properties:
+ endpoint:
+ required: [usb-role-switch, mux-state]
properties:
+ usb-psy-name:
+ description:
+ Name of the USB power supply accepting the input current limit
+ requested by the gadget after USB configuration, and during bus
+ suspend and resume.
+ $ref: /schemas/types.yaml#/definitions/string
+
+ port-select-gpios:
+ maxItems: 1
+ description:
+ Selects between two external connectors sharing the controller's HS
+ data bus. The port@0 and port@2 endpoints specify distinct mux-state
+ values and provide independent role-switch inputs. The controller
+ stops the previous role before changing the data path. With neither
+ connector requesting data, the last selection is retained.
+
extcon:
maxItems: 1
deprecated: true
@@ -51,13 +109,6 @@ properties:
- items:
pattern: "^usb(2-([0-9]|1[0-4])|3-[0-3])$"
- usb-psy-name:
- description:
- Name of the USB power supply accepting the input current limit
- requested by the gadget after USB configuration, and during bus
- suspend and resume.
- $ref: /schemas/types.yaml#/definitions/string
-
snps,usb2-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM for host
mode.
@@ -418,13 +469,17 @@ properties:
properties:
port@0:
- $ref: /schemas/graph.yaml#/properties/port
+ $ref: '#/$defs/hs-port'
description: High Speed (HS) data bus.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Super Speed (SS) data bus.
+ port@2:
+ $ref: '#/$defs/hs-port'
+ description: Alternate connector on the multiplexed HS data bus.
+
wakeup-source:
$ref: /schemas/types.yaml#/definitions/flag
description:
@@ -436,4 +491,3 @@ required:
additionalProperties: true
...
-
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus
2026-10-13 10:29 [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-13 10:29 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name Qianhui Xu
2026-10-13 10:29 ` [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path Qianhui Xu
@ 2026-10-13 10:29 ` Qianhui Xu
2026-10-14 20:19 ` kernel test robot
2026-10-14 11:04 ` [PATCH 0/3] " Konrad Dybcio
3 siblings, 1 reply; 8+ messages in thread
From: Qianhui Xu @ 2026-10-13 10:29 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-arm-msm, linux-kernel, Xu Qianhui
From: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
On some boards a single DWC3 controller drives two receptacles: the Super
Speed pairs are wired to separate PHYs, while the High Speed data lines are
muxed to whichever connector is in use. The controller therefore has to
service two independent role-switch inputs and move the shared data path
before it reinitialises the controller for the requested role.
Register one usb_role_switch per connector endpoint described in
"ports"/"mux-state" when "port-select-gpios" is present. Requests from the
two switches are serialised by a mutex, the oldest connector holding a role
wins, and the level of the port-select GPIO is only changed once the
previous host or gadget instance has been torn down, which is where
__dwc3_set_mode() runs. A connector only reports its own role while its
data path is the active one, so the connector drivers see a consistent view
of a shared controller.
While the two-connector mux owns role selection, the debugfs "mode" write
used to force a specific role is refused instead of racing with it.
Signed-off-by: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
---
drivers/usb/dwc3/core.c | 30 ++++++-
drivers/usb/dwc3/core.h | 22 +++++
drivers/usb/dwc3/debugfs.c | 4 +
drivers/usb/dwc3/drd.c | 177 ++++++++++++++++++++++++++++++++++++-
4 files changed, 231 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c..76e9e5d180 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/list.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_graph.h>
@@ -166,6 +167,9 @@ static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
unsigned long flags;
+ struct dwc3_role_mux *mux = dwc->role_mux;
+ enum usb_role mux_role = USB_ROLE_NONE;
+ int mux_port = 0;
int ret;
u32 reg;
u32 desired_dr_role;
@@ -174,8 +178,15 @@ static void __dwc3_set_mode(struct work_struct *work)
mutex_lock(&dwc->mutex);
spin_lock_irqsave(&dwc->lock, flags);
desired_dr_role = dwc->desired_dr_role;
+ if (mux) {
+ mux_port = mux->desired_port;
+ mux_role = mux->desired_role;
+ }
spin_unlock_irqrestore(&dwc->lock, flags);
+ if (mux && READ_ONCE(mux->error))
+ goto out;
+
pm_runtime_get_sync(dwc->dev);
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
@@ -184,7 +195,8 @@ static void __dwc3_set_mode(struct work_struct *work)
if (!desired_dr_role)
goto out;
- if (desired_dr_role == dwc->current_dr_role)
+ if (desired_dr_role == dwc->current_dr_role &&
+ (!mux || mux_port == mux->active_port))
goto out;
if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
@@ -233,6 +245,22 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_writel(dwc, DWC3_GCTL, reg);
}
+ /*
+ * The previous host/gadget has been torn down above, so it is safe to
+ * move the shared HS data path to the requesting connector.
+ */
+ if (mux && mux_port != mux->active_port) {
+ ret = gpiod_direction_output(mux->select, mux_port);
+ if (ret) {
+ WRITE_ONCE(mux->error, ret);
+ goto out;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ mux->active_port = mux_port;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_prtcap(dwc, desired_dr_role, false);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f3dabb37f8..ffd07092da 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1008,6 +1008,26 @@ struct dwc3_glue_ops {
void (*pre_run_stop)(struct dwc3 *dwc, bool is_on);
};
+struct dwc3_role_port {
+ struct dwc3 *dwc;
+ struct usb_role_switch *sw;
+ struct fwnode_handle *fwnode;
+ enum usb_role role;
+ int state;
+};
+
+struct dwc3_role_mux {
+ struct gpio_desc *select;
+ /* Serialize requests from the two connector role switches. */
+ struct mutex lock;
+ struct dwc3_role_port ports[2];
+ enum usb_role desired_role;
+ int desired_port;
+ int active_port;
+ int error;
+ bool stopping;
+};
+
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
@@ -1064,6 +1084,7 @@ struct dwc3_glue_ops {
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
* @role_sw: usb_role_switch handle
+ * @role_mux: optional shared data-port mux and per-connector role switches
* @role_switch_default_mode: default operation mode of controller while
* usb role is USB_ROLE_NONE.
* @usb_psy: pointer to power supply interface.
@@ -1265,6 +1286,7 @@ struct dwc3 {
struct notifier_block edev_nb;
enum usb_phy_interface hsphy_mode;
struct usb_role_switch *role_sw;
+ struct dwc3_role_mux *role_mux;
enum usb_dr_mode role_switch_default_mode;
struct power_supply *usb_psy;
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 65d909e4b7..e8616dea6b 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -438,6 +438,10 @@ static ssize_t dwc3_mode_write(struct file *file,
u32 mode = 0;
char buf[32];
+ /* The connector providers own both role and data-path selection. */
+ if (dwc->role_mux)
+ return -EBUSY;
+
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index f3e37d3836..4bf0497e90 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -8,6 +8,7 @@
*/
#include <linux/extcon.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/property.h>
@@ -501,10 +502,178 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
return role;
}
+static int dwc3_mux_role_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ struct dwc3_role_port *port = usb_role_switch_get_drvdata(sw);
+ struct dwc3 *dwc = port->dwc;
+ struct dwc3_role_mux *mux = dwc->role_mux;
+ enum usb_role selected_role;
+ unsigned long flags;
+ int selected, ret;
+ u32 mode;
+
+ if (role < USB_ROLE_NONE || role > USB_ROLE_DEVICE)
+ return -EINVAL;
+
+ mutex_lock(&mux->lock);
+ if (mux->stopping) {
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+ if (READ_ONCE(mux->error)) {
+ ret = READ_ONCE(mux->error);
+ goto out;
+ }
+
+ port->role = role;
+ if (mux->ports[0].role != USB_ROLE_NONE)
+ selected = 0;
+ else if (mux->ports[1].role != USB_ROLE_NONE)
+ selected = 1;
+ else
+ selected = mux->desired_port;
+ selected_role = mux->ports[selected].role;
+ if (selected == mux->desired_port && selected_role == mux->desired_role) {
+ ret = 0;
+ goto out;
+ }
+
+ if (selected_role == USB_ROLE_HOST ||
+ (selected_role == USB_ROLE_NONE &&
+ dwc->role_switch_default_mode == USB_DR_MODE_HOST))
+ mode = DWC3_GCTL_PRTCAP_HOST;
+ else
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ mux->desired_port = selected;
+ mux->desired_role = selected_role;
+ dwc->desired_dr_role = mode;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ queue_work(system_freezable_wq, &dwc->drd_work);
+ flush_work(&dwc->drd_work);
+ ret = READ_ONCE(mux->error);
+out:
+ mutex_unlock(&mux->lock);
+ return ret;
+}
+
+static enum usb_role dwc3_mux_role_get(struct usb_role_switch *sw)
+{
+ struct dwc3_role_port *port = usb_role_switch_get_drvdata(sw);
+ struct dwc3_role_mux *mux = port->dwc->role_mux;
+ unsigned long flags;
+ enum usb_role role = USB_ROLE_NONE;
+
+ mutex_lock(&mux->lock);
+ spin_lock_irqsave(&port->dwc->lock, flags);
+ if (!READ_ONCE(mux->error) && mux->active_port == port->state)
+ role = port->role;
+ spin_unlock_irqrestore(&port->dwc->lock, flags);
+ mutex_unlock(&mux->lock);
+ return role;
+}
+
+static void dwc3_role_mux_unregister(struct dwc3 *dwc)
+{
+ struct dwc3_role_mux *mux = dwc->role_mux;
+ unsigned int i;
+
+ if (!mux)
+ return;
+
+ mutex_lock(&mux->lock);
+ mux->stopping = true;
+ mutex_unlock(&mux->lock);
+ for (i = 0; i < ARRAY_SIZE(mux->ports); i++) {
+ usb_role_switch_unregister(mux->ports[i].sw);
+ mux->ports[i].sw = NULL;
+ fwnode_handle_put(mux->ports[i].fwnode);
+ mux->ports[i].fwnode = NULL;
+ }
+}
+
+/* Returns one when the optional two-connector mux is configured. */
+static int dwc3_setup_role_mux(struct dwc3 *dwc, u32 mode)
+{
+ struct usb_role_switch_desc desc = {};
+ struct fwnode_handle *ep;
+ struct dwc3_role_mux *mux;
+ struct gpio_desc *select;
+ char name[64];
+ u32 state;
+ int ret, i;
+
+ select = devm_gpiod_get_optional(dwc->dev, "port-select", GPIOD_ASIS);
+ if (IS_ERR(select))
+ return PTR_ERR(select);
+ if (!select)
+ return 0;
+ ret = gpiod_get_value_cansleep(select);
+ if (ret < 0)
+ return ret;
+
+ mux = devm_kzalloc(dwc->dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+ mux->select = select;
+ mux->desired_port = ret;
+ mux->active_port = -1;
+ mutex_init(&mux->lock);
+ dwc->role_mux = mux;
+
+ fwnode_graph_for_each_endpoint(dev_fwnode(dwc->dev), ep) {
+ if (!fwnode_property_present(ep, "usb-role-switch"))
+ continue;
+ ret = fwnode_property_read_u32(ep, "mux-state", &state);
+ if (ret || state >= ARRAY_SIZE(mux->ports)) {
+ ret = -EINVAL;
+ goto put_ep;
+ }
+ if (mux->ports[state].fwnode) {
+ ret = -EINVAL;
+ goto put_ep;
+ }
+ mux->ports[state].fwnode = fwnode_handle_get(ep);
+ mux->ports[state].state = state;
+ mux->ports[state].dwc = dwc;
+ }
+ for (i = 0; i < ARRAY_SIZE(mux->ports); i++) {
+ if (!mux->ports[i].fwnode) {
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ /* All state is initialized before either role provider becomes visible. */
+ dwc3_set_mode(dwc, mode);
+ for (i = 0; i < ARRAY_SIZE(mux->ports); i++) {
+ snprintf(name, sizeof(name), "%s-port%d", dev_name(dwc->dev), i);
+ desc.name = name;
+ desc.fwnode = mux->ports[i].fwnode;
+ desc.set = dwc3_mux_role_set;
+ desc.get = dwc3_mux_role_get;
+ desc.driver_data = &mux->ports[i];
+ mux->ports[i].sw = usb_role_switch_register(dwc->dev, &desc);
+ if (IS_ERR(mux->ports[i].sw)) {
+ ret = PTR_ERR(mux->ports[i].sw);
+ goto err;
+ }
+ }
+ return 1;
+
+put_ep:
+ fwnode_handle_put(ep);
+err:
+ dwc3_drd_exit(dwc);
+ return ret;
+}
+
static int dwc3_setup_role_switch(struct dwc3 *dwc)
{
struct usb_role_switch_desc dwc3_role_switch = {NULL};
u32 mode;
+ int ret;
dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev);
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) {
@@ -513,6 +682,10 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
mode = DWC3_GCTL_PRTCAP_DEVICE;
}
+ ret = dwc3_setup_role_mux(dwc, mode);
+ if (ret)
+ return ret < 0 ? ret : 0;
+
dwc3_set_mode(dwc, mode);
dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
@@ -526,7 +699,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
if (dwc->dev->of_node) {
/* populate connector entry */
- int ret = devm_of_platform_populate(dwc->dev);
+ ret = devm_of_platform_populate(dwc->dev);
if (ret) {
usb_role_switch_unregister(dwc->role_sw);
@@ -541,6 +714,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
#else
#define ROLE_SWITCH 0
#define dwc3_setup_role_switch(x) 0
+static inline void dwc3_role_mux_unregister(struct dwc3 *dwc) {}
#endif
int dwc3_drd_init(struct dwc3 *dwc)
@@ -597,6 +771,7 @@ void dwc3_drd_exit(struct dwc3 *dwc)
{
unsigned long flags;
+ dwc3_role_mux_unregister(dwc);
if (dwc->role_sw)
usb_role_switch_unregister(dwc->role_sw);
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus
2026-10-13 10:29 [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
` (2 preceding siblings ...)
2026-10-13 10:29 ` [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
@ 2026-10-14 11:04 ` Konrad Dybcio
3 siblings, 0 replies; 8+ messages in thread
From: Konrad Dybcio @ 2026-10-14 11:04 UTC (permalink / raw)
To: Qianhui Xu, Greg Kroah-Hartman, Thinh Nguyen, linux-usb
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-arm-msm, linux-kernel
On 10/13/26 12:29 PM, Qianhui Xu wrote:
> From: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
>
> Some boards wire the High Speed data lines of a single DWC3 controller to
> two receptacles and select between them with a GPIO, while the SuperSpeed
> lanes stay per connector. This series makes such a design usable:
>
> - patch 1 documents "usb-psy-name", a property the dwc3 glue drivers
> already parse but which the bindings never described;
>
> - patch 2 describes the two-connector HS data path: "port-select-gpios"
> plus a "ports" node whose connector endpoints carry "mux-state" (the
> logical port-select level of that connector) and "usb-role-switch";
>
> - patch 3 adds the driver side: one role switch per connector, mutual
> arbitration between them, and a data-path switch performed after the
> previous host/gadget instance has been torn down.
>
> Single-connector designs are unaffected: the mux path is only taken when
> "port-select-gpios" is present.
>
> This work was developed on the Qualcomm SM8750 (Lenovo TB322FC) tablet;
> the board device tree is not upstream yet, so no in-tree user is switched
> over by this series.
This is an incredibly strange setup. Are you sure it's actually how it's
wired up?
Are there any hubs or other devices connected inline?
Konrad
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name
2026-10-13 10:29 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name Qianhui Xu
@ 2026-10-14 11:12 ` Krzysztof Kozlowski
0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-14 11:12 UTC (permalink / raw)
To: Qianhui Xu
Cc: Greg Kroah-Hartman, Thinh Nguyen, linux-usb, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-arm-msm,
linux-kernel
On Tue, Oct 13, 2026 at 06:29:25PM +0800, Qianhui Xu wrote:
> From: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
>
> The dwc3 driver looks up "usb-psy-name" to find the power supply which
> accepts the input current limit requested by the gadget after USB
It does it for ACPI, no?
> configuration, and while the bus is suspended or resumed
> (dwc3_vbus_draw_work()), but the property is not described by any
> binding:
>
> $ git grep -n usb-psy-name -- Documentation/
> (no match)
>
> As a result, any DTS using it fails dtbs_check. The property is read
No. Just run:
git grep usb-psy-name
There is no isngle usage of it.
> from the DWC3 device node, which for the single-node flavours (e.g.
> qcom,snps-dwc3.yaml) is the node that $refs this file, so document it
> here.
>
> Signed-off-by: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
> ---
> .../devicetree/bindings/usb/snps,dwc3-common.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
> index 6c0b8b6538..6609a709c1 100644
> --- a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
> +++ b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
> @@ -51,6 +51,13 @@ properties:
> - items:
> pattern: "^usb(2-([0-9]|1[0-4])|3-[0-3])$"
>
> + usb-psy-name:
> + description:
> + Name of the USB power supply accepting the input current limit
> + requested by the gadget after USB configuration, and during bus
> + suspend and resume.
Power supplies are not referenced by names, but phandles.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path
2026-10-13 10:29 ` [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path Qianhui Xu
@ 2026-10-14 11:13 ` Krzysztof Kozlowski
0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-14 11:13 UTC (permalink / raw)
To: Qianhui Xu
Cc: Greg Kroah-Hartman, Thinh Nguyen, linux-usb, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-arm-msm,
linux-kernel
On Tue, Oct 13, 2026 at 06:29:26PM +0800, Qianhui Xu wrote:
> - usb-psy-name:
> - description:
> - Name of the USB power supply accepting the input current limit
> - requested by the gadget after USB configuration, and during bus
> - suspend and resume.
> - $ref: /schemas/types.yaml#/definitions/string
No, you just added it!
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus
2026-10-13 10:29 ` [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
@ 2026-10-14 20:19 ` kernel test robot
0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2026-10-14 20:19 UTC (permalink / raw)
To: Qianhui Xu, Greg Kroah-Hartman, Thinh Nguyen, linux-usb
Cc: llvm, oe-kbuild-all, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, devicetree, linux-arm-msm, linux-kernel,
Xu Qianhui
Hi Qianhui,
kernel test robot noticed the following build warnings:
[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linus/master v7.3-rc7 next-20261012]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Qianhui-Xu/dt-bindings-usb-snps-dwc3-document-usb-psy-name/20261013-182925
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/20261013102927.1141696-4-xiaqinghuan%40mail.ustc.edu.cn
patch subject: [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus
config: arm-defconfig (https://download.01.org/0day-ci/archive/20261015/202610150418.XE8EkA7P-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project 6719b9e31d6a93099612c474eec76d9f41d0275a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261015/202610150418.XE8EkA7P-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202610150418.XE8EkA7P-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/usb/dwc3/core.c:171:16: warning: variable 'mux_role' set but not used [-Wunused-but-set-variable]
171 | enum usb_role mux_role = USB_ROLE_NONE;
| ^
1 warning generated.
vim +/mux_role +171 drivers/usb/dwc3/core.c
165
166 static void __dwc3_set_mode(struct work_struct *work)
167 {
168 struct dwc3 *dwc = work_to_dwc(work);
169 unsigned long flags;
170 struct dwc3_role_mux *mux = dwc->role_mux;
> 171 enum usb_role mux_role = USB_ROLE_NONE;
172 int mux_port = 0;
173 int ret;
174 u32 reg;
175 u32 desired_dr_role;
176 int i;
177
178 mutex_lock(&dwc->mutex);
179 spin_lock_irqsave(&dwc->lock, flags);
180 desired_dr_role = dwc->desired_dr_role;
181 if (mux) {
182 mux_port = mux->desired_port;
183 mux_role = mux->desired_role;
184 }
185 spin_unlock_irqrestore(&dwc->lock, flags);
186
187 if (mux && READ_ONCE(mux->error))
188 goto out;
189
190 pm_runtime_get_sync(dwc->dev);
191
192 if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
193 dwc3_otg_update(dwc, 0);
194
195 if (!desired_dr_role)
196 goto out;
197
198 if (desired_dr_role == dwc->current_dr_role &&
199 (!mux || mux_port == mux->active_port))
200 goto out;
201
202 if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
203 goto out;
204
205 switch (dwc->current_dr_role) {
206 case DWC3_GCTL_PRTCAP_HOST:
207 dwc3_host_exit(dwc);
208 break;
209 case DWC3_GCTL_PRTCAP_DEVICE:
210 dwc3_gadget_exit(dwc);
211 dwc3_event_buffers_cleanup(dwc);
212 break;
213 case DWC3_GCTL_PRTCAP_OTG:
214 dwc3_otg_exit(dwc);
215 spin_lock_irqsave(&dwc->lock, flags);
216 dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
217 spin_unlock_irqrestore(&dwc->lock, flags);
218 dwc3_otg_update(dwc, 1);
219 break;
220 default:
221 break;
222 }
223
224 /*
225 * When current_dr_role is not set, there's no role switching.
226 * Only perform GCTL.CoreSoftReset when there's DRD role switching.
227 */
228 if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
229 DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
230 desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
231 reg = dwc3_readl(dwc, DWC3_GCTL);
232 reg |= DWC3_GCTL_CORESOFTRESET;
233 dwc3_writel(dwc, DWC3_GCTL, reg);
234
235 /*
236 * Wait for internal clocks to synchronized. DWC_usb31 and
237 * DWC_usb32 may need at least 50ms (less for DWC_usb3). To
238 * keep it consistent across different IPs, let's wait up to
239 * 100ms before clearing GCTL.CORESOFTRESET.
240 */
241 msleep(100);
242
243 reg = dwc3_readl(dwc, DWC3_GCTL);
244 reg &= ~DWC3_GCTL_CORESOFTRESET;
245 dwc3_writel(dwc, DWC3_GCTL, reg);
246 }
247
248 /*
249 * The previous host/gadget has been torn down above, so it is safe to
250 * move the shared HS data path to the requesting connector.
251 */
252 if (mux && mux_port != mux->active_port) {
253 ret = gpiod_direction_output(mux->select, mux_port);
254 if (ret) {
255 WRITE_ONCE(mux->error, ret);
256 goto out;
257 }
258
259 spin_lock_irqsave(&dwc->lock, flags);
260 mux->active_port = mux_port;
261 spin_unlock_irqrestore(&dwc->lock, flags);
262 }
263
264 spin_lock_irqsave(&dwc->lock, flags);
265
266 dwc3_set_prtcap(dwc, desired_dr_role, false);
267
268 spin_unlock_irqrestore(&dwc->lock, flags);
269
270 switch (desired_dr_role) {
271 case DWC3_GCTL_PRTCAP_HOST:
272 ret = dwc3_host_init(dwc);
273 if (ret) {
274 dev_err(dwc->dev, "failed to initialize host\n");
275 } else {
276 if (dwc->usb2_phy)
277 otg_set_vbus(dwc->usb2_phy->otg, true);
278
279 for (i = 0; i < dwc->num_usb2_ports; i++)
280 phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
281 for (i = 0; i < dwc->num_usb3_ports; i++)
282 phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
283
284 if (dwc->dis_split_quirk) {
285 reg = dwc3_readl(dwc, DWC3_GUCTL3);
286 reg |= DWC3_GUCTL3_SPLITDISABLE;
287 dwc3_writel(dwc, DWC3_GUCTL3, reg);
288 }
289 }
290 break;
291 case DWC3_GCTL_PRTCAP_DEVICE:
292 dwc3_core_soft_reset(dwc);
293
294 dwc3_event_buffers_setup(dwc);
295
296 if (dwc->usb2_phy)
297 otg_set_vbus(dwc->usb2_phy->otg, false);
298 phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
299 phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
300
301 ret = dwc3_gadget_init(dwc);
302 if (ret)
303 dev_err(dwc->dev, "failed to initialize peripheral\n");
304 break;
305 case DWC3_GCTL_PRTCAP_OTG:
306 dwc3_otg_init(dwc);
307 dwc3_otg_update(dwc, 0);
308 break;
309 default:
310 break;
311 }
312
313 out:
314 pm_runtime_put_autosuspend(dwc->dev);
315 mutex_unlock(&dwc->mutex);
316 }
317
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-14 20:19 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-13 10:29 [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-13 10:29 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name Qianhui Xu
2026-10-14 11:12 ` Krzysztof Kozlowski
2026-10-13 10:29 ` [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path Qianhui Xu
2026-10-14 11:13 ` Krzysztof Kozlowski
2026-10-13 10:29 ` [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-14 20:19 ` kernel test robot
2026-10-14 11:04 ` [PATCH 0/3] " Konrad Dybcio
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®