UFS Link Startup 介绍
一. UFS Link Startup简介
1. UFS Link Startup是UFS Host和UFS Device之间的链路连接,连接成功之后,才可以进行UFS操作,比如UFS读写,UFS Power Mode Change, UFS RPMB读写等
2. UFS Link 的相关概念
Data Lane: A physical interconnect between two UniPorts which
carries data. A Link can consist of 1, 2, 3
or 4 Data Lanes per direction. A Data Lane in UniPro is used
unidirectionally.
A Lane with embedded clocking information is also considered a
Data Lane. Data Link (DL) Layer (L2): A Protocol Layer.
The Data Link Layer provides the functional and procedural
means to transfer data between two adjacent L2 instances.
The Data Link Layer also detects, and possibly corrects,
errors that may occur in the Physical Layer. In addition,
the Data Link Layer maintains Flow Control between the
L2 entities
Link: A bidirectional interconnection between two Devices
or Switches.
Link Startup Sequence: The Link Startup Sequence is an L1.5
handshake to establish initial Link
communication between two directly attached UniPorts.
Logical Lane Number (M-PHY): A number assigned by a Device to
outbound Data Lanes after the Link
Startup Sequence. It is used for L1.5 Lane mapping whereby only
usable Physical Lanes are assigned a
Logical Lane Number.
二. UFS Link Startup概述
Link Startup Initiation from Both Ends
在Link Startup Sequence过程中,链路的收发器(transceivers)会被初始化为默认配置。随后,连接的 M-PHY 通道(Lanes)会被识别并枚举,以生成物理通道(physical Lanes)与逻辑通道(logical Lanes)之间的映射关系。最后,对等的 PA 层(Peer PA Layers)会相互交换能力信息(capability information)。
Link Startup Sequence主要包括下面的过程:
1 . 初始化阶段
PA层会通过发送PA_LM_LINKSTARTUP.req 触发 Link Startup Sequence,主要做下面的事情:
(1) 所有可用的M-TX都会被请求退出M-PHY HIBERN8 state
(2) 应在所有 TX 通道(TX Lanes)上发出 LINE-RESET,以重置任何配置错误的基本 MCs(主控控制器)和对端 M-RXs(接收器)
LINE-RESET 的作用:
(1)LINE-RESET 是一种重置信号,用于清除通道上的错误状态或错误配置。
(2)它可以重置发送端(TX)和接收端(RX)的逻辑状态,确保通道恢复到初始状态。
LINE-RESET 的应用场景:
(1)当检测到通道配置错误或通信异常时,系统会触发 LINE-RESET。
(2)该信号会发送到所有 TX 通道,以确保所有相关组件(如 MCs 和 M-RXs)都被重置。
LINE-RESET的目标组件:
(1)基本 MCs(主控控制器):负责管理通道的配置和状态。
(2)对端 M-RXs(接收器):接收来自发送端的数据信号。
(3)这些组件在错误状态下可能导致通信失败,因此需要通过 LINE-RESET 进行重置
LINE-RESET 重置的意义:
(1)通过重置,系统可以清除错误状态,重新初始化通道配置。
(2)这有助于恢复通信的稳定性和可靠性
(3)此后,所有可用的M-TX都处于PWM-G1模式,本地(Local )PA层应进入阶段0。本地(Local)的M-RX处于HIBERN8状态,需要来自对等端的M-LANE-HIBERN8Exit信号,接着是M-CTRL-LINE-RESET信号,才能进入PWM-G1模式.
(4) 在M-RX上接收到的LINE-RESET信号不应中断链路启动过程。PA层应被配置为在PWM-G1模式下监听所有可用通道上的TRG_UPR0信号。
TRG_UPR0 = MK1 + TRG0_code + TxLaneNumber
(5)进入阶段0时,应启动一个计时器PA_LINKSTARTUP_TIMER,如果该过程未能在规定时间(100 ms,精度为+/-10%)内完成,则提供超时信号。
(6) PA_LINKSTARTUP_TIMER应在本地PA_LM_LINKSTARTUP.req之后,从对端设备接收到第一个TRG_UPR0信号时重新开始计时。如果发生超时,PA层应立即中止链路启动过程,并终止所有正在进行的突发传输。
2. Data Lane Discovery(数据通道发现)---> (Phases 0 and 0b)
LinkStartup Sequence 从发现连接的 M-PHY Data Lane开始 ,让Local 端的TX Data Lane可以找到对应匹配的Peer端的RX Data Lane, 后面数据传输就是按照匹配的TX < = > RX Data Lane 来传输。
(1)在所有可用的 TX 通道上开始突发(burst )传输(包括发送去斜模式)。
Burst(突发)的含义:
在数据传输中,"burst" 表示数据以连续的方式一次性发送多个数据单元,而不是逐个发送。
这种模式可以提高数据传输效率,减少通信开销。
UFS UNIPRO burst 的含义:
在 UFS 系统中,当主机和设备通过 UniPro 协议通信时,数据通常以突发模式传输。
这意味着:数据会被打包成较大的块(burst),然后一次性发送。
这种模式充分利用了 UFS 和 UniPro 的高带宽特性,提高了数据传输速度。
突发传输还可以减少协议开销,因为不需要为每个小数据单元单独建立通信。
在 Phase 0 中,突发传输包括去斜模式(deskew pattern),用于校准通道之间的时序偏差。
在 UFS 链路初始化过程中,发送端会通过 TX 通道发送 deskew pattern(去斜模式),
接收端利用这些模式来测量和校准各通道之间的时序偏差,从而确保数据的同步和可靠传输。
(2)在所有可用的 TX 通道上发送一个 TRG_UPR0。
(3)在所有可用的 TX 通道上结束突发传输。
(4)等待 PA_TActivate 时间,以将 M-RX 从 HIBERN8 状态唤醒(参见 [MIPI02])。
(5)如果接收到一个 TRG_UPR0,则从步骤(7) 继续;否则,从步骤 (1) 继续。
(6)在所有可用的 TX 通道上开始突发传输(包括发送去斜模式)。
(7)在所有可用的 TX 通道上发送两个额外的 TRG_UPR0。
(8)退出并进入 Phase 1
M-PHY Lanes Discovery
3 . Data Lane Realignment(数据通道重新对齐)
在数据通道对齐阶段, 两个设备(指的是Local侧设备和Peer侧设备)都应在所有可用的数据通道上重复发送 TRG_UPR1 触发信号。
在UFS(通用闪存存储)协议中,数据通道重新对齐(Data Lane Realignment)是指
在多通道数据传输过程中,由于某些原因(如通道失效或重新配置),需要对数据通道
进行重新调整和同步的过程。这一过程确保数据传输的完整性和可靠性,尤其是在通道
数量或配置发生变化时。
具体来说,数据通道重新对齐可能涉及以下操作:
检测通道状态:识别哪些通道是活动的,哪些通道需要重新配置或关闭。
重新配置通道:将活动的通道重新对齐到新的逻辑通道编号,并关闭未使用的通道
(例如使其进入低功耗状态,如HIBERN8)。
同步数据传输:确保所有活动通道在重新对齐后能够正确同步并继续传输数据。
这一过程通常发生在UFS设备的初始化、错误恢复或动态配置更改时,以确保数据传输
的高效性和稳定性
(1) 在所有可用的TX通道上发送一个TRG_UPR1信号。
(2) 如果已经接收到TRG_UPR1信号,则从步骤(4)继续。
(3) 继续从步骤(1)开始。
(4) 在所有可用的TX通道上再发送两个TRG_UPR1信号。
(5) 重新配置所有可用的M-TX和M-RX,以便在未来的逻辑通道#0上使用单通道PWM-G1模式。未连接或未激活的通道将被配置为进入HIBERN8状态。新配置将在Phase 4结束时,当前突发关闭后生效。
(6) 退出到Phase 3。
Repeated Transmission of TRG_UPR1 Messages
4 . Link Startup Sequence Termination(链路启动流程收尾) - - - >(Phases 3 and 4)
在最后的阶段中,连接的PHY数据通道的两端将具有匹配的逻辑数据通道编号。
每个设备应更新其 PA_ConnectedTxDataLanes 和 PA_ConnectedRxDataLanes 属性,以反映发现的已连接数据通道数量
(1) 在所有可用的TX通道上发送一个 TRG_UPR2 信号。
(2) 如果已经接收到 TRG_UPR2 信号,则从步骤(4)继续。
(3) 继续从步骤(1)开始。
(4) 在所有可用的TX通道上再发送两个 TRG_UPR2 信号。
(5) 关闭突发传输(这将激活在Phase 2配置期间应用的新通道设置)。
(6) 更新 PA_LogicalLaneMap,并开始使用逻辑通道编号。
(7) 未连接通道上的 M-TX 和 M-RX 应配置为 UNPOWERED(未供电)状态。
(8) 退出并进入 Capability Phase(能力协商阶段)。
Repeated Transmission of TRG_UPR2 Messages
5. Capability Exchange(能力交换)
在完成链路启动序列的 Phase 4 后,Local PA层 应在逻辑通道 #0 上启动一个突发传输,并按照以下顺序向对端设备发送其能力信息和本地版本信息:
(1)PACP_CAP_EXT2_ind
(2)PACP_CAP_EXT1_ind
(3)PACP_CAP_ind
这些信需要使用PHY配置进行传输。
-
如果接收到 PACP_CAP_EXT1_ind 帧,PA层应在激活的M-TX(逻辑通道 #0)上配置M-PHY属性 TX_LCC_Enable = “NO”,并忽略可能从M-RX接收到的 M-CTRL2464 LCCReadStatus.indication 原语。
-
当PA层接收到 PACP_CAP_ind 帧时,应根据 PACP_CAP_ind、PACP_CAP_EXT1_ind 以及(如果接收到)PACP_CAP_EXT2_ind 中的信息,执行能力降级操作。与OMC相关的M-PHY MC能力属性 MC_*_Capability 未定义,且不会在后续降级过程中被考虑。能力管理应假设入站和出站链路为直接电气连接。
-
如果对端版本信息表明对端支持 UniPro v1.8,但未接收到 PACP_CAP_EXT2_ind 帧,则PA层应使 LINK-STARTUP 过程超时。
-
如果在接收到 PACP_CAP_ind 之前未接收到 PACP_CAP_EXT1_ind 帧,则PA层应使 LINK-STARTUP 过程超时。
一旦接收到 PACP_CAP_ind,PA层应关闭突发传输。
对端设备的版本信息从 PACP_CAP_ind 中接收,并存储在 PA_RemoteVerInfo 中。这标志着链路启动序列的完成。此时,M-PHY链路处于默认的电源模式,即 PWM-G1,单通道。
三. UFS Link Startup详解
1. Android系统的Link Startup
(1) romcode 阶段link startup
romcode是固化在手机SOC芯片(MTK/QCOM)的一段代码, 出厂的时候烧录进去的,用户看不到也改不了,romcode阶段是开机的第一阶段,可以使用抓PA分析romcoded阶段的ufs link startup流程, PA层都是通过原语(primitive)来进行命令通信的。
(2) UEFI/LK阶段link startup
UEFI/LK是开机的第二阶段,手机芯片厂商可以给客户提供配套源码,用户可以用来修改调试,
在UEFI/LK阶段,link startup会发送DME Link startup建链成功,还会设置Unipro的一些寄存器,来加强建链成功之后链路层的稳定性,有时间的同学可以去研究研究这些寄存器对于数据链路层的影响,根据一些实验反馈来看,去掉这些操作(设置Unipro的一些寄存器)会影响Link Startup/Data Transfer/Power Mode Change.
(3) Kernel 阶段link startup
Kernel是开机的第三阶段,手机芯片厂商也会提供给客户配套源码,用户可以修改调试。
在Kernel阶段,link startup会发送DME Link startup建链成功,建链成功默认是SlowAuto_Mode, 还会设置Unipro的一些寄存器,来加强建链成功之后链路层的稳定性
2. UFS Link Startup失败遇到的问题
(1) UFS Vcc/Vccq/Vccq2供电异常, 可以使用万用表/示波器排查
(2) UFS Device接触异常,可能是虚焊,也可能是芯片的球点损坏,可以重新植球加焊一下
(3) UFS 内部状态异常,需要重新刷FW解决
(4) UFS HCI 状态异常,没有准备好发送/接收UIC命令, 可能是HCI没有使能或者HCI没有进行初始化
3. UFS Link Startup的Unipro相关
观察UFS Link Startup的流程可以通过抓PA协议包来分析 (PA就是抓UFS Phy Adapter数据包的协议分析仪)
四. UFS Link Startup代码实现
1 . Unipro link startup初始化
/**
* ufshcd_link_startup - Initialize unipro link startup
* @hba: per adapter instance
*
* Returns 0 for success, non-zero in case of failure
*/
static int ufshcd_link_startup(struct ufs_hba *hba)
{
int ret;
int retries = DME_LINKSTARTUP_RETRIES;
bool link_startup_again = false;
/*
* If UFS device isn't active then we will have to issue link startup
* 2 times to make sure the device state move to active.
*/
if (!ufshcd_is_ufs_dev_active(hba))
link_startup_again = true;
link_startup:
do {
ufshcd_vops_link_startup_notify(hba, PRE_CHANGE);
ret = ufshcd_dme_link_startup(hba);
/* check if device is detected by inter-connect layer */
if (!ret && !ufshcd_is_device_present(hba)) {
dev_err(hba->dev, "%s: Device not present\n", __func__);
ret = -ENXIO;
goto out;
}
/*
* DME link lost indication is only received when link is up,
* but we can't be sure if the link is up until link startup
* succeeds. So reset the local Uni-Pro and try again.
*/
if (ret && ufshcd_hba_enable(hba))
goto out;
} while (ret && retries--);
if (ret)
/* failed to get the link up... retire */
goto out;
if (link_startup_again) {
link_startup_again = false;
retries = DME_LINKSTARTUP_RETRIES;
goto link_startup;
}
/* Mark that link is up in PWM-G1, 1-lane, SLOW-AUTO mode */
ufshcd_init_pwr_info(hba);
ufshcd_print_pwr_info(hba);
if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) {
ret = ufshcd_disable_device_tx_lcc(hba);
if (ret)
goto out;
}
/* Include any host controller configuration via UIC commands */
ret = ufshcd_vops_link_startup_notify(hba, POST_CHANGE);
if (ret)
goto out;
ret = ufshcd_make_hba_operational(hba);
out:
if (ret) {
dev_err(hba->dev, "link startup failed %d\n", ret);
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_host_regs(hba);
}
return ret;
}
2. ufshcd_vops_link_startup_notify:linkstartup之前/之后的预处理,主要是ufs host平台(qcom/mtk/ss平台) unipro的初始化,这个很重要,如果没做平台相关初始化,linkstartup之后切到高速,读写会不稳定。
下面以qcom host为例:
static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
int err = 0;
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
switch (status) {
case PRE_CHANGE:
if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE,
0, true)) {
dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
__func__);
err = -EINVAL;
goto out;
}
if (ufs_qcom_cap_qunipro(host))
/*
* set unipro core clock cycles to 150 & clear clock
* divider
*/
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
150);
/*
* Some UFS devices (and may be host) have issues if LCC is
* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
* before link startup which will make sure that both host
* and device TX LCC are disabled once link startup is
* completed.
*/
if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
err = ufshcd_dme_set(hba,
UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
0);
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
break;
default:
break;
}
out:
return err;
}
/**
* struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
*
* The variant operations configure the necessary controller and PHY
* handshake during initialization.
*/
static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
.name = "qcom",
.init = ufs_qcom_init,
.exit = ufs_qcom_exit,
.get_ufs_hci_version = ufs_qcom_get_ufs_hci_version,
.clk_scale_notify = ufs_qcom_clk_scale_notify,
.setup_clocks = ufs_qcom_setup_clocks,
.hce_enable_notify = ufs_qcom_hce_enable_notify,
.link_startup_notify = ufs_qcom_link_startup_notify,
.pwr_change_notify = ufs_qcom_pwr_change_notify,
.apply_dev_quirks = ufs_qcom_apply_dev_quirks,
.suspend = ufs_qcom_suspend,
.resume = ufs_qcom_resume,
.dbg_register_dump = ufs_qcom_dump_dbg_regs,
};
3. 发送dme linkstartup命令,并等待uic命令的返回
/**
* ufshcd_dme_link_startup - Notify Unipro to perform link startup
* @hba: per adapter instance
*
* UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
* in order to initialize the Unipro link startup procedure.
* Once the Unipro links are up, the device connected to the controller
* is detected.
*
* Returns 0 on success, non-zero value on failure
*/
static int ufshcd_dme_link_startup(struct ufs_hba *hba)
{
struct uic_command uic_cmd = {0};
int ret;
uic_cmd.command = UIC_CMD_DME_LINK_STARTUP;
ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
if (ret)
dev_dbg(hba->dev,
"dme-link-startup: error code %d\n", ret);
return ret;
}
/**
* ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
* @hba: per adapter instance
* @uic_cmd: UIC command
*
* Returns 0 only if success.
*/
static int
ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
{
int ret;
unsigned long flags;
ufshcd_hold(hba, false);
mutex_lock(&hba->uic_cmd_mutex);
ufshcd_add_delay_before_dme_cmd(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
ret = __ufshcd_send_uic_cmd(hba, uic_cmd, true);
spin_unlock_irqrestore(hba->host->host_lock, flags);
if (!ret)
ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd);
mutex_unlock(&hba->uic_cmd_mutex);
ufshcd_release(hba);
return ret;
}
static int
__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd,
bool completion)
{
if (!ufshcd_ready_for_uic_cmd(hba)) {
dev_err(hba->dev,
"Controller not ready to accept UIC commands\n");
return -EIO;
}
if (completion)
init_completion(&uic_cmd->done);
ufshcd_dispatch_uic_cmd(hba, uic_cmd);
return 0;
}
4. host 平台通过设置寄存器, 将linkstartuo的组成的uic command 通过HCI发送给Device
/**
* ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
* @hba: per adapter instance
* @uic_cmd: UIC command
*
* Mutex must be held.
*/
static inline void
ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
{
WARN_ON(hba->active_uic_cmd);
hba->active_uic_cmd = uic_cmd;
/* Write Args */
ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1);
ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2);
ufshcd_writel(hba, uic_cmd->argument3, REG_UIC_COMMAND_ARG_3);
/* Write UIC Cmd */
ufshcd_writel(hba, uic_cmd->command & COMMAND_OPCODE_MASK,
REG_UIC_COMMAND);
}
(1)UIC Command
(2) UIC Command Argument 1(3) UIC Command Argument 2
(4) UIC Command Argument 3
五. 参考资料
1 . UniPro v1.8 Specification
2 . JESD220E Universal Flash Storage (UFS) Version 3.1
3. JESD223B Universal Flash Storage Host Controller Interface (UFSHCI) Version 2.0
4. Linux Kernel 4.19 souce code