UFS 的個人理解

 

一 . 控制器層 

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 = <GICxx  中斷號  中斷觸發方式>), 註冊的是GIC共享中斷

/* 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);
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章