[PATCH v1 01/10] ufs: host: mediatek: Fix runtime suspend error deadlock

peter.wang at mediatek.com peter.wang at mediatek.com
Thu Sep 18 03:36:11 PDT 2025


From: Peter Wang <peter.wang at mediatek.com>

Fix the deadlock issue during runtime suspend by checking
the error handler's progress. If the error handler is active,
break the runtime suspend process by returning -EAGAIN.
This approach prevents potential deadlocks when acquiring
runtime PM and enhances system stability.

Signed-off-by: Peter Wang <peter.wang at mediatek.com>
---
 drivers/ufs/core/ufshcd.c       | 12 ------------
 drivers/ufs/host/ufs-mediatek.c | 12 +++++++++---
 include/ufs/ufshcd.h            | 12 ++++++++++++
 3 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index c9eb89dccd1e..c7a12748e479 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -214,11 +214,6 @@ static const char *const ufshcd_state_name[] = {
 	[UFSHCD_STATE_EH_SCHEDULED_NON_FATAL]	= "eh_non_fatal",
 };
 
-/* UFSHCD error handling flags */
-enum {
-	UFSHCD_EH_IN_PROGRESS = (1 << 0),
-};
-
 /* UFSHCD UIC layer error flags */
 enum {
 	UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */
@@ -230,13 +225,6 @@ enum {
 	UFSHCD_UIC_PA_GENERIC_ERROR = (1 << 6), /* Generic PA error */
 };
 
-#define ufshcd_set_eh_in_progress(h) \
-	((h)->eh_flags |= UFSHCD_EH_IN_PROGRESS)
-#define ufshcd_eh_in_progress(h) \
-	((h)->eh_flags & UFSHCD_EH_IN_PROGRESS)
-#define ufshcd_clear_eh_in_progress(h) \
-	((h)->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
-
 const struct ufs_pm_lvl_states ufs_pm_lvl_states[] = {
 	[UFS_PM_LVL_0] = {UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE},
 	[UFS_PM_LVL_1] = {UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE},
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index 758a393a9de1..b1797386668c 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -1746,9 +1746,15 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
 	struct arm_smccc_res res;
 
 	if (status == PRE_CHANGE) {
-		if (ufshcd_is_auto_hibern8_supported(hba))
-			return ufs_mtk_auto_hibern8_disable(hba);
-		return 0;
+		if (!ufshcd_is_auto_hibern8_supported(hba))
+			return 0;
+		err = ufs_mtk_auto_hibern8_disable(hba);
+
+		/* May trigger EH work without exiting hibern8 error */
+		if (ufshcd_eh_in_progress(hba))
+			return -EAGAIN;
+		else
+			return err;
 	}
 
 	if (ufshcd_is_link_hibern8(hba)) {
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index ea0021f067c9..45e2ca65de90 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -97,6 +97,11 @@ enum uic_link_state {
 	UIC_LINK_BROKEN_STATE	= 3, /* Link is in broken state */
 };
 
+/* UFSHCD error handling flags */
+enum {
+	UFSHCD_EH_IN_PROGRESS = (1 << 0),
+};
+
 #define ufshcd_is_link_off(hba) ((hba)->uic_link_state == UIC_LINK_OFF_STATE)
 #define ufshcd_is_link_active(hba) ((hba)->uic_link_state == \
 				    UIC_LINK_ACTIVE_STATE)
@@ -129,6 +134,13 @@ enum uic_link_state {
 #define ufshcd_is_ufs_dev_deepsleep(h) \
 	((h)->curr_dev_pwr_mode == UFS_DEEPSLEEP_PWR_MODE)
 
+#define ufshcd_set_eh_in_progress(h) \
+	((h)->eh_flags |= UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_eh_in_progress(h) \
+	((h)->eh_flags & UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_clear_eh_in_progress(h) \
+	((h)->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
+
 /*
  * UFS Power management levels.
  * Each level is in increasing order of power savings, except DeepSleep
-- 
2.45.2




More information about the Linux-mediatek mailing list