diff options
Diffstat (limited to 'drivers/ufs/core/ufshcd.c')
-rw-r--r-- | drivers/ufs/core/ufshcd.c | 256 |
1 files changed, 207 insertions, 49 deletions
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 0b2dc692d1b8..ab00305be412 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -157,7 +157,6 @@ EXPORT_SYMBOL_GPL(ufshcd_dump_regs); enum { UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_ID = 1, - UFSHCD_NUM_RESERVED = 1, UFSHCD_CMD_PER_LUN = 32 - UFSHCD_NUM_RESERVED, UFSHCD_CAN_QUEUE = 32 - UFSHCD_NUM_RESERVED, }; @@ -285,7 +284,6 @@ static int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on); static int ufshcd_setup_vreg(struct ufs_hba *hba, bool on); static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, struct ufs_vreg *vreg); -static int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag); static void ufshcd_wb_toggle_buf_flush_during_h8(struct ufs_hba *hba, bool enable); static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba); @@ -2953,13 +2951,50 @@ static int ufshcd_compose_dev_cmd(struct ufs_hba *hba, } /* - * Clear all the requests from the controller for which a bit has been set in - * @mask and wait until the controller confirms that these requests have been - * cleared. + * Check with the block layer if the command is inflight + * @cmd: command to check. + * + * Returns true if command is inflight; false if not. + */ +bool ufshcd_cmd_inflight(struct scsi_cmnd *cmd) +{ + struct request *rq; + + if (!cmd) + return false; + + rq = scsi_cmd_to_rq(cmd); + if (!blk_mq_request_started(rq)) + return false; + + return true; +} + +/* + * Clear the pending command in the controller and wait until + * the controller confirms that the command has been cleared. + * @hba: per adapter instance + * @task_tag: The tag number of the command to be cleared. */ -static int ufshcd_clear_cmds(struct ufs_hba *hba, u32 mask) +static int ufshcd_clear_cmd(struct ufs_hba *hba, u32 task_tag) { + u32 mask = 1U << task_tag; unsigned long flags; + int err; + + if (is_mcq_enabled(hba)) { + /* + * MCQ mode. Clean up the MCQ resources similar to + * what the ufshcd_utrl_clear() does for SDB mode. + */ + err = ufshcd_mcq_sq_cleanup(hba, task_tag); + if (err) { + dev_err(hba->dev, "%s: failed tag=%d. err=%d\n", + __func__, task_tag, err); + return err; + } + return 0; + } /* clear outstanding transaction before retry */ spin_lock_irqsave(hba->host->host_lock, flags); @@ -3060,7 +3095,16 @@ retry: err = -ETIMEDOUT; dev_dbg(hba->dev, "%s: dev_cmd request timedout, tag %d\n", __func__, lrbp->task_tag); - if (ufshcd_clear_cmds(hba, 1U << lrbp->task_tag) == 0) { + + /* MCQ mode */ + if (is_mcq_enabled(hba)) { + err = ufshcd_clear_cmd(hba, lrbp->task_tag); + hba->dev_cmd.complete = NULL; + return err; + } + + /* SDB mode */ + if (ufshcd_clear_cmd(hba, lrbp->task_tag) == 0) { /* successfully cleared the command, retry if needed */ err = -EAGAIN; /* @@ -3822,10 +3866,8 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) /* Configure UTRD with command descriptor base address */ cmd_desc_element_addr = (cmd_desc_dma_addr + (cmd_desc_size * i)); - utrdlp[i].command_desc_base_addr_lo = - cpu_to_le32(lower_32_bits(cmd_desc_element_addr)); - utrdlp[i].command_desc_base_addr_hi = - cpu_to_le32(upper_32_bits(cmd_desc_element_addr)); + utrdlp[i].command_desc_base_addr = + cpu_to_le64(cmd_desc_element_addr); /* Response upiu and prdt offset should be in double words */ if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) { @@ -5370,8 +5412,8 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) } /* Release the resources allocated for processing a SCSI command. */ -static void ufshcd_release_scsi_cmd(struct ufs_hba *hba, - struct ufshcd_lrb *lrbp) +void ufshcd_release_scsi_cmd(struct ufs_hba *hba, + struct ufshcd_lrb *lrbp) { struct scsi_cmnd *cmd = lrbp->cmd; @@ -5486,6 +5528,57 @@ static int ufshcd_poll(struct Scsi_Host *shost, unsigned int queue_num) } /** + * ufshcd_mcq_compl_pending_transfer - MCQ mode function. It is + * invoked from the error handler context or ufshcd_host_reset_and_restore() + * to complete the pending transfers and free the resources associated with + * the scsi command. + * + * @hba: per adapter instance + * @force_compl: This flag is set to true when invoked + * from ufshcd_host_reset_and_restore() in which case it requires special + * handling because the host controller has been reset by ufshcd_hba_stop(). + */ +static void ufshcd_mcq_compl_pending_transfer(struct ufs_hba *hba, + bool force_compl) +{ + struct ufs_hw_queue *hwq; + struct ufshcd_lrb *lrbp; + struct scsi_cmnd *cmd; + unsigned long flags; + u32 hwq_num, utag; + int tag; + + for (tag = 0; tag < hba->nutrs; tag++) { + lrbp = &hba->lrb[tag]; + cmd = lrbp->cmd; + if (!ufshcd_cmd_inflight(cmd) || + test_bit(SCMD_STATE_COMPLETE, &cmd->state)) + continue; + + utag = blk_mq_unique_tag(scsi_cmd_to_rq(cmd)); + hwq_num = blk_mq_unique_tag_to_hwq(utag); + hwq = &hba->uhq[hwq_num + UFSHCD_MCQ_IO_QUEUE_OFFSET]; + + if (force_compl) { + ufshcd_mcq_compl_all_cqes_lock(hba, hwq); + /* + * For those cmds of which the cqes are not present + * in the cq, complete them explicitly. + */ + if (cmd && !test_bit(SCMD_STATE_COMPLETE, &cmd->state)) { + spin_lock_irqsave(&hwq->cq_lock, flags); + set_host_byte(cmd, DID_REQUEUE); + ufshcd_release_scsi_cmd(hba, lrbp); + scsi_done(cmd); + spin_unlock_irqrestore(&hwq->cq_lock, flags); + } + } else { + ufshcd_mcq_poll_cqe_lock(hba, hwq); + } + } +} + +/** * ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance * @@ -6049,9 +6142,13 @@ out: } /* Complete requests that have door-bell cleared */ -static void ufshcd_complete_requests(struct ufs_hba *hba) +static void ufshcd_complete_requests(struct ufs_hba *hba, bool force_compl) { - ufshcd_transfer_req_compl(hba); + if (is_mcq_enabled(hba)) + ufshcd_mcq_compl_pending_transfer(hba, force_compl); + else + ufshcd_transfer_req_compl(hba); + ufshcd_tmc_handler(hba); } @@ -6292,18 +6389,36 @@ static bool ufshcd_abort_all(struct ufs_hba *hba) bool needs_reset = false; int tag, ret; - /* Clear pending transfer requests */ - for_each_set_bit(tag, &hba->outstanding_reqs, hba->nutrs) { - ret = ufshcd_try_to_abort_task(hba, tag); - dev_err(hba->dev, "Aborting tag %d / CDB %#02x %s\n", tag, - hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1, - ret ? "failed" : "succeeded"); - if (ret) { - needs_reset = true; - goto out; + if (is_mcq_enabled(hba)) { + struct ufshcd_lrb *lrbp; + int tag; + + for (tag = 0; tag < hba->nutrs; tag++) { + lrbp = &hba->lrb[tag]; + if (!ufshcd_cmd_inflight(lrbp->cmd)) + continue; + ret = ufshcd_try_to_abort_task(hba, tag); + dev_err(hba->dev, "Aborting tag %d / CDB %#02x %s\n", tag, + hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1, + ret ? "failed" : "succeeded"); + if (ret) { + needs_reset = true; + goto out; + } + } + } else { + /* Clear pending transfer requests */ + for_each_set_bit(tag, &hba->outstanding_reqs, hba->nutrs) { + ret = ufshcd_try_to_abort_task(hba, tag); + dev_err(hba->dev, "Aborting tag %d / CDB %#02x %s\n", tag, + hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1, + ret ? "failed" : "succeeded"); + if (ret) { + needs_reset = true; + goto out; + } } } - /* Clear pending task management requests */ for_each_set_bit(tag, &hba->outstanding_tasks, hba->nutmrs) { if (ufshcd_clear_tm_cmd(hba, tag)) { @@ -6314,7 +6429,7 @@ static bool ufshcd_abort_all(struct ufs_hba *hba) out: /* Complete the requests that are cleared by s/w */ - ufshcd_complete_requests(hba); + ufshcd_complete_requests(hba, false); return needs_reset; } @@ -6354,7 +6469,7 @@ static void ufshcd_err_handler(struct work_struct *work) spin_unlock_irqrestore(hba->host->host_lock, flags); ufshcd_err_handling_prepare(hba); /* Complete requests that have door-bell cleared by h/w */ - ufshcd_complete_requests(hba); + ufshcd_complete_requests(hba, false); spin_lock_irqsave(hba->host->host_lock, flags); again: needs_restore = false; @@ -6725,7 +6840,7 @@ static irqreturn_t ufshcd_handle_mcq_cq_events(struct ufs_hba *hba) ufshcd_mcq_write_cqis(hba, events, i); if (events & UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS) - ufshcd_mcq_poll_cqe_nolock(hba, hwq); + ufshcd_mcq_poll_cqe_lock(hba, hwq); } return IRQ_HANDLED; @@ -7235,7 +7350,9 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) unsigned long flags, pending_reqs = 0, not_cleared = 0; struct Scsi_Host *host; struct ufs_hba *hba; - u32 pos; + struct ufs_hw_queue *hwq; + struct ufshcd_lrb *lrbp; + u32 pos, not_cleared_mask = 0; int err; u8 resp = 0xF, lun; @@ -7250,6 +7367,20 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) goto out; } + if (is_mcq_enabled(hba)) { + for (pos = 0; pos < hba->nutrs; pos++) { + lrbp = &hba->lrb[pos]; + if (ufshcd_cmd_inflight(lrbp->cmd) && + lrbp->lun == lun) { + ufshcd_clear_cmd(hba, pos); + hwq = ufshcd_mcq_req_to_hwq(hba, scsi_cmd_to_rq(lrbp->cmd)); + ufshcd_mcq_poll_cqe_lock(hba, hwq); + } + } + err = 0; + goto out; + } + /* clear the commands that were pending for corresponding LUN */ spin_lock_irqsave(&hba->outstanding_lock, flags); for_each_set_bit(pos, &hba->outstanding_reqs, hba->nutrs) @@ -7258,17 +7389,20 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) hba->outstanding_reqs &= ~pending_reqs; spin_unlock_irqrestore(&hba->outstanding_lock, flags); - if (ufshcd_clear_cmds(hba, pending_reqs) < 0) { - spin_lock_irqsave(&hba->outstanding_lock, flags); - not_cleared = pending_reqs & - ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); - hba->outstanding_reqs |= not_cleared; - spin_unlock_irqrestore(&hba->outstanding_lock, flags); + for_each_set_bit(pos, &pending_reqs, hba->nutrs) { + if (ufshcd_clear_cmd(hba, pos) < 0) { + spin_lock_irqsave(&hba->outstanding_lock, flags); + not_cleared = 1U << pos & + ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + hba->outstanding_reqs |= not_cleared; + not_cleared_mask |= not_cleared; + spin_unlock_irqrestore(&hba->outstanding_lock, flags); - dev_err(hba->dev, "%s: failed to clear requests %#lx\n", - __func__, not_cleared); + dev_err(hba->dev, "%s: failed to clear request %d\n", + __func__, pos); + } } - __ufshcd_transfer_req_compl(hba, pending_reqs & ~not_cleared); + __ufshcd_transfer_req_compl(hba, pending_reqs & ~not_cleared_mask); out: hba->req_abort_count = 0; @@ -7306,7 +7440,7 @@ static void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap) * * Returns zero on success, non-zero on failure */ -static int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) +int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) { struct ufshcd_lrb *lrbp = &hba->lrb[tag]; int err = 0; @@ -7329,6 +7463,20 @@ static int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) */ dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n", __func__, tag); + if (is_mcq_enabled(hba)) { + /* MCQ mode */ + if (ufshcd_cmd_inflight(lrbp->cmd)) { + /* sleep for max. 200us same delay as in SDB mode */ + usleep_range(100, 200); + continue; + } + /* command completed already */ + dev_err(hba->dev, "%s: cmd at tag=%d is cleared.\n", + __func__, tag); + goto out; + } + + /* Single Doorbell Mode */ reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); if (reg & (1 << tag)) { /* sleep for max. 200us to stabilize */ @@ -7365,7 +7513,7 @@ static int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) goto out; } - err = ufshcd_clear_cmds(hba, 1U << tag); + err = ufshcd_clear_cmd(hba, tag); if (err) dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n", __func__, tag, err); @@ -7394,13 +7542,16 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) WARN_ONCE(tag < 0, "Invalid tag %d\n", tag); ufshcd_hold(hba); - reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); - /* If command is already aborted/completed, return FAILED. */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - dev_err(hba->dev, - "%s: cmd at tag %d already completed, outstanding=0x%lx, doorbell=0x%x\n", - __func__, tag, hba->outstanding_reqs, reg); - goto release; + + if (!is_mcq_enabled(hba)) { + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (!test_bit(tag, &hba->outstanding_reqs)) { + /* If command is already aborted/completed, return FAILED. */ + dev_err(hba->dev, + "%s: cmd at tag %d already completed, outstanding=0x%lx, doorbell=0x%x\n", + __func__, tag, hba->outstanding_reqs, reg); + goto release; + } } /* Print Transfer Request of aborted task */ @@ -7425,7 +7576,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) } hba->req_abort_count++; - if (!(reg & (1 << tag))) { + if (!is_mcq_enabled(hba) && !(reg & (1 << tag))) { + /* only execute this code in single doorbell mode */ dev_err(hba->dev, "%s: cmd was completed, but without a notifying intr, tag = %d", __func__, tag); @@ -7451,6 +7603,12 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) goto release; } + if (is_mcq_enabled(hba)) { + /* MCQ mode. Branch off to handle abort for mcq mode */ + err = ufshcd_mcq_abort(cmd); + goto release; + } + /* Skip task abort in case previous aborts failed and report failure */ if (lrbp->req_abort_skip) { dev_err(hba->dev, "%s: skipping abort\n", __func__); @@ -7506,7 +7664,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) ufshpb_toggle_state(hba, HPB_PRESENT, HPB_RESET); ufshcd_hba_stop(hba); hba->silence_err_logs = true; - ufshcd_complete_requests(hba); + ufshcd_complete_requests(hba, true); hba->silence_err_logs = false; /* scale up clocks to max frequency before full reinitialization */ |