Linux USB
 help / color / mirror / Atom feed
* [PATCH 0/3] usb: dwc3: two Type-C connectors sharing one HS data bus
@ 2026-11-04  3:27 Qianhui Xu
  2026-11-04  3:27 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: describe two connectors sharing one HS bus Qianhui Xu
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Qianhui Xu @ 2026-11-04  3:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Thinh Nguyen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-usb, devicetree, linux-arm-msm, linux-kernel

*** AI-assistance disclosure ***
This cover letter and the patches below were prepared with the help of an
LLM coding assistant.  The author reviewed every line, ran every gate
quoted in this letter himself, and takes full responsibility for the
content.

This is one complete, self-contained series of three patches: the binding,
the role-switch lookup it needs, and the DWC3 arbitration between the two
connectors.  Nothing is to be applied on top of it, and no patch of it is
posted separately.  It supersedes the v4 series
(<20261026123211.243224-1-xiaqinghuan@mail.ustc.edu.cn>) and the v5
fixups (<20261031150154.1106455-1-xiaqinghuan@mail.ustc.edu.cn>) in
full: their content is folded into the three patches below.

Two connectors on one DWC3 controller
=====================================

The board (Lenovo Legion Y700 4th gen, SM8750) wires the Super Speed pairs
of two USB-C receptacles to separate PHYs, while the High Speed D+/D- pair
is shared through a 2:1 mux controlled by a single GPIO.  Both connectors
are data-role dual and hand their role to the SoC through pmic-glink, so
the controller has to arbitrate between two independent role requests and
move the shared data path before it is reinitialised.

	usb@a600000 {
		port-select-gpios = <&tlmm 29 GPIO_ACTIVE_HIGH>;

		ports {
			port@0 {			/* HS path, connector 0 */
				endpoint {
					remote-endpoint = <&usb_0_dwc3_hs>;
					usb-role-switch;
					mux-state = <0>;
				};
			};

			port@1 {			/* Super Speed, QMP PHY */
			};

			port@2 {			/* HS path, connector 1 */
				endpoint {
					remote-endpoint = <&usb_1_dwc3_hs>;
					usb-role-switch;
					mux-state = <1>;
				};
			};
		};
	};

Series layout
=============

1/3 documents that wiring: "port-select-gpios", one High Speed port per
    connector (port@0 and port@2, port@1 being the Super Speed lane), and,
    on the endpoint of each of those ports, "mux-state" (the GPIO level
    that reaches that connector) and "usb-role-switch" (this connector has
    its own role-switch input).

2/3 adds usb_role_switch_at_endpoint(), so a role switch registered
    against an OF graph endpoint can be found by the consumer that owns
    the other end of that link.  Without it the endpoint registration in
    1/3 would have no lookup path.

3/3 implements the arbitration in DWC3: one role switch per connector, the
    oldest pending request wins, the GPIO is flipped inside
    __dwc3_set_mode() after the previous host or gadget instance is torn
    down, and a connector reads back the role it requested.

Reply to the two procedural comments
====================================

Krishna Kurapati wrote, on the v5 posting: "If v5 is being sent, it must
not to be applied on top of v4.  It must include (v4 + any additional
changes/improvements)."  Agreed, and that is what this series is.  v4
1/3..3/3 and the v5 fixups were squashed per patch instead of stacked:
1/3 = v4 1/3 + v5 1/2, 2/3 = v4 2/3 + the usb_role_switch_at_endpoint()
hunk of v5 2/2, 3/3 = v4 3/3 + the remaining hunks of v5 2/2.  Applying
this series to a v7.3-rc7 tree gives the same three source files as
"v4 + v5", and there is no "1/2 on top of 3/3" left anywhere.

Krzysztof Kozlowski wrote: "Anyway, this is misthreaded - missing rest of
patchset.  Please read submitting patches document and switch to b4", and
separately dropped v4 1/3 ("Unanswered Sashiko comments plus some
confusion with duplicated posting of v5 without the rest, dropping").

Both points are taken.  Posting v5 as a two-patch fixup and then re-sending
only its 1/2 on 11-02 was wrong: a series has to be complete and threaded.
This posting is the complete series as one thread -- cover plus 1/3..3/3,
"base-commit" at the bottom, and In-Reply-To pointing at this cover only,
so it applies with `b4 shazam` (or `git am`) as-is.  Nothing is re-sent
piecemeal; the 11-02 1/2 has no successor and should be ignored.  On the
"git log" remark: the per-revision account now lives only here, the commit
messages describe the code as it finally is.

Dropping v4 1/3 is fine: this 1/3 replaces it, together with the schema
holes that were reported against it.  No action is needed to "un-drop" it.

Response to the review comments folded into these patches
=========================================================

Sashiko on the binding (three Medium findings):

 - "hs-port patternProperties does not match what the driver expects /
   escapes unevaluatedProperties": fixed, the endpoint name pattern is the
   one graph.yaml uses, so an unnumbered endpoint or endpoint@2+ is
   validated too (1/3).

 - "usb-role-switch should not be required only indirectly": fixed, the
   controller node must carry "usb-role-switch" whenever port-select-gpios
   is present, and dr_mode is required to be "otg".  This is a driver
   fact, not schema cosmetics: without it dwc3_get_dr_mode() forces dr_mode
   back to peripheral, dwc3_get_extcon() skips the role-switch branch and
   dwc3_drd_init() never sets the mux up, so a DT that passed the old
   schema did not describe a DT the driver could act on (1/3).

 - "mux-state complementarity is not expressed": fixed, the pair is one
   oneOf over the two assignments, so two connectors both claiming
   mux-state = <1> are rejected (1/3).

 - standing question, "model the board mux with the standard
   mux-controls/mux-states properties": kept the graph-endpoint shape.  The
   arbitration is "earliest request wins" between two connectors that must
   each be a usb role switch consumer, and the endpoint is the fwnode the
   connector is attached to; a mux_control consumer has no per-connector
   identity to hang that on, and drivers/usb/ has no mux_control consumer
   to follow as precedent.  Happy to reshape if the DT maintainers prefer
   the standard property.

Sashiko on usb: roles (one High finding):

 - "-EPROBE_DEFER is swallowed by !IS_ERR_OR_NULL(sw)": fixed, the error is
   passed up, as suggested (2/3).  This was also the root cause we hit on
   real hardware: the UCSI glue kept a NULL switch and the connectors never
   bound.

Sashiko on usb: dwc3 (five High and three Medium findings):

 - "dwc3_set_mode() under dwc->lock self-deadlocks": fixed, the request
   decision and its issue are paired by mux->lock, and dwc->lock is only
   held while individual fields are updated (3/3).

 - "glue layer is never notified": fixed, dwc3_pre_set_role() is called
   with the effective role, outside spinlocks, before dwc3_set_mode(),
   mirroring the single-connector path (3/3).

 - "same-role request that moves the data path to the other connector":
   fixed, dwc3_set_prtcap() only clears SUSPHY when PRTCAP changes, so that
   case now clears it explicitly (3/3).

 - "registration earlier than the initial mode request races with a
   connector driver": fixed, register and request the initial mode under
   mux->lock, tear down in reverse order on failure (3/3).

 - "fwnode references leak on error paths": fixed, dwc->role_mux is
   published before the error paths that used to leak them, so
   dwc3_role_mux_exit() can unwind (3/3).

 - "sticky mux->error is reported to the next caller after it already
   committed its own request": fixed, the sticky value is gone; a failed
   data-path switch is logged and counted per connector (3/3).

 - found while testing on the board, same area: dwc3_mux_role_get()
   returned USB_ROLE_NONE for a connector that did not own the data path,
   which hid its pending request; the connector driver then never sent the
   set_role(USB_ROLE_NONE) that clears it, and the stale request kept
   winning arbitration after unplug.  get() now reports the requested role
   (3/3).

 - "add dwc3_pre_set_role()" (raised against v2): this is exactly what the
   folded code does now, both in the single-connector and the mux path.

Changes relative to the previous postings
=========================================

 - v4 1/3..3/3 -> this series: 5 files changed, 143 insertions(+),
   80 deletions(-) (binding 1/3 and core.c/core.h/drd.c/class.c), which
   is the folded v5 fixup plus one documentation fix: after the reshape to
   port@0 + port@2 the "port-select-gpios" description still said
   "a numbered endpoint of port@0"; it now names port@0/port@2 and their
   endpoint.  Description only, no schema change.
 - v5 1/2..2/2 -> this series: 3 lines of that description, nothing else;
   v5's code changes are bit-identical inside 1/3, 2/3 and 3/3.

Testing
=======

Mechanical, all re-run on this exact series:

 - checkpatch.pl --strict --patch - (mbox on stdin): 0 errors, 0 warnings,
   0 checks for each of the three patches (138 / 63 / 441 lines).
 - dt-doc-validate on snps,dwc3-common.yaml: clean, exit 0.
 - make W=1 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- for
   drivers/usb/dwc3/ and drivers/usb/roles/ with all 19 dwc3 glue drivers
   built as modules: 0 warnings, 0 errors.
 - git am of 1/3..3/3 on 24a19b953e: applies cleanly patch by patch, and the
   resulting tree is identical to the series tip.
 - scripts/check-series-selfcontained.sh on the series directory against
   the pristine base: PASS, 3/3 patches replayed in order.
 - git send-email --dry-run of the produced mbox set: 1 cover + 3 patches,
   threaded with In-Reply-To on the cover only.

Board behaviour: the same driver source runs on this board's daily kernel
and registers both per-connector switches
(a600000.usb-port0-role-switch, a600000.usb-port1-role-switch).  With
function_graph tracing, 12 consecutive plug/unplug cycles each went
usb_role_switch_set_role -> dwc3_mux_role_set -> glue notifier ->
dwc3_set_mode with zero WARNING/BUG/lockdep splats; USB data and charging
were stable throughout, and /sys/class/typec shows port0, port1 and a
bound partner.

Two honest limitations: (a) the arbitration detail -- which connector wins
a simultaneous request -- is verified by code reading and by the stale
request test above, not by a complete per-port role-value trace; (b) the
behaviour evidence comes from that daily kernel, not from a v7.3-rc7
kernel with this series applied, because pristine-lineage kernels do not
bring this board up yet for reasons unrelated to USB (no display/platform
support).  I can re-run the plug sequence on request.

Links
=====

v4: https://lore.kernel.org/linux-usb/20261026123211.243224-1-xiaqinghuan@mail.ustc.edu.cn/
v5: https://lore.kernel.org/linux-usb/20261031150154.1106455-1-xiaqinghuan@mail.ustc.edu.cn/
Krishna's reply: <ba544091-1da4-4616-b1d5-fe23f32679d9@oss.qualcomm.com>
Krzysztof's reply: <20261102-armored-arcane-serval-43faf4@quoll>
v4 1/3 dropped: <20261102-screeching-utopian-avocet-492500@quoll>

Qianhui Xu (3):
  dt-bindings: usb: snps,dwc3: describe two connectors sharing one HS
    bus
  usb: roles: look up role switches attached to a graph endpoint
  usb: dwc3: support two connectors sharing one HS data bus

 .../bindings/usb/snps,dwc3-common.yaml        | 116 +++++++-
 drivers/usb/dwc3/core.c                       |  47 +++-
 drivers/usb/dwc3/core.h                       |  39 +++
 drivers/usb/dwc3/debugfs.c                    |   4 +
 drivers/usb/dwc3/drd.c                        | 254 +++++++++++++++++-
 drivers/usb/roles/class.c                     |  45 ++++
 6 files changed, 501 insertions(+), 4 deletions(-)

base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-11-04  3:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04  3:27 [PATCH 0/3] usb: dwc3: two Type-C connectors sharing one HS data bus Qianhui Xu
2026-11-04  3:27 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: describe two connectors sharing one HS bus Qianhui Xu
2026-11-04  3:44   ` sashiko-bot
2026-11-04  3:27 ` [PATCH 2/3] usb: roles: look up role switches attached to a graph endpoint Qianhui Xu
2026-11-04  3:34   ` sashiko-bot
2026-11-04  3:27 ` [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-11-04  3:44   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox