scsi: ufs: Support clearing multiple commands at once

Modify ufshcd_clear_cmd() such that it supports clearing multiple commands
at once instead of one command at a time. This change will be used in a
later patch to reduce the time spent in the reset handler.

Link: https://lore.kernel.org/r/20220613214442.212466-3-bvanassche@acm.org
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Bart Van Assche 2022-06-13 14:44:41 -07:00 committed by Martin K. Petersen
parent da8badd7d3
commit d1a7644648
1 changed files with 28 additions and 14 deletions

View File

@ -748,17 +748,28 @@ static enum utp_ocs ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
}
/**
* ufshcd_utrl_clear - Clear a bit in UTRLCLR register
* ufshcd_utrl_clear() - Clear requests from the controller request list.
* @hba: per adapter instance
* @pos: position of the bit to be cleared
* @mask: mask with one bit set for each request to be cleared
*/
static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos)
static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 mask)
{
if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR)
ufshcd_writel(hba, (1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR);
else
ufshcd_writel(hba, ~(1 << pos),
REG_UTP_TRANSFER_REQ_LIST_CLEAR);
mask = ~mask;
/*
* From the UFSHCI specification: "UTP Transfer Request List CLear
* Register (UTRLCLR): This field is bit significant. Each bit
* corresponds to a slot in the UTP Transfer Request List, where bit 0
* corresponds to request slot 0. A bit in this field is set to 0
* by host software to indicate to the host controller that a transfer
* request slot is cleared. The host controller
* shall free up any resources associated to the request slot
* immediately, and shall set the associated bit in UTRLDBR to 0. The
* host software indicates no change to request slots by setting the
* associated bits in this field to 1. Bits in this field shall only
* be set 1 or 0 by host software when UTRLRSR is set to 1."
*/
ufshcd_writel(hba, ~mask, REG_UTP_TRANSFER_REQ_LIST_CLEAR);
}
/**
@ -2863,15 +2874,18 @@ static int ufshcd_compose_dev_cmd(struct ufs_hba *hba,
return ufshcd_compose_devman_upiu(hba, lrbp);
}
static int
ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
/*
* 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.
*/
static int ufshcd_clear_cmds(struct ufs_hba *hba, u32 mask)
{
unsigned long flags;
u32 mask = 1 << tag;
/* clear outstanding transaction before retry */
spin_lock_irqsave(hba->host->host_lock, flags);
ufshcd_utrl_clear(hba, tag);
ufshcd_utrl_clear(hba, mask);
spin_unlock_irqrestore(hba->host->host_lock, flags);
/*
@ -2959,7 +2973,7 @@ static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
err = -ETIMEDOUT;
dev_dbg(hba->dev, "%s: dev_cmd request timedout, tag %d\n",
__func__, lrbp->task_tag);
if (!ufshcd_clear_cmd(hba, lrbp->task_tag))
if (!ufshcd_clear_cmds(hba, 1U << lrbp->task_tag))
/* successfully cleared the command, retry if needed */
err = -EAGAIN;
/*
@ -6982,7 +6996,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
/* clear the commands that were pending for corresponding LUN */
for_each_set_bit(pos, &hba->outstanding_reqs, hba->nutrs) {
if (hba->lrb[pos].lun == lun) {
err = ufshcd_clear_cmd(hba, pos);
err = ufshcd_clear_cmds(hba, 1U << pos);
if (err)
break;
__ufshcd_transfer_req_compl(hba, 1U << pos);
@ -7084,7 +7098,7 @@ static int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag)
goto out;
}
err = ufshcd_clear_cmd(hba, tag);
err = ufshcd_clear_cmds(hba, 1U << tag);
if (err)
dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
__func__, tag, err);