Linux USB 驱动架构
Linux USB 协议栈是一个分层的架构,如下图 5-1 所示,左边是 USB Device 驱动,右边是 USB Host 驱动,最底层是 Rockchip 系列芯片不同 USB 控制器和 PHY 的驱动。
Linux USB 驱动架构
USB PHY 驱动开发
USB 2.0 PHY 驱动开发
Rockchip 系列芯片,主要使用两种 USB 2.0 PHY IP:Innosilicon IP 和 Synopsis IP。这两种 IP 的硬件设计不同,所以需要独立的 USB PHY 驱动。同时,使用同一种 USB 2.0 PHY IP 的系列芯片,复用同一个USB 2.0 PHY 驱动,而不是每种芯片都有一个专用的 USB 2.0 PHY 驱动。
- USB 2.0 PHY 驱动代码路径
Innosilicon USB 2.0 PHY 驱动代码
drivers/phy/phy-rockchip-inno-usb2.c
Synopsis USB 2.0 PHY 驱动代码 (用于 RK3188/RK3288)
drivers/phy/rockchip/phy-rockchip-usb.c
考虑到目前大部分的 Rockchip SoC (除了 RK3188/RK3288外) 都使用 Innosilicon IP,所以本章节重点介
绍 Innosilicon IP。
- Innosilicon USB 2.0 PHY IP 特性
完全符合 USB 2.0 规范
支持 480Mbps/12Mbps/1.5Mbps 数据传输
支持 USB 2.0 规范定义的所有测试模式
支持 1 个 PHY 1 个 port,或者 1 个 PHY 2 个 ports (1 个 OTG port 和 1 个 Host port)
OTG Port 支持 Device/Host 两种模式
支持 Battery Charge 1.2 规范
- USB 2.0 PHY 重要结构体
USB 2.0 PHY 驱动中,有一个重要的结构体 rockchip_usb2phy_cfg,主要作用是操作 USB PHY 相关的寄存器,在添加一个新芯片的 Innosilicon USB 2.0 PHY 的支持时,主要的工作就是增加芯片对应的rkxxxx_phy_cfgs 结构体。rockchip_usb2phy_cfg 结构体成员说明如下:
reg:USB PHY 位于 GRF 模块中的偏移地址,该地址与 DTS USB 2.0 PHY 对应的 reg 地址应一致,
作用是匹配 DTS PHY 和 驱动中的 PHY 的配置;
num_ports:定义 USB PHY 支持的 port 数量。比如,支持 OTG port 和 Host port,则 num_ports 为
2;phy_tuning:用于 USB PHY 信号的调整,比如:提高预加重,提高信号幅值等;
clkout_ctl:控制 USB PHY 480MHz 的输出时钟;
port_cfgs:USB PHY port 的寄存器配置;
chg_det:充电检测相关的寄存器配置;
- USB 2.0 PHY 状态机
USB 2.0 PHY 有三个 work,分别用于处理不同的状态机:
rockchip_chg_detect_work:用于 OTG port Device mode 的充电检测功能;
rockchip_usb2phy_otg_sm_work:用于 OTG port 的连接状态检测,以及控制 PHY 进入/退出
suspend;rockchip_usb2phy_sm_work:用于 Host port 的连接状态检测,以及控制 PHY 进入/退出 suspend;驱动代码中,在关键的地方都有加了 dev_dbg log,可以方便查看设备连接和断开过程中的状态机轮转。
- USB 2.0 PHY 驱动开发实例
以 RK3399 USB 2.0 PHY 驱动开发为例。
RK3399 支持两个独立的 USB 2.0 PHY。并且,每个 PHY 都包含两个 port:OTG port 和 Host port。其中,OTG port 用于 USB 3.0 OTG controller 的USB2 部分,与 Type-C USB 3.0 PHY 组成完整的 Type-C 功
能。Host port 用于USB 2.0 Host 控制器。具体的 rk3399_phy_cfgs 结构体代码如下:
其中,port_cfgs 中的寄存器,主要用 PHY suspend mode 的控制、VBUS 电平状态的检测、OTG ID 电平
状态的检测、DP/DM 线上电平状态的检测等。每个成员的具体功能说明,请参考驱动中结构体成员的注
释。
static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
{
.reg = 0xe450,
.num_ports = 2,
.phy_tuning = rk3399_usb2phy_tuning,
.clkout_ctl = { 0xe450, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0xe454, 8, 0, 0x052, 0x1d1 },
.bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 },
.bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 },
.bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
.bypass_dm_en = { 0xe450, 2, 2, 0, 1 },
.bypass_sel = { 0xe450, 3, 3, 0, 1 },
.idfall_det_en = { 0xe3c0, 5, 5, 0, 1 },
.idfall_det_st = { 0xe3e0, 5, 5, 0, 1 },
.idfall_det_clr = { 0xe3d0, 5, 5, 0, 1 },
.idrise_det_en = { 0xe3c0, 4, 4, 0, 1 },
.idrise_det_st = { 0xe3e0, 4, 4, 0, 1 },
.idrise_det_clr = { 0xe3d0, 4, 4, 0, 1 },
.ls_det_en = { 0xe3c0, 2, 2, 0, 1 },
.ls_det_st = { 0xe3e0, 2, 2, 0, 1 },
.ls_det_clr = { 0xe3d0, 2, 2, 0, 1 },
.utmi_avalid = { 0xe2ac, 7, 7, 0, 1 },
.utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 },
.utmi_iddig = { 0xe2ac, 8, 8, 0, 1 },
.utmi_ls = { 0xe2ac, 14, 13, 0, 1 },
.vbus_det_en = { 0x449c, 15, 15, 1, 0 },
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0xe458, 1, 0, 0x2, 0x1 },
.ls_det_en = { 0xe3c0, 6, 6, 0, 1 },
.ls_det_st = { 0xe3e0, 6, 6, 0, 1 },
.ls_det_clr = { 0xe3d0, 6, 6, 0, 1 },
.utmi_ls = { 0xe2ac, 22, 21, 0, 1 },
.utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 }
}
},
.chg_det = {
.opmode = { 0xe454, 3, 0, 5, 1 },
.cp_det = { 0xe2ac, 2, 2, 0, 1 },
.dcp_det = { 0xe2ac, 1, 1, 0, 1 },
.dp_det = { 0xe2ac, 0, 0, 0, 1 },
.idm_sink_en = { 0xe450, 8, 8, 0, 1 },
.idp_sink_en = { 0xe450, 7, 7, 0, 1 },
.idp_src_en = { 0xe450, 9, 9, 0, 1 },
.rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 },
.vdm_src_en = { 0xe450, 12, 12, 0, 1 },
- USB 2.0 PHY 调试接口
.vdp_src_en = { 0xe450, 11, 11, 0, 1 },
},
},
{
.reg = 0xe460,
.num_ports = 2,
.phy_tuning = rk3399_usb2phy_tuning,
.clkout_ctl = { 0xe460, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0xe464, 8, 0, 0x052, 0x1d1 },
.bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 },
.bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 },
.bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
.idfall_det_en = { 0xe3c0, 10, 10, 0, 1 },
.idfall_det_st = { 0xe3e0, 10, 10, 0, 1 },
.idfall_det_clr = { 0xe3d0, 10, 10, 0, 1 },
.idrise_det_en = { 0xe3c0, 9, 9, 0, 1 },
.idrise_det_st = { 0xe3e0, 9, 9, 0, 1 },
.idrise_det_clr = { 0xe3d0, 9, 9, 0, 1 },
.ls_det_en = { 0xe3c0, 7, 7, 0, 1 },
.ls_det_st = { 0xe3e0, 7, 7, 0, 1 },
.ls_det_clr = { 0xe3d0, 7, 7, 0, 1 },
.utmi_avalid = { 0xe2ac, 10, 10, 0, 1 },
.utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 },
.utmi_iddig = { 0xe2ac, 11, 11, 0, 1 },
.utmi_ls = { 0xe2ac, 18, 17, 0, 1 },
.vbus_det_en = { 0x451c, 15, 15, 1, 0 },
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0xe468, 1, 0, 0x2, 0x1 },
.ls_det_en = { 0xe3c0, 11, 11, 0, 1 },
.ls_det_st = { 0xe3e0, 11, 11, 0, 1 },
.ls_det_clr = { 0xe3d0, 11, 11, 0, 1 },
.utmi_ls = { 0xe2ac, 26, 25, 0, 1 },
.utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 }
}
},
.chg_det = {
.opmode = { 0xe464, 3, 0, 5, 1 },
.cp_det = { 0xe2ac, 5, 5, 0, 1 },
.dcp_det = { 0xe2ac, 4, 4, 0, 1 },
.dp_det = { 0xe2ac, 3, 3, 0, 1 },
.idm_sink_en = { 0xe460, 8, 8, 0, 1 },
.idp_sink_en = { 0xe460, 7, 7, 0, 1 },
.idp_src_en = { 0xe460, 9, 9, 0, 1 },
.rdm_pdwn_en = { 0xe460, 10, 10, 0, 1 },
.vdm_src_en = { 0xe460, 12, 12, 0, 1 },
.vdp_src_en = { 0xe460, 11, 11, 0, 1 },
},
},
{ /* sentinel */ }
};
"otg_mode" 节点用于软件强制切换 OTG Device/Host 模式,并且不受 OTG ID 电平状态的影响。
举例:
强制切换为 Host 模式
echo host > /sys/devices/platform/[u2phy dev name]/otg_mode
强制切换为 Device 模式
echo peripheral > /sys/devices/platform/[u2phy dev name]/otg_mode
强制切换为 OTG 模式
echo otg > /sys/devices/platform/[u2phy dev name]/otg_mode
同时,该节点仍然兼容 Linux-3.10 及更早以前的旧命令,即:
强制切换为 Host 模式
echo 1 > /sys/devices/platform/[u2phy dev name]/otg_mode
强制切换为 Device 模式
echo 2 > /sys/devices/platform/[u2phy dev name]/otg_mode
强制切换为 OTG 模式
echo 0 > /sys/devices/platform/[u2phy dev name]/otg_mode
Note:
-
USB 2.0 PHY 完整路径中 [u2phy dev name] 需要修改为芯片对应的具体 PHY 节点名称。
-
RV1126/RV1109 USB OTG 建议按照如下方法切换模式,可以提高各种应用场景(如:保持 USB 连
接到 PC,然后使用命令交替切换Host/Device模式)的切换稳定性。
RV1126/RV1109 USB OTG 强制切换为 Host 模式:
echo disconnect > /sys/class/udc/ffd00000.dwc3/soft_connect (断开 usb device 的连
接)
echo host > /sys/devices/platform/ff4c0000.usb2-phy/otg_mode
RV1126/RV1109 USB OTG 强制切换为 Device 模式:
echo peripheral > /sys/devices/platform/ff4c0000.usb2-phy/otg_mode
echo connect > /sys/class/udc/ffd00000.dwc3/soft_connect (使能 usb device 的连接)
RV1126/RV1109 USB OTG 强制切换为 OTG 模式:
echo otg > /sys/devices/platform/ff4c0000.usb2-phy/otg_mode
echo connect > /sys/class/udc/ffd00000.dwc3/soft_connect (使能 usb device 的连接)
USB 3.0 PHY 驱动开发
Rockchip 系列芯片,主要使用三种 USB 3.0 PHY IP:Type-C PHY IP,Innosilicon USB 3.0 PHY IP 和
Innosilicon USB 3.0 CombPhy IP。这三种 IP 的硬件设计不同,所以需要独立的 USB PHY 驱动。
/sys/devices/platform/[u2phy dev name] # ls
driver extcon of_node phy subsystem
driver_override modalias otg_mode power uevent
需要注意的是,这三种 USB 3.0 PHY IP 都只支持 SuperSpeed,所以要与 USB 2.0 PHY (支持
HighSpeed/FullSpeed/LowSpeed) 一起配合使用,才能完整支持 USB 3.0 协议。
下面分别对这三种不同的 USB 3.0 PHY IP 驱动进行简要的描述。
- Type-C PHY 驱动开发
Type-C USB 3.0 PHY 驱动代码路径
drivers/phy/rockchip/phy-rockchip-typec.c
Type-C USB 3.0 PHY 驱动实例
以 RK3399 Type-C PHY 为例。
RK3399 Type-C PHY 是一个CombPhy,包含一个 USB 3.0 SuperSpeed PHY 和一个 DisplayPort Transmit
PHY。Type-C PHY 的特性,请参考USB 3.0 Type-C PHY
在 Type-C PHY 驱动的 probe 函数中,会分别创建 "dp-port" 的 rockchip_dp_phy_ops 和 "usb3-port" 的
rockchip_usb3_phy_ops,也即 USB 3.0 PHY 和 DP PHY 的操作函数 (如:power_on 和 power_off) 是独立
的,互不影响。Type-C PHY 驱动可以支持如下 4 种工作模式:
USB 3.0 only:只工作在 USB 3.0 模式,比如连接 Type-C to Type-A USB 3.0 的转接线;
DP only:只工作在 DP 模式,比如连接 DP 线缆;
USB 3.0 + DP 2 lanes:同时支持 USB 3.0 和 DP 2 lanes 工作,比如连接 Type-C dongle;
USB 2.0 + DP 4 lanes:同时支持 USB 2.0 和 DP 4 lanes 工作,比如连接 Type-C VR 头盔;
为了支持上述 4 种工作模式,Type-C PHY 需要结合 CC 芯片 (推荐使用 FUSB302 芯片) 来检测插入的
Type-C 线缆的类型。CC 芯片使用 extcon 通知机制发消息给 Type-C PHY。
重要函数:
rockchip_usb3_phy_power_on():获取 PHY 的工作模式,并初始化 PHY;
rockchip_usb3_phy_power_off():反初始化 PHY,设置 PHY 处于 reset 状态,并关闭时钟;
tcphy_cfg_usb3_to_usb2_only():关闭 USB 3.0 逻辑模块,强制工作在 USB 2.0 only 模式;
tcphy_cfg_usb3_pll():配置 USB 3.0 相关的 PLL;
- Innosilicon USB 3.0 PHY 驱动开发
Innosilicon USB 3.0 PHY 驱动代码路径
drivers/phy/rockchip/phy-rockchip-inno-usb3.c
Innosilicon USB 3.0 PHY 驱动实例
以 RK3328 USB 3.0 PHY 为例。
RK33228 USB 3.0 PHY 由 USB 3.0 PHY 和 USB 2.0 PHY 两部分组成。
Innosilicon USB 3.0 PHY 硬件特性:
支持 5.0Gb/s 传输速率
支持 8位、16位或32位并行接口传输和接收USB超高速数据
允许集成高速组件 (USB 2.0 PHY) 作为独立的功能模块
从USB超高速总线上的串行流恢复数据和时钟
支持符合 USB 3.0 协议电气规范的 compliance test pattern
支持8b/10b 编解码及错误指示
无法检测外设断开的状态
Innosilicon USB 3.0 PHY 驱动有两个特别之处:
同时实现了 USB 2.0 PHY 和 USB 3.0 PHY 的操作函数(虽然从硬件原理上,这两个 PHY 是独立
的),这与其他 USB 3.0 PHY 不同。驱动中,使用 "U3PHY_TYPE_UTMI" 和
"U3PHY_TYPE_PIPE" 分别作为 USB 2.0 PHY 和 USB 3.0 PHY的索引,具体请参考驱动代码中的如
下函数:
rockchip_u3phy_port_init():对 USB 3.0 的 USB 2.0 port 和 USB 3.0 port 作初始化;
rockchip_u3phy_power_on():打开时钟,并且配置 USB 2.0 PHY 为 Normal mode,配置 USB 3.0
PHY 进入 P0 state;
rockchip_u3phy_power_off():配置 USB 2.0 PHY 为 Suspend mode,配置 USB 3.0 PHY 为 进入 P3
state,并且关闭时钟,以节省 PHY 的整体功耗。
为了解决 USB 3.0 PHY 无法检测外设断开的状态,增加了特殊的函数,这与其他 USB 3.0 PHY 不
同。具体请参考驱动中的如下函数:
rockchip_u3phy_on_disconnect():当 USB HUB core 驱动通过检测 linkstate 状态的变化,判断外设已
经断开时,会通过 PHY 注册的 notifier,调用该 disconnect 函数,从而完成soft disconnect的一系列
操作;
rockchip_u3phy_on_shutdown():该函数提供给 DWC3 控制器驱动调用,作用是,在 soft disconnect
流程中,对 USB3 PHY 进行复位操作;
rockchip_u3phy_on_init():该函数提供给 DWC3 控制器驱动调用,作用是,在 soft disconnect 流程
的最后,释放 USB 3.0 PHY 的复位信号;
Innosilicon USB 3.0 PHY 支持通过软件命令,强制 PHY 只工作于 USB 2.0 only的模式:
配置 USB 3.0 PHY 为 USB 2.0 only 模式的命令
echo u2 > /sys/kernel/debug/[phy name]/u3phy_mode
配置 USB 3.0 PHY 同时支持 USB 3.0/2.0 模式的命令 (驱动初始化后,默认支持)
echo u3 > /sys/kernel/debug/[phy name]/u3phy_mode
Note: [phy name] 需要修改为芯片对应的具体 PHY 节点名。
- Innosilicon USB 3.0 CombPhy 驱动开发
Innosilicon USB 3.0 CombPhy 驱动代码路径
drivers/phy/rockchip/phy-rockchip-inno-combphy.c
Innosilicon USB 3.0 CombPhy 驱动实例
以 RK1808 USB 3.0 CombPhy 为例。
RK1808 USB 3.0 CombPhy 包含了 USB 3.0 SuperSpeed PHY 和 PCIe PHY,并且 USB 3.0 PHY 和 PCIe
PHY无法同时工作。PHY 驱动中,会注册 PHY 的接口函数 rockchip_combphy_xlate(),提供给 USB 3.0控制器驱动和 PCIe 驱动调用,以配置 USB 3.0 CombPHY 工作在控制器要求的类型。
如果要使用 USB 3.0,则配置 USB 3.0 控制器 DTS 的 phys 属性为
如果要使用 PCIe,则配置 PCIe 控制器 DTS 的 phys 属性为
phys = <&u2phy_otg>, <&combphy PHY_TYPE_USB3>;
phy-names = "usb2-phy", "usb3-phy";RK1808 USB 3.0 CombPhy 的 USB 3.0 SuperSpeed PHY 模块,只支持 SuperSpeed,它需要配合 USB 2.0PHY OTG port (支持 HighSpeed/FullSpeed/LowSpeed) 一起使用,才能完整支持 USB 3.0/2.0/1.1/1.0 协议。Innosilicon USB 3.0 CombPhy PHY 硬件特性:
支持 5.0Gb/s 传输速率支持 8位、16位或32位并行接口传输和接收USB超高速数据
允许集成高速组件 (USB 2.0 PHY) 作为独立的功能模块
从USB超高速总线上的串行流恢复数据和时钟
支持符合 USB 3.0 协议电气规范的 compliance test pattern
支持8b/10b 编解码及错误指示Innosilicon USB 3.0 CombPhy PHY 驱动代码中,USB 3.0 PHY 和 PCIe PHY 复用 phy_ops 函数:rockchip_combphy_init():打开 PHY 参考时钟,设置 PHY 类型,初始化 PHY 寄存器;rockchip_combphy_exit():关闭 PHY 参考时钟;
rockchip_combphy_power_on():power on USB 3.0 PHY 的逻辑模块,并配置 PHY 进入 P0 state;
rockchip_combphy_power_off():power off USB 3.0 PHY 的逻辑模块,并配置 PHY 进入 P3 state;
Innosilicon USB 3.0 CombPhy PHY 支持通过软件命令,强制 PHY 只工作于 USB 2.0 only的模式:
Note:
[u3phy dev name] 和 [u2phy dev name] 需要修改为芯片对应的具体 PHY 节点名称;
USB 的 default mode,由 DWC3 控制器 DTS 中的属性 "dr_mode" 决定;
在切换 USB 3.0 和 USB 2.0 only 工作模式时,需要设置 otg_mode 节点的原因是为了重新初始化
xHCI 控制器,否则切换 PHY 的工作模式,会导致控制器工作异常;