1. JESD220 中关于UFS初始化的描述
原文
13.1.3 Initialization and boot code download process
The initialization and boot code download process is made up of the following phases: partial initialization, boot transfer and initialization completion.
13.1.3.1 Partial initialization
The partial initialization phase starts after power on, or hardware reset, or EndPointReset and involves the entire UFS stack. At the end of this phase, the UniPro boot sequence shall be completed, and the UTP layer shall be capable of accessing Device Descriptor (if the bDescrAccessEn field of the Device Descriptor is ‘01h’) and exchanging UPIU for READ command and TEST UNIT READY command. If the bDescrAccessEn field is ‘00h‘ descriptors will be accessible only after the initialization completion phase. Each single layer in the UFS protocol stack executes the initialization process on both UFS host and UFS device sides.
a) Physical Layer (M-PHY) --> No SW action.
After reset events, the physical layer will move from DISABLED state to HIBERN8 state.
b) Link Layer (UniPro)
On host and device side UniPro boot sequence takes place:
c) UFS Transport Layer (UTP)
At the end of the UFS Interconnect Layer initialization on both host and device side, the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready. For some implementations, the device UTP layer may not be initialized yet, therefore the device may not respond promptly to NOP OUT UPIU sending NOP IN UPIU. The host waits until it receives the NOP IN UPIU from the device. When the NOP IN UPIU is received, the host is acknowledged that the UTP layer on the device is ready to execute UTP transactions.
ufs_check_resp(&utrd, NOP_IN_UPIU, NOP_OUT_TIMEOUT_MS);
d) Link Configuration
The host may configure the Link Attributes (i.e., Gear, HS Series, PWM Mode in Rx and Tx) by using DME primitives at UniPro level.
e) Device Descriptor Reading
The UFS host may optionally discover relevant device info for the boot process by accessing the Device Descriptor (i.e., Device Class/Subclass, Boot Enable, Boot LUs size, etc.). The UFS host is allowed to access the Device Descriptor only if the bDescrAccessEn is ‘01h’, otherwise this descriptor can be accessed only after the device has fully completed its initialization.
13.1.3.2 Boot transfer
The following steps can be executed only if bBootEnable field is set.
13.1.3.3 Initialization completion
After the host has completed the boot code download from the Boot well known logical unit, the initialization process proceeds as described in the following. The host sets the fDeviceInit flag to “01h” to communicate to the UFS device that it can complete its initialization. The device shall reset the fDeviceInit flag when the initialization is complete. The host polls the fDeviceInit flag to check the completion of the process. When the fDeviceInit is reset, the device is ready to accept any command.
翻译
UFS 学习笔记(功能认识篇) 有中文版。以下内容摘自UFS 学习笔记(功能认识篇)
3.1 部分初始化
部分初始化阶段在上电、硬件复位或端点复位之后开始,并涉及整个 UFS 堆栈。在此阶段结束时,应完成 UniPro 引导序列,并且UTP层应能够访问设备描述符(如果设备描述符的 bDescrAccessEn(描述符访问使能)字段为 01h)并交换读取命令和测试单元就绪命令的 UPIU。如果 bDescrAccessEn 字段为 00h,则只有在初始化完成阶段之后才能访问描述符。UFS 协议堆栈中的每个单独层在 UFS 主机和 UFS 设备两侧执行初始化过程。
a) 物理层(M-PHY)
复位事件后,物理层将从 DISABLED 状态转移到 HIBERN8 状态。
b) 链路层(UniPro)
在主机和设备端进行 UniPro 引导序列:
使用 DME_RESET.req 原语重置 UniPro 堆栈;
等待通过 DME_RESET.cnf_L 原语指示的复位完成;
使用 DME_ENABLE.req 原语启用 UniPro 堆栈;
等待通过 DME_ENABLE.cnf_L 原语指示的启用完成;
使用 DME_LINKSTARTUP.req 原语启动 UniPro 链路启动序列。UniPro 链路启动由一系列多相握手组成,以在 UFS 主机和设备之间建立初始双向链路通信;
等待通过 DME_LINKSTARTUP.cnf_L 原语指示的链路启动完成。
c) UFS传输层(UTP)
在主机和设备两侧的 UFS 互连层初始化结束后,主机应发送 NOP OUT UPIU 以验证设备的 UTP 层是否就绪。对于某些实现,设备的 UTP 层可能尚未初始化,因此设备可能无法及时响应 NOP OUT UPIU,并发送 NOP IN UPIU。主机等待接收到设备的 NOP IN UPIU。当接收到 NOP IN UPIU 时,主机确认设备的 UTP 层已准备好执行 UTP 事务。
d) 链路配置
主机可以使用 UniPro 级别的 DME 原语配置链路属性(如 Gear、HS Series、PWM Mode 在 Rx 和 Tx 中)。
e) 设备描述符读取
UFS 主机可以通过访问设备描述符(如设备类/子类、引导使能、引导 LU 大小等)来可选地发现引导过程的相关设备信息。只有在 bDescrAccessEn 为‘01h’时,UFS 主机才允许访问设备描述符,否则此描述符只能在设备完全完成初始化后才能访问。
3.2 引导传输(可选)
仅当 bBootEnable 字段设置时,才执行引导代码下载:
首先,UFS 主机向 Boot 固定逻辑单元发出 TEST UNIT READY 命令,以验证是否可以访问后者。如果命令成功,UFS 主机通过发出 SCSI READ 命令读取 Boot 固定逻辑单元,而 UFS 设备将开始在上行链路上发送引导代码。在此阶段,仅 Boot 固定逻辑单元可访问:该逻辑单元应接受读取命令,而其他逻辑单元可能尚未准备好。
3.3 初始化完成
在主机从 Boot 固定逻辑单元完成引导代码下载后,初始化过程按照以下描述继续进行。主机将 fDeviceInit 标志设置为“01h”,以向 UFS 设备通信,表示它可以完成其初始化。当初始化完成时,设备应将 fDeviceInit 标志复位。主机轮询 fDeviceInit 标志以检查进程的完成情况。当 fDeviceInit 被复位时,设备准备好接受任何。
2.ATF中代码与协议中的实现
田园诗人之园的ATF BL1 UFS初始化简单分析 中分析了ATF中UFS驱动初始化的流程。
田园诗人之园的ATF bl1 ufshc_dme_get/set处理流程分析分析了ATF DME操作是如何完成的。
这里就不做流程分析了,而是看一看ATF UFS驱动中是如何和JESD 220协议中的内容一一对应实现的。
a) 物理层(M-PHY)
物理层的初始化没有代码来控制,这部分是硬件实现的。
b) 链路层(UniPro)
在主机和设备端进行 UniPro 引导序列:
- 使用 DME_RESET.req 原语重置 UniPro 堆栈;
- 等待通过 DME_RESET.cnf_L 原语指示的复位完成;
- 使用 DME_ENABLE.req 原语启用 UniPro 堆栈;
- 等待通过 DME_ENABLE.cnf_L 原语指示的启用完成;
在ATF中ufs_init–>ufshc_reset实现了这个操作。软件这边主要通过操作HCE寄存器的HCE_ENABLE位来实现UNIPRO的reset, 代码片段如下。
static int ufshc_reset(uintptr_t base)
{
....
/* disable controller if enabled */
if (mmio_read_32(base + HCE) & HCE_ENABLE) {
result = ufshc_hce_disable(base);
...
}
for (retries = 0; retries < HCE_ENABLE_OUTER_RETRIES; ++retries) {
result = ufshc_hce_enable(base);
}
...
}
下图是HCE寄存器的定义。
- 使用 DME_LINKSTARTUP.req 原语启动 UniPro 链路启动序列。UniPro 链路启动由一系列多相握手组成,以在 UFS 主机和设备之间建立初始双向链路通信;
- 等待通过 DME_LINKSTARTUP.cnf_L 原语指示的链路启动完成。
ATF UFS驱动在ufs_init–>ufshc_link_startup中实现了这个逻辑
static int ufshc_link_startup(uintptr_t base)
{
int data, result;
int retries;
for (retries = DME_LINKSTARTUP_RETRIES; retries > 0; retries--) {
result = ufshc_dme_link_startup(base);
if (result != 0) {
/* Reset controller before trying again */
result = ufshc_reset(base);
...
}
assert(mmio_read_32(base + HCS) & HCS_DP);
data = mmio_read_32(base + IS);
if (data & UFS_INT_ULSS)
mmio_write_32(base + IS, UFS_INT_ULSS);
/* clear UE set due to line-reset */
if (data & UFS_INT_UE) {
mmio_write_32(base + IS, UFS_INT_UE);
}
/* clearing line-reset, UECPA is cleared on read */
mmio_read_32(base + UECPA);
return 0;
}
return -EIO;
}
ufshc_dme_link_startup实现非常简单,就是调用了UFSHC DME的寄存器发送了DME_LINKSTARTUP的UIC opcode给UNIPRO,使能链路层。
static int ufshc_dme_link_startup(uintptr_t base)
{
uic_cmd_t cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.op = DME_LINKSTARTUP;
return ufshc_send_uic_cmd(base, &cmd);
}
c) UFS传输层(UTP)
UTP初始化需要主机发送NOP OUT UPIU给设备,然后设备返回正确的NOP IN UPIU给主机。在ATF中实现的代码在ufs_init–>ufs_enum–>ufs_verify_init中。
static void ufs_verify_init(void)
{
utp_utrd_t utrd;
int result;
//初始化一个NOP OUT UPIU并发送给设备
get_utrd(&utrd);
ufs_prepare_nop_out(&utrd);
ufs_send_request(utrd.task_tag);
//接收NOP IN UPIUAQ
result = ufs_check_resp(&utrd, NOP_IN_UPIU, NOP_OUT_TIMEOUT_MS);
assert(result == 0);
(void)result;
}
d) 链路配置
主机可以使用 UniPro 级别的 DME 原语配置链路属性(如 Gear、HS Series、PWM Mode 在 Rx 和 Tx 中)
链路配置主要在dwufs_phy_init和dwufs_phy_set_pwr_mode中实现。
static int dwufs_phy_init(ufs_params_t *params)
{
...
/* Unipro VS_MPHY disable */
ufshc_dme_set(VS_MPHY_DISABLE_OFFSET, 0, VS_MPHY_DISABLE_MPHYDIS); //Disable MPHY
//选择HS-Gear的模式,HS-Gear有Series A和B选,可以看下面的图。
//以下是UNIPRO中的Attribute模式原文:Phy Adapter 5.8 TX and RX Frequency Series in High Speed Mode A=1 B=2
ufshc_dme_set(PA_HS_SERIES_OFFSET, 0, 2);
//下面这些hardcode的0x81**的Attribute可能是kirin960芯片里UFS PHY特有的Attribute,在UNIPRO和MPHY中协议中没找到,我也不知道具体的bit是指代什么
/* MPHY CBRATESEL */
ufshc_dme_set(0x8114, 0, 1);
/* MPHY CBOVRCTRL2 */
ufshc_dme_set(0x8121, 0, 0x2d);
/* MPHY CBOVRCTRL3 */
ufshc_dme_set(0x8122, 0, 0x1);
ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
/* MPHY RXOVRCTRL4 rx0 */
ufshc_dme_set(0x800d, 4, 0x58);
/* MPHY RXOVRCTRL4 rx1 */
ufshc_dme_set(0x800d, 5, 0x58);
/* MPHY RXOVRCTRL5 rx0 */
ufshc_dme_set(0x800e, 4, 0xb);
/* MPHY RXOVRCTRL5 rx1 */
ufshc_dme_set(0x800e, 5, 0xb);
/* MPHY RXSQCONTROL rx0 */
ufshc_dme_set(0x8009, 4, 0x1);
/* MPHY RXSQCONTROL rx1 */
ufshc_dme_set(0x8009, 5, 0x1);
ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
ufshc_dme_set(0x8113, 0, 0x1);
ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
//配置RX_HS_G3_SYNC_LENGTH_Capability, RX_HS_G2_SYNC_LENGTH_Capability,
//RX_Min_ActivateTime_Capability和TX_Hibern8Time_Capability Attribute
ufshc_dme_set(RX_HS_G3_SYNC_LENGTH_CAP_OFFSET, 4, 0x4a);
ufshc_dme_set(RX_HS_G3_SYNC_LENGTH_CAP_OFFSET, 5, 0x4a);
ufshc_dme_set(RX_HS_G2_SYNC_LENGTH_CAP_OFFSET, 4, 0x4a);
ufshc_dme_set(RX_HS_G2_SYNC_LENGTH_CAP_OFFSET, 5, 0x4a);
ufshc_dme_set(RX_MIN_ACTIVATETIME_CAP_OFFSET, 4, 0x7);
ufshc_dme_set(RX_MIN_ACTIVATETIME_CAP_OFFSET, 5, 0x7);
ufshc_dme_set(TX_HIBERN8TIME_CAP_OFFSET, 0, 0x5);
ufshc_dme_set(TX_HIBERN8TIME_CAP_OFFSET, 1, 0x5);
ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
ufshc_dme_set(PA_LOCAL_TX_LCC_ENABLE_OFFSET, 0, 0);
这个在UNIPRO和MPHY协议中没找到。VS_MK2_EXTN_SUPPORT_OFFSET 0xD0AB。感觉是使能某个硬件。
ufshc_dme_set(VS_MK2_EXTN_SUPPORT_OFFSET, 0, 0);
result = ufshc_dme_get(VS_MK2_EXTN_SUPPORT_OFFSET, 0, &data);
assert((result == 0) && (data == 0));
ufshc_dme_set(DL_AFC0_CREDIT_THRESHOLD_OFFSET, 0, 0);
ufshc_dme_set(DL_TC0_OUT_ACK_THRESHOLD_OFFSET, 0, 0);
ufshc_dme_set(DL_TC0_TX_FC_THRESHOLD_OFFSET, 0, 9);
(void)result;
return 0;
}
0xd0a0和0x1556这两个Attribute在UNIPRO和MPHY协议中没找到
static int dwufs_phy_set_pwr_mode(ufs_params_t *params)
{
....
if ((flags & UFS_FLAGS_VENDOR_SKHYNIX) != 0U) {
NOTICE("ufs: H**** device must set VS_DebugSaveConfigTime 0x10\n");
/* VS_DebugSaveConfigTime */
result = ufshc_dme_set(0xd0a0, 0x0, 0x10);
/* sync length */
result = ufshc_dme_set(0x1556, 0x0, 0x48);
}
result = ufshc_dme_get(PA_TACTIVATE_OFFSET, 0, &data);
if (data < 7) {
result = ufshc_dme_set(PA_TACTIVATE_OFFSET, 0, 7);
}
获取支持的tx和rx的lane并设置
result = ufshc_dme_get(PA_CONNECTED_TX_DATA_LANES_OFFSET, 0, &tx_lanes);
result = ufshc_dme_get(PA_CONNECTED_RX_DATA_LANES_OFFSET, 0, &rx_lanes);
result = ufshc_dme_set(PA_ACTIVE_TX_DATA_LANES_OFFSET, 0, tx_lanes);
result = ufshc_dme_set(PA_ACTIVE_RX_DATA_LANES_OFFSET, 0, rx_lanes);
result = ufshc_dme_set(PA_TX_SKIP_OFFSET, 0, 0);
配置UFS为HS-GEAR3 Series B
result = ufshc_dme_set(PA_TX_GEAR_OFFSET, 0, 3);
result = ufshc_dme_set(PA_RX_GEAR_OFFSET, 0, 3);
result = ufshc_dme_set(PA_HS_SERIES_OFFSET, 0, 2);
result = ufshc_dme_set(PA_TX_TERMINATION_OFFSET, 0, 1);
result = ufshc_dme_set(PA_RX_TERMINATION_OFFSET, 0, 1);
result = ufshc_dme_set(PA_SCRAMBLING_OFFSET, 0, 0);
result = ufshc_dme_set(PA_PWR_MODE_USER_DATA0_OFFSET, 0, 8191);
result = ufshc_dme_set(PA_PWR_MODE_USER_DATA1_OFFSET, 0, 65535);
result = ufshc_dme_set(PA_PWR_MODE_USER_DATA2_OFFSET, 0, 32767);
result = ufshc_dme_set(DME_FC0_PROTECTION_TIMEOUT_OFFSET, 0, 8191);
result = ufshc_dme_set(DME_TC0_REPLAY_TIMEOUT_OFFSET, 0, 65535);
result = ufshc_dme_set(DME_AFC0_REQ_TIMEOUT_OFFSET, 0, 32767);
result = ufshc_dme_set(PA_PWR_MODE_USER_DATA3_OFFSET, 0, 8191);
result = ufshc_dme_set(PA_PWR_MODE_USER_DATA4_OFFSET, 0, 65535);
result = ufshc_dme_set(PA_PWR_MODE_USER_DATA5_OFFSET, 0, 32767);
result = ufshc_dme_set(DME_FC1_PROTECTION_TIMEOUT_OFFSET, 0, 8191);
result = ufshc_dme_set(DME_TC1_REPLAY_TIMEOUT_OFFSET, 0, 65535);
result = ufshc_dme_set(DME_AFC1_REQ_TIMEOUT_OFFSET, 0, 32767);
配置power mode为fast mode
result = ufshc_dme_set(PA_PWR_MODE_OFFSET, 0, 0x11);
do {
data = mmio_read_32(base + IS);
} while ((data & UFS_INT_UPMS) == 0);
mmio_write_32(base + IS, UFS_INT_UPMS);
data = mmio_read_32(base + HCS);
if ((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL)
INFO("ufs: change power mode success\n");
else
WARN("ufs: HCS.UPMCRS error, HCS:0x%x\n", data);
(void)result;
return 0;
e) 设备描述符读取
static void ufs_get_device_info(struct ufs_dev_desc *card_data)
{
uint8_t desc_buf[DESC_DEVICE_MAX_SIZE];
ufs_read_desc(DESC_TYPE_DEVICE, 0, (uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE);
/*
* getting vendor (manufacturerID) and Bank Index in big endian
* format
*/
card_data->wmanufacturerid = (uint16_t)((desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8) |
(desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]));
}
初始化完成
ufs_init->ufs_enum->ufs_set_fdevice_init。通过QUERY UPIU 给UFS设备之上FDEVICE_INIT flag。
static int ufs_set_fdevice_init(void)
{
unsigned int result;
int timeout;
ufs_set_flag(FLAG_DEVICE_INIT);
timeout = FDEVICEINIT_TIMEOUT_MS;
do {
result = ufs_read_flag(FLAG_DEVICE_INIT);
if (!result) {
break;
}
mdelay(5);
timeout -= 5;
} while (timeout > 0);
if (result != 0U) {
return -ETIMEDOUT;
}
return 0;
}
3. Dump出代码中的初始化过程中的UPIU
UTRD header
NOP UPIU
NOP OUT UPIU
NOP OUT UPIU
UTRD header: 0x11000000 0x0 0xf 0x0 0x1ffc0080 0x0 0x8000e 0x0
CMD UPIU: 0x1000000 0x0 0x0 0x0 0x0 0x0 0x0 0x0
RESP UPIU: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
PRDT: 0x0 0x0 0x0 0x0
UTRD Header | |
---|---|
CCI | 0 |
CE | 0 |
I | 1 |
DD | 0 - no data transfer |
Command Type | 1 |
Overall Command Status | 0xF INVALID_OSC_VALUE |
DUNU | 0 |
UTP Command Descriptor Base Address | 0x1ffc0080 |
UTP Command Descriptor Base Address Upper 32-bits | 0 |
Response UPIU Offset | 0x8 |
Response UPIU Length | 0xE |
PRDT Length | 0 |
PRDT Offset | 0 |
[31:24] | [23:16] | [15:8] | [7:0] |
---|---|---|---|
Command Type=00(NOP OUT) | Flags=00 | Reserved=00 | Task Tag=01 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Total EHS Length=00h | Reserved=00 | Data Length MSB=00 | Data Length LSB=00 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Header E2ECRC[31:24]=00 | Header E2ECRC[23:16]=00 | Header E2ECRC[15:8]=00 | Header E2ECRC[7:0]=00 |
NOP IN UPIU
UTRD header: 0x11000000 0x0 0x0 0x0 0x1ffc0080 0x0 0x8000e 0x0
CMD UPIU: 0x1000000 0x0 0x0 0x0 0x0 0x0 0x0 0x0
RESP UPIU: 0x1000020 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
PRDT: 0x0 0x0 0x0 0x0
UTRD Header | |
---|---|
CCI | 0 |
CE | 0 |
I | 1 |
DD | 0 - no data transfer |
Command Type | 1 |
Overall Command Status | 0x0 SUCCESS |
DUNU | 0 |
UTP Command Descriptor Base Address | 0x1ffc0080 |
UTP Command Descriptor Base Address Upper 32-bits | 0 |
Response UPIU Offset | 0x8 |
Response UPIU Length | 0xE |
PRDT Length | 0 |
PRDT Offset | 0 |
[31:24] | [23:16] | [15:8] | [7:0] |
---|---|---|---|
Command Type=20(NOP IN) | Flags=00 | Reserved=00 | Task Tag=01 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Total EHS Length=00h | Reserved=00 | Data Length MSB=00 | Data Length LSB=00 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Reserved=00 | Reserved=00 | Reserved=00 | Reserved=00 |
Header E2ECRC[31:24]=00 | Header E2ECRC[23:16]=00 | Header E2ECRC[15:8]=00 | Header E2ECRC[7:0]=00 |