Fork me on GitHub

USB总线-Linux内核USB3.0主机控制器驱动初始化流程分析(十三)

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的主要特性如下:

  1. 兼容USB3.0 Revision 1.0规范,兼容USB2.0规范,兼容xHCI Revision 1.1规范。
  2. 支持Control/Bulk (including stream)/Interrupt/Isochronous传输。
  3. USB3.0同时进行输入和输出传输,最大可达到8Gbps带宽。
  4. 具有描述符缓存和数据预取功能,可以改善高延迟系统的性能。
  5. USB2.0支持LPM低功耗协议,USB3.0支持U0、U1、U2、U3四种低功耗状态。
  6. 端点具有动态FIFO memory分配能力。
  7. LS模式具有Keep-Alive特性,HS/FS模式具有(micro-)SOFs特性。
  8. 支持AXI Master和AHB Slave接口。
  9. 最大支持64个设备。
  10. 支持1个中断。
  11. 支持USB2.0 port和USB3.0 port。
  12. 支持标准的和开源的xHCI和USB类驱动。
  13. USB2.0 PHY支持充电检测。
  14. 支持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驱动的主要工作如下:

  1. 首先初始化的是外围的rockchip驱动,内部的dwc3驱动由rockchip驱动进行匹配。

  2. 接着进入dwc3的驱动初始化流程,工作流程如下:解复位控制器。

  3. 获取控制器硬件配置的模式,并和软件配置的模式进行比较,起到校验作用

  4. dwc3核心初始化,包括配置PHY接口、初始化PHY、给PHY上电、调整帧长度、使能Auto retry Feature等。

  5. 根据配置的模式,进入对应的模式。若是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主机驱动的初始化过程如下图所示,具体的流程如下:

  1. USB主机的设备和驱动通过platform_match匹配,匹配成功后,驱动的xhci_plat_probe函数被调用。
  2. 调用__usb_create_hcd函数 创建USB主机数据结构,即main_hcd(primary_hcd)和shared_hcd,前者为USB2.0,后者为USB3.0;创建USB主机数据结构的时候会初始化一个非常重要的定时器rh_timer,到期会调用rh_timer_func函数,该定时器的作用是轮询Hub的状态,检测USB设备连接、断开等情况。
  3. 调用usb_add_hcd注册USB2.0和USB3.0主机控制器,USB主机驱动的核心初始化工作在注册控制器时完成。
    1. 注册USB总线。
    2. 分配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设备通信。
    3. 调用xhci_plat_setup通过读取控制器的hcs_params寄存器获取寄存器地址、能力等信息,然后复位复位控制器,最后初始化xHCI需要的数据结构。
    4. 初始化root hub的tasklets,设置回调函数为usb_giveback_urb_bh,当root hub的port状态变化时,会调用usb_giveback_urb_bh处理。
    5. 注册USB2.0控制器的中断处理函数usb_hcd_irq(中断只需要注册一次,2.0和3.0公用),usb_hcd_irq内部会调用xhci_irq,该中断用于处理控制器传输数据、事件。
    6. 调用xhci_run使能xHCI控制器。
    7. 调用register_root_hub注册root hub。root hub虽然不是真实的USB设备,但也需要获取描述符,走枚举的流程。
      1. 获取设备描述符。使用控制传输,调用usb_control_msg
      2. 初始化root hub。主要是获取配置描述符(获取配置描述符时,会一并获取该配置下的接口描述符、端点描述符等)。
      3. 枚举成功后,打印root hub的设备信息(需要开启对应的选项,否则不打印)。
      4. 注册USB设备。内部会调用usb_device_match匹配USB设备驱动,对于hub,则会匹配到hub_driver,调用hub_probe函数。
    8. 当设置uses_new_pollingHCD_POLL_RH(hcd)标志时,内核会调用usb_hcd_poll_rh_status函数轮询hub状态,以此探测设备的连接、断开等状态变化。详细的轮询过程在下一节分析。

posted @ 2024-09-14 21:14  yooooooo  阅读(22)  评论(0编辑  收藏  举报