On 5/7/2013 2:57 PM, Seungwon Jeon wrote: > On Monday, May 06, 2013, Sujit Reddy Thumma wrote: >> As part of device initialization sequence, sending NOP OUT UPIU and >> waiting for NOP IN UPIU response is mandatory. This confirms that the >> device UFS Transport (UTP) layer is functional and the host can configure >> the device with further commands. Add support for sending NOP OUT UPIU to >> check the device connection path and test whether the UTP layer on the >> device side is functional during initialization. >> >> Signed-off-by: Sujit Reddy Thumma <sthumma@xxxxxxxxxxxxxx> >> >> --- >> v3: >> - minor bug fix in error path >> v2: >> - fixed INTERNAL_CMD_TAG check in readl_poll_timeout >> - minor cleanup from v1 >> - rebased on Seungwon Jeon's UFS V3 patchset >> --- >> drivers/scsi/ufs/ufshcd.c | 163 ++++++++++++++++++++++++++++++++++++++++++--- >> drivers/scsi/ufs/ufshcd.h | 4 + >> 2 files changed, 158 insertions(+), 9 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >> index 3607ffb..4090f6c 100644 >> --- a/drivers/scsi/ufs/ufshcd.c >> +++ b/drivers/scsi/ufs/ufshcd.c >> @@ -34,6 +34,7 @@ >> */ >> >> #include <linux/async.h> >> +#include <linux/iopoll.h> > linux/iopoll.h: No such file or directory > Please check. > >> >> #include "ufshcd.h" >> >> @@ -44,6 +45,13 @@ >> /* UIC command timeout, unit: ms */ >> #define UIC_CMD_TIMEOUT 500 >> >> +/* NOP OUT retries waiting for NOP IN response */ >> +#define NOP_OUT_RETRIES 10 >> +/* Timeout after 30 msecs if NOP OUT hangs without response */ >> +#define NOP_OUT_TIMEOUT 30 /* msecs */ >> +/* Reserved tag for internal commands */ >> +#define INTERNAL_CMD_TAG 0 >> + >> enum { >> UFSHCD_MAX_CHANNEL = 0, >> UFSHCD_MAX_ID = 1, >> @@ -630,7 +638,7 @@ static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags) >> { >> struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr; >> enum dma_data_direction cmd_dir = >> - lrbp->cmd->sc_data_direction; >> + lrbp->cmd ? lrbp->cmd->sc_data_direction : DMA_NONE; >> u32 data_direction; >> u32 dword_0; >> >> @@ -647,6 +655,8 @@ static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags) >> >> dword_0 = data_direction | (lrbp->command_type >> << UPIU_COMMAND_TYPE_OFFSET); >> + if (lrbp->intr_cmd) >> + dword_0 |= UTP_REQ_DESC_INT_CMD; >> >> /* Transfer request descriptor header fields */ >> req_desc->header.dword_0 = cpu_to_le32(dword_0); >> @@ -735,6 +745,18 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, >> >> } >> >> +static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp) >> +{ >> + struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr; >> + >> + memset(ucd_req_ptr, 0, sizeof(struct utp_upiu_req)); >> + >> + /* command descriptor fields */ >> + ucd_req_ptr->header.dword_0 = >> + UPIU_HEADER_DWORD( >> + UPIU_TRANSACTION_NOP_OUT, 0, 0, lrbp->task_tag); >> +} >> + >> /** >> * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU) >> * @hba - UFS hba >> @@ -749,11 +771,13 @@ static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) >> case UTP_CMD_TYPE_SCSI: >> case UTP_CMD_TYPE_DEV_MANAGE: >> ufshcd_prepare_req_desc(lrbp, &upiu_flags); >> - if (lrbp->command_type == UTP_CMD_TYPE_SCSI) >> + if (lrbp->cmd && lrbp->command_type == UTP_CMD_TYPE_SCSI) >> ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags); >> - else >> + else if (lrbp->cmd && ufshcd_is_query_req(lrbp)) >> ufshcd_prepare_utp_query_req_upiu(hba, lrbp, >> upiu_flags); >> + else if (!lrbp->cmd) >> + ufshcd_prepare_utp_nop_upiu(lrbp); >> break; >> case UTP_CMD_TYPE_UFS: >> /* For UFS native command implementation */ >> @@ -802,6 +826,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) >> lrbp->sense_buffer = cmd->sense_buffer; >> lrbp->task_tag = tag; >> lrbp->lun = cmd->device->lun; >> + lrbp->intr_cmd = false; >> >> if (ufshcd_is_query_req(lrbp)) >> lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE; >> @@ -1261,6 +1286,103 @@ int ufshcd_dme_endpt_reset(struct ufs_hba *hba) >> } >> EXPORT_SYMBOL_GPL(ufshcd_dme_endpt_reset); >> >> +static int >> +ufshcd_compose_nop_out_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) >> +{ >> + lrbp->cmd = NULL; >> + lrbp->sense_bufflen = 0; >> + lrbp->sense_buffer = NULL; >> + lrbp->task_tag = INTERNAL_CMD_TAG; >> + lrbp->lun = 0; /* NOP OUT is not specific to any LUN */ >> + lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE; >> + lrbp->intr_cmd = true; /* No interrupt aggregation */ >> + >> + return ufshcd_compose_upiu(hba, lrbp); >> +} >> + >> +static int ufshcd_wait_for_nop_cmd(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) >> +{ >> + int err = 0; >> + unsigned long timeout; >> + unsigned long flags; >> + >> + timeout = wait_for_completion_timeout( >> + lrbp->completion, msecs_to_jiffies(NOP_OUT_TIMEOUT)); >> + >> + if (timeout) { >> + spin_lock_irqsave(hba->host->host_lock, flags); >> + err = ufshcd_get_tr_ocs(lrbp); >> + spin_unlock_irqrestore(hba->host->host_lock, flags); >> + } else { >> + err = -ETIMEDOUT; >> + } >> + >> + return err; >> +} >> + >> +/** >> + * ufshcd_validate_dev_connection() - Check device connection status >> + * @hba: per-adapter instance >> + * >> + * Send NOP OUT UPIU and wait for NOP IN response to check whether the >> + * device Transport Protocol (UTP) layer is ready after a reset. >> + * If the UTP layer at the device side is not initialized, it may >> + * not respond with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT >> + * and we retry sending NOP OUT for %NOP_OUT_RETRIES iterations. >> + */ >> +static int ufshcd_validate_dev_connection(struct ufs_hba *hba) >> +{ >> + int err; >> + struct ufshcd_lrb *lrbp; >> + unsigned long flags; >> + struct completion wait; >> + int retries = NOP_OUT_RETRIES; >> + >> +retry: >> + init_completion(&wait); >> + >> + spin_lock_irqsave(hba->host->host_lock, flags); >> + lrbp = &hba->lrb[INTERNAL_CMD_TAG]; >> + err = ufshcd_compose_nop_out_upiu(hba, lrbp); >> + if (unlikely(err)) { >> + spin_unlock_irqrestore(hba->host->host_lock, flags); >> + goto out; >> + } >> + >> + lrbp->completion = &wait; >> + ufshcd_send_command(hba, INTERNAL_CMD_TAG); >> + spin_unlock_irqrestore(hba->host->host_lock, flags); >> + >> + err = ufshcd_wait_for_nop_cmd(hba, lrbp); >> + >> + if (err == -ETIMEDOUT) { >> + u32 reg; >> + >> + /* clear outstanding transaction before retry */ >> + spin_lock_irqsave(hba->host->host_lock, flags); >> + ufshcd_utrl_clear(hba, INTERNAL_CMD_TAG); >> + __clear_bit(INTERNAL_CMD_TAG, &hba->outstanding_reqs); >> + spin_unlock_irqrestore(hba->host->host_lock, flags); >> + >> + /* poll for max. 1 sec to clear door bell register by h/w */ >> + if (readl_poll_timeout( >> + hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL, >> + reg, !(reg & (1 << INTERNAL_CMD_TAG)), >> + 1000, 1000)) >> + retries = 0; > Also, readl_poll_timeout function is not present in Linux kernel. > Could you check your kernel? Ah.. Sorry about that. Just realized that the API and iopoll.h exist only in codeaurora kernel versions. Thanks for pointing this out. > You should implement this part in other way. > Additionally, is there no need to clear 'lrbp->completion'? > It will have invalid value after current function is out. Right. Will clear it accordingly. Thanks. > > Thanks, > Seungwon Jeon > >> + } >> + >> + if (err && retries--) { >> + dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err); >> + goto retry; >> + } >> + >> +out: >> + if (err) >> + dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err); >> + return err; >> +} >> + >> /** >> * ufshcd_make_hba_operational - Make UFS controller operational >> * @hba: per adapter instance >> @@ -1734,6 +1856,16 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) >> complete(&hba->hibern8_done); >> } >> >> +/* >> + * ufshcd_is_nop_out_upiu() - check if the command is NOP OUT UPIU >> + * @lrbp: pointer to logical reference block >> + */ >> +static inline bool ufshcd_is_nop_out_upiu(struct ufshcd_lrb *lrbp) >> +{ >> + return (be32_to_cpu(lrbp->ucd_req_ptr->header.dword_0) >> 24) == >> + UPIU_TRANSACTION_NOP_OUT; >> +} >> + >> /** >> * ufshcd_transfer_req_compl - handle SCSI and query command completion >> * @hba: per adapter instance >> @@ -1745,6 +1877,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) >> u32 tr_doorbell; >> int result; >> int index; >> + bool int_aggr_reset = true; >> >> lrb = hba->lrb; >> tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); >> @@ -1752,17 +1885,20 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) >> >> for (index = 0; index < hba->nutrs; index++) { >> if (test_bit(index, &completed_reqs)) { >> - >> - result = ufshcd_transfer_rsp_status(hba, &lrb[index]); >> - >> if (lrb[index].cmd) { >> + result = ufshcd_transfer_rsp_status( >> + hba, &lrb[index]); >> scsi_dma_unmap(lrb[index].cmd); >> lrb[index].cmd->result = result; >> lrb[index].cmd->scsi_done(lrb[index].cmd); >> >> /* Mark completed command as NULL in LRB */ >> lrb[index].cmd = NULL; >> + } else if (ufshcd_is_nop_out_upiu(&lrb[index])) { >> + complete(lrb[index].completion); >> } >> + /* Don't reset counters for interrupt cmd */ >> + int_aggr_reset = lrb[index].intr_cmd ? false : true; >> } /* end of if */ >> } /* end of for */ >> >> @@ -1770,7 +1906,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) >> hba->outstanding_reqs ^= completed_reqs; >> >> /* Reset interrupt aggregation counters */ >> - ufshcd_config_int_aggr(hba, INT_AGGR_RESET); >> + if (int_aggr_reset) >> + ufshcd_config_int_aggr(hba, INT_AGGR_RESET); >> } >> >> /** >> @@ -2068,8 +2205,16 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) >> int ret; >> >> ret = ufshcd_link_startup(hba); >> - if (!ret) >> - scsi_scan_host(hba->host); >> + if (ret) >> + goto out; >> + >> + ret = ufshcd_validate_dev_connection(hba); >> + if (ret) >> + goto out; >> + >> + scsi_scan_host(hba->host); >> +out: >> + return; >> } >> >> static struct scsi_host_template ufshcd_driver_template = { >> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h >> index 84f758e..17f4995 100644 >> --- a/drivers/scsi/ufs/ufshcd.h >> +++ b/drivers/scsi/ufs/ufshcd.h >> @@ -102,6 +102,8 @@ struct uic_command { >> * @command_type: SCSI, UFS, Query. >> * @task_tag: Task tag of the command >> * @lun: LUN of the command >> + * @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation) >> + * @completion: holds the state of completion (used for internal commands) >> */ >> struct ufshcd_lrb { >> struct utp_transfer_req_desc *utr_descriptor_ptr; >> @@ -117,6 +119,8 @@ struct ufshcd_lrb { >> int command_type; >> int task_tag; >> unsigned int lun; >> + bool intr_cmd; >> + struct completion *completion; >> }; >> >> /** >> -- >> QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member >> of Code Aurora Forum, hosted by The Linux Foundation. >> -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html