[PATCH v5 1/2] dt-bindings: usb: snps,dwc3: tighten the two-connector shared HS bus

From: Qianhui Xu

Date: Mon Nov 02 2026 - 00:41:49 EST


Resending only 1/2 of this series.

The earlier post of this patch (Message-ID
<20261031150154.1106455-2-xiaqinghuan@xxxxxxxxxxxxxxxx>, 0/2 cover
<20261031150154.1106455-1-xiaqinghuan@xxxxxxxxxxxxxxxx>, 2026-10-31) used the
wrong device-tree list address: it was sent to
linux-devicetree@xxxxxxxxxxxxxxx, which vger rejects with

550 5.1.1 <linux-devicetree@xxxxxxxxxxxxxxx>: Recipient address rejected:
User unknown in virtual alias table

so it never reached devicetree@xxxxxxxxxxxxxxx. The patch text below is
unchanged, byte for byte, and is the same text as in the 0/2 cover. The
sibling 2/2 went to linux-usb and linux-arm-msm and is archived there; this
resend is only about putting 1/2 into the devicetree archive. The subject is
kept as "[PATCH v5 1/2]" so it still lines up with that cover.

AI-assistance disclosure: this patch is drafted with an AI assistant and
edited by me; the author reviewed, built and tested it, and the content and
the measurements are the author's own.

Two connectors behind one controller only take the mux path when the
controller node itself carries "usb-role-switch": dwc3_get_dr_mode()
otherwise forces dr_mode back to peripheral, dwc3_get_extcon() skips the
role-switch branch and dwc3_drd_init() never calls dwc3_role_mux_init().
The binding accepted such a DT, so a DT that passed the schema check did
not describe a DT the driver could act on. Require the property whenever
port-select-gpios is present.

Two more holes in the endpoint schema are fixed at the same time:

- the hs-port patternProperties only covered endpoint@0 and endpoint@1,
so an unnumbered endpoint or endpoint@2+ escaped the
unevaluatedProperties: false rule of the graph endpoint. Use the same
endpoint name pattern as graph.yaml so every endpoint is validated.

- the mux-state complementarity was expressed with two independent "if
const: 0" blocks, which only rejected two zeroes; two endpoints both
claiming mux-state = <1> passed validation. Express the pair as oneOf
so exactly one of the two assignments is accepted.

Signed-off-by: Qianhui Xu <xiaqinghuan@xxxxxxxxxxxxxxxx>
---
.../bindings/usb/snps,dwc3-common.yaml | 105 +++++++++---------
1 file changed, 55 insertions(+), 50 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
index 6b06b0027b..b23d5d8d74 100644
--- a/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
+++ b/Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
@@ -31,7 +31,9 @@ $defs:
enum: [0, 1]
description:
Logical level of the port-select GPIO which connects this
- connector to the shared High Speed data bus.
+ connector to the shared High Speed data bus. The two connectors of
+ one controller must use different levels; the driver rejects a
+ device tree where two connectors claim the same one.

dependentRequired:
usb-role-switch: [mux-state]
@@ -42,7 +44,7 @@ $defs:
unevaluatedProperties: false

patternProperties:
- '^endpoint@[01]$':
+ '^endpoint(@[0-9a-f]+)?$':
$ref: '#/$defs/hs-endpoint'

allOf:
@@ -61,70 +63,74 @@ allOf:
- if:
required: [port-select-gpios]
then:
- required: [dr_mode, ports]
+ # The mux path is only taken when the controller itself carries
+ # "usb-role-switch": dwc3_get_dr_mode() forces dr_mode back to
+ # peripheral, dwc3_get_extcon() skips the role-switch branch and
+ # dwc3_drd_init() never calls dwc3_role_mux_init() without it.
+ required: [dr_mode, ports, usb-role-switch]
properties:
dr_mode:
const: otg
ports:
- required: [port@0]
+ # Each connector is one port with a single endpoint: the mux-state
+ # property on that endpoint says which level of the port-select GPIO
+ # reaches it, so two connectors are two ports, not two endpoints of
+ # one port. port@1 is the SuperSpeed lane of the PHY and is already
+ # described by snps,dwc3.yaml, so the second High Speed connector
+ # lands on port@2.
+ required: [port@0, port@2]
properties:
port@0:
- required: ['#address-cells', '#size-cells', endpoint@0, endpoint@1]
+ $ref: '#/$defs/hs-port'
+ port@2:
+ $ref: '#/$defs/hs-port'
+
+ # The two connectors must claim opposite levels of the port-select
+ # GPIO; requiring both ports in every branch is what makes the two
+ # cases mutually exclusive for oneOf.
+ oneOf:
+ - required: [port@0, port@2]
properties:
- endpoint@0:
- required: [usb-role-switch, mux-state]
- endpoint@1:
- required: [usb-role-switch, mux-state]
- else:
- properties:
- ports:
- properties:
- port@0:
- patternProperties:
- '^endpoint@[01]$':
+ port@0:
+ $ref: '#/$defs/hs-port'
properties:
- usb-role-switch: false
- mux-state: false
- - if:
- properties:
- ports:
- properties:
- port@0:
- properties:
- endpoint@0:
+ endpoint:
+ properties:
+ mux-state:
+ const: 0
+ port@2:
+ $ref: '#/$defs/hs-port'
properties:
- mux-state:
- const: 0
- then:
- properties:
- ports:
- properties:
- port@0:
+ endpoint:
+ properties:
+ mux-state:
+ const: 1
+ - required: [port@0, port@2]
properties:
- endpoint@1:
+ port@0:
+ $ref: '#/$defs/hs-port'
properties:
- mux-state:
- const: 1
- - if:
- properties:
- ports:
- properties:
- port@0:
- properties:
- endpoint@1:
+ endpoint:
+ properties:
+ mux-state:
+ const: 1
+ port@2:
+ $ref: '#/$defs/hs-port'
properties:
- mux-state:
- const: 0
- then:
+ endpoint:
+ properties:
+ mux-state:
+ const: 0
+ else:
properties:
ports:
properties:
port@0:
- properties:
- endpoint@0:
+ patternProperties:
+ '^endpoint(@[0-9a-f]+)?$':
properties:
- mux-state:
- const: 1
+ usb-role-switch: false
+ mux-state: false

properties:
port-select-gpios:
@@ -536,4 +542,3 @@ required:

additionalProperties: true
...
-
base-commit: fd69bd54e328c407b1853378b25d15803d3453fa
--
2.53.0