[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