scsi: ufs: Add API to execute raw upiu commands
The UFS host software uses a combination of a host register set and Transfer Request Descriptors in system memory to communicate with host controller hardware. In its mmio space, a separate places are assigned to UTP Transfer Request Descriptor ("utrd") list, and to UTP Task Management Request Descriptor ("utmrd") list. The provided API supports utrd-typed requests: nop out and device management commands. It also supports utmrd-type requests: task management requests. Other UPIU types are not supported for now. We utilize the already existing code for tag and task work queues. That is, all utrd-typed UPIUs are "disguised" as device management commands. Similarly, the utmrd-typed UPUIs uses the task management infrastructure. It is up to the caller to fill the upiu request properly, as it will be copied without any further input validations. Signed-off-by: Avri Altman <avri.altman@wdc.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Bart Van Assche <Bart.VanAssche@wdc.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
220d17a69d
commit
5e0a86eed8
|
@ -414,6 +414,7 @@ enum {
|
|||
MASK_RSP_UPIU_DATA_SEG_LEN = 0xFFFF,
|
||||
MASK_RSP_EXCEPTION_EVENT = 0x10000,
|
||||
MASK_TM_SERVICE_RESP = 0xFF,
|
||||
MASK_TM_FUNC = 0xFF,
|
||||
};
|
||||
|
||||
/* Task management service response */
|
||||
|
|
|
@ -5675,6 +5675,182 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_issue_devman_upiu_cmd - API for sending "utrd" type requests
|
||||
* @hba: per-adapter instance
|
||||
* @req_upiu: upiu request
|
||||
* @rsp_upiu: upiu reply
|
||||
* @msgcode: message code, one of UPIU Transaction Codes Initiator to Target
|
||||
* @desc_buff: pointer to descriptor buffer, NULL if NA
|
||||
* @buff_len: descriptor size, 0 if NA
|
||||
* @desc_op: descriptor operation
|
||||
*
|
||||
* Those type of requests uses UTP Transfer Request Descriptor - utrd.
|
||||
* Therefore, it "rides" the device management infrastructure: uses its tag and
|
||||
* tasks work queues.
|
||||
*
|
||||
* Since there is only one available tag for device management commands,
|
||||
* the caller is expected to hold the hba->dev_cmd.lock mutex.
|
||||
*/
|
||||
static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba,
|
||||
struct utp_upiu_req *req_upiu,
|
||||
struct utp_upiu_req *rsp_upiu,
|
||||
u8 *desc_buff, int *buff_len,
|
||||
int cmd_type,
|
||||
enum query_opcode desc_op)
|
||||
{
|
||||
struct ufshcd_lrb *lrbp;
|
||||
int err = 0;
|
||||
int tag;
|
||||
struct completion wait;
|
||||
unsigned long flags;
|
||||
u32 upiu_flags;
|
||||
|
||||
down_read(&hba->clk_scaling_lock);
|
||||
|
||||
wait_event(hba->dev_cmd.tag_wq, ufshcd_get_dev_cmd_tag(hba, &tag));
|
||||
|
||||
init_completion(&wait);
|
||||
lrbp = &hba->lrb[tag];
|
||||
WARN_ON(lrbp->cmd);
|
||||
|
||||
lrbp->cmd = NULL;
|
||||
lrbp->sense_bufflen = 0;
|
||||
lrbp->sense_buffer = NULL;
|
||||
lrbp->task_tag = tag;
|
||||
lrbp->lun = 0;
|
||||
lrbp->intr_cmd = true;
|
||||
hba->dev_cmd.type = cmd_type;
|
||||
|
||||
switch (hba->ufs_version) {
|
||||
case UFSHCI_VERSION_10:
|
||||
case UFSHCI_VERSION_11:
|
||||
lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
|
||||
break;
|
||||
default:
|
||||
lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* update the task tag in the request upiu */
|
||||
req_upiu->header.dword_0 |= cpu_to_be32(tag);
|
||||
|
||||
ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE);
|
||||
|
||||
/* just copy the upiu request as it is */
|
||||
memcpy(lrbp->ucd_req_ptr, req_upiu, sizeof(*lrbp->ucd_req_ptr));
|
||||
if (desc_buff && desc_op == UPIU_QUERY_OPCODE_WRITE_DESC) {
|
||||
/* The Data Segment Area is optional depending upon the query
|
||||
* function value. for WRITE DESCRIPTOR, the data segment
|
||||
* follows right after the tsf.
|
||||
*/
|
||||
memcpy(lrbp->ucd_req_ptr + 1, desc_buff, *buff_len);
|
||||
*buff_len = 0;
|
||||
}
|
||||
|
||||
memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
|
||||
|
||||
hba->dev_cmd.complete = &wait;
|
||||
|
||||
/* Make sure descriptors are ready before ringing the doorbell */
|
||||
wmb();
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
ufshcd_send_command(hba, tag);
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
|
||||
/*
|
||||
* ignore the returning value here - ufshcd_check_query_response is
|
||||
* bound to fail since dev_cmd.query and dev_cmd.type were left empty.
|
||||
* read the response directly ignoring all errors.
|
||||
*/
|
||||
ufshcd_wait_for_dev_cmd(hba, lrbp, QUERY_REQ_TIMEOUT);
|
||||
|
||||
/* just copy the upiu response as it is */
|
||||
memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu));
|
||||
|
||||
ufshcd_put_dev_cmd_tag(hba, tag);
|
||||
wake_up(&hba->dev_cmd.tag_wq);
|
||||
up_read(&hba->clk_scaling_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_exec_raw_upiu_cmd - API function for sending raw upiu commands
|
||||
* @hba: per-adapter instance
|
||||
* @req_upiu: upiu request
|
||||
* @rsp_upiu: upiu reply - only 8 DW as we do not support scsi commands
|
||||
* @msgcode: message code, one of UPIU Transaction Codes Initiator to Target
|
||||
* @desc_buff: pointer to descriptor buffer, NULL if NA
|
||||
* @buff_len: descriptor size, 0 if NA
|
||||
* @desc_op: descriptor operation
|
||||
*
|
||||
* Supports UTP Transfer requests (nop and query), and UTP Task
|
||||
* Management requests.
|
||||
* It is up to the caller to fill the upiu conent properly, as it will
|
||||
* be copied without any further input validations.
|
||||
*/
|
||||
int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
|
||||
struct utp_upiu_req *req_upiu,
|
||||
struct utp_upiu_req *rsp_upiu,
|
||||
int msgcode,
|
||||
u8 *desc_buff, int *buff_len,
|
||||
enum query_opcode desc_op)
|
||||
{
|
||||
int err;
|
||||
int cmd_type = DEV_CMD_TYPE_QUERY;
|
||||
struct utp_task_req_desc treq = { { 0 }, };
|
||||
int ocs_value;
|
||||
u8 tm_f = be32_to_cpu(req_upiu->header.dword_1) >> 16 & MASK_TM_FUNC;
|
||||
|
||||
if (desc_buff && desc_op != UPIU_QUERY_OPCODE_WRITE_DESC) {
|
||||
err = -ENOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (msgcode) {
|
||||
case UPIU_TRANSACTION_NOP_OUT:
|
||||
cmd_type = DEV_CMD_TYPE_NOP;
|
||||
/* fall through */
|
||||
case UPIU_TRANSACTION_QUERY_REQ:
|
||||
ufshcd_hold(hba, false);
|
||||
mutex_lock(&hba->dev_cmd.lock);
|
||||
err = ufshcd_issue_devman_upiu_cmd(hba, req_upiu, rsp_upiu,
|
||||
desc_buff, buff_len,
|
||||
cmd_type, desc_op);
|
||||
mutex_unlock(&hba->dev_cmd.lock);
|
||||
ufshcd_release(hba);
|
||||
|
||||
break;
|
||||
case UPIU_TRANSACTION_TASK_REQ:
|
||||
treq.header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
|
||||
treq.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
|
||||
|
||||
memcpy(&treq.req_header, req_upiu, sizeof(*req_upiu));
|
||||
|
||||
err = __ufshcd_issue_tm_cmd(hba, &treq, tm_f);
|
||||
if (err == -ETIMEDOUT)
|
||||
break;
|
||||
|
||||
ocs_value = le32_to_cpu(treq.header.dword_2) & MASK_OCS;
|
||||
if (ocs_value != OCS_SUCCESS) {
|
||||
dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", __func__,
|
||||
ocs_value);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(rsp_upiu, &treq.rsp_header, sizeof(*rsp_upiu));
|
||||
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_eh_device_reset_handler - device reset handler registered to
|
||||
* scsi layer.
|
||||
|
|
|
@ -895,6 +895,13 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
|
|||
|
||||
u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba);
|
||||
|
||||
int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
|
||||
struct utp_upiu_req *req_upiu,
|
||||
struct utp_upiu_req *rsp_upiu,
|
||||
int msgcode,
|
||||
u8 *desc_buff, int *buff_len,
|
||||
enum query_opcode desc_op);
|
||||
|
||||
/* Wrapper functions for safely calling variant operations */
|
||||
static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue