1.概述
RK3588有2个USB3.0 DRD控制器,2个USB2.0 Host控制器。USB3.0 DRD控制器既可以做Host,也可以做Device,向下兼容USB2.0和USB1.0。USB3.0 DRD控制器的内部结构如下图所示;总线接口为AXI或AHB;USB3.0和USB2.0及USB1.0硬件上独立;USB3.0控制器数字逻辑部分对应SS MAC,PHY接口为PIPE,PHY为USBDP PHY,和DP使用同一个PHY;USB2.0和USB1.0控制器数字逻辑部分对应HS/FS/LS MAC,PHY接口为UTMI+,PHY为USB2.0 PHY。
USB3.0 DRD控制器作为Host的主要特性如下:
- 兼容USB3.0 Revision 1.0规范,兼容USB2.0规范,兼容xHCI Revision 1.1规范。
- 支持Control/Bulk (including stream)/Interrupt/Isochronous传输。
- USB3.0同时进行输入和输出传输,最大可达到8Gbps带宽。
- 具有描述符缓存和数据预取功能,可以改善高延迟系统的性能。
- USB2.0支持LPM低功耗协议,USB3.0支持U0、U1、U2、U3四种低功耗状态。
- 端点具有动态FIFO memory分配能力。
- LS模式具有Keep-Alive特性,HS/FS模式具有(micro-)SOFs特性。
- 支持AXI Master和AHB Slave接口。
- 最大支持64个设备。
- 支持1个中断。
- 支持USB2.0 port和USB3.0 port。
- 支持标准的和开源的xHCI和USB类驱动。
- USB2.0 PHY支持充电检测。
- 支持USB Type-C和DP Alt模式。
2.设备树
usbdrd3_1设备树定义如下,默认模式为Host。
[arch/arm64/boot/dts/rockchip/rk3588.dtsi]
/ {......usbdrd3_1: usbdrd3_1 {compatible = "rockchip,rk3588-dwc3", "rockchip,rk3399-dwc3";clocks = <&cru REF_CLK_USB3OTG1>, <&cru SUSPEND_CLK_USB3OTG1>,<&cru ACLK_USB3OTG1>;clock-names = "ref", "suspend", "bus";#address-cells = <2>;#size-cells = <2>;ranges;status = "disabled";usbdrd_dwc3_1: usb@fc400000 {compatible = "snps,dwc3";reg = <0x0 0xfc400000 0x0 0x400000>;interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;power-domains = <&power RK3588_PD_USB>;resets = <&cru SRST_A_USB3OTG1>;reset-names = "usb3-otg";/* USB模式,host:主机,otg:otg模式,peripheral:设备 */dr_mode = "host";/* USB2.0和USB1.0 PHY USB3.0 PHY */phys = <&u2phy1_otg>, <&usbdp_phy1_u3>;phy-names = "usb2-phy", "usb3-phy";phy_type = "utmi_wide"; /* USB2.0 PHY接口 *//* When set clears the enblslpm in GUSB2PHYCFG,* disabling the suspend signal to the PHY*/snps,dis_enblslpm_quirk;/* When set, clear the u2_freeclk_exists in GUSB2PHYCFG,* specify that USB2 PHY doesn't provide a free-running* PHY clock.*/snps,dis-u2-freeclk-exists-quirk;/* When set core will change PHY power from P0 to* P1/P2/P3 without delay.*/snps,dis-del-phy-power-chg-quirk;/* When set, disable u2mac linestate check during HS transmit */snps,dis-tx-ipgap-linecheck-quirk;status = "disabled";};};
};[arch/arm64/boot/dts/rockchip/rk3588-evb.dtsi]
&usbdrd3_1 {status = "okay";
};
3.DWC3驱动初始化
在xhci驱动初始化之前,需要dwc3驱动做一些底层的初始化,主要是非XHCI协议的硬件初始化,如模式、时钟、PHY等。dwc3驱动的主要工作如下:
-
首先初始化的是外围的rockchip驱动,内部的dwc3驱动由rockchip驱动进行匹配。
-
接着进入dwc3的驱动初始化流程,工作流程如下:解复位控制器。
-
获取控制器硬件配置的模式,并和软件配置的模式进行比较,起到校验作用
-
dwc3核心初始化,包括配置PHY接口、初始化PHY、给PHY上电、调整帧长度、使能Auto retry Feature等。
-
根据配置的模式,进入对应的模式。若是Host,则需要将USB2.0 PHY和USB3.0 PHY设置为PHY_MODE_USB_HOST,然后注册xhci的
platform_device
。
4.xHCI驱动初始化
dwc3驱动将设备树中的USB Host设备节点转换成platform_device,并将其name设置为"xhci-hcd",然后调用platform_device_add注册到系统中。USB Host驱动通过platform_driver实现,通过xhci_plat_init和xhci_plat_exit注册和注销。当USB Host设备或USB Host驱动注册的时候,都会调用platform_match
去匹配对方,若匹配成功,则会调用USB Host驱动中的probe函数,即usb_xhci_driver
中的xhci_plat_probe
函数。
[drivers/usb/host/xhci-plat.c]
static struct platform_driver usb_xhci_driver = {.probe = xhci_plat_probe,.remove = xhci_plat_remove,.shutdown = usb_hcd_platform_shutdown,.driver = {.name = "xhci-hcd",.pm = &xhci_plat_pm_ops,.of_match_table = of_match_ptr(usb_xhci_of_match),.acpi_match_table = ACPI_PTR(usb_xhci_acpi_match),},
};
MODULE_ALIAS("platform:xhci-hcd");static int __init xhci_plat_init(void)
{xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides);return platform_driver_register(&usb_xhci_driver);
}
module_init(xhci_plat_init);static void __exit xhci_plat_exit(void)
{platform_driver_unregister(&usb_xhci_driver);
}
module_exit(xhci_plat_exit);
xhci_plat_probe
函数中会注册xHCI控制器的硬件操作函数集合,即xhci_hc_driver
数据结构,如下所示。
[drivers/usb/host/xhci.c]
static const struct hc_driver xhci_hc_driver = {.description = "xhci-hcd",.product_desc = "xHCI Host Controller",.hcd_priv_size = sizeof(struct xhci_hcd),/* generic hardware linkage */.irq = xhci_irq,.flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED | HCD_BH,/* basic lifecycle operations */.reset = NULL, /* xhci_plat_setup */.start = xhci_run, /* xhci_plat_start */.stop = xhci_stop,.shutdown = xhci_shutdown,/* managing i/o requests and associated device resources */.map_urb_for_dma = xhci_map_urb_for_dma,.urb_enqueue = xhci_urb_enqueue,.urb_dequeue = xhci_urb_dequeue,.alloc_dev = xhci_alloc_dev,.free_dev = xhci_free_dev,.alloc_streams = xhci_alloc_streams,.free_streams = xhci_free_streams,.add_endpoint = xhci_add_endpoint,.drop_endpoint = xhci_drop_endpoint,.endpoint_disable = xhci_endpoint_disable,.endpoint_reset = xhci_endpoint_reset,.check_bandwidth = xhci_check_bandwidth,.reset_bandwidth = xhci_reset_bandwidth,.address_device = xhci_address_device,.enable_device = xhci_enable_device,.update_hub_device = xhci_update_hub_device,.reset_device = xhci_discover_or_reset_device,/* scheduling support */.get_frame_number = xhci_get_frame,/* root hub support */.hub_control = xhci_hub_control,.hub_status_data = xhci_hub_status_data,.bus_suspend = xhci_bus_suspend,.bus_resume = xhci_bus_resume,.get_resuming_ports = xhci_get_resuming_ports,/* call back when device connected and addressed */.update_device = xhci_update_device,.set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,.enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,.find_raw_port_number = xhci_find_raw_port_number,.clear_tt_buffer_complete = xhci_clear_tt_buffer_complete,
};
USB主机驱动的初始化过程如下图所示,具体的流程如下:
- USB主机的设备和驱动通过
platform_match
匹配,匹配成功后,驱动的xhci_plat_probe
函数被调用。 - 调用__usb_create_hcd函数 创建USB主机数据结构,即main_hcd(primary_hcd)和shared_hcd,前者为USB2.0,后者为USB3.0;创建USB主机数据结构的时候会初始化一个非常重要的定时器rh_timer,到期会调用rh_timer_func函数,该定时器的作用是轮询Hub的状态,检测USB设备连接、断开等情况。
- 调用
usb_add_hcd
注册USB2.0和USB3.0主机控制器,USB主机驱动的核心初始化工作在注册控制器时完成。- 注册USB总线。
- 分配root hub的数据结构
usb_device
(root hub不是一个真实的USB设备,无需调用xhci_alloc_dev
分配主机控制器资源);设置总线类型、设备类型和属性,总线类型usb_bus_type
中的usb_device_match
函数用于匹配USB设备和USB设备驱动,具体的匹配过程后面分析;使能使能root hub端点0,即将端点0的数据结构usb_host_endpoint
放到usb_device
输入输出端点的数组中,便于后续和USB设备通信。 - 调用
xhci_plat_setup
通过读取控制器的hcs_params寄存器获取寄存器地址、能力等信息,然后复位复位控制器,最后初始化xHCI需要的数据结构。 - 初始化root hub的tasklets,设置回调函数为
usb_giveback_urb_bh
,当root hub的port状态变化时,会调用usb_giveback_urb_bh
处理。 - 注册USB2.0控制器的中断处理函数
usb_hcd_irq
(中断只需要注册一次,2.0和3.0公用),usb_hcd_irq
内部会调用xhci_irq
,该中断用于处理控制器传输数据、事件。 - 调用
xhci_run
使能xHCI控制器。 - 调用
register_root_hub
注册root hub。root hub虽然不是真实的USB设备,但也需要获取描述符,走枚举的流程。- 获取设备描述符。使用控制传输,调用
usb_control_msg
。 - 初始化root hub。主要是获取配置描述符(获取配置描述符时,会一并获取该配置下的接口描述符、端点描述符等)。
- 枚举成功后,打印root hub的设备信息(需要开启对应的选项,否则不打印)。
- 注册USB设备。内部会调用
usb_device_match
匹配USB设备驱动,对于hub,则会匹配到hub_driver
,调用hub_probe
函数。
- 获取设备描述符。使用控制传输,调用
- 当设置
uses_new_polling
和HCD_POLL_RH(hcd)
标志时,内核会调用usb_hcd_poll_rh_status
函数轮询hub状态,以此探测设备的连接、断开等状态变化。详细的轮询过程在下一节分析。