A single large request (up to 32 MiB, 128 PRDT entries) can stall on
some controllers, and a stuck request hung U-Boot forever because the
UTP completion timeout was only checked once the link had left
SLOWAUTO mode.

- reduce the maximum request size to 4 MiB (16 PRDT entries)
- always enforce the UTP completion timeout
- latch io_failed after a timeout or controller error so further
  requests fail immediately instead of reusing broken descriptors

Signed-off-by: Qing Li <[email protected]>
---
 drivers/ufs/ufs-uclass.c | 30 ++++++++++++++++++++----------
 drivers/ufs/ufs.h        |  2 ++
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 6ef01f8e4aa..7a8d3e9de53 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -60,7 +60,7 @@
 #define MAX_PRDT_ENTRY 262144
 
 /* maximum bytes per request */
-#define UFS_MAX_BYTES  (128 * 256 * 1024)
+#define UFS_MAX_BYTES  (4 * 1024 * 1024)
 
 static inline bool ufshcd_is_hba_active(struct ufs_hba *hba);
 static inline void ufshcd_hba_stop(struct ufs_hba *hba);
@@ -915,6 +915,9 @@ static int ufshcd_send_command(struct ufs_hba *hba, 
unsigned int task_tag)
        u32 intr_status;
        u32 enabled_intr_status;
 
+       if (hba->io_failed)
+               return -EIO;
+
        ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
 
        /* Make sure doorbell reg is updated before reading interrupt status */
@@ -926,16 +929,15 @@ static int ufshcd_send_command(struct ufs_hba *hba, 
unsigned int task_tag)
                enabled_intr_status = intr_status & hba->intr_mask;
                ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
 
-               if (hba->max_pwr_info.info.pwr_rx != SLOWAUTO_MODE &&
-                   hba->max_pwr_info.info.pwr_tx != SLOWAUTO_MODE) {
-                       if (get_timer(start) > QUERY_REQ_TIMEOUT) {
-                               dev_err(hba->dev,
-                                       "Timedout waiting for UTP response\n");
-                               return -ETIMEDOUT;
-                       }
+               if (get_timer(start) > QUERY_REQ_TIMEOUT) {
+                       hba->io_failed = true;
+                       dev_err(hba->dev,
+                               "Timedout waiting for UTP response\n");
+                       return -ETIMEDOUT;
                }
 
                if (enabled_intr_status & UFSHCD_ERROR_MASK) {
+                       hba->io_failed = true;
                        dev_err(hba->dev, "Error in status:%08x\n",
                                enabled_intr_status);
 
@@ -1030,6 +1032,9 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, enum 
dev_cmd_type cmd_type,
        int err;
        int resp;
 
+       if (hba->io_failed)
+               return -EIO;
+
        err = ufshcd_comp_devman_upiu(hba, cmd_type);
        if (err)
                return err;
@@ -1661,16 +1666,21 @@ static int ufs_scsi_exec(struct udevice *scsi_dev, 
struct scsi_cmd *pccb)
 {
        struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent);
        u32 upiu_flags;
-       int ocs, result = 0;
+       int err, ocs, result = 0;
        u8 scsi_status;
 
+       if (hba->io_failed)
+               return -EIO;
+
        ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, pccb->dma_dir);
        ufshcd_prepare_utp_scsi_cmd_upiu(hba, pccb, upiu_flags);
        prepare_prdt_table(hba, pccb);
 
        ufshcd_cache_flush(pccb->pdata, pccb->datalen);
 
-       ufshcd_send_command(hba, TASK_TAG);
+       err = ufshcd_send_command(hba, TASK_TAG);
+       if (err)
+               return err;
 
        ufshcd_cache_invalidate(pccb->pdata, pccb->datalen);
 
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index 0f6c93fbce7..c35f395ea7a 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -719,6 +719,8 @@ struct ufs_hba {
        struct ufs_pwr_mode_info max_pwr_info;
 
        struct ufs_dev_cmd dev_cmd;
+       /* Do not reuse UTP descriptors after a request failed to quiesce. */
+       bool io_failed;
 };
 
 static inline int ufshcd_ops_init(struct ufs_hba *hba)
-- 
2.50.1 (Apple Git-155)

Reply via email to