[PATCH v3 2/2] scsi: ufs: core: Retry the full device init on fatal link loss

Shawn Lin shawn.lin at rock-chips.com
Sun Nov 1 18:21:54 PST 2026


On an RK3572 board the UFS device is sometimes reported dead during the
initialization at boot: DEVICE_FATAL_ERROR | UIC_LINK_LOST is latched and
the following UIC query fails with -EIO, leaving the device unusable.

Recover by resetting the device and the controller and redoing the whole
initialization sequence, up to three times with a 100 ms settle delay in
between. Doing this in ufshcd_init() rather than in the platform driver's
probe keeps the interrupt, clock and regulator resources acquired only
once, and the UFS device is already reachable through hba->priv at that
point.

Each retry stops the controller first, so that no interrupt can interfere
with the cleanup, then stops a pending error handler and clears the error
state latched by the failed attempt. The controller reset clears the MCQ
mode, so it is restored after the controller re-enable. The hwmon device
and the exception event masks of the failed attempt are dropped as well,
since the device reset wiped their state on the device side.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

Changes in v3:
- Stop the controller before draining the error handler and clearing
  the error state, so that no interrupt can re-latch errors or
  schedule the error handler during the cleanup(sashiko)
- Remove the hwmon device and reset the exception event masks
  between retries, since the device reset wipes their state on the
  device side; also remove the hwmon device on the init failure
  path(sashiko)
- Only restore the MCQ mode after the controller re-enable and let
  the next attempt configure the queues(sashiko)
- Take the address of the by-value host_lock for
  spin_lock_irqsave()(sashiko)

Changes in v2:
- Move the initialization sequence into a new function and retry by
  looping over its call site instead of adding more gotos to
  ufshcd_init()(Bart)
- Stop a pending error handler and clear the error state latched by the
  failed attempt, so that the retry starts from a clean state(sashiko)
- Reconfigure and re-enable MCQ after the controller re-enable, since
  the HCE toggle clears the MCQ mode setting(sashiko)

 drivers/ufs/core/ufshcd.c | 60 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 8a8083a8f024..12ada46d2b7e 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -11476,8 +11476,65 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
 
 	/* Initialize hba, detect and initialize UFS device */
 	ktime_t probe_start = ktime_get();
+	int retries = 3;
 
-	err = ufshcd_full_device_init(hba);
+	while (retries--) {
+		unsigned long flags;
+
+		err = ufshcd_full_device_init(hba);
+		if (!err)
+			break;
+
+		if (!retries)
+			break;
+
+		dev_warn(hba->dev, "UFS init failed %d, full retry (%d left)\n",
+			 err, retries);
+
+		/*
+		 * The failed attempt may have latched fatal errors and
+		 * scheduled the error handler. Stop the controller first
+		 * so that no interrupt can interfere with the cleanup,
+		 * then drain the error handler and start the retry from a
+		 * clean state.
+		 */
+		ufshcd_hba_stop(hba);
+		cancel_work_sync(&hba->eh_work);
+		spin_lock_irqsave(&hba->host->host_lock, flags);
+		hba->saved_err = 0;
+		hba->saved_uic_err = 0;
+		hba->force_reset = false;
+		hba->ufshcd_state = UFSHCD_STATE_RESET;
+		spin_unlock_irqrestore(&hba->host->host_lock, flags);
+
+		ufshcd_device_reset(hba);
+		ufs_put_device_desc(hba);
+
+		/*
+		 * The device reset cleared the exception event control of
+		 * the device: drop the hwmon device of the previous attempt
+		 * and reset the masks, so that the next attempt registers a
+		 * fresh hwmon device and re-enables the exception events.
+		 */
+		mutex_lock(&hba->ee_ctrl_mutex);
+		hba->ee_ctrl_mask = 0;
+		hba->ee_drv_mask = 0;
+		mutex_unlock(&hba->ee_ctrl_mutex);
+		ufs_hwmon_remove(hba);
+
+		if (ufshcd_hba_enable(hba)) {
+			dev_err(hba->dev, "Host controller enable failed\n");
+			ufshcd_print_evt_hist(hba);
+			ufshcd_print_host_state(hba);
+			goto out_release;
+		}
+
+		/* The controller reset cleared the MCQ mode: restore it. */
+		if (hba->mcq_enabled)
+			ufshcd_mcq_enable(hba);
+
+		msleep(100);
+	}
 
 	ufshcd_process_probe_result(hba, probe_start, err);
 	if (err)
@@ -11493,6 +11550,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
 	return 0;
 
 out_release:
+	ufs_hwmon_remove(hba);
 	ufshcd_release(hba);
 out_disable:
 	hba->is_irq_enabled = false;
-- 
2.25.1




More information about the Linux-rockchip mailing list