一 . 控制器层
1.UFS is a simple, high performance, serial interface. It is primarily for use in mobile systems, between host processing and NVM mass storage devices.
2.Interface Architecture
UFS host software uses a combination of a host register set and Transfer Request Descriptors in system memory to communicate with host controller hardware. Figure 2 illustrates a conceptual block diagram of UFS Host Controller Interface.
UFSHCI defines two interface spaces.
MMIO Space. In this space, a set of hardware registers are defined as the host controller interface to system software. It is normally implemented as Memory-Mapped I/O (MMIO) space, consisting of three types of registers:
o Host Controller Capability Registers. These registers provide description of host controller capabilities. They include UFS standard version, the size of the command queue the host controller supported, and host controller identification data.
o Runtime and Operation Registers. These include support for the following:
Interrupt configuration. These registers provide an interface for host SW to enable/disable interrupt and status of the interrupts.
Host controller status. This register shows the status of the host controller and allows host software to initialize/deactivate the host controller.
UTP transfer Request List management. These registers provide an interface to UTP Transfer Request List.
UTP Task Management request lists management. These registers provide an interface to UTP Transfer Request List.
UIC Command Registers. These registers provide an interface for UniPro configuration and control.
o Vendor Specific Registers. These registers are defined by vendors.
Host Memory Space. This space includes data structures that provide description of the commands for execution and the data buffers which are a part of each command. The data structures and data buffers are application protocol specific (UTP).
3..A UFS Host System is composed of a number of hardware and software layers. Figure 3 illustrates a conceptual block diagram of the building block layers in a host system.
2This standard defines a set of registers and data structures that work in concert with UFS host SW to implement the four service access points (SAPs) as described in the Figure 3: UIO_SAP, UDM_SAP, UTP_CMD_SAP and UTP_TM_SAP. Refer to [UFS], UFS Standard for the definition of the service access points.
To manage themmc的读写浅析e communication between host SW and the UFS devices attached, the host controller provides three independent interfaces that host software uses to send a transfer request.
UTP Transfer Request List. This list is used by host software to implement UTP_CMD_SAP and UDM_SAP.
o UTP_CMD_SAP incudes support for the following command types:
All INCITS T10 draft standard functionality adopted by UFS.
Native UFS command set.
o UDM_SAP includes support for the following command types:
Device management function via QUERY REQUEST UPIU/QUERY RESPONSE UPIU and NOP IN/NOP OUT UPIU
The list consists of a data structure called UFS Transfer Request Descriptor (UTRD). UTRD describes a command to be executed and the data associated it. UFS host SW issues a command to the host controller by placing a UTRD on the List then rings the Host Controller doorbell for the list. Commands are dispatched for execution by the UFSHCI in the order that they are placed on the List even though they may be completed out of the order. The host controller acts on behalf of host processor to manage all the data transfer operations associated with the command. All commands could result in a Command Completion interrupt or status field of the UTRD for the command in the List being updated. UFS SW may add commands to the List while it is running. The host controller supports interrupt aggregation, such that a single command completion interrupt is generated for a pre-defined number of command completions.
UTP Task Management Request List. This list is used by host software to implement UTP_TM_SAP. The List consists of a data structure called UFS Task Management Request Descriptor (UTMRD). UTMRD describes a task management function that host software wants the attached device to execute. All the Task Management Requests will be prioritized over the transfer requests listed in UTP Transfer Request List as described above. UFS host SW issues a task management function to the host controller by placing a UTMRD on the List then rings the Host Controller doorbell for the list. Functions are dispatched for execution by the UFSHCI in the order that they are placed on the List even though they may be completed out of the order. All task management functions could result in a Request Completion interrupt or status field of the UTMRD for the function in the List being updated. UFS SW may add task management function to the List while it is running. Interrupt aggregation is not supported for this list.
UIC Command Register. This register set is used by host software to execute a UIC command directly.
2. 数据结构:
(1)2.1Command Descriptor Block (CDB)
2.1.1CDB usage and structure
A command is communicated by sending a command descriptor block (CDB) to the device server. For several
commands, the CDB is accompanied by a list of parameters in the Data-Out Buffer. See the specific commands
for detailed information.
If a logical unit validates reserved CDB fields and receives a reserved field within the CDB that is not zero, then
the logical unit shall terminate the command with CHECK CONDITION status, with the sense key set to ILLEGAL
REQUEST, and the additional sense code set to INVALID FIELD IN CDB.
If a logical unit receives a reserved CDB code value in a field other than the OPERATION CODE field, then the
logical unit shall terminate the command with CHECK CONDITION status, with the sense key set to ILLEGAL
REQUEST, and the additional sense code set to INVALID FIELD IN CDB.
The fixed length CDB formats are described in 2.1.2. The variable length CDB formats are described in 2.1.4.
The CDB fields that are common to most commands are described in 2.1.5. The fields shown in 2.1.2 and
2.1.3 and described in 2.1.4 are used consistently by most commands. However, the actual usage of any field
(except OPERATION CODE and CONTROL) is described in the subclause defining that command. If a device
server receives a CDB containing an operation code that is invalid or not supported, the command shall be terminated
with CHECK CONDITION status, with the sense key set to ILLEGAL REQUEST, and the additional
sense code set to INVALID COMMAND OPERATION CODE.
For all commands, if there is an invalid parameter in the CDB, the device server shall terminate the command
without altering the medium.
(2)UTP Transfer Request Descriptor(UTRD)
应用层的命令下发到UFS Devices的途中,需要经过UTP层,UTP是传输层,它会将应用层的命令打包成UPIU,往下下发到UIC层,再下发到UFS Devices,其中UPIU又粉娃娃好多种类,有Command UPIU(里面含有CDB), Response UPIU, 种类详情请见UPIU Transactions 表格。
其中Command UPIU: The Command transaction originates in the Initiator device and is sent to a logical unit within a Target device. A COMMAND UPIU will contain a Command Descriptor Block as the command and the command parameters. This represents the COMMAND phase of the command
Response UPIU: The Response transaction originates in the Target device and is sent back to the Initiator device. A RESPONSE UPIU will contain a command specific operation status and other response information. This represents the STATUS phase of the command.
(3)UTP Command Descriptor (UCD) :
UTRD包含一个数据结构叫做UCD,这个数据结构包括由命令的UPIU, 与命令相关联的检测缓冲区的长度和偏移量,PRDT的偏移量和长度。
Note: Both Command UPIU (Transfer Request) and Response UPIU (Transfer Response) are in big endian format and PRDT is in little endian format.
(4)UPIU Transactions
Every UPIU data structure contains a Transaction Code. This code defines the content and implied function or use of the UPIU data structure. Table 10-1 lists currently defined transaction codes.
Communication between the Initiator device and Target device is divided into a series of messages. These 1225 messages are formatted into UFS Protocol Information Units (UPIU) as defined within this standard. 1226 There are a number of different UPIU types defined. All UPIU structures contain a common header area 1227 at the beginning of the data structure (lowest address). The remaining fields of the structure vary 1228 according to the type of UPIU.
Command structures consist of Command Descriptor Blocks (CDB) that contain a command opcode and 1236 related parameters, flags and attributes. The description of the CDB content and structure are defined in 1237 detail in [SAM], [SBC] and [SPC] INCITS T10 draft standards.
二. 驱动流程:
(1) drivers/scsi/ufs/ufs-xxsoc.c ----> ufs soc控制器驱动代码(相当于mmc/host/xx.c)
(2)drivers/scsi/ufs/ufshcd.c --->ufs 协议层代码(相当于mmc/core/core.c)
(3)drivers/scsi/sd.c -------> ufs 设备层的代码(相当于mmc/card/block.c)
(4)arch/arm64/boot/dts/qcom/xx.dtsi------>soc的芯片级别的dtsi
arch/arm64/boot/dts/qcom/xx.dts--------->board级别的dts
1. 首先整理一下UTP传输正常完成的流程
(1)上层发送SCSI命令,在发送命令的同时会设置往对应的door_bell(tags)写1,会往对应的outstanding_reqs(每一个硬件上的tags软件上我们虚拟一个req_tags,也称为outstanding_reqs)写1
send command
**
* ufshcd_send_command - Send SCSI or device management commands
* @hba: per adapter instance
* @task_tag: Task tag of the command
*/
static inline
void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
{
hba->lrb[task_tag].issue_time_stamp = ktime_get();
hba->lrb[task_tag].compl_time_stamp = ktime_set(0, 0);
ufshcd_clk_scaling_start_busy(hba);
__set_bit(task_tag, &hba->outstanding_reqs);
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* Make sure that doorbell is committed immediately */
wmb();
ufshcd_add_command_trace(hba, task_tag, "send");
}
(2)注册SOC侧的中断处理函数(dts配置一般是interrupts =
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
dev_err(hba->dev, "request irq failed\n");
goto exit_gating;
} else {
hba->is_irq_enabled = true;
}
(3) 我们每次传输完成的时候都会触发一个中断,这个时候就会往soc的中断控制器对应的中断状态位写1,同时回调中断处理函数,每次进入中断处理函数我们会将对应的中断位清除(清除REG_INTERRUPT_STATUS 0x20中断状态位: 往0x20对应的中断位写1就是清除这个中断), 要进入真正的中断处理流程ufshcd_sl_intr,还需要满足0x24REG_INTERRUPT_ENABLE对应的中断使能开关是否打开(即产生这个中断之后,控制器是否会响应,是否会去处理,是否会去执行中断处理函数),同时满足中断状态位和中断使能位置1后,才进入ufshcd_sl_intr,进入ufshcd_sl_intr之后,我们会先检查是位否控制器中断状态位是否有错误的中断,检查中断为正常中断后,就走中断完成的处理流程处理,我就以UTP Transfer Complete传输中断完成为例:首先是reset interrupt agreegation counter(首先要支持interrupt agreegation), 然后会读取door_bell的值tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); 然后会选择出UTP完成的req: completed_reqs = tr_doorbell ^ hba->outstanding_reqs(这里outstanding_reqs在传输完成之前都是为1,只有在中断处理函数中__ufshcd_transfer_req_compl 会将hba->outstanding_reqs这个对应的bit清零,或者是在ufshcd_abort或者是send scsi cmd timeout, 其中ufshcd_abort和send scsi cmd timeout都是在异常出错的情况才清零,但是在__ufshcd_transfer_req_compl 是正常处理中断的时候清零,所以一般情况下hba->outstanding_reqs都是为1,这个也是对应软件上的door_bell, 我们可以这么理解, 硬件上的door_bell在进中断处理函数之前就清零了,但是软件上的door_bell即hba->outstanding_reqs是在进中断处理函数__ufshcd_transfer_req_compl之后清零的),然后处理对应的slot的utp传输完成的中断处理流程__ufshcd_transfer_req_compl(hba, completed_reqs),进入之后遍历传输完成的slot去获取response status(result = ufshcd_transfer_rsp_status(hba, lrbp) ), 首先获取ocs, 获取到的ocs的状态是OCS_SUCCESS之后再去获取utp upiu 的response(result =ufshcd_get_req_response()), 执行cmd->scsi_done(cmd)回调scsi_done回调函数,然后执行__ufshcd_release释放ufs_hba, 然后会清除完成的软件door_bell即hba->outstanding_reqs
/* clear corresponding bits of completed commands */ -----> hba->outstanding_reqs ^= completed_reqs; 然后唤醒正在slot之外的pending的IO request ---->/* we might have free'd some tags above */ ----->wake_up(&hba->dev_cmd.tag_wq);
* ufshcd_intr - Main interrupt service routine
* @irq: irq number
* @__hba: pointer to adapter instance
*
* Returns IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/
static irqreturn_t ufshcd_intr(int irq, void *__hba)
{
u32 intr_status, enabled_intr_status;
irqreturn_t retval = IRQ_NONE;
struct ufs_hba *hba = __hba;
spin_lock(hba->host->host_lock);
intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
enabled_intr_status =
intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
if (intr_status)
ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
if (enabled_intr_status) {
ufshcd_sl_intr(hba, enabled_intr_status);
retval = IRQ_HANDLED;
}
spin_unlock(hba->host->host_lock);
return retval;
}
/**
* ufshcd_sl_intr - Interrupt service routine
* @hba: per adapter instance
* @intr_status: contains interrupts generated by the controller
*/
static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
{
hba->errors = UFSHCD_ERROR_MASK & intr_status;
if (hba->errors)
ufshcd_check_errors(hba);
if (intr_status & UFSHCD_UIC_MASK)
ufshcd_uic_cmd_compl(hba, intr_status);
if (intr_status & UTP_TASK_REQ_COMPL)
ufshcd_tmc_handler(hba);
if (intr_status & UTP_TRANSFER_REQ_COMPL)
ufshcd_transfer_req_compl(hba);
}
/**
* ufshcd_transfer_req_compl - handle SCSI and query command completion
* @hba: per adapter instance
*/
static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
{
unsigned long completed_reqs;
u32 tr_doorbell;
/* Resetting interrupt aggregation counters first and reading the
* DOOR_BELL afterward allows us to handle all the completed requests.
* In order to prevent other interrupts starvation the DB is read once
* after reset. The down side of this solution is the possibility of
* false interrupt if device completes another request after resetting
* aggregation and before reading the DB.
*/
if (ufshcd_is_intr_aggr_allowed(hba))
ufshcd_reset_intr_aggr(hba);
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
__ufshcd_transfer_req_compl(hba, completed_reqs);
}
/**
* __ufshcd_transfer_req_compl - handle SCSI and query command completion
* @hba: per adapter instance
* @completed_reqs: requests to complete
*/
static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
unsigned long completed_reqs)
{
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
int result;
int index;
for_each_set_bit(index, &completed_reqs, hba->nutrs) {
lrbp = &hba->lrb[index];
cmd = lrbp->cmd;
if (cmd) {
ufshcd_add_command_trace(hba, index, "complete");
result = ufshcd_transfer_rsp_status(hba, lrbp);
scsi_dma_unmap(cmd);
cmd->result = result;
/* Mark completed command as NULL in LRB */
lrbp->cmd = NULL;
clear_bit_unlock(index, &hba->lrb_in_use);
/* Do not touch lrbp after scsi done */
cmd->scsi_done(cmd);
__ufshcd_release(hba);
} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE ||
lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) {
if (hba->dev_cmd.complete) {
ufshcd_add_command_trace(hba, index,
"dev_complete");
complete(hba->dev_cmd.complete);
}
}
if (ufshcd_is_clkscaling_supported(hba))
hba->clk_scaling.active_reqs--;
lrbp->compl_time_stamp = ktime_get();
}
/* clear corresponding bits of completed commands */
hba->outstanding_reqs ^= completed_reqs;
ufshcd_clk_scaling_update_busy(hba);
/* we might have free'd some tags above */
wake_up(&hba->dev_cmd.tag_wq);
}