赞
踩
关键词:linux、驱动、usb、phy
同以太网类似,USB芯片也分为Host Controller部分(主机控制器/设备控制器)和PHY部分(收发器) 两大部分组成。
USB 的 PHY 与以太网的 PHY 类似,用于数字信号和电气信号的转换。
主机控制器Controller部分主要实现USB的协议和控制,内部逻辑主要有 MAC层,CSR层,FIFO层等。
USB PHY负责最底层的信号转换,作用类似于网口的PHY。主要实现 并转串的功能,把控制器通过 UTMI或ULPI总线传递过来的并行数据 转换为串行数据,再通过差分数据线输出到USB接口。或反之。
总之USB芯片内部实现的功能就是接受软件的控制,进而从内存搬运数据并按照USB协议进行数据打包,并串转换后输出到芯片外部。或者从芯片外部接收差分数据信号,串并转换后进行数据打包并写到内存中。
以Controller 和 PHY 都被封装到SOC为例,如图
一般来说如果芯片的usb phy封装在芯片内,基本采用UTMI+的接口。不封装到芯片内的一般采用ULPI接口,这样可以降低pin的数量。
如下图是嵌入式设备上的USB系统,其中SOC内嵌了USB控制器,USB 收发器(PHY)没有封装到芯片内。该控制器支持4条总线和3中操作模式。
USB 主机控制器分为以下几种:
主机控制器内嵌了一个叫 根集线器 的硬件。很重要!!!!
USB集线器又称为USB Hub,用于拓展计算机USB接口。计算机主板上对外往往提供多个USB接口,这些接口往往都是通过主板上的USB集线器芯片来拓展出来的。在USB总线通信协议中,通过设备描述符和接口描述符来判断该USB是否为USB集线器。
USB 根集线器(USB Root Hub)指的是直接连接到USB主控制器上的USB Hub。USB根集线器供电与USB主控器供电来源相同。USB总线中只有USB主机和USB集线器可以向外部供电。
以 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 设备树配置如下:
- grf: syscon@20008000 { //System Controller Registers R/W driver,syscon参见内核文档:Documentation\devicetree\bindings\mfd\syscon.txt
- compatible = "rockchip,rk3128-grf", "syscon", "simple-mfd";
- reg = <0x20008000 0x1000>;
- #address-cells = <1>;
- #size-cells = <1>;
- u2phy: usb2-phy@17c {
- compatible = "rockchip,rk3128-usb2phy";
- reg = <0x017c 0x0c>;
- clocks = <&cru SCLK_OTGPHY0>;
- clock-names = "phyclk";
- #clock-cells = <0>;
- clock-output-names = "usb480m_phy";
- assigned-clocks = <&cru SCLK_USB480M>;
- assigned-clock-parents = <&u2phy>;
- status = "okay";
-
- u2phy_otg: otg-port { #usb otg 的设备树配置
- #phy-cells = <0>;
- interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "otg-bvalid", "otg-id",
- "linestate";
- status = "okay";
- };
-
- u2phy_host: host-port { #usb host 的设备树配置
- #phy-cells = <0>;
- interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "linestate";
- status = "okay";
- };
- };
- };
设备树 u2phy 对应 /sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c :
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# ll
- total 0
- drwxr-xr-x 5 root root 0 Jan 1 00:53 ./
- drwxr-xr-x 4 root root 0 Jan 1 00:53 ../
- lrwxrwxrwx 1 root root 0 Jan 1 01:35 driver -> ../../../../bus/platform/drivers/rockchip-usb2phy/
- -rw-r--r-- 1 root root 4096 Jan 1 01:35 driver_override
- drwxr-xr-x 3 root root 0 Jan 1 00:53 extcon/
- -r--r--r-- 1 root root 4096 Jan 1 01:35 modalias
- lrwxrwxrwx 1 root root 0 Jan 1 01:35 of_node -> ../../../../firmware/devicetree/base/syscon@20008000/usb2-phy@17c/
- -rw-r--r-- 1 root root 4096 Jan 1 01:35 otg_mode
- drwxr-xr-x 4 root root 0 Jan 1 00:53 phy/
- drwxr-xr-x 2 root root 0 Jan 1 01:35 power/
- lrwxrwxrwx 1 root root 0 Jan 1 01:35 subsystem -> ../../../../bus/platform/
- -rw-r--r-- 1 root root 4096 Jan 1 00:53 uevent
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat modalias
- of:Nusb2-phyT<NULL>Crockchip,rk3128-usb2phy
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat otg_mode
- otg
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat uevent
- DRIVER=rockchip-usb2phy
- OF_NAME=usb2-phy
- OF_FULLNAME=/syscon@20008000/usb2-phy@17c
- OF_COMPATIBLE_0=rockchip,rk3128-usb2phy
- OF_COMPATIBLE_N=1
- MODALIAS=of:Nusb2-phyT<NULL>Crockchip,rk3128-usb2phy
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cd extcon/
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon# ls
- extcon0
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon# cd extcon0/
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon/extcon0# ls
- cable.0 cable.1 cable.2 cable.3 cable.4 cable.5 cable.6 device name power state subsystem uevent
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon/extcon0# cd ../..
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# ls
- driver driver_override extcon modalias of_node otg_mode phy power subsystem uevent
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cd phy/
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# ls
- phy-20008000.syscon:usb2-phy@17c.0 phy-20008000.syscon:usb2-phy@17c.1
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# cd phy-20008000.syscon\:usb2-phy@17c.0/
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# ls
- device of_node power subsystem uevent
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# cat uevent
- OF_NAME=otg-port
- OF_FULLNAME=/syscon@20008000/usb2-phy@17c/otg-port
- OF_COMPATIBLE_N=0
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# cd ..
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# cd phy-20008000.syscon\:usb2-phy@17c.1/
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# ls
- device of_node power subsystem uevent
-
- root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# cat uevent
- OF_NAME=host-port
- OF_FULLNAME=/syscon@20008000/usb2-phy@17c/host-port
- OF_COMPATIBLE_N=0
-
- 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:
- static struct platform_driver rockchip_usb2phy_driver = {
- .probe = rockchip_usb2phy_probe,
- .driver = {
- .name = "rockchip-usb2phy",
- .pm = ROCKCHIP_USB2PHY_DEV_PM,
- .of_match_table = rockchip_usb2phy_dt_match,
- /*
- static const struct of_device_id rockchip_usb2phy_dt_match[] = {
- { .compatible = "rockchip,rk3128-usb2phy", .data = &rk312x_phy_cfgs },
- { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs },
- { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
- {}
- };
- */
- },
- };
- module_platform_driver(rockchip_usb2phy_driver);
- #define module_platform_driver(__platform_driver) \
- module_driver(__platform_driver, platform_driver_register, \
- 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,其执行过程如下:
- static int rockchip_usb2phy_probe(struct platform_device *pdev)->
- struct rockchip_usb2phy *rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
- const struct of_device_id *match = of_match_device(dev->driver->of_match_table, dev);
- rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
- of_property_read_u32(np, "reg", ®);
- rphy->dev = dev;
- phy_cfgs = match->data;
- rphy->chg_state = USB_CHG_STATE_UNDEFINED;
- rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
- rphy->edev_self = false;
- platform_set_drvdata(pdev, rphy);
- ret = rockchip_usb2phy_extcon_register(rphy)->
- struct extcon_dev *edev = devm_extcon_dev_allocate(rphy->dev, rockchip_usb2phy_extcon_cable);
- ret = devm_extcon_dev_register(rphy->dev, edev);
- rphy->edev = edev;
- struct rockchip_usb2phy *rphy->clk = of_clk_get_by_name(np, "phyclk");
- clk_prepare_enable(rphy->clk); //使用能 PHY 时钟
- ret = rphy->phy_cfg->phy_tuning(rphy);
- index = 0;
- for_each_available_child_of_node(np, child_np) {
- struct rockchip_usb2phy_port *rport = &rphy->ports[index];
- struct phy *phy;
-
- /* This driver aims to support both otg-port and host-port */
- if (of_node_cmp(child_np->name, "host-port") && of_node_cmp(child_np->name, "otg-port"))
- goto next_child;
- /*
- static const struct phy_ops rockchip_usb2phy_ops = {
- .init = rockchip_usb2phy_init,
- .exit = rockchip_usb2phy_exit,
- .power_on = rockchip_usb2phy_power_on,
- .power_off = rockchip_usb2phy_power_off,
- .set_mode = rockchip_usb2phy_set_mode,
- .owner = THIS_MODULE,
- };
- */
- phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); //创建 struct phy *phy
- ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
- struct phy *phy = phy_create(dev, node, ops)->
- struct phy *phy = kzalloc(sizeof(*phy), GFP_KERNEL);
- id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
- device_initialize(&phy->dev);
- //phy_class = class_create(THIS_MODULE, "phy"); //对应目录 /sys/class/phy
- phy->dev.class = phy_class;//对应目录 /sys/class/phy
- phy->dev.parent = dev;
- phy->dev.of_node = node ?: dev->of_node;
- phy->id = id;
- phy->ops = ops= struct phy_ops rockchip_usb2phy_ops
- //对应 /sys/class/phy/phy-20008000.syscon:usb2-phy@17c.0 和 /sys/class/phy/phy-20008000.syscon:usb2-phy@17c.1
- ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
- phy->pwr = regulator_get_optional(&phy->dev, "phy");
- ret = device_add(&phy->dev);
- return phy;
- rport->phy = phy;
- phy_set_drvdata(rport->phy, rport);
- /* initialize otg/host port separately : 对 usb otg 和 usb host 分开处理 */
- if (!of_node_cmp(child_np->name, "host-port"))
- {
- ret = rockchip_usb2phy_host_port_init(rphy, rport,child_np)->
- struct rockchip_usb2phy_port *rport->port_id = USB2PHY_PORT_HOST = 1;
- rport->low_power_en = of_property_read_bool(child_np, "rockchip,low-power-mode");
- INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
- rport->ls_irq = of_irq_get_byname(child_np, "linestate"); //中断
- ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
- rockchip_usb2phy_linestate_irq,
- IRQF_ONESHOT,
- "rockchip_usb2phy", rport);
- /*
- root@mxlos:~# cat /proc/interrupts
- CPU0 CPU1 CPU2 CPU3
- 187: 0 0 0 0 GIC 67 Level rockchip_usb2phy_bvalid
- 188: 0 0 0 0 GIC 84 Level rockchip_usb2phy
- 189: 0 0 0 0 GIC 83 Level rockchip_usb2phy_id
- 190: 1 0 0 0 GIC 85 Level rockchip_usb2phy
- root@mxlos:~#
- */
- ret = property_enable(base, &rport->port_cfg->phy_sus, true);
- }
- else
- {
- ret = rockchip_usb2phy_otg_port_init(rphy, rport,child_np)->
- struct rockchip_usb2phy_port *rport->port_id = USB2PHY_PORT_OTG = 0;
- rport->state = OTG_STATE_UNDEFINED;
- rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus");
- rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
- wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg");
- INIT_DELAYED_WORK(&rport->bypass_uart_work, rockchip_usb_bypass_uart_work);
- INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
- INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
- rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
- ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq,
- NULL,
- rockchip_usb2phy_bvalid_irq,
- IRQF_ONESHOT,
- "rockchip_usb2phy_bvalid",
- rport);
- rport->ls_irq = of_irq_get_byname(child_np, "linestate");
- ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
- rockchip_usb2phy_linestate_irq,
- IRQF_ONESHOT,
- "rockchip_usb2phy", rport);
- rport->id_irq = of_irq_get_byname(child_np, "otg-id");
- ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, &rport->event_nb);
- }
- next_child:
- /* to prevent out of boundary */
- if (++index >= rphy->phy_cfg->num_ports)
- break;
- }
- provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate)->
- __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
- struct phy_provider *phy_provider = __of_phy_provider_register(dev, owner, of_xlate)->
- struct phy_provider *phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
- phy_provider->dev = dev;
- phy_provider->owner = owner;
- phy_provider->of_xlate = of_xlate;
- list_add_tail(&phy_provider->list, &phy_provider_list);
- /*
- USB 主机控制器使用函数 usb_create_hcd() 和 usb_add_hcd() 创建并注册,
- 在函数usb_add_hcd() 中会调用函数phy_get() 遍历链表phy_provider_list 获取 USB PHY驱动中注册的 struct phy *phy
- err = usb_add_hcd(hcd, irq, IRQF_SHARED)->
- struct phy *phy = phy_get(hcd->self.controller, "usb");
- index = of_property_match_string(dev->of_node, "phy-names", string);
- struct phy *phy = _of_phy_get(dev->of_node, index)-> //获取 usb phy 驱动中注册的 struct phy
- ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", index, &args);
- struct phy_provider *phy_provider = of_phy_provider_lookup(args.np)->
- list_for_each_entry(phy_provider, &phy_provider_list, list)
- */
- /* Attributes */
- ret = sysfs_create_group(&dev->kobj, &usb2_phy_attr_group);
- ret = rockchip_usb2phy_clk480m_register(rphy);
- if (of_property_read_bool(np, "wakeup-source"))
- device_init_wakeup(rphy->dev, true);
- else
- device_init_wakeup(rphy->dev, false);
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。