--- /dev/null
+From fda1e0af7c28f96d4f33e57cf51565b0e9c14e63 Mon Sep 17 00:00:00 2001
+Date: Fri, 31 Oct 2025 16:58:23 +0100
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: Add command queue support for rockchip
+ SOCs
+
+This adds CQE support for the Rockchip RK3588 and RK3576 platform. To
+be functional, the eMMC device-tree node must have a 'supports-cqe;'
+flag property.
+
+As the RK3576 device-tree has been upstreamed with the 'supports-cqe;'
+property set by default, the kernel already tried to use CQE, which
+results in system hang during suspend. This fixes the issue.
+
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 93 ++++++++++++++++++++++++++++-
+ 1 file changed, 90 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -24,6 +24,7 @@
+
+ #include "sdhci-pltfm.h"
+ #include "cqhci.h"
++#include "sdhci-cqhci.h"
+
+ #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
+
+@@ -82,6 +83,8 @@
+ #define DWCMSHC_EMMC_DLL_TXCLK 0x808
+ #define DWCMSHC_EMMC_DLL_STRBIN 0x80c
+ #define DECMSHC_EMMC_DLL_CMDOUT 0x810
++#define DECMSHC_EMMC_MISC_CON 0x81C
++#define MISC_INTCLK_EN BIT(1)
+ #define DWCMSHC_EMMC_DLL_STATUS0 0x840
+ #define DWCMSHC_EMMC_DLL_START BIT(0)
+ #define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
+@@ -234,6 +237,7 @@ struct dwcmshc_priv {
+
+ struct dwcmshc_pltfm_data {
+ const struct sdhci_pltfm_data pdata;
++ const struct cqhci_host_ops *cqhci_host_ops;
+ int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
+ void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
+ };
+@@ -603,6 +607,68 @@ static void dwcmshc_cqhci_dumpregs(struc
+ sdhci_dumpregs(mmc_priv(mmc));
+ }
+
++static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
++{
++ struct sdhci_host *host = mmc_priv(mmc);
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
++ u32 reg;
++
++ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
++ reg |= CQHCI_ENABLE;
++ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
++}
++
++static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
++{
++ struct sdhci_host *host = mmc_priv(mmc);
++ u32 reg;
++
++ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
++ while (reg & SDHCI_DATA_AVAILABLE) {
++ sdhci_readl(host, SDHCI_BUFFER);
++ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
++ }
++
++ sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
++
++ sdhci_cqe_enable(mmc);
++}
++
++static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
++{
++ struct sdhci_host *host = mmc_priv(mmc);
++ unsigned long flags;
++ u32 ctrl;
++
++ /*
++ * During CQE command transfers, command complete bit gets latched.
++ * So s/w should clear command complete interrupt status when CQE is
++ * either halted or disabled. Otherwise unexpected SDCHI legacy
++ * interrupt gets triggered when CQE is halted/disabled.
++ */
++ spin_lock_irqsave(&host->lock, flags);
++ ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
++ ctrl |= SDHCI_INT_RESPONSE;
++ sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
++ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
++ spin_unlock_irqrestore(&host->lock, flags);
++
++ sdhci_cqe_disable(mmc, recovery);
++}
++
++static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
++{
++ struct sdhci_host *host = mmc_priv(mmc);
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
++ u32 ctrl;
++
++ ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
++ ctrl &= ~CQHCI_ENABLE;
++ sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
++}
++
+ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
+ {
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+@@ -721,6 +787,10 @@ static void rk35xx_sdhci_reset(struct sd
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ struct rk35xx_priv *priv = dwc_priv->priv;
++ u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
++
++ if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
++ cqhci_deactivate(host->mmc);
+
+ if (mask & SDHCI_RESET_ALL && priv->reset) {
+ reset_control_assert(priv->reset);
+@@ -729,6 +799,9 @@ static void rk35xx_sdhci_reset(struct sd
+ }
+
+ sdhci_reset(host, mask);
++
++ /* Enable INTERNAL CLOCK */
++ sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
+ }
+
+ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
+@@ -1230,6 +1303,15 @@ static const struct dwcmshc_pltfm_data s
+ };
+ #endif
+
++static const struct cqhci_host_ops rk35xx_cqhci_ops = {
++ .pre_enable = rk35xx_sdhci_cqe_pre_enable,
++ .enable = rk35xx_sdhci_cqe_enable,
++ .disable = rk35xx_sdhci_cqe_disable,
++ .post_disable = rk35xx_sdhci_cqe_post_disable,
++ .dumpregs = dwcmshc_cqhci_dumpregs,
++ .set_tran_desc = dwcmshc_set_tran_desc,
++};
++
+ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+@@ -1238,6 +1320,7 @@ static const struct dwcmshc_pltfm_data s
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
++ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk35xx_postinit,
+ };
+@@ -1287,7 +1370,8 @@ static const struct cqhci_host_ops dwcms
+ .set_tran_desc = dwcmshc_set_tran_desc,
+ };
+
+-static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
++static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
++ const struct dwcmshc_pltfm_data *pltfm_data)
+ {
+ struct cqhci_host *cq_host;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+@@ -1317,7 +1401,10 @@ static void dwcmshc_cqhci_init(struct sd
+ }
+
+ cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
+- cq_host->ops = &dwcmshc_cqhci_ops;
++ if (pltfm_data->cqhci_host_ops)
++ cq_host->ops = pltfm_data->cqhci_host_ops;
++ else
++ cq_host->ops = &dwcmshc_cqhci_ops;
+
+ /* Enable using of 128-bit task descriptors */
+ dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
+@@ -1486,7 +1573,7 @@ static int dwcmshc_probe(struct platform
+ priv->vendor_specific_area2 =
+ sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
+
+- dwcmshc_cqhci_init(host, pdev);
++ dwcmshc_cqhci_init(host, pdev, pltfm_data);
+ }
+
+ if (pltfm_data->postinit)