USB OTG not working properly RockPi 4b

I am currently using the USB OTG port on the Rock Pi 4 as a mass storage device. I got the port to work as OTG, however it only works if I flip the OTG selector switch on and off (or if I start the Rock Pi with the switch as host and then flip it to device).

This behavior can be observed both with the Ubuntu Server and Debian images. After booting I configure the g_mass_storage module so that a file can be used as a partition:

sudo modprobe g_mass_storage file=/mnt/mass_storage stall=1 iSerialNumber=aaaaaaa removable=y

However, it does not get detected until the switch has been flipped.

After some investigation, it seems to me that something in here regarding the phy-ff770000.syscon:usb2 is not working properly:

 [ 1.662051] phy phy-ff770000.syscon:usb2-phy@e450.0: Looking up phy-supply from device tree
  [    1.662200] vcc5v0_host: could not add device link phy-ff770000.syscon:usb2-phy@e450.0 err -2
[    1.662534] phy phy-ff770000.syscon:usb2-phy@e450.1: Looking up phy-supply from device tree
[    1.662546] phy phy-ff770000.syscon:usb2-phy@e450.1: Looking up phy-supply property in node /syscon@ff770000/usb2-phy@e450/otg-port failed
    [    1.662765] phy phy-ff770000.syscon:usb2-phy@e450.1: Looking up vbus-supply from device tree
[    1.662775] phy phy-ff770000.syscon:usb2-phy@e450.1: Looking up vbus-supply property in node /syscon@ff770000/usb2-phy@e450/otg-port failed
[    1.662788] phy phy-ff770000.syscon:usb2-phy@e450.1: Failed to get VBUS supply regulator
[    1.663531] phy phy-ff770000.syscon:usb2-phy@e460.2: Looking up phy-supply from device tree
[    1.663588] vcc5v0_host: could not add device link phy-ff770000.syscon:usb2-phy@e460.2 err -2
[    1.663853] phy phy-ff770000.syscon:usb2-phy@e460.3: Looking up phy-supply from device tree
[    1.663864] phy phy-ff770000.syscon:usb2-phy@e460.3: Looking up phy-supply property in node /syscon@ff770000/usb2-phy@e460/otg-port failed
[    1.664076] phy phy-ff770000.syscon:usb2-phy@e460.3: Looking up vbus-supply from device tree
[    1.664086] phy phy-ff770000.syscon:usb2-phy@e460.3: Looking up vbus-supply property in node /syscon@ff770000/usb2-phy@e460/otg-port failed`

Because once I flip the switch charger is set to USB_FLOATING_CHARGER and everything starts to work again:

[  691.550549] rockchip-dwc3 usb0: USB HOST connected
[  692.423924] xhci-hcd remove, state 4
[  692.423984] usb usb8: USB disconnect, device number 1
[  692.429824] xhci-hcd USB bus 8 deregistered
[  692.430410] xhci-hcd remove, state 4
[  692.430453] usb usb7: USB disconnect, device number 1
[  692.431561] xhci-hcd USB bus 7 deregistered
[  692.432267] rockchip-dwc3 usb0: USB unconnected
[  694.068386] phy phy-ff770000.syscon:usb2-phy@e450.1: charger = USB_FLOATING_CHARGER

For reference this is the uname -a of the system currently:

Linux rockpi4b 4.4.154-112-rockchip-gfdb18c8bab17 #1 SMP Thu Jan 21 04:50:13 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux

What can be done here?, I have been trying to disable the usb subsystem at runtime, but no cigar. I have also tried to recompile the kernel from source using radaxa’s tutorial (4.4 and mainline) but the behavior is the same.

Any help will be greatly appreciated. Thank a lot.

Have a nice day.

Edit: I will also add the whole dmesg output, it has to be a pastebin since there is a character limit:


I made some progress. By doing this:

echo -n "peripheral" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode

I can force the OTG driver to switch to device or peripheral mode. After doing that, I can see on the kernel output that the outcome is basically the same as flipping the switch.

This is a bug or at least an overlook in my humble opinion, since the port will never truly work as a true OTG port AFAIK that can hotswap between the two roles, and will (or at least should ) have the role assigned by the switch.

I can still make this solution work by writing an udev rule or something but the discussion is still open I guess…

This issue exists because of the USB A port using as OTG. If you want to keep the cable always plugged and use as peripheral mode, you’d better keep the mode fixed in the dts.

I still do not understand what is the point of OTG mode as default. The port itself cannot act as a true OTG right?. If it were a true OTG cable, no switch would be required anyways. And I still find hard to understand that the computer boots in OTG mode but transitions to peripheral when the switch is flipped. Should the driver not follow the position of the switch?.

Maybe you can provide more information as of the design behind this.


I’d be interested in how you got as far as you have, I’m using the rock Pi 4b to work with a drone controller and if I leave the switch on OTG mode and start up the device it leaves it trying to boot from the otg, I can live with that and plug it in after but my issue lies in that the software will not connect to the device, Did you make your driver changes after the standard android 11 image flsh or did you build the image and create the driver changes with them? I’m tring to buld a fast controller for DJI Drones that is for personal usage to enable multiple video out put using the Rock Pi 4b’s GPU power in tandem with a built in touch screen? any pointers on your journey would be appreciated

A true OTG cable has USB_ID detect pin, to detect the USB acts as Host or Device, comes only in two form factor, Micro USB B 2.0 and 3.0, which are not widely used. For ROCK Pi 4, the reason we use USB A for OTG is, there is no more USB in the SoC. We use USB A as host most of the time but we have to download images/firmware from OTG sometimes.

Set this USB as otg function by default is convenient for some functions such as adb.

So in fact then the issue that presents itself to me is the cable I am using for the device and not the configuration of the port? The reason I’m asking in this way is older generation DJI Products did use a true USB A to Micro USB cable and the A was present on the controller under neath allowing the phone to connect via the micro. Most Phones these days are obviously USB-C is this a cabling issue or is there a set way to suggest to move forwards… I dropped this a while back as I had other priorities but I am back on it now

David Apologies I have hijacked your thread, I am genuinly interested in your results to learn more on the subject. Please delete comments if needed

OK that is not a problem for me that the OTG mode is set by default. However, a little help would be much appreciated with the g_mass_storage mode.

Right now, I can only set up the mass storage gadget by executing after the cable has been connected.

echo -n "host" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode
echo -n "peripheral" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode
sudo modprobe g_mass_storage file=/mnt/mass_storage stall=1 iSerialNumber=aaaaaaa removable=y

Once the cable has been removed, the mass_storage stops working again. To make it work again I remove the g_mass_storage module:

modprobe -r g_mass_storage

And by executing again the comands, once the cable is plugged again, I can manage to activate again.

This is very cumbersome, but still doable. However, i’m finding it really hard to automate it because I cannot detect that the cable has been connected in any way, no udev, no /sys/… flags. Hence, I can’t use a trigger.

Any tips @jack as of how this could be done, or what is the best way to proceed here?

I still thin that there is something very wrong with the drivers of, since the kernel ring buffer has plenty of relevant errors regarding the USB otg subsystem.


@Slarti, I am afraid I am not familiar with the android image, I am using Ubuntu. Perhaps you could define what device and software are here:

I can live with that and plug it in after but my issue lies in that the software will not connect to the device,

I don’t think the cable might be a problem. But, still, If your issue is different, you should open it in a different thread, it might be easier to read in the future.

Chreers to both of you,

Hi David,

this might be of use for you …

That depends on your kernel. Since v5.11 it’s not: arm64: dts: rockchip: use USB host by default on rk3399-rock-pi-4 I use a device tree overlay and set mine to peripheral mode:


/ {
    compatible = "rockchip,rk3399";
    fragment@0 {
      target = <&usbdrd_dwc3_0>;
      __overlay__ {
        dr_mode = "peripheral";

I don’t do the legacy module loading and havn’t tried it, but configure my gadget by writing entries into configfs: /sys/kernel/config/usb_gadget/... and don’t have problems setting it up with or without a cable plugged. That’s my stripped (and therefor untested) setup script:


set -e

udc=$(basename /sys/class/udc/*)

function set_verbose() {
    echo "$1 = $2 ($(< $dst))"
    echo $2 > $dst

function setup() {
    echo "Init $g"
    mkdir -p $g/configs/c.1
    set_verbose bMaxPacketSize0 64
    # set_verbose bcdUSB    0x0200  # USB 2.0
    set_verbose bcdUSB    0x0310  # USB 3.1 SuperSpeed device
    set_verbose idVendor  0x1D6B
    set_verbose idProduct 0x0104
    set_verbose bcdDevice 0x0001  # device release number

    # suggested:
    # set_verbose bDeviceClass    0xEF
    # set_verbose bDeviceSubClass 0x02
    # set_verbose bDeviceProtocol 0x01
    # legacy:
    set_verbose bDeviceClass    0x00
    set_verbose bDeviceSubClass 0x00
    set_verbose bDeviceProtocol 0x00

    mkdir -p $g/functions/mass_storage.0
    set_verbose functions/mass_storage.0/lun.0/file ""
    set_verbose functions/mass_storage.0/lun.0/removable 1
    ln -sf $g/functions/mass_storage.0 $g/configs/c.1/

    echo $udc > $g/UDC


But, mine isn’t working without problems either. I have to:

echo > /sys/kernel/config/usb_gadget/moep/UDC  # deactivate gadget
echo fe800000.usb > /sys/kernel/config/usb_gadget/moep/UDC  # reactivate

Otherwise the target machine detects a full-speed device and fails reading the device descriptor. At least, I don’t have to touch the otg switch, which is locked at device mode.

Note: As the/my problem can be fixed by reactivating the gadget in software (writing to …/UDC), this might be a kernel bug, which should be fixable. That’s what I’m investigating next.

Cheers, Daniel

Will do David many thanks

Thank you so much @bartsch. I have been away from office for some days. Your advice seems very promising. It is definitely a lot of useful information that can be used to investigate. I am stoked to give it another go with all of this. I have tried the gadgetfs system in the past, but to no avail. I am quite busy at the moment. But I will report back as soon as possible.

Again, thank you.


Hello all,

I’ve tried your tips @bartsch. It did not work, I repurposed your script, and I’ve recompiled the kernel as per your instructions. Just to let you know. It was a very good post regardless. Thank you.

I’ve come across this thread as well. Witch is exactly the same issue.

They arrived to the same conclusion, peripheral mode cannot be set properly without either flipping the switch or connecting the cable from boot. I’ve followed @jack 's instructions on that thread as well. They also did not work.

So I come here to ask again. Is there any way at all to detect that an USB has been plugged? So that I can run the commands that enable OTG once the cable is plugged?.
Maybe a workaround that fixes the problem altogether? @jack , do you have any thoughts on the issue?

Instead of deactivating and reactivating the gadget completly, I’m triggering a soft connect with this hack (*) atm:

#!/usr/bin/env bash

set -e

UDC_NAME=$(ls /sys/class/udc)


while true; do
    link_cur=$(< $UDC_DBG_D/link_state)  # XXX: link state from debugfs

    if [ "$link_prev" == "$link_cur" ]; then
        echo -n "."
        echo -en "\n$link_cur"

    if [ "$link_cur" == "SS.Disabled" ]; then
        echo connect > $UDC_CLS_D/soft_connect
    elif [ "$link_cur" == "Disconnected" ]; then
        echo disconnect > $UDC_CLS_D/soft_connect
    # elif [ "$link_cur" == "U0" ]; then
    #   echo "up and running

    sleep 1

Major problem with this is, it relies on a state from debugfs. At least, it works for me. :confused:

(*) Well, within the kernel a soft connect is pretty close to (de)activating the gadget.

Hi @bartsch !!

You were on point there.

The script you posted did not quite cut it for me but the fact that you can check the state of the connection on your script was everything I needed.

I’ve checked the link_state variable before, but in my case it only shows changes after you’ve plugged the cable once and then run the commands I was discussing before.

  echo -n "host" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode
  echo -n "peripheral" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode

So what my version of the script does is constantly switching on and off the variables until the cable happens to connect. Once the cable Is connected and the commands are executed, then I can see on the link_state variable that the cable is in fact connected.

This solution is very hacky in my opinion since it requires me to toggle the f770000 drivers from host to peripheral constantly until the cable is connected. Still better than nothing. This was driving me insane.

This is my version of the script.

#!/usr/bin/env bash

set -e

UDC_NAME=$(ls /sys/class/udc)


while true; do
	echo -n "host" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode
	sleep 3
	echo ""
	echo -n "peripheral" | tee /sys/bus/platform/drivers/rockchip-usb2phy/ff770000.syscon\:usb2-phy\@e450/otg_mode
	echo ""
	sleep 3

	link_cur=$(< $UDC_DBG_D/link_state)  # XXX: link state from debugfs
	echo $link_cur
    #U0 is the state of "unconnected", so continue running if we are
    #in the state
	if [ "$link_cur" == "U0" ]; then
		echo -n "."
    #The connected state is usually "U2"
		exit 0


Again, thank you @bartsch . You have helped me a lot here.

Maybe @jack can tell us how to properly solve this issue.