gk7205v200_otg: settle the USB role before xHCI probes (#147)
The OTG profile could not see USB devices in host mode. On a board with a UVC webcam on the USB tail, `lsusb` showed only the two root hubs, while the same board running a plain lite image enumerated the webcam normally. The controller was in the host role and the host controller was running -- mode file "host", xhci-hcd bound, USBCMD 0x5, PORTSC1 port-power set -- and the port still never reported a connect (PORTSC1 0x00000280, CCS clear). dr_mode = "otg" makes dwc3_core_init() program GCTL.PRTCAPDIR = OTG(3), and dwc3_core_init_mode() registers xhci-hcd on top of that. This 4.9 dwc3 has no working OTG role-detection block, so xHCI probes, resets the controller and brings up its root hubs while the core is in a role it cannot drive. The port is then dead for the life of that probe. S72usbmode's `usb-mode host` did not repair it and could not: writing the debugfs mode file reaches dwc3_set_mode(), which flips GCTL.PRTCAPDIR and nothing else, leaving the already-probed xHCI with the port state machine it built in OTG. It also skipped the bind, because xhci-hcd was already bound. Fix it where the ordering is: patch dwc3_core_init() to program PRTCAPDIR = HOST for the OTG case, so xHCI probes into a role the core can drive. dwc3_gadget_init() only registers a UDC and does not care, and `usb-mode device` re-aims the controller itself when it takes the port. usb-mode's host path also learns to re-probe an xHCI that was bound in some other role, which is what makes a newer rootfs recover on an older kernel.
This commit is contained in:
@@ -2,12 +2,13 @@
|
||||
#
|
||||
# Force a concrete USB role at boot.
|
||||
#
|
||||
# This is NOT optional on this board. The device tree sets dr_mode = "otg" so
|
||||
# that dwc3 registers both an xHCI host and a UDC, which is what makes runtime
|
||||
# role switching possible. But dwc3_core_init() then leaves
|
||||
# GCTL.PRTCAPDIR = OTG(3), and this 4.9 dwc3 has no working OTG role-detection
|
||||
# block -- nothing ever moves it off OTG on its own. Left alone the controller
|
||||
# sits in a role it cannot drive and USB works in neither direction.
|
||||
# The device tree sets dr_mode = "otg" so that dwc3 registers both an xHCI host
|
||||
# and a UDC, which is what makes runtime role switching possible. The role
|
||||
# itself is settled in the kernel: all-patches 0018 programs
|
||||
# GCTL.PRTCAPDIR = HOST before the xHCI host is registered, because a host that
|
||||
# probes while the core is still in OTG(3) comes up unable to drive the port --
|
||||
# it runs, powers the port, and never reports a connect. So the board boots as
|
||||
# a host, and this script exists to apply a persisted "device" choice on top.
|
||||
#
|
||||
# Runs after S70vendor so the vendor modules are up first.
|
||||
|
||||
|
||||
@@ -99,18 +99,15 @@ to_device() {
|
||||
need_debugfs
|
||||
|
||||
if X=$(xhci_device) && [ -e "/sys/bus/platform/drivers/xhci-hcd/$X" ]; then
|
||||
# Coming straight from boot the controller is still in its power-on
|
||||
# GCTL.PRTCAPDIR = OTG(3) (see the header note: nothing moves it off
|
||||
# OTG on its own). xhci-hcd binds and registers both buses on top of
|
||||
# that anyway, but it is not actually driving the port -- so unbinding
|
||||
# directly from OTG asks xhci_halt() to halt a host that was never
|
||||
# really running, and it times out:
|
||||
# Unbind only from the host role. Asking xhci_halt() to halt a
|
||||
# controller whose GCTL.PRTCAPDIR is not HOST means halting a host
|
||||
# that was never really running, and it times out:
|
||||
# xhci-hcd.0.auto: Host not halted after 16000 microseconds.
|
||||
# xhci-hcd.0.auto: Host controller not halted, aborting reset.
|
||||
# Leaving the controller un-halted is precisely the live-DMA-engine
|
||||
# state this unbind exists to avoid. Flip to host first so the already
|
||||
# bound xHCI is genuinely running, and the unbind then halts cleanly.
|
||||
# A switch from host is already in that state, so only boot pays for it.
|
||||
# Leaving it un-halted is precisely the live-DMA-engine state this
|
||||
# unbind exists to avoid. Since all-patches 0018 the core is already
|
||||
# in the host role whenever xHCI is bound, so this flip is a no-op; it
|
||||
# stays as the cheap guarantee that the unbind below halts cleanly.
|
||||
[ "$(cat "$MODE_FILE")" = host ] || echo host > "$MODE_FILE"
|
||||
|
||||
echo "usb-mode: unbinding host controller $X"
|
||||
@@ -141,11 +138,32 @@ to_host() {
|
||||
|
||||
teardown_gadget
|
||||
|
||||
# Remember which role we are leaving: it decides whether an already-bound
|
||||
# host stack has to be re-probed. See below.
|
||||
was=$(cat "$MODE_FILE")
|
||||
echo host > "$MODE_FILE"
|
||||
|
||||
if X=$(xhci_device) && [ ! -e "/sys/bus/platform/drivers/xhci-hcd/$X" ]; then
|
||||
echo "usb-mode: rebinding host controller $X"
|
||||
echo "$X" > /sys/bus/platform/drivers/xhci-hcd/bind
|
||||
if X=$(xhci_device); then
|
||||
if [ ! -e "/sys/bus/platform/drivers/xhci-hcd/$X" ]; then
|
||||
echo "usb-mode: rebinding host controller $X"
|
||||
echo "$X" > /sys/bus/platform/drivers/xhci-hcd/bind
|
||||
elif [ "$was" != host ]; then
|
||||
# Bound, but bound while the core was in another role: it
|
||||
# probed, reset the controller and brought up its root hubs
|
||||
# against a port it was not driving, and it will never report a
|
||||
# connect -- the port powers up and stays at CCS = 0, so lsusb
|
||||
# shows nothing but the root hubs however many devices are
|
||||
# plugged in. Writing MODE_FILE only flips GCTL.PRTCAPDIR; it
|
||||
# does not re-initialise a live xHCI. Making it probe again is
|
||||
# what actually brings the port up.
|
||||
#
|
||||
# The kernel now settles the role before registering the host
|
||||
# (all-patches 0018), so this cannot trigger on a matching
|
||||
# kernel. It is here for a rootfs running on an older one.
|
||||
echo "usb-mode: re-probing host controller $X (was $was)"
|
||||
echo "$X" > /sys/bus/platform/drivers/xhci-hcd/unbind
|
||||
echo "$X" > /sys/bus/platform/drivers/xhci-hcd/bind
|
||||
fi
|
||||
fi
|
||||
|
||||
echo host > "$STATE"
|
||||
|
||||
+6
-4
@@ -18,11 +18,13 @@ same node (eps_directions, eps_map, snps,eps_new_init), which the vendor would
|
||||
only have added if device mode was exercised; and CONFIG_USB_DWC3_DUAL_ROLE is
|
||||
already enabled, so dwc3's gadget.o/ep0.o are already linked in.
|
||||
|
||||
IMPORTANT: dr_mode = "otg" also makes dwc3_core_init() leave
|
||||
IMPORTANT: on its own, dr_mode = "otg" would also make dwc3_core_init() leave
|
||||
GCTL.PRTCAPDIR = OTG(3), and this 4.9 dwc3 has no working OTG role-detection
|
||||
block. A concrete role must be forced early in userspace -- see
|
||||
/etc/init.d/S72usbmode, which writes "host" at boot. Without it the controller
|
||||
sits in a role it cannot drive and USB does not work in either direction.
|
||||
block -- nothing would ever move it off OTG, and an xHCI host that probes in
|
||||
that state comes up unable to drive the port. Patch 0018 settles the role in
|
||||
the kernel instead, programming PRTCAPDIR = HOST before the host is
|
||||
registered, so the board boots usable as a host; /etc/init.d/S72usbmode then
|
||||
only has to apply a persisted "device" choice.
|
||||
|
||||
This patch ships only in the gk7205v200_otg_generic device profile, so other
|
||||
gk7205v200 variants keep dr_mode = "host".
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
From: OpenIPC
|
||||
Subject: [PATCH] usb: dwc3: start a dual-role part in the host role, not in OTG
|
||||
|
||||
With dr_mode = "otg", dwc3_core_init() programs GCTL.PRTCAPDIR = OTG(3) and
|
||||
dwc3_core_init_mode() then registers the xHCI host on top of it. This 4.9 dwc3
|
||||
has no working OTG role-detection block, so nothing ever moves the controller
|
||||
off OTG on its own -- xhci-hcd probes, resets the controller and brings up its
|
||||
root hubs while the core is in a role it cannot drive.
|
||||
|
||||
The result is a host controller that looks completely healthy and does nothing.
|
||||
Measured on gk7205v200 with a UVC webcam wired to the USB tail:
|
||||
|
||||
USBCMD 0x00000005 R/S set, controller running
|
||||
PORTSC1 0x00000280 PP set, CCS clear -- no connect, ever
|
||||
lsusb only the two root hubs
|
||||
|
||||
The port never reports the connect, so `lsusb` on the OTG image shows nothing
|
||||
attached while the same board on a dr_mode = "host" image enumerates the webcam
|
||||
normally. This is the bug users hit as "the OTG firmware does not see USB
|
||||
devices".
|
||||
|
||||
Forcing the role from userspace afterwards does not repair it. Writing "host"
|
||||
to /sys/kernel/debug/<dwc3>/mode reaches dwc3_set_mode(), which flips
|
||||
GCTL.PRTCAPDIR and nothing else; the already-probed xHCI keeps the port state
|
||||
machine it initialised while the core was in OTG. Confirmed by experiment: the
|
||||
write leaves PORTSC1 at 0x00000280, and only unbinding and re-binding xhci-hcd
|
||||
-- i.e. making it probe again, with the core now in host role -- brings the
|
||||
port up (PORTSC1 0x0C000E63, device enumerated).
|
||||
|
||||
So the role has to be concrete before the host stack is registered. Set
|
||||
PRTCAPDIR = HOST for the OTG case: dwc3_core_init() runs before
|
||||
dwc3_core_init_mode(), so xHCI probes into a role the core can drive.
|
||||
|
||||
This costs the gadget nothing. dwc3_gadget_init() only registers a UDC, which
|
||||
does not care about PRTCAPDIR, and the device role is entered explicitly by
|
||||
`usb-mode device` (which flips PRTCAPDIR after unbinding the host). Booting in
|
||||
the host role is also what upstream settled on: __dwc3_set_mode() treats
|
||||
DWC3_GCTL_PRTCAP_OTG as "pick a role now" rather than as a state to sit in.
|
||||
|
||||
--- a/drivers/usb/dwc3/core.c
|
||||
+++ b/drivers/usb/dwc3/core.c
|
||||
@@ -755,7 +755,17 @@
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
+ /*
|
||||
+ * Deliberately HOST and not OTG: dwc3_core_init_mode() is
|
||||
+ * about to register xhci-hcd, and a host that probes while
|
||||
+ * the core sits in OTG comes up unable to drive the port --
|
||||
+ * it runs, powers the port and never sees a connect. Nothing
|
||||
+ * in this tree moves the core off OTG later, and flipping
|
||||
+ * PRTCAPDIR afterwards does not re-initialise the host, so
|
||||
+ * the role must be settled here. The gadget path re-aims the
|
||||
+ * controller itself when it takes the port.
|
||||
+ */
|
||||
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
break;
|
||||
default:
|
||||
dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
|
||||
Reference in New Issue
Block a user