当前位置:   article > 正文

linux USB驱动分析(一)USB PHY驱动分析

linux USB驱动分析(一)USB PHY驱动分析

关键词:linux、驱动、usb、phy

以太网类似,USB芯片也分为Host Controller部分(主机控制器/设备控制器)和PHY部分(收发器) 两大部分组成。

USB 的 PHY 与以太网的 PHY 类似,用于数字信号和电气信号的转换。

主机控制器Controller部分主要实现USB的协议和控制,内部逻辑主要有 MAC层,CSR层,FIFO层等。

  1. MAC层实现安装USB协议进行数据包打包和解包,并把数据按照 UTMI/ULPI总线格式发送给PHY。
  2. FIFO控制层主要是和DDR进行数据交互,控制USB从DDR搬运数据的通道

USB PHY负责最底层的信号转换,作用类似于网口的PHY。主要实现 并转串的功能,把控制器通过 UTMI或ULPI总线传递过来的并行数据 转换为串行数据,再通过差分数据线输出到USB接口。或反之。

总之USB芯片内部实现的功能就是接受软件的控制,进而从内存搬运数据并按照USB协议进行数据打包,并串转换后输出到芯片外部。或者从芯片外部接收差分数据信号,串并转换后进行数据打包并写到内存中。

以Controller 和 PHY 都被封装到SOC为例,如图

  1. VBUS :电压线,主机利用VBUS给USB设备提供工作电压。
  2. D+ : 正向传送数据 数据线
  3. D-: 反向传送数据 数据线

一般来说如果芯片的usb phy封装在芯片内,基本采用UTMI+的接口。不封装到芯片内的一般采用ULPI接口,这样可以降低pin的数量。

如下图是嵌入式设备上的USB系统,其中SOC内嵌了USB控制器,USB 收发器(PHY)没有封装到芯片内。该控制器支持4条总线和3中操作模式。

  • 总线1工作在主机模式下,通过USB收发器(PHY) 和 A型接口( USB Type A)连接。USB Type A常用于个人电脑PC及消费类电子产品中,用于连接键盘,鼠标等外设
  • 总线2 也工作在主机模式下,只不过它的USB收发器(PHY)连接的是内嵌USB设备,如打印机等
  • 总线3工作在设备模式下,通过USB收发器(PHY) 和 B型接口(USB Type B)连接。B型接口通过一条 B-A线和主机连接。在这种模式下,该嵌入式设备可以当做USB从设备使用。同PC机相比,嵌入式设备(如MP3,手机等)作为USB的设备端,所以 大部分嵌入式设备 除了包含主机控制器之外,还包含USB设备控制器。
  • 总线4接的是 OTG(On-The-Go)控制器,既可以做主机,也可以作从机,与前三种总线不同,总线4的USB收发器是智能的,能够通过I2C 和处理器交换控制信息,USB OTG 收发器(PHY)的另一端和Mini-AB OTG接口相连。如果两个设备都支持OTG,他们不需要作为主机的计算机介入就可以直接通信。

USB 主机控制器分为以下几种:

  • UHCI(Universal Host Controller Interface 通用主机控制器接口) 该标准是英特尔提出。
  • OHCI(Open Host Controller Interface 开放主机控制器接口)该接口是康柏和微软等公司提出,兼容OHCI的控制器硬件智能程度比UHCI高。
  • EHCI(Enhanced Host Controller Interface 增强型主机控制器接口) 该主机控制器支持高速的USB2.0S设备。为支持低速的USB设备,该控制器通常同时包含UHCI 和 OHCI控制器。
  • USB OTG控制器,这类控制器在嵌入式微控制器领域越来越受欢迎,由于采用了OTG控制器,每个通信终端 即能作主机也能作从机,设备可以根据功能需要在主机模式和设备模式之间任意切换。

主机控制器内嵌了一个叫 根集线器 的硬件。很重要!!!!

USB集线器又称为USB Hub,用于拓展计算机USB接口。计算机主板上对外往往提供多个USB接口,这些接口往往都是通过主板上的USB集线器芯片来拓展出来的。在USB总线通信协议中,通过设备描述符和接口描述符来判断该USB是否为USB集线器。

USB 根集线器(USB Root Hub)指的是直接连接到USB主控制器上的USB Hub。USB根集线器供电与USB主控器供电来源相同。USB总线中只有USB主机和USB集线器可以向外部供电。

USB PHY驱动

以 RK3128为例,其USB PHY 使用的是 USB2.0 PHY ,使用的是 Innosilicon 的IP ,对应的驱动文件为linux-4.4 \drivers\phy\rockchip\phy-rockchip-inno-usb2.c。

Innosilicon USB2.0 PHY 的硬件框架如下图 2-1 所示,主要包括五个子模块:Transceiver block,PLL clock multiplier,digital UTMI+ core,automatic test functionality,OTG Circuitry(optional)。

rk3128 USB PHY 设备树配置如下:

  1. grf: syscon@20008000 { //System Controller Registers R/W driver,syscon参见内核文档:Documentation\devicetree\bindings\mfd\syscon.txt
  2. compatible = "rockchip,rk3128-grf", "syscon", "simple-mfd";
  3. reg = <0x20008000 0x1000>;
  4. #address-cells = <1>;
  5. #size-cells = <1>;
  6. u2phy: usb2-phy@17c {
  7. compatible = "rockchip,rk3128-usb2phy";
  8. reg = <0x017c 0x0c>;
  9. clocks = <&cru SCLK_OTGPHY0>;
  10. clock-names = "phyclk";
  11. #clock-cells = <0>;
  12. clock-output-names = "usb480m_phy";
  13. assigned-clocks = <&cru SCLK_USB480M>;
  14. assigned-clock-parents = <&u2phy>;
  15. status = "okay";
  16. u2phy_otg: otg-port { #usb otg 的设备树配置
  17. #phy-cells = <0>;
  18. interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
  19. <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
  20. <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
  21. interrupt-names = "otg-bvalid", "otg-id",
  22. "linestate";
  23. status = "okay";
  24. };
  25. u2phy_host: host-port { #usb host 的设备树配置
  26. #phy-cells = <0>;
  27. interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
  28. interrupt-names = "linestate";
  29. status = "okay";
  30. };
  31. };
  32. };

 设备树 u2phy 对应 /sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c :

  1. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# ll
  2. total 0
  3. drwxr-xr-x 5 root root 0 Jan 1 00:53 ./
  4. drwxr-xr-x 4 root root 0 Jan 1 00:53 ../
  5. lrwxrwxrwx 1 root root 0 Jan 1 01:35 driver -> ../../../../bus/platform/drivers/rockchip-usb2phy/
  6. -rw-r--r-- 1 root root 4096 Jan 1 01:35 driver_override
  7. drwxr-xr-x 3 root root 0 Jan 1 00:53 extcon/
  8. -r--r--r-- 1 root root 4096 Jan 1 01:35 modalias
  9. lrwxrwxrwx 1 root root 0 Jan 1 01:35 of_node -> ../../../../firmware/devicetree/base/syscon@20008000/usb2-phy@17c/
  10. -rw-r--r-- 1 root root 4096 Jan 1 01:35 otg_mode
  11. drwxr-xr-x 4 root root 0 Jan 1 00:53 phy/
  12. drwxr-xr-x 2 root root 0 Jan 1 01:35 power/
  13. lrwxrwxrwx 1 root root 0 Jan 1 01:35 subsystem -> ../../../../bus/platform/
  14. -rw-r--r-- 1 root root 4096 Jan 1 00:53 uevent
  15. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat modalias
  16. of:Nusb2-phyT<NULL>Crockchip,rk3128-usb2phy
  17. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat otg_mode
  18. otg
  19. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat uevent
  20. DRIVER=rockchip-usb2phy
  21. OF_NAME=usb2-phy
  22. OF_FULLNAME=/syscon@20008000/usb2-phy@17c
  23. OF_COMPATIBLE_0=rockchip,rk3128-usb2phy
  24. OF_COMPATIBLE_N=1
  25. MODALIAS=of:Nusb2-phyT<NULL>Crockchip,rk3128-usb2phy
  26. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cd extcon/
  27. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon# ls
  28. extcon0
  29. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon# cd extcon0/
  30. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon/extcon0# ls
  31. cable.0 cable.1 cable.2 cable.3 cable.4 cable.5 cable.6 device name power state subsystem uevent
  32. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon/extcon0# cd ../..
  33. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# ls
  34. driver driver_override extcon modalias of_node otg_mode phy power subsystem uevent
  35. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cd phy/
  36. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# ls
  37. phy-20008000.syscon:usb2-phy@17c.0 phy-20008000.syscon:usb2-phy@17c.1
  38. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# cd phy-20008000.syscon\:usb2-phy@17c.0/
  39. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# ls
  40. device of_node power subsystem uevent
  41. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# cat uevent
  42. OF_NAME=otg-port
  43. OF_FULLNAME=/syscon@20008000/usb2-phy@17c/otg-port
  44. OF_COMPATIBLE_N=0
  45. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# cd ..
  46. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# cd phy-20008000.syscon\:usb2-phy@17c.1/
  47. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# ls
  48. device of_node power subsystem uevent
  49. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# cat uevent
  50. OF_NAME=host-port
  51. OF_FULLNAME=/syscon@20008000/usb2-phy@17c/host-port
  52. OF_COMPATIBLE_N=0
  53. root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1#

以 RK3128为例,其USB PHY 使用的是 USB2.0 PHY ,使用的是 Innosilicon 的IP ,对应的驱动文件为linux-4.4 \drivers\phy\rockchip\phy-rockchip-inno-usb2.c,对应的平台驱动为 struct platform_driver rockchip_usb2phy_driver:

  1. static struct platform_driver rockchip_usb2phy_driver = {
  2. .probe = rockchip_usb2phy_probe,
  3. .driver = {
  4. .name = "rockchip-usb2phy",
  5. .pm = ROCKCHIP_USB2PHY_DEV_PM,
  6. .of_match_table = rockchip_usb2phy_dt_match,
  7. /*
  8. static const struct of_device_id rockchip_usb2phy_dt_match[] = {
  9. { .compatible = "rockchip,rk3128-usb2phy", .data = &rk312x_phy_cfgs },
  10. { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs },
  11. { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
  12. {}
  13. };
  14. */
  15. },
  16. };
  17. module_platform_driver(rockchip_usb2phy_driver);
  18. #define module_platform_driver(__platform_driver) \
  19. module_driver(__platform_driver, platform_driver_register, \
  20. platform_driver_unregister)

RK3128 USB PHY 驱动 struct platform_driver rockchip_usb2phy_driver 与设备 /sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c 匹配时调用 probe = struct platform_driver rockchip_usb2phy_driverb.probe= rockchip_usb2phy_probe,其执行过程如下:

  1. static int rockchip_usb2phy_probe(struct platform_device *pdev)->
  2. struct rockchip_usb2phy *rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
  3. const struct of_device_id *match = of_match_device(dev->driver->of_match_table, dev);
  4. rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
  5. of_property_read_u32(np, "reg", &reg);
  6. rphy->dev = dev;
  7. phy_cfgs = match->data;
  8. rphy->chg_state = USB_CHG_STATE_UNDEFINED;
  9. rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
  10. rphy->edev_self = false;
  11. platform_set_drvdata(pdev, rphy);
  12. ret = rockchip_usb2phy_extcon_register(rphy)->
  13. struct extcon_dev *edev = devm_extcon_dev_allocate(rphy->dev, rockchip_usb2phy_extcon_cable);
  14. ret = devm_extcon_dev_register(rphy->dev, edev);
  15. rphy->edev = edev;
  16. struct rockchip_usb2phy *rphy->clk = of_clk_get_by_name(np, "phyclk");
  17. clk_prepare_enable(rphy->clk); //使用能 PHY 时钟
  18. ret = rphy->phy_cfg->phy_tuning(rphy);
  19. index = 0;
  20. for_each_available_child_of_node(np, child_np) {
  21. struct rockchip_usb2phy_port *rport = &rphy->ports[index];
  22. struct phy *phy;
  23. /* This driver aims to support both otg-port and host-port */
  24. if (of_node_cmp(child_np->name, "host-port") && of_node_cmp(child_np->name, "otg-port"))
  25. goto next_child;
  26. /*
  27. static const struct phy_ops rockchip_usb2phy_ops = {
  28. .init = rockchip_usb2phy_init,
  29. .exit = rockchip_usb2phy_exit,
  30. .power_on = rockchip_usb2phy_power_on,
  31. .power_off = rockchip_usb2phy_power_off,
  32. .set_mode = rockchip_usb2phy_set_mode,
  33. .owner = THIS_MODULE,
  34. };
  35. */
  36. phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); //创建 struct phy *phy
  37. ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
  38. struct phy *phy = phy_create(dev, node, ops)->
  39. struct phy *phy = kzalloc(sizeof(*phy), GFP_KERNEL);
  40. id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
  41. device_initialize(&phy->dev);
  42. //phy_class = class_create(THIS_MODULE, "phy"); //对应目录 /sys/class/phy
  43. phy->dev.class = phy_class;//对应目录 /sys/class/phy
  44. phy->dev.parent = dev;
  45. phy->dev.of_node = node ?: dev->of_node;
  46. phy->id = id;
  47. phy->ops = ops= struct phy_ops rockchip_usb2phy_ops
  48. //对应 /sys/class/phy/phy-20008000.syscon:usb2-phy@17c.0 和 /sys/class/phy/phy-20008000.syscon:usb2-phy@17c.1
  49. ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
  50. phy->pwr = regulator_get_optional(&phy->dev, "phy");
  51. ret = device_add(&phy->dev);
  52. return phy;
  53. rport->phy = phy;
  54. phy_set_drvdata(rport->phy, rport);
  55. /* initialize otg/host port separately : 对 usb otg 和 usb host 分开处理 */
  56. if (!of_node_cmp(child_np->name, "host-port"))
  57. {
  58. ret = rockchip_usb2phy_host_port_init(rphy, rport,child_np)->
  59. struct rockchip_usb2phy_port *rport->port_id = USB2PHY_PORT_HOST = 1;
  60. rport->low_power_en = of_property_read_bool(child_np, "rockchip,low-power-mode");
  61. INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
  62. rport->ls_irq = of_irq_get_byname(child_np, "linestate"); //中断
  63. ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
  64. rockchip_usb2phy_linestate_irq,
  65. IRQF_ONESHOT,
  66. "rockchip_usb2phy", rport);
  67. /*
  68. root@mxlos:~# cat /proc/interrupts
  69. CPU0 CPU1 CPU2 CPU3
  70. 187: 0 0 0 0 GIC 67 Level rockchip_usb2phy_bvalid
  71. 188: 0 0 0 0 GIC 84 Level rockchip_usb2phy
  72. 189: 0 0 0 0 GIC 83 Level rockchip_usb2phy_id
  73. 190: 1 0 0 0 GIC 85 Level rockchip_usb2phy
  74. root@mxlos:~#
  75. */
  76. ret = property_enable(base, &rport->port_cfg->phy_sus, true);
  77. }
  78. else
  79. {
  80. ret = rockchip_usb2phy_otg_port_init(rphy, rport,child_np)->
  81. struct rockchip_usb2phy_port *rport->port_id = USB2PHY_PORT_OTG = 0;
  82. rport->state = OTG_STATE_UNDEFINED;
  83. rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus");
  84. rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
  85. wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg");
  86. INIT_DELAYED_WORK(&rport->bypass_uart_work, rockchip_usb_bypass_uart_work);
  87. INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
  88. INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
  89. rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
  90. ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq,
  91. NULL,
  92. rockchip_usb2phy_bvalid_irq,
  93. IRQF_ONESHOT,
  94. "rockchip_usb2phy_bvalid",
  95. rport);
  96. rport->ls_irq = of_irq_get_byname(child_np, "linestate");
  97. ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
  98. rockchip_usb2phy_linestate_irq,
  99. IRQF_ONESHOT,
  100. "rockchip_usb2phy", rport);
  101. rport->id_irq = of_irq_get_byname(child_np, "otg-id");
  102. ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, &rport->event_nb);
  103. }
  104. next_child:
  105. /* to prevent out of boundary */
  106. if (++index >= rphy->phy_cfg->num_ports)
  107. break;
  108. }
  109. provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate)->
  110. __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
  111. struct phy_provider *phy_provider = __of_phy_provider_register(dev, owner, of_xlate)->
  112. struct phy_provider *phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
  113. phy_provider->dev = dev;
  114. phy_provider->owner = owner;
  115. phy_provider->of_xlate = of_xlate;
  116. list_add_tail(&phy_provider->list, &phy_provider_list);
  117. /*
  118. USB 主机控制器使用函数 usb_create_hcd() 和 usb_add_hcd() 创建并注册,
  119. 在函数usb_add_hcd() 中会调用函数phy_get() 遍历链表phy_provider_list 获取 USB PHY驱动中注册的 struct phy *phy
  120. err = usb_add_hcd(hcd, irq, IRQF_SHARED)->
  121. struct phy *phy = phy_get(hcd->self.controller, "usb");
  122. index = of_property_match_string(dev->of_node, "phy-names", string);
  123. struct phy *phy = _of_phy_get(dev->of_node, index)-> //获取 usb phy 驱动中注册的 struct phy
  124. ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", index, &args);
  125. struct phy_provider *phy_provider = of_phy_provider_lookup(args.np)->
  126. list_for_each_entry(phy_provider, &phy_provider_list, list)
  127. */
  128. /* Attributes */
  129. ret = sysfs_create_group(&dev->kobj, &usb2_phy_attr_group);
  130. ret = rockchip_usb2phy_clk480m_register(rphy);
  131. if (of_property_read_bool(np, "wakeup-source"))
  132. device_init_wakeup(rphy->dev, true);
  133. else
  134. device_init_wakeup(rphy->dev, false);

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/263534
推荐阅读
相关标签
  

闽ICP备14008679号