RK3399 USB Driver

USB 驱动

Posted by LXG on September 9, 2022

USB 概念

USB(Universal Serial Bus) 通用串行总线,目标是实现即插即用

USB 和其他接口的区别如下图

usb_vs_others

USB Host Controller Interface

usb_host_controller_interface

USB Connector Type

usb_connector

RK USB 控制器

Rockchip SOC 通常内置多个 USB 控制器,不同控制器互相独立。

RK3399 2 个 USB 2.0 HOST(EHCI/OHCI), 1 个 USB HSIC(EHCI),2 个 USB 3.0/2.0 OTG(DWC3/xHCI)

HSIC—USB High Speed Inter-Chip 是一个两线源同步的串行接口,使用240MHz双倍数据速率产生480MHz的高速速率,和传统的USB电缆连接拓扑结构的主机完全兼容, 常用在3G 和 4G模块中。

rk3399_usb

USB 2.0 HOST

usb2_host

USB 2.0 Host 硬件电路有两种:USB 2.0 Host 和 USB 2.0 HSIC。虽然 USB 2.0 Host 和 HSIC 都使用 EHCI 控制器,但使用的 USB 2.0 PHY 不同,所以对应的硬件电路也不同。

硬件电路

usb_hub

FE1.1S是用于USB2.0 HUB的主控IC, 提供4个USB Port

USB 2.0 OTG

usb2_otg

USB 3.0 OTG

usb3_otg

USB 2.0 PHY

USB 2.0 PHY 支持 1 个 port 和 2 个 port 两种设计,如下图 1-4 是支持 2 个 port 的框图。

usb2_phy

  1. Host Port:通过 UTMI+ 连接到 USB 2.0 Host 控制器;
  2. OTG Port:通过 UTMI+ 连接 USB 3.0 或者 USB 2.0 OTG 控制器的 USB 2.0 逻辑模块;

USB 3.0 Type-C PHY

usb_typec

内核配置

kernel/arch/arm64/configs/rockchip_defconfig

# USB PHY CONFIG
CONFIG_PHY_ROCKCHIP_USB=y
CONFIG_PHY_ROCKCHIP_INNO_USB2=y
CONFIG_PHY_ROCKCHIP_INNO_USB3=y
CONFIG_PHY_ROCKCHIP_TYPEC=y

# USB Host CONFIG
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y

# USB OTG CONFIG
CONFIG_USB_DWC3=y
CONFIG_USB_DWC2=y

# USB Gadget
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_VBUS_DRAW=500
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_F_MTP=y
CONFIG_USB_CONFIGFS_F_PTP=y
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_MIDI=y

# Others

USB DTS 配置说明

USB PHY 分为 USB 2.0 PHY 和 USB 3.0 PHY 两种。这两种 PHY 是互相独立的,并且特性差异比较大,所以需要分别配置 DTS。

Note:RK3399 芯片的 USB PHY DTS 配置比较灵活且复杂,请参考文档:《Rockchip_RK3399_Developer_Guide_USB_DTS_CN》

rk3399_usb_control

USB 2.0 PHY DTS

arch/arm64/boot/dts/rockchip/rk3399.dtsi


        grf: syscon@ff770000 {
                compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
                reg = <0x0 0xff770000 0x0 0x10000>;
                #address-cells = <1>;
                #size-cells = <1>;

                u2phy0: usb2-phy@e450 {
                        compatible = "rockchip,rk3399-usb2phy";
                        reg = <0xe450 0x10>;
                        clocks = <&cru SCLK_USB2PHY0_REF>;
                        clock-names = "phyclk";
                        #clock-cells = <0>;
                        clock-output-names = "clk_usbphy0_480m";
                        power-domains = <&power RK3399_PD_PERIHP>;
                        status = "disabled";

                        u2phy0_otg: otg-port {
                                #phy-cells = <0>;
                                interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH 0>,
                                             <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH 0>,
                                             <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH 0>;
                                interrupt-names = "otg-bvalid", "otg-id",
                                                  "linestate";
                                status = "disabled";
                        };

                        u2phy0_host: host-port {
                                #phy-cells = <0>;
                                interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH 0>;
                                interrupt-names = "linestate";
                                status = "disabled";
                        };
                };

        }

对于 Host port 和 OTG port Host 模式,需要为 USB 外设提供 5V 供电电源,所以我们要在 DTS 中配置 USB VBUS 5V

RK3399 USB 2.0 Host VBUS 的控制方式是: GPIO 拉高,则使能 VBUS 5V 输出,GPIO 拉低,则关闭 VBUS 5V 输出。DTS 中使用 regulator 的方式,来配置 GPIO。 其中,属性 “regulator-always-on” 表示,系统启动后,就拉高 GPIO 以使能 VBUS 5V 输出,直到系统关机。

arch/arm64/boot/dts/rockchip/rk3399-evb.dtsi


        vcc5v0_host: vcc5v0-host-regulator {
                compatible = "regulator-fixed";
                enable-active-high;
                gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
                pinctrl-names = "default";
                pinctrl-0 = <&host_vbus_drv>;
                regulator-name = "vcc5v0_host";
                regulator-always-on;
        };

        usb2 {
                host_vbus_drv: host-vbus-drv {
                        rockchip,pins =
                                <4 25 RK_FUNC_GPIO &pcfg_pull_none>;
                };
        };


&u2phy0 {
        status = "okay";
        //extcon = <&fusb0>;

        vbus-5v-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; /* Vbus GPIO 配置 */
        u2phy0_otg: otg-port {
                status = "okay";
        };

        u2phy0_host: host-port {
                phy-supply = <&vcc5v0_host>;
                status = "okay";
        };
};

&u2phy1 {
        status = "okay";
        //extcon = <&fusb1>;

        u2phy1_otg: otg-port {
                status = "okay";
        };

        u2phy1_host: host-port {
                phy-supply = <&vcc5v0_host>;
                status = "okay";
        };
};

USB 3.0 PHY DTS

arch/arm64/boot/dts/rockchip/rk3399.dtsi


        // type-c 0
        usbdrd3_0: usb@fe800000 {
                compatible = "rockchip,rk3399-dwc3";
                clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
                         <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
                clock-names = "ref_clk", "suspend_clk",
                              "bus_clk", "grf_clk";
                power-domains = <&power RK3399_PD_USB3>;
                resets = <&cru SRST_A_USB3_OTG0>;
                reset-names = "usb3-otg";
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;
                status = "disabled";
                usbdrd_dwc3_0: dwc3@fe800000 {
                        compatible = "snps,dwc3";
                        reg = <0x0 0xfe800000 0x0 0x100000>;
                        interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>;
                        dr_mode = "otg";
                        phys = <&u2phy0_otg>, <&tcphy0_usb3>;
                        phy-names = "usb2-phy", "usb3-phy";
                        phy_type = "utmi_wide";
                        snps,dis_enblslpm_quirk;
                        snps,dis-u2-freeclk-exists-quirk;
                        snps,dis_u2_susphy_quirk;
                        snps,dis-del-phy-power-chg-quirk;
                        snps,tx-ipgap-linecheck-dis-quirk;
                        snps,xhci-slow-suspend-quirk;
                        snps,xhci-trb-ent-quirk;
                        snps,usb3-warm-reset-on-resume-quirk;
                        status = "disabled";
                };
        };

        // type-c 1
        usbdrd3_1: usb@fe900000 {
                compatible = "rockchip,rk3399-dwc3";
                clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
                         <&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_GRF>;
                clock-names = "ref_clk", "suspend_clk",
                              "bus_clk", "grf_clk";
                power-domains = <&power RK3399_PD_USB3>;
                resets = <&cru SRST_A_USB3_OTG1>;
                reset-names = "usb3-otg";
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;
                status = "disabled";
                usbdrd_dwc3_1: dwc3@fe900000 {
                        compatible = "snps,dwc3";
                        reg = <0x0 0xfe900000 0x0 0x100000>;
                        interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH 0>;
                        dr_mode = "host";
                        phys = <&u2phy1_otg>, <&tcphy1_usb3>;
                        phy-names = "usb2-phy", "usb3-phy";
                        phy_type = "utmi_wide";
                        snps,dis_enblslpm_quirk;
                        snps,dis-u2-freeclk-exists-quirk;
                        snps,dis_u2_susphy_quirk;
                        snps,dis-del-phy-power-chg-quirk;
                        snps,tx-ipgap-linecheck-dis-quirk;
                        snps,xhci-slow-suspend-quirk;
                        snps,xhci-trb-ent-quirk;
                        snps,usb3-warm-reset-on-resume-quirk;
                        status = "disabled";
                };
        };


        // type-c 0
        tcphy0: phy@ff7c0000 {
                compatible = "rockchip,rk3399-typec-phy";
                reg = <0x0 0xff7c0000 0x0 0x40000>;
                rockchip,grf = <&grf>;
                #phy-cells = <1>;
                clocks = <&cru SCLK_UPHY0_TCPDCORE>,
                         <&cru SCLK_UPHY0_TCPDPHY_REF>;
                clock-names = "tcpdcore", "tcpdphy-ref";
                assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
                assigned-clock-rates = <50000000>;
                power-domains = <&power RK3399_PD_TCPD0>;
                resets = <&cru SRST_UPHY0>,
                         <&cru SRST_UPHY0_PIPE_L00>,
                         <&cru SRST_P_UPHY0_TCPHY>;
                reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
                rockchip,typec-conn-dir = <0xe580 0 16>;
                rockchip,usb3tousb2-en = <0xe580 3 19>;
                rockchip,usb3-host-disable = <0x2434 0 16>;
                rockchip,usb3-host-port = <0x2434 12 28>;
                rockchip,external-psm = <0xe588 14 30>;
                rockchip,pipe-status = <0xe5c0 0 0>;
                rockchip,uphy-dp-sel = <0x6268 19 19>;
                status = "disabled";

                tcphy0_dp: dp-port {
                        #phy-cells = <0>;
                };

                tcphy0_usb3: usb3-port {
                        #phy-cells = <0>;
                };
        };

        //type-c 1
        tcphy1: phy@ff800000 {
                compatible = "rockchip,rk3399-typec-phy";
                reg = <0x0 0xff800000 0x0 0x40000>;
                rockchip,grf = <&grf>;
                #phy-cells = <1>;
                clocks = <&cru SCLK_UPHY1_TCPDCORE>,
                         <&cru SCLK_UPHY1_TCPDPHY_REF>;
                clock-names = "tcpdcore", "tcpdphy-ref";
                assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
                assigned-clock-rates = <50000000>;
                power-domains = <&power RK3399_PD_TCPD1>;
                resets = <&cru SRST_UPHY1>,
                         <&cru SRST_UPHY1_PIPE_L00>,
                         <&cru SRST_P_UPHY1_TCPHY>;
                reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
                rockchip,typec-conn-dir = <0xe58c 0 16>;
                rockchip,usb3tousb2-en = <0xe58c 3 19>;
                rockchip,usb3-host-disable = <0x2444 0 16>;
                rockchip,usb3-host-port = <0x2444 12 28>;
                rockchip,external-psm = <0xe594 14 30>;
                rockchip,pipe-status = <0xe5c0 16 16>;
                rockchip,uphy-dp-sel = <0x6268 3 19>;
                status = "disabled";

                tcphy1_dp: dp-port {
                        #phy-cells = <0>;
                };

                tcphy1_usb3: usb3-port {
                        #phy-cells = <0>;
                };
        };


arch/arm64/boot/dts/rockchip/rk3399-evb.dtsi


&u2phy0 {
        status = "okay";
        //extcon = <&fusb0>;

        vbus-5v-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; /* Vbus GPIO 配置 */
        u2phy0_otg: otg-port {
                status = "okay";
        };

        u2phy0_host: host-port {
                phy-supply = <&vcc5v0_host>;
                status = "okay";
        };
};


&usbdrd3_0 {
        extcon = <&u2phy0>;
        status = "okay";
};

&usbdrd_dwc3_0 {
        dr_mode = "otg" ;
        status = "okay" ;
};

&usbdrd3_1 {
        status = "okay";
};

&usbdrd_dwc3_1 {
        dr_mode = "host";
        status = "okay";
};