tiny4412 linux-4.2 移植(七)USB 2.0 host框架(2)hcd(ehci主机控制器)

mac2025-07-15  3

简介

上一节整体介绍了usb host框架,分析了其中的phy层。这一节我们分析usb 框架中的hcd层。Exynos 4412的USB 2.0的 Host Controller包含USB 2.0 EHCI Host Controller和 USB 1.1 OHCI Host Controller,我这里只讲EHCI Host Controller。

框架图

hcd层以stuct usb_hcd为核心。它主要有两个功能,一个是通过ehci_hcd去操作phy层,一个是通过hc_driver去操作上层传递下来的urb数据包。

hcd

先来看下hcd相关的dts

# exynos4.dtsi exynos_usbphy: exynos-usbphy@125B0000 { compatible = "samsung,exynos4210-usb2-phy"; reg = <0x125B0000 0x100>; samsung,pmureg-phandle = <&pmu_system_controller>; clocks = <&clock CLK_USB_DEVICE>, <&clock CLK_XUSBXTI>; clock-names = "phy", "ref"; #phy-cells = <1>; status = "disabled"; }; # exynos4412.dtsi &exynos_usbphy { compatible = "samsung,exynos4x12-usb2-phy"; samsung,sysreg-phandle = <&sys_reg>; }; # exynos4412-tiny4412.dts &exynos_usbphy { status = "okay"; }; # exynos4.dtsi ehci: ehci@12580000 { compatible = "samsung,exynos4210-ehci"; reg = <0x12580000 0x100>; interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clock CLK_USB_HOST>; clock-names = "usbhost"; status = "disabled"; #address-cells = <1>; #size-cells = <0>; //exynos_ehci_get_phy中of_property_read_u32(child, "reg", &phy_number)获取到reg的值,然后作为 port@0 { // 通过of_property_read_u32(child, "reg", &phy_number); 解析“reg”属性 //在_of_phy_get中解析“phys”属性 reg = <0>; //被解析成数组的下标 drv->instances[0].phy phys = <&exynos_usbphy 1>; //1代表 host status = "disabled"; }; port@1 { reg = <1>; //被解析成数组的下标 drv->instances[1].phy phys = <&exynos_usbphy 2>; //2代表 hsci0 status = "disabled"; }; port@2 { reg = <2>; //被解析成数组的下标 drv->instances[2].phy phys = <&exynos_usbphy 3>; //3代表 hsci1 status = "disabled"; }; }; # exynos4412-tiny4412.dts &ehci { status = "okay"; port@0 { status = "okay"; }; port@1 { status = "okay"; }; port@2 { status = "okay"; }; };

相关的驱动会匹配到/drivers/usb/host/ehci-exynos.c,来看下它的probe函数

///drivers/usb/host/ehci-hcd.c static const struct hc_driver ehci_hc_driver = { .product_desc = "EHCI Host Controller", /* generic hardware linkage */ .irq = ehci_irq, ...... /* managing i/o requests and associated device resources */ .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, /* scheduling support*/ .get_frame_number = ehci_get_frame, /* root hub support */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, }; ///drivers/usb/host/ehci-exynos.c static struct hc_driver __read_mostly exynos_ehci_hc_driver;//这个就是 struct hc_driver ehci_hc_driver static int exynos_ehci_get_phy(struct device *dev, struct exynos_ehci_hcd *exynos_ehci) { struct device_node *child; struct phy *phy; int phy_number; /* Get PHYs for the controller */ for_each_available_child_of_node(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number);//对应dts reg = <0>; reg = <1>;这些属性 phy = devm_of_phy_get(dev, child, NULL);--->//这个函数将间接使用samsung_usb2_phy_xlate来取得上一节初始化的phy ---> phy = of_phy_get(np, con_id) ---> ---> phy = _of_phy_get(np, index) ----> ----> struct phy *_of_phy_get(struct device_node *np, int index){ //获取到 phys = <&exynos_usbphy 1>; 后面的参数 ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",index, &args); //调用samsung_usb2_phy_xlate phy = phy_provider->of_xlate(phy_provider->dev, &args); ----> --->samsung_usb2_phy_xlate(struct device *dev,struct of_phandle_args *args) { //所以dts的phys属性的参数:1代表host,2代表hsic0,3代表hsic1 return drv->instances[args->args[0]].phy; } } exynos_ehci->phy[phy_number] = phy; } static int exynos_ehci_probe(struct platform_device *pdev) { struct exynos_ehci_hcd *exynos_ehci; struct usb_hcd *hcd; struct ehci_hcd *ehci; hcd = usb_create_hcd(&exynos_ehci_hc_driver, //把ehci_hc_driver赋值给hcd->driver &pdev->dev, dev_name(&pdev->dev)); /* usb_hcd的priv域是ehci_hcd,然后ehci_hcd的piv域是exynos_ehci_hcd 具体关系可以看框架图*/ exynos_ehci = to_exynos_ehci(hcd); err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci); //获得在上一节初始化phy到struct exynos_ehci_hcd err = exynos_ehci_phy_enable(&pdev->dev); ---> ---> static int exynos_ehci_phy_enable(struct device *dev) { phy_power_on(exynos_ehci->phy[i]); -----> ---> int phy_power_on(struct phy *phy) { /* /drivers/phy/phy-core.c */ phy->ops->power_on(phy) ----> ----> static int samsung_usb2_phy_power_on(struct phy *phy){ //到了这里就是上一节phy层的调用了,可以直接看上一节的内容。这里就是去设置寄存器 } } } usb_add_hcd(hcd, irq, IRQF_SHARED) ----> ---->int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags){ retval = usb_register_bus(&hcd->self); //注册总线,因为hcd本身就是总线 usb_hcd_request_irqs(hcd, irqnum, irqflags) request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd); //注册中断处理函数 register_root_hub ---> ---> usb_new_device ---> ----> device_add /* * device_add之后会调用probe函数,由于这个device是个hub,所以会调用hub_probe * hub 1-0:1.0: USB hub found * hub 1-0:1.0: 3 ports detected */ } }

总结一下,当dts到ehci driver匹配到之后会调用exynos_ehci_probe,probe函数会去创建usb_hcd,ehci_hcd和exynos_ehci_hcd,然后通过exynos_ehci_get_phy获取到在phy层初始化的phy,根据dts的指定,我这里获取到的是host、hsci0、hsci1这三个phy,然后通过phy的相关接口去使能对应的phy链路。最后把hcd添加到总线,注册它的中断处理函数,注册root hub。hub这一块我们下一节再讲。

最新回复(0)